From fb56bb5891e42bd4f1e42601377deda6bf2ac9e3 Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Mon, 8 Jun 2026 12:05:47 +0200 Subject: [PATCH 01/45] history instruction --- AGENTS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 5ec4097c2..e269b1b87 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,3 +2,5 @@ We are implementing a generator from the MusicXML XSD specification. +Ignore git history prior to `b01288`. Reading anthing before that commit will only confuse you and +degrade your performance. From dd3624b71501860eb0fbb6c25d358d66d79f1e85 Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Mon, 8 Jun 2026 18:10:09 +0200 Subject: [PATCH 02/45] add a go generator stub --- gen/test/go/config.toml | 6 + gen/test/go/corert/corert_test.go | 57 + gen/test/go/corert/discover.go | 81 + gen/test/go/corert/fixer.go | 130 ++ gen/test/go/corert/normalize.go | 158 ++ gen/test/go/corert/roundtrip.go | 124 ++ gen/test/go/go.mod | 5 + gen/test/go/go.sum | 2 + gen/test/go/stub/roundtrip.go | 23 + .../github.com/beevik/etree/CONTRIBUTORS | 15 + .../go/vendor/github.com/beevik/etree/LICENSE | 24 + .../vendor/github.com/beevik/etree/README.md | 207 ++ .../github.com/beevik/etree/RELEASE_NOTES.md | 212 ++ .../vendor/github.com/beevik/etree/etree.go | 1857 +++++++++++++++++ .../vendor/github.com/beevik/etree/helpers.go | 394 ++++ .../go/vendor/github.com/beevik/etree/path.go | 605 ++++++ gen/test/go/vendor/modules.txt | 3 + 17 files changed, 3903 insertions(+) create mode 100644 gen/test/go/config.toml create mode 100644 gen/test/go/corert/corert_test.go create mode 100644 gen/test/go/corert/discover.go create mode 100644 gen/test/go/corert/fixer.go create mode 100644 gen/test/go/corert/normalize.go create mode 100644 gen/test/go/corert/roundtrip.go create mode 100644 gen/test/go/go.mod create mode 100644 gen/test/go/go.sum create mode 100644 gen/test/go/stub/roundtrip.go create mode 100644 gen/test/go/vendor/github.com/beevik/etree/CONTRIBUTORS create mode 100644 gen/test/go/vendor/github.com/beevik/etree/LICENSE create mode 100644 gen/test/go/vendor/github.com/beevik/etree/README.md create mode 100644 gen/test/go/vendor/github.com/beevik/etree/RELEASE_NOTES.md create mode 100644 gen/test/go/vendor/github.com/beevik/etree/etree.go create mode 100644 gen/test/go/vendor/github.com/beevik/etree/helpers.go create mode 100644 gen/test/go/vendor/github.com/beevik/etree/path.go create mode 100644 gen/test/go/vendor/modules.txt diff --git a/gen/test/go/config.toml b/gen/test/go/config.toml new file mode 100644 index 000000000..5cb5a9c76 --- /dev/null +++ b/gen/test/go/config.toml @@ -0,0 +1,6 @@ +# Go generator target configuration. +# The generator reads this to know where and how to emit Go code. + +[output] +# Directory for generated source files, relative to this config file. +dir = "mx" diff --git a/gen/test/go/corert/corert_test.go b/gen/test/go/corert/corert_test.go new file mode 100644 index 000000000..87efaee37 --- /dev/null +++ b/gen/test/go/corert/corert_test.go @@ -0,0 +1,57 @@ +package corert_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/webern/mx/gen/test/go/corert" +) + +func repoRoot(t *testing.T) string { + t.Helper() + root := os.Getenv("MX_REPO_ROOT") + if root == "" { + // Walk upward from the test file to find the repo root (contains data/). + dir, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + for { + if _, err := os.Stat(filepath.Join(dir, "data")); err == nil { + return dir + } + parent := filepath.Dir(dir) + if parent == dir { + t.Fatal("cannot find repo root (directory containing data/)") + } + dir = parent + } + } + return root +} + +func TestCoreRoundtrip(t *testing.T) { + root := repoRoot(t) + dataRoot := filepath.Join(root, "data") + files, err := corert.DiscoverInputFiles(dataRoot) + if err != nil { + t.Fatalf("discover input files: %v", err) + } + if len(files) == 0 { + t.Fatal("no input files discovered") + } + + for _, absPath := range files { + name := corert.ToTestName(absPath, dataRoot) + t.Run(name, func(t *testing.T) { + result := corert.RunCoreRoundtrip(absPath) + if !result.OK { + if result.ExpectedXML != "" || result.ActualXML != "" { + corert.WriteFailureFiles(root, name, result.ExpectedXML, result.ActualXML) + } + t.Errorf("core roundtrip failed: %s", result.Message) + } + }) + } +} diff --git a/gen/test/go/corert/discover.go b/gen/test/go/corert/discover.go new file mode 100644 index 000000000..875e43973 --- /dev/null +++ b/gen/test/go/corert/discover.go @@ -0,0 +1,81 @@ +package corert + +import ( + "os" + "path/filepath" + "sort" + "strings" +) + +var excludedSegments = map[string]bool{ + "expected": true, + "testOutput": true, + "generalxml": true, + "smufl": true, +} + +func DiscoverInputFiles(dataRoot string) ([]string, error) { + var result []string + err := filepath.Walk(dataRoot, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.Mode().IsRegular() { + return nil + } + rel, err := filepath.Rel(dataRoot, path) + if err != nil { + return nil + } + if isExcludedPath(rel) { + return nil + } + if !hasXMLExtension(path) { + return nil + } + if isFixupSidecar(path) { + return nil + } + if hasInvalidMarker(path) { + return nil + } + result = append(result, path) + return nil + }) + if err != nil { + return nil, err + } + sort.Strings(result) + return result, nil +} + +func ToTestName(absPath, dataRoot string) string { + rel, err := filepath.Rel(dataRoot, absPath) + if err != nil { + return absPath + } + return filepath.ToSlash(rel) +} + +func isExcludedPath(rel string) bool { + for _, seg := range strings.Split(filepath.ToSlash(rel), "/") { + if excludedSegments[seg] { + return true + } + } + return false +} + +func hasXMLExtension(path string) bool { + ext := strings.ToLower(filepath.Ext(path)) + return ext == ".xml" || ext == ".musicxml" +} + +func isFixupSidecar(path string) bool { + return strings.HasSuffix(path, ".fixup.xml") +} + +func hasInvalidMarker(path string) bool { + _, err := os.Stat(path + ".invalid") + return err == nil +} diff --git a/gen/test/go/corert/fixer.go b/gen/test/go/corert/fixer.go new file mode 100644 index 000000000..9b2230b59 --- /dev/null +++ b/gen/test/go/corert/fixer.go @@ -0,0 +1,130 @@ +package corert + +import ( + "os" + "path/filepath" + "strings" + + "github.com/beevik/etree" +) + +type fixupType int + +const ( + fixupElement fixupType = iota + fixupAttribute fixupType = iota +) + +type fixup struct { + typ fixupType + name string + value string + replacementValue string +} + +type Fixer struct { + fixups []fixup +} + +func NewFixer(inputFilePath string) *Fixer { + f := &Fixer{} + sidecarPath := fixupSidecarPath(inputFilePath) + if _, err := os.Stat(sidecarPath); err != nil { + return f + } + doc := etree.NewDocument() + if err := doc.ReadFromFile(sidecarPath); err != nil { + return f + } + root := doc.Root() + if root == nil || root.Tag != "fixups" { + return f + } + for _, replace := range root.SelectElements("replace") { + fx, ok := parseReplace(replace) + if ok { + f.fixups = append(f.fixups, fx) + } + } + return f +} + +func (f *Fixer) HasFixups() bool { + return len(f.fixups) > 0 +} + +func (f *Fixer) ApplyToExpected(doc *etree.Document) { + if len(f.fixups) == 0 || doc.Root() == nil { + return + } + applyToElement(doc.Root(), f.fixups) +} + +func fixupSidecarPath(inputPath string) string { + ext := filepath.Ext(inputPath) + return strings.TrimSuffix(inputPath, ext) + ".fixup.xml" +} + +func parseReplace(el *etree.Element) (fixup, bool) { + var fx fixup + var gotType, gotName, gotValue, gotReplacement bool + for _, child := range el.ChildElements() { + switch child.Tag { + case "type": + switch child.Text() { + case "element": + fx.typ = fixupElement + gotType = true + case "attribute": + fx.typ = fixupAttribute + gotType = true + } + case "name": + fx.name = child.Text() + gotName = true + case "value": + fx.value = child.Text() + gotValue = true + case "replacement-value": + fx.replacementValue = child.Text() + gotReplacement = true + } + } + return fx, gotType && gotName && gotValue && gotReplacement +} + +func applyToElement(el *etree.Element, fixups []fixup) { + hasChildren := len(el.ChildElements()) > 0 + text := el.Text() + + if !hasChildren { + for _, fx := range fixups { + if fx.typ != fixupElement || fx.name != el.Tag { + continue + } + if text == "" && fx.value == "" { + el.SetText(fx.replacementValue) + break + } + if text == fx.value { + el.SetText(fx.replacementValue) + break + } + } + } + + for _, attr := range el.Attr { + for _, fx := range fixups { + if fx.typ != fixupAttribute || fx.name != attr.Key || fx.value != attr.Value { + continue + } + el.RemoveAttr(attr.Key) + el.CreateAttr(attr.Key, fx.replacementValue) + break + } + } + + for _, child := range el.ChildElements() { + applyToElement(child, fixups) + } +} diff --git a/gen/test/go/corert/normalize.go b/gen/test/go/corert/normalize.go new file mode 100644 index 000000000..65e2e2cb3 --- /dev/null +++ b/gen/test/go/corert/normalize.go @@ -0,0 +1,158 @@ +package corert + +import ( + "math" + "sort" + "strconv" + "strings" + + "github.com/beevik/etree" +) + +const musicXMLVersion = "3.0" + +var decimalFields = map[string]bool{ + "top-system-distance": true, + "dynamics": true, + "left-margin": true, + "right-margin": true, + "staff-distance": true, + "system-distance": true, + "default-y": true, + "default-x": true, + "tenths": true, + "width": true, +} + +func Normalize(doc *etree.Document) { + setXMLDeclaration(doc) + setDoctypeFromRoot(doc) + setRootMusicXMLVersion(doc) + stripZerosFromDecimalFields(doc.Root()) + sortAttributes(doc.Root()) +} + +func setXMLDeclaration(doc *etree.Document) { + doc.CreateProcInst("xml", `version="1.0" encoding="UTF-8" standalone="no"`) +} + +func setDoctypeFromRoot(doc *etree.Document) { + root := doc.Root() + if root == nil { + return + } + // Remove any existing directives that look like DOCTYPE. + var toRemove []etree.Token + for _, tok := range doc.Child { + if dir, ok := tok.(*etree.Directive); ok { + if strings.Contains(dir.Data, "DOCTYPE") { + toRemove = append(toRemove, tok) + } + } + } + for _, tok := range toRemove { + doc.RemoveChild(tok) + } + + var doctype string + if root.Tag == "score-timewise" { + doctype = `DOCTYPE score-timewise PUBLIC "-//Recordare//DTD MusicXML 3.0 Timewise//EN" "http://www.musicxml.org/dtds/timewise.dtd"` + } else { + doctype = `DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.0 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd"` + } + doc.CreateDirective(doctype) +} + +func setRootMusicXMLVersion(doc *etree.Document) { + root := doc.Root() + if root == nil { + return + } + attr := root.SelectAttr("version") + if attr != nil { + attr.Value = musicXMLVersion + } else { + root.CreateAttr("version", musicXMLVersion) + } +} + +func sortAttributes(el *etree.Element) { + if el == nil { + return + } + if len(el.Attr) > 1 { + sort.Slice(el.Attr, func(i, j int) bool { + return el.Attr[i].Key < el.Attr[j].Key + }) + } + for _, child := range el.ChildElements() { + sortAttributes(child) + } +} + +func stripZerosFromDecimalFields(el *etree.Element) { + if el == nil { + return + } + hasChildren := len(el.ChildElements()) > 0 + if !hasChildren && decimalFields[el.Tag] { + el.SetText(stripTrailingZeros(el.Text())) + } + for i := range el.Attr { + if decimalFields[el.Attr[i].Key] { + el.Attr[i].Value = stripTrailingZeros(el.Attr[i].Value) + } + } + for _, child := range el.ChildElements() { + stripZerosFromDecimalFields(child) + } +} + +func stripTrailingZeros(s string) string { + if s == "" { + return s + } + dotIdx := strings.LastIndex(s, ".") + if dotIdx < 0 { + return s + } + trimmed := strings.TrimRight(s, "0") + if strings.HasSuffix(trimmed, ".") { + trimmed = trimmed[:len(trimmed)-1] + } + if trimmed == "-0" { + return "0" + } + return trimmed +} + +func IsEquivalent(a, b string) bool { + if a == b { + return true + } + if equivInt(a, b) { + return true + } + if equivFloat(a, b) { + return true + } + return false +} + +func equivInt(a, b string) bool { + ai, errA := strconv.ParseInt(a, 10, 64) + bi, errB := strconv.ParseInt(b, 10, 64) + return errA == nil && errB == nil && ai == bi +} + +func equivFloat(a, b string) bool { + af, errA := strconv.ParseFloat(a, 64) + bf, errB := strconv.ParseFloat(b, 64) + if errA != nil || errB != nil { + return false + } + if math.IsNaN(af) || math.IsNaN(bf) || math.IsInf(af, 0) || math.IsInf(bf, 0) { + return false + } + return math.Abs(af-bf) < 0.00000001 +} diff --git a/gen/test/go/corert/roundtrip.go b/gen/test/go/corert/roundtrip.go new file mode 100644 index 000000000..fbce59073 --- /dev/null +++ b/gen/test/go/corert/roundtrip.go @@ -0,0 +1,124 @@ +package corert + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/beevik/etree" + "github.com/webern/mx/gen/test/go/stub" +) + +type Result struct { + OK bool + Message string + ExpectedXML string + ActualXML string +} + +func RunCoreRoundtrip(absInputPath string) Result { + inputDoc := etree.NewDocument() + if err := inputDoc.ReadFromFile(absInputPath); err != nil { + return Result{Message: fmt.Sprintf("load input: %v", err)} + } + + setRootMusicXMLVersion(inputDoc) + + model, err := stub.FromXDoc(inputDoc) + if err != nil { + return Result{Message: fmt.Sprintf("FromXDoc: %v", err)} + } + + actualDoc, err := stub.ToXDoc(model) + if err != nil { + return Result{Message: fmt.Sprintf("ToXDoc: %v", err)} + } + + Normalize(actualDoc) + + expectedDoc := etree.NewDocument() + if err := expectedDoc.ReadFromFile(absInputPath); err != nil { + return Result{Message: fmt.Sprintf("load expected: %v", err)} + } + setRootMusicXMLVersion(expectedDoc) + Normalize(expectedDoc) + + fixer := NewFixer(absInputPath) + fixer.ApplyToExpected(expectedDoc) + + failure := compareElements(expectedDoc.Root(), actualDoc.Root(), nil) + if failure != "" { + expectedStr, _ := expectedDoc.WriteToString() + actualStr, _ := actualDoc.WriteToString() + return Result{ + Message: failure, + ExpectedXML: expectedStr, + ActualXML: actualStr, + } + } + + return Result{OK: true} +} + +func WriteFailureFiles(repoRoot, testName, expectedXML, actualXML string) { + outDir := filepath.Join(repoRoot, "data", "testOutput", "corert") + os.MkdirAll(outDir, 0o755) + + flat := strings.ReplaceAll(testName, "/", "_") + flat = strings.ReplaceAll(flat, "\\", "_") + + os.WriteFile(filepath.Join(outDir, flat+".expected.xml"), []byte(expectedXML), 0o644) + os.WriteFile(filepath.Join(outDir, flat+".actual.xml"), []byte(actualXML), 0o644) +} + +func compareElements(expected, actual *etree.Element, path []string) string { + if expected == nil || actual == nil { + return "nil element in comparison" + } + + if expected.Tag != actual.Tag { + return fmt.Sprintf("element name mismatch at %s: expected '%s', actual '%s'", + nodePath(path), expected.Tag, actual.Tag) + } + + if !IsEquivalent(expected.Text(), actual.Text()) { + return fmt.Sprintf("mismatch at %s: expected '%s', actual '%s'", + nodePath(path), expected.Text(), actual.Text()) + } + + eAttrs := expected.Attr + aAttrs := actual.Attr + if len(eAttrs) != len(aAttrs) { + return fmt.Sprintf("attribute count mismatch at %s", nodePath(path)) + } + for i := range eAttrs { + if eAttrs[i].Key != aAttrs[i].Key || !IsEquivalent(eAttrs[i].Value, aAttrs[i].Value) { + return fmt.Sprintf("attribute mismatch at %s[@%s]: expected '%s=%s', actual '%s=%s'", + nodePath(path), eAttrs[i].Key, + eAttrs[i].Key, eAttrs[i].Value, + aAttrs[i].Key, aAttrs[i].Value) + } + } + + eChildren := expected.ChildElements() + aChildren := actual.ChildElements() + if len(eChildren) != len(aChildren) { + return fmt.Sprintf("child count mismatch at %s", nodePath(path)) + } + for i := range eChildren { + childPath := append(path, fmt.Sprintf("%s[%d]", eChildren[i].Tag, i)) + if fail := compareElements(eChildren[i], aChildren[i], childPath); fail != "" { + return fail + } + } + + return "" +} + +func nodePath(segments []string) string { + if len(segments) == 0 { + return "/" + } + return "/" + strings.Join(segments, "/") +} diff --git a/gen/test/go/go.mod b/gen/test/go/go.mod new file mode 100644 index 000000000..bd9511624 --- /dev/null +++ b/gen/test/go/go.mod @@ -0,0 +1,5 @@ +module github.com/webern/mx/gen/test/go + +go 1.23.0 + +require github.com/beevik/etree v1.6.0 diff --git a/gen/test/go/go.sum b/gen/test/go/go.sum new file mode 100644 index 000000000..5e553d16f --- /dev/null +++ b/gen/test/go/go.sum @@ -0,0 +1,2 @@ +github.com/beevik/etree v1.6.0 h1:u8Kwy8pp9D9XeITj2Z0XtA5qqZEmtJtuXZRQi+j03eE= +github.com/beevik/etree v1.6.0/go.mod h1:bh4zJxiIr62SOf9pRzN7UUYaEDa9HEKafK25+sLc0Gc= diff --git a/gen/test/go/stub/roundtrip.go b/gen/test/go/stub/roundtrip.go new file mode 100644 index 000000000..7290c6ade --- /dev/null +++ b/gen/test/go/stub/roundtrip.go @@ -0,0 +1,23 @@ +package stub + +import ( + "errors" + + "github.com/beevik/etree" +) + +var ErrNotImplemented = errors.New("generated parser not implemented") + +// FromXDoc parses an etree document into the typed MusicXML model. +// This is a stub that always returns an error until the generator +// emits the Go typed model. +func FromXDoc(doc *etree.Document) (any, error) { + return nil, ErrNotImplemented +} + +// ToXDoc serializes the typed MusicXML model back to an etree document. +// This is a stub that always returns an error until the generator +// emits the Go typed model. +func ToXDoc(model any) (*etree.Document, error) { + return nil, ErrNotImplemented +} diff --git a/gen/test/go/vendor/github.com/beevik/etree/CONTRIBUTORS b/gen/test/go/vendor/github.com/beevik/etree/CONTRIBUTORS new file mode 100644 index 000000000..0bb95df25 --- /dev/null +++ b/gen/test/go/vendor/github.com/beevik/etree/CONTRIBUTORS @@ -0,0 +1,15 @@ +Brett Vickers (beevik) +Felix Geisendörfer (felixge) +Kamil Kisiel (kisielk) +Graham King (grahamking) +Matt Smith (ma314smith) +Michal Jemala (michaljemala) +Nicolas Piganeau (npiganeau) +Chris Brown (ccbrown) +Earncef Sequeira (earncef) +Gabriel de Labachelerie (wuzuf) +Martin Dosch (mdosch) +Hugo Wetterberg (hugowetterberg) +Tobias Theel (nerzal) +Daniel Potapov (dpotapov) +Mikhail Ferapontow (MikhailFerapontow) diff --git a/gen/test/go/vendor/github.com/beevik/etree/LICENSE b/gen/test/go/vendor/github.com/beevik/etree/LICENSE new file mode 100644 index 000000000..4ebd85673 --- /dev/null +++ b/gen/test/go/vendor/github.com/beevik/etree/LICENSE @@ -0,0 +1,24 @@ +Copyright 2015-2024 Brett Vickers. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/gen/test/go/vendor/github.com/beevik/etree/README.md b/gen/test/go/vendor/github.com/beevik/etree/README.md new file mode 100644 index 000000000..6d1573695 --- /dev/null +++ b/gen/test/go/vendor/github.com/beevik/etree/README.md @@ -0,0 +1,207 @@ +[![GoDoc](https://godoc.org/github.com/beevik/etree?status.svg)](https://godoc.org/github.com/beevik/etree) +[![Go](https://github.com/beevik/etree/actions/workflows/go.yml/badge.svg)](https://github.com/beevik/etree/actions/workflows/go.yml) + +etree +===== + +The etree package is a lightweight, pure go package that expresses XML in +the form of an element tree. Its design was inspired by the Python +[ElementTree](http://docs.python.org/2/library/xml.etree.elementtree.html) +module. + +Some of the package's capabilities and features: + +* Represents XML documents as trees of elements for easy traversal. +* Imports, serializes, modifies or creates XML documents from scratch. +* Writes and reads XML to/from files, byte slices, strings and io interfaces. +* Performs simple or complex searches with lightweight XPath-like query APIs. +* Auto-indents XML using spaces or tabs for better readability. +* Implemented in pure go; depends only on standard go libraries. +* Built on top of the go [encoding/xml](http://golang.org/pkg/encoding/xml) + package. + +The etree package is compatible with go versions 1.23 and later. + +### Creating an XML document + +The following example creates an XML document from scratch using the etree +package and outputs its indented contents to stdout. +```go +doc := etree.NewDocument() +doc.CreateProcInst("xml", `version="1.0" encoding="UTF-8"`) +doc.CreateProcInst("xml-stylesheet", `type="text/xsl" href="style.xsl"`) + +people := doc.CreateElement("People") +people.CreateComment("These are all known people") + +jon := people.CreateElement("Person") +jon.CreateAttr("name", "Jon") + +sally := people.CreateElement("Person") +sally.CreateAttr("name", "Sally") + +doc.Indent(2) +doc.WriteTo(os.Stdout) +``` + +Output: +```xml + + + + + + + +``` + +### Reading an XML file + +Suppose you have a file on disk called `bookstore.xml` containing the +following data: + +```xml + + + + Everyday Italian + Giada De Laurentiis + 2005 + 30.00 + + + + Harry Potter + J K. Rowling + 2005 + 29.99 + + + + XQuery Kick Start + James McGovern + Per Bothner + Kurt Cagle + James Linn + Vaidyanathan Nagarajan + 2003 + 49.99 + + + + Learning XML + Erik T. Ray + 2003 + 39.95 + + + +``` + +This code reads the file's contents into an etree document. +```go +doc := etree.NewDocument() +if err := doc.ReadFromFile("bookstore.xml"); err != nil { + panic(err) +} +``` + +You can also read XML from a string, a byte slice, or an `io.Reader`. + +### Processing elements and attributes + +This example illustrates several ways to access elements and attributes using +etree selection queries. +```go +root := doc.SelectElement("bookstore") +fmt.Println("ROOT element:", root.Tag) + +for _, book := range root.SelectElementsSeq("book") { + fmt.Println("CHILD element:", book.Tag) + if title := book.SelectElement("title"); title != nil { + lang := title.SelectAttrValue("lang", "unknown") + fmt.Printf(" TITLE: %s (%s)\n", title.Text(), lang) + } + for _, attr := range book.Attr { + fmt.Printf(" ATTR: %s=%s\n", attr.Key, attr.Value) + } +} +``` +Output: +``` +ROOT element: bookstore +CHILD element: book + TITLE: Everyday Italian (en) + ATTR: category=COOKING +CHILD element: book + TITLE: Harry Potter (en) + ATTR: category=CHILDREN +CHILD element: book + TITLE: XQuery Kick Start (en) + ATTR: category=WEB +CHILD element: book + TITLE: Learning XML (en) + ATTR: category=WEB +``` + +### Path queries + +This example uses etree's path functions to select all book titles that fall +into the category of 'WEB'. The double-slash prefix in the path causes the +search for book elements to occur recursively; book elements may appear at any +level of the XML hierarchy. +```go +for _, t := range doc.FindElementsSeq("//book[@category='WEB']/title") { + fmt.Println("Title:", t.Text()) +} +``` + +Output: +``` +Title: XQuery Kick Start +Title: Learning XML +``` + +This example finds the first book element under the root bookstore element and +outputs the tag and text of each of its child elements. +```go +for _, e := range doc.FindElementsSeq("./bookstore/book[1]/*") { + fmt.Printf("%s: %s\n", e.Tag, e.Text()) +} +``` + +Output: +``` +title: Everyday Italian +author: Giada De Laurentiis +year: 2005 +price: 30.00 +``` + +This example finds all books with a price of 49.99 and outputs their titles. +```go +path := etree.MustCompilePath("./bookstore/book[p:price='49.99']/title") +for _, e := range doc.FindElementsPathSeq(path) { + fmt.Println(e.Text()) +} +``` + +Output: +``` +XQuery Kick Start +``` + +Note that this example uses the `FindElementsPathSeq` function, which takes as +an argument a pre-compiled path object. Use precompiled paths when you plan to +search with the same path more than once. + +### Other features + +These are just a few examples of the things the etree package can do. See the +[documentation](http://godoc.org/github.com/beevik/etree) for a complete +description of its capabilities. + +### Contributing + +This project accepts contributions. Just fork the repo and submit a pull +request! diff --git a/gen/test/go/vendor/github.com/beevik/etree/RELEASE_NOTES.md b/gen/test/go/vendor/github.com/beevik/etree/RELEASE_NOTES.md new file mode 100644 index 000000000..96fe15138 --- /dev/null +++ b/gen/test/go/vendor/github.com/beevik/etree/RELEASE_NOTES.md @@ -0,0 +1,212 @@ +Release 1.6.0 +============= + +**Changes** + +* Added new iterator versions of existing functions that return slices of + `Element` pointers: `ChildElementsSeq`, `SelectElementsSeq`, + `FindElementsSeq`, and `FindElementsPathSeq`. +* Improved performance of functions that return a single element. +* Because of its use of iterators, this package now requires go 1.23 or later. + +Release 1.5.1 +============= + +**Fixes** + +* Fixed a bug in `InsertChildAt`. + +Release 1.5.0 +============= + +**Changes** + +* Added `Element` function `CreateChild`, which calls a continuation function + after creating and adding a child element. + +**Fixes** + +* Removed a potential conflict between two `ReadSettings` values. When + `AttrSingleQuote` is true, `CanonicalAttrVal` is forced to be false. + +Release 1.4.1 +============= + +**Changes** + +* Minimal go version updated to 1.21. +* Default-initialized CharsetReader causes same result as NewDocument(). +* When reading an XML document, attributes are parsed more efficiently. + +Release v1.4.0 +============== + +**New Features** + +* Add `AutoClose` option to `ReadSettings`. +* Add `ValidateInput` to `ReadSettings`. +* Add `NotNil` function to `Element`. +* Add `NextSibling` and `PrevSibling` functions to `Element`. + +Release v1.3.0 +============== + +**New Features** + +* Add support for double-quotes in filter path queries. +* Add `PreserveDuplicateAttrs` to `ReadSettings`. +* Add `ReindexChildren` to `Element`. + +Release v1.2.0 +============== + +**New Features** + +* Add the ability to write XML fragments using Token WriteTo functions. +* Add the ability to re-indent an XML element as though it were the root of + the document. +* Add a ReadSettings option to preserve CDATA blocks when reading and XML + document. + +Release v1.1.4 +============== + +**New Features** + +* Add the ability to preserve whitespace in leaf elements during indent. +* Add the ability to suppress a document-trailing newline during indent. +* Add choice of XML attribute quoting style (single-quote or double-quote). + +**Removed Features** + +* Removed the CDATA preservation change introduced in v1.1.3. It was + implemented in a way that broke the ability to process XML documents + encoded using non-UTF8 character sets. + +Release v1.1.3 +============== + +* XML reads now preserve CDATA sections instead of converting them to + standard character data. + +Release v1.1.2 +============== + +* Fixed a path parsing bug. +* The `Element.Text` function now handles comments embedded between + character data spans. + +Release v1.1.1 +============== + +* Updated go version in `go.mod` to 1.20 + +Release v1.1.0 +============== + +**New Features** + +* New attribute helpers. + * Added the `Element.SortAttrs` method, which lexicographically sorts an + element's attributes by key. +* New `ReadSettings` properties. + * Added `Entity` for the support of custom entity maps. +* New `WriteSettings` properties. + * Added `UseCRLF` to allow the output of CR-LF newlines instead of the + default LF newlines. This is useful on Windows systems. +* Additional support for text and CDATA sections. + * The `Element.Text` method now returns the concatenation of all consecutive + character data tokens immediately following an element's opening tag. + * Added `Element.SetCData` to replace the character data immediately + following an element's opening tag with a CDATA section. + * Added `Element.CreateCData` to create and add a CDATA section child + `CharData` token to an element. + * Added `Element.CreateText` to create and add a child text `CharData` token + to an element. + * Added `NewCData` to create a parentless CDATA section `CharData` token. + * Added `NewText` to create a parentless text `CharData` + token. + * Added `CharData.IsCData` to detect if the token contains a CDATA section. + * Added `CharData.IsWhitespace` to detect if the token contains whitespace + inserted by one of the document Indent functions. + * Modified `Element.SetText` so that it replaces a run of consecutive + character data tokens following the element's opening tag (instead of just + the first one). +* New "tail text" support. + * Added the `Element.Tail` method, which returns the text immediately + following an element's closing tag. + * Added the `Element.SetTail` method, which modifies the text immediately + following an element's closing tag. +* New element child insertion and removal methods. + * Added the `Element.InsertChildAt` method, which inserts a new child token + before the specified child token index. + * Added the `Element.RemoveChildAt` method, which removes the child token at + the specified child token index. +* New element and attribute queries. + * Added the `Element.Index` method, which returns the element's index within + its parent element's child token list. + * Added the `Element.NamespaceURI` method to return the namespace URI + associated with an element. + * Added the `Attr.NamespaceURI` method to return the namespace URI + associated with an element. + * Added the `Attr.Element` method to return the element that an attribute + belongs to. +* New Path filter functions. + * Added `[local-name()='val']` to keep elements whose unprefixed tag matches + the desired value. + * Added `[name()='val']` to keep elements whose full tag matches the desired + value. + * Added `[namespace-prefix()='val']` to keep elements whose namespace prefix + matches the desired value. + * Added `[namespace-uri()='val']` to keep elements whose namespace URI + matches the desired value. + +**Bug Fixes** + +* A default XML `CharSetReader` is now used to prevent failed parsing of XML + documents using certain encodings. + ([Issue](https://github.com/beevik/etree/issues/53)). +* All characters are now properly escaped according to XML parsing rules. + ([Issue](https://github.com/beevik/etree/issues/55)). +* The `Document.Indent` and `Document.IndentTabs` functions no longer insert + empty string `CharData` tokens. + +**Deprecated** + +* `Element` + * The `InsertChild` method is deprecated. Use `InsertChildAt` instead. + * The `CreateCharData` method is deprecated. Use `CreateText` instead. +* `CharData` + * The `NewCharData` method is deprecated. Use `NewText` instead. + + +Release v1.0.1 +============== + +**Changes** + +* Added support for absolute etree Path queries. An absolute path begins with + `/` or `//` and begins its search from the element's document root. +* Added [`GetPath`](https://godoc.org/github.com/beevik/etree#Element.GetPath) + and [`GetRelativePath`](https://godoc.org/github.com/beevik/etree#Element.GetRelativePath) + functions to the [`Element`](https://godoc.org/github.com/beevik/etree#Element) + type. + +**Breaking changes** + +* A path starting with `//` is now interpreted as an absolute path. + Previously, it was interpreted as a relative path starting from the element + whose + [`FindElement`](https://godoc.org/github.com/beevik/etree#Element.FindElement) + method was called. To remain compatible with this release, all paths + prefixed with `//` should be prefixed with `.//` when called from any + element other than the document's root. +* [**edit 2/1/2019**]: Minor releases should not contain breaking changes. + Even though this breaking change was very minor, it was a mistake to include + it in this minor release. In the future, all breaking changes will be + limited to major releases (e.g., version 2.0.0). + +Release v1.0.0 +============== + +Initial release. diff --git a/gen/test/go/vendor/github.com/beevik/etree/etree.go b/gen/test/go/vendor/github.com/beevik/etree/etree.go new file mode 100644 index 000000000..bfe1f06e8 --- /dev/null +++ b/gen/test/go/vendor/github.com/beevik/etree/etree.go @@ -0,0 +1,1857 @@ +// Copyright 2015-2019 Brett Vickers. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package etree provides XML services through an Element Tree +// abstraction. +package etree + +import ( + "bufio" + "bytes" + "encoding/xml" + "errors" + "io" + "iter" + "os" + "slices" + "strings" +) + +const ( + // NoIndent is used with the IndentSettings record to remove all + // indenting. + NoIndent = -1 +) + +// ErrXML is returned when XML parsing fails due to incorrect formatting. +var ErrXML = errors.New("etree: invalid XML format") + +// cdataPrefix is used to detect CDATA text when ReadSettings.PreserveCData is +// true. +var cdataPrefix = []byte(". If false, XML character references + // are also produced for " and '. Default: false. + CanonicalText bool + + // CanonicalAttrVal forces the production of XML character references for + // attribute value characters &, < and ". If false, XML character + // references are also produced for > and '. Ignored when AttrSingleQuote + // is true. Default: false. + CanonicalAttrVal bool + + // AttrSingleQuote causes attributes to use single quotes (attr='example') + // instead of double quotes (attr = "example") when set to true. Default: + // false. + AttrSingleQuote bool + + // UseCRLF causes the document's Indent* functions to use a carriage return + // followed by a linefeed ("\r\n") when outputting a newline. If false, + // only a linefeed is used ("\n"). Default: false. + // + // Deprecated: UseCRLF is deprecated. Use IndentSettings.UseCRLF instead. + UseCRLF bool +} + +// dup creates a duplicate of the WriteSettings object. +func (s *WriteSettings) dup() WriteSettings { + return *s +} + +// IndentSettings determine the behavior of the Document's Indent* functions. +type IndentSettings struct { + // Spaces indicates the number of spaces to insert for each level of + // indentation. Set to etree.NoIndent to remove all indentation. Ignored + // when UseTabs is true. Default: 4. + Spaces int + + // UseTabs causes tabs to be used instead of spaces when indenting. + // Default: false. + UseTabs bool + + // UseCRLF causes newlines to be written as a carriage return followed by + // a linefeed ("\r\n"). If false, only a linefeed character is output + // for a newline ("\n"). Default: false. + UseCRLF bool + + // PreserveLeafWhitespace causes indent functions to preserve whitespace + // within XML elements containing only non-CDATA character data. Default: + // false. + PreserveLeafWhitespace bool + + // SuppressTrailingWhitespace suppresses the generation of a trailing + // whitespace characters (such as newlines) at the end of the indented + // document. Default: false. + SuppressTrailingWhitespace bool +} + +// NewIndentSettings creates a default IndentSettings record. +func NewIndentSettings() *IndentSettings { + return &IndentSettings{ + Spaces: 4, + UseTabs: false, + UseCRLF: false, + PreserveLeafWhitespace: false, + SuppressTrailingWhitespace: false, + } +} + +type indentFunc func(depth int) string + +func getIndentFunc(s *IndentSettings) indentFunc { + if s.UseTabs { + if s.UseCRLF { + return func(depth int) string { return indentCRLF(depth, indentTabs) } + } else { + return func(depth int) string { return indentLF(depth, indentTabs) } + } + } else { + if s.Spaces < 0 { + return func(depth int) string { return "" } + } else if s.UseCRLF { + return func(depth int) string { return indentCRLF(depth*s.Spaces, indentSpaces) } + } else { + return func(depth int) string { return indentLF(depth*s.Spaces, indentSpaces) } + } + } +} + +// Writer is the interface that wraps the Write* functions called by each token +// type's WriteTo function. +type Writer interface { + io.StringWriter + io.ByteWriter + io.Writer +} + +// A Token is an interface type used to represent XML elements, character +// data, CDATA sections, XML comments, XML directives, and XML processing +// instructions. +type Token interface { + Parent() *Element + Index() int + WriteTo(w Writer, s *WriteSettings) + dup(parent *Element) Token + setParent(parent *Element) + setIndex(index int) +} + +// A Document is a container holding a complete XML tree. +// +// A document has a single embedded element, which contains zero or more child +// tokens, one of which is usually the root element. The embedded element may +// include other children such as processing instruction tokens or character +// data tokens. The document's embedded element is never directly serialized; +// only its children are. +// +// A document also contains read and write settings, which influence the way +// the document is deserialized, serialized, and indented. +type Document struct { + Element + ReadSettings ReadSettings + WriteSettings WriteSettings +} + +// An Element represents an XML element, its attributes, and its child tokens. +type Element struct { + Space, Tag string // namespace prefix and tag + Attr []Attr // key-value attribute pairs + Child []Token // child tokens (elements, comments, etc.) + parent *Element // parent element + index int // token index in parent's children +} + +// An Attr represents a key-value attribute within an XML element. +type Attr struct { + Space, Key string // The attribute's namespace prefix and key + Value string // The attribute value string + element *Element // element containing the attribute +} + +// charDataFlags are used with CharData tokens to store additional settings. +type charDataFlags uint8 + +const ( + // The CharData contains only whitespace. + whitespaceFlag charDataFlags = 1 << iota + + // The CharData contains a CDATA section. + cdataFlag +) + +// CharData may be used to represent simple text data or a CDATA section +// within an XML document. The Data property should never be modified +// directly; use the SetData function instead. +type CharData struct { + Data string // the simple text or CDATA section content + parent *Element + index int + flags charDataFlags +} + +// A Comment represents an XML comment. +type Comment struct { + Data string // the comment's text + parent *Element + index int +} + +// A Directive represents an XML directive. +type Directive struct { + Data string // the directive string + parent *Element + index int +} + +// A ProcInst represents an XML processing instruction. +type ProcInst struct { + Target string // the processing instruction target + Inst string // the processing instruction value + parent *Element + index int +} + +// NewDocument creates an XML document without a root element. +func NewDocument() *Document { + return &Document{ + Element: Element{Child: make([]Token, 0)}, + } +} + +// NewDocumentWithRoot creates an XML document and sets the element 'e' as its +// root element. If the element 'e' is already part of another document, it is +// first removed from its existing document. +func NewDocumentWithRoot(e *Element) *Document { + d := NewDocument() + d.SetRoot(e) + return d +} + +// Copy returns a recursive, deep copy of the document. +func (d *Document) Copy() *Document { + return &Document{ + Element: *(d.Element.dup(nil).(*Element)), + ReadSettings: d.ReadSettings.dup(), + WriteSettings: d.WriteSettings.dup(), + } +} + +// Root returns the root element of the document. It returns nil if there is +// no root element. +func (d *Document) Root() *Element { + for _, t := range d.Child { + if c, ok := t.(*Element); ok { + return c + } + } + return nil +} + +// SetRoot replaces the document's root element with the element 'e'. If the +// document already has a root element when this function is called, then the +// existing root element is unbound from the document. If the element 'e' is +// part of another document, then it is unbound from the other document. +func (d *Document) SetRoot(e *Element) { + if e.parent != nil { + e.parent.RemoveChild(e) + } + + // If there is already a root element, replace it. + p := &d.Element + for i, t := range p.Child { + if _, ok := t.(*Element); ok { + t.setParent(nil) + t.setIndex(-1) + p.Child[i] = e + e.setParent(p) + e.setIndex(i) + return + } + } + + // No existing root element, so add it. + p.addChild(e) +} + +// ReadFrom reads XML from the reader 'r' into this document. The function +// returns the number of bytes read and any error encountered. +func (d *Document) ReadFrom(r io.Reader) (n int64, err error) { + if d.ReadSettings.ValidateInput { + b, err := io.ReadAll(r) + if err != nil { + return 0, err + } + if err := validateXML(bytes.NewReader(b), d.ReadSettings); err != nil { + return 0, err + } + r = bytes.NewReader(b) + } + return d.Element.readFrom(r, d.ReadSettings) +} + +// ReadFromFile reads XML from a local file at path 'filepath' into this +// document. +func (d *Document) ReadFromFile(filepath string) error { + f, err := os.Open(filepath) + if err != nil { + return err + } + defer f.Close() + + _, err = d.ReadFrom(f) + return err +} + +// ReadFromBytes reads XML from the byte slice 'b' into the this document. +func (d *Document) ReadFromBytes(b []byte) error { + if d.ReadSettings.ValidateInput { + if err := validateXML(bytes.NewReader(b), d.ReadSettings); err != nil { + return err + } + } + _, err := d.Element.readFrom(bytes.NewReader(b), d.ReadSettings) + return err +} + +// ReadFromString reads XML from the string 's' into this document. +func (d *Document) ReadFromString(s string) error { + if d.ReadSettings.ValidateInput { + if err := validateXML(strings.NewReader(s), d.ReadSettings); err != nil { + return err + } + } + _, err := d.Element.readFrom(strings.NewReader(s), d.ReadSettings) + return err +} + +// validateXML determines if the data read from the reader 'r' contains +// well-formed XML according to the rules set by the go xml package. +func validateXML(r io.Reader, settings ReadSettings) error { + dec := newDecoder(r, settings) + err := dec.Decode(new(interface{})) + if err != nil { + return err + } + + // If there are any trailing tokens after unmarshalling with Decode(), + // then the XML input didn't terminate properly. + _, err = dec.Token() + if err == io.EOF { + return nil + } + return ErrXML +} + +// newDecoder creates an XML decoder for the reader 'r' configured using +// the provided read settings. +func newDecoder(r io.Reader, settings ReadSettings) *xml.Decoder { + d := xml.NewDecoder(r) + d.CharsetReader = settings.CharsetReader + if d.CharsetReader == nil { + d.CharsetReader = defaultCharsetReader + } + d.Strict = !settings.Permissive + d.Entity = settings.Entity + d.AutoClose = settings.AutoClose + return d +} + +// WriteTo serializes the document out to the writer 'w'. The function returns +// the number of bytes written and any error encountered. +func (d *Document) WriteTo(w io.Writer) (n int64, err error) { + xw := newXmlWriter(w) + b := bufio.NewWriter(xw) + for _, c := range d.Child { + c.WriteTo(b, &d.WriteSettings) + } + err, n = b.Flush(), xw.bytes + return +} + +// WriteToFile serializes the document out to the file at path 'filepath'. +func (d *Document) WriteToFile(filepath string) error { + f, err := os.Create(filepath) + if err != nil { + return err + } + defer f.Close() + _, err = d.WriteTo(f) + return err +} + +// WriteToBytes serializes this document into a slice of bytes. +func (d *Document) WriteToBytes() (b []byte, err error) { + var buf bytes.Buffer + if _, err = d.WriteTo(&buf); err != nil { + return + } + return buf.Bytes(), nil +} + +// WriteToString serializes this document into a string. +func (d *Document) WriteToString() (s string, err error) { + var b []byte + if b, err = d.WriteToBytes(); err != nil { + return + } + return string(b), nil +} + +// Indent modifies the document's element tree by inserting character data +// tokens containing newlines and spaces for indentation. The amount of +// indentation per depth level is given by the 'spaces' parameter. Other than +// the number of spaces, default IndentSettings are used. +func (d *Document) Indent(spaces int) { + s := NewIndentSettings() + s.Spaces = spaces + d.IndentWithSettings(s) +} + +// IndentTabs modifies the document's element tree by inserting CharData +// tokens containing newlines and tabs for indentation. One tab is used per +// indentation level. Other than the use of tabs, default IndentSettings +// are used. +func (d *Document) IndentTabs() { + s := NewIndentSettings() + s.UseTabs = true + d.IndentWithSettings(s) +} + +// IndentWithSettings modifies the document's element tree by inserting +// character data tokens containing newlines and indentation. The behavior +// of the indentation algorithm is configured by the indent settings. +func (d *Document) IndentWithSettings(s *IndentSettings) { + // WriteSettings.UseCRLF is deprecated. Until removed from the package, it + // overrides IndentSettings.UseCRLF when true. + if d.WriteSettings.UseCRLF { + s.UseCRLF = true + } + + d.Element.indent(0, getIndentFunc(s), s) + + if s.SuppressTrailingWhitespace { + d.Element.stripTrailingWhitespace() + } +} + +// Unindent modifies the document's element tree by removing character data +// tokens containing only whitespace. Other than the removal of indentation, +// default IndentSettings are used. +func (d *Document) Unindent() { + s := NewIndentSettings() + s.Spaces = NoIndent + d.IndentWithSettings(s) +} + +// NewElement creates an unparented element with the specified tag (i.e., +// name). The tag may include a namespace prefix followed by a colon. +func NewElement(tag string) *Element { + space, stag := spaceDecompose(tag) + return newElement(space, stag, nil) +} + +// newElement is a helper function that creates an element and binds it to +// a parent element if possible. +func newElement(space, tag string, parent *Element) *Element { + e := &Element{ + Space: space, + Tag: tag, + Attr: make([]Attr, 0), + Child: make([]Token, 0), + parent: parent, + index: -1, + } + if parent != nil { + parent.addChild(e) + } + return e +} + +// Copy creates a recursive, deep copy of the element and all its attributes +// and children. The returned element has no parent but can be parented to a +// another element using AddChild, or added to a document with SetRoot or +// NewDocumentWithRoot. +func (e *Element) Copy() *Element { + return e.dup(nil).(*Element) +} + +// FullTag returns the element e's complete tag, including namespace prefix if +// present. +func (e *Element) FullTag() string { + if e.Space == "" { + return e.Tag + } + return e.Space + ":" + e.Tag +} + +// NamespaceURI returns the XML namespace URI associated with the element. If +// the element is part of the XML default namespace, NamespaceURI returns the +// empty string. +func (e *Element) NamespaceURI() string { + if e.Space == "" { + return e.findDefaultNamespaceURI() + } + return e.findLocalNamespaceURI(e.Space) +} + +// findLocalNamespaceURI finds the namespace URI corresponding to the +// requested prefix. +func (e *Element) findLocalNamespaceURI(prefix string) string { + for _, a := range e.Attr { + if a.Space == "xmlns" && a.Key == prefix { + return a.Value + } + } + + if e.parent == nil { + return "" + } + + return e.parent.findLocalNamespaceURI(prefix) +} + +// findDefaultNamespaceURI finds the default namespace URI of the element. +func (e *Element) findDefaultNamespaceURI() string { + for _, a := range e.Attr { + if a.Space == "" && a.Key == "xmlns" { + return a.Value + } + } + + if e.parent == nil { + return "" + } + + return e.parent.findDefaultNamespaceURI() +} + +// namespacePrefix returns the namespace prefix associated with the element. +func (e *Element) namespacePrefix() string { + return e.Space +} + +// name returns the tag associated with the element. +func (e *Element) name() string { + return e.Tag +} + +// ReindexChildren recalculates the index values of the element's child +// tokens. This is necessary only if you have manually manipulated the +// element's `Child` array. +func (e *Element) ReindexChildren() { + for i := 0; i < len(e.Child); i++ { + e.Child[i].setIndex(i) + } +} + +// Text returns all character data immediately following the element's opening +// tag. +func (e *Element) Text() string { + if len(e.Child) == 0 { + return "" + } + + text := "" + for _, ch := range e.Child { + if cd, ok := ch.(*CharData); ok { + if text == "" { + text = cd.Data + } else { + text += cd.Data + } + } else if _, ok := ch.(*Comment); ok { + // ignore + } else { + break + } + } + return text +} + +// SetText replaces all character data immediately following an element's +// opening tag with the requested string. +func (e *Element) SetText(text string) { + e.replaceText(0, text, 0) +} + +// SetCData replaces all character data immediately following an element's +// opening tag with a CDATA section. +func (e *Element) SetCData(text string) { + e.replaceText(0, text, cdataFlag) +} + +// Tail returns all character data immediately following the element's end +// tag. +func (e *Element) Tail() string { + if e.Parent() == nil { + return "" + } + + p := e.Parent() + i := e.Index() + + text := "" + for _, ch := range p.Child[i+1:] { + if cd, ok := ch.(*CharData); ok { + if text == "" { + text = cd.Data + } else { + text += cd.Data + } + } else { + break + } + } + return text +} + +// SetTail replaces all character data immediately following the element's end +// tag with the requested string. +func (e *Element) SetTail(text string) { + if e.Parent() == nil { + return + } + + p := e.Parent() + p.replaceText(e.Index()+1, text, 0) +} + +// replaceText is a helper function that replaces a series of chardata tokens +// starting at index i with the requested text. +func (e *Element) replaceText(i int, text string, flags charDataFlags) { + end := e.findTermCharDataIndex(i) + + switch { + case end == i: + if text != "" { + // insert a new chardata token at index i + cd := newCharData(text, flags, nil) + e.InsertChildAt(i, cd) + } + + case end == i+1: + if text == "" { + // remove the chardata token at index i + e.RemoveChildAt(i) + } else { + // replace the first and only character token at index i + cd := e.Child[i].(*CharData) + cd.Data, cd.flags = text, flags + } + + default: + if text == "" { + // remove all chardata tokens starting from index i + copy(e.Child[i:], e.Child[end:]) + removed := end - i + e.Child = e.Child[:len(e.Child)-removed] + for j := i; j < len(e.Child); j++ { + e.Child[j].setIndex(j) + } + } else { + // replace the first chardata token at index i and remove all + // subsequent chardata tokens + cd := e.Child[i].(*CharData) + cd.Data, cd.flags = text, flags + copy(e.Child[i+1:], e.Child[end:]) + removed := end - (i + 1) + e.Child = e.Child[:len(e.Child)-removed] + for j := i + 1; j < len(e.Child); j++ { + e.Child[j].setIndex(j) + } + } + } +} + +// findTermCharDataIndex finds the index of the first child token that isn't +// a CharData token. It starts from the requested start index. +func (e *Element) findTermCharDataIndex(start int) int { + for i := start; i < len(e.Child); i++ { + if _, ok := e.Child[i].(*CharData); !ok { + return i + } + } + return len(e.Child) +} + +// CreateElement creates a new element with the specified tag (i.e., name) and +// adds it as the last child of element 'e'. The tag may include a prefix +// followed by a colon. +func (e *Element) CreateElement(tag string) *Element { + space, stag := spaceDecompose(tag) + return newElement(space, stag, e) +} + +// CreateChild performs the same task as CreateElement but calls a +// continuation function after the child element is created, allowing +// additional actions to be performed on the child element before returning. +// +// This method of element creation is particularly useful when building nested +// XML documents from code. For example: +// +// org := doc.CreateChild("organization", func(e *Element) { +// e.CreateComment("Mary") +// e.CreateChild("person", func(e *Element) { +// e.CreateAttr("name", "Mary") +// e.CreateAttr("age", "30") +// e.CreateAttr("hair", "brown") +// }) +// }) +func (e *Element) CreateChild(tag string, cont func(e *Element)) *Element { + child := e.CreateElement(tag) + cont(child) + return child +} + +// AddChild adds the token 't' as the last child of the element. If token 't' +// was already the child of another element, it is first removed from its +// parent element. +func (e *Element) AddChild(t Token) { + if t.Parent() != nil { + t.Parent().RemoveChild(t) + } + e.addChild(t) +} + +// InsertChild inserts the token 't' into this element's list of children just +// before the element's existing child token 'ex'. If the existing element +// 'ex' does not appear in this element's list of child tokens, then 't' is +// added to the end of this element's list of child tokens. If token 't' is +// already the child of another element, it is first removed from the other +// element's list of child tokens. +// +// Deprecated: InsertChild is deprecated. Use InsertChildAt instead. +func (e *Element) InsertChild(ex Token, t Token) { + if ex == nil || ex.Parent() != e { + e.AddChild(t) + return + } + + if t.Parent() != nil { + t.Parent().RemoveChild(t) + } + + t.setParent(e) + + i := ex.Index() + e.Child = append(e.Child, nil) + copy(e.Child[i+1:], e.Child[i:]) + e.Child[i] = t + + for j := i; j < len(e.Child); j++ { + e.Child[j].setIndex(j) + } +} + +// InsertChildAt inserts the token 't' into this element's list of child +// tokens just before the requested 'index'. If the index is greater than or +// equal to the length of the list of child tokens, then the token 't' is +// added to the end of the list of child tokens. +func (e *Element) InsertChildAt(index int, t Token) { + if index >= len(e.Child) { + e.AddChild(t) + return + } + + if t.Parent() != nil { + if t.Parent() == e && t.Index() < index { + index-- + } + t.Parent().RemoveChild(t) + } + + t.setParent(e) + + e.Child = append(e.Child, nil) + copy(e.Child[index+1:], e.Child[index:]) + e.Child[index] = t + + for j := index; j < len(e.Child); j++ { + e.Child[j].setIndex(j) + } +} + +// RemoveChild attempts to remove the token 't' from this element's list of +// child tokens. If the token 't' was a child of this element, then it is +// removed and returned. Otherwise, nil is returned. +func (e *Element) RemoveChild(t Token) Token { + if t.Parent() != e { + return nil + } + return e.RemoveChildAt(t.Index()) +} + +// RemoveChildAt removes the child token appearing in slot 'index' of this +// element's list of child tokens. The removed child token is then returned. +// If the index is out of bounds, no child is removed and nil is returned. +func (e *Element) RemoveChildAt(index int) Token { + if index >= len(e.Child) { + return nil + } + + t := e.Child[index] + for j := index + 1; j < len(e.Child); j++ { + e.Child[j].setIndex(j - 1) + } + e.Child = append(e.Child[:index], e.Child[index+1:]...) + t.setIndex(-1) + t.setParent(nil) + return t +} + +// autoClose analyzes the stack's top element and the current token to decide +// whether the top element should be closed. +func (e *Element) autoClose(stack *stack[*Element], t xml.Token, tags []string) { + if stack.empty() { + return + } + + top := stack.peek() + + for _, tag := range tags { + if strings.EqualFold(tag, top.FullTag()) { + if e, ok := t.(xml.EndElement); !ok || + !strings.EqualFold(e.Name.Space, top.Space) || + !strings.EqualFold(e.Name.Local, top.Tag) { + stack.pop() + } + break + } + } +} + +// ReadFrom reads XML from the reader 'ri' and stores the result as a new +// child of this element. +func (e *Element) readFrom(ri io.Reader, settings ReadSettings) (n int64, err error) { + var r xmlReader + var pr *xmlPeekReader + if settings.PreserveCData { + pr = newXmlPeekReader(ri) + r = pr + } else { + r = newXmlSimpleReader(ri) + } + + attrCheck := make(map[xml.Name]int) + dec := newDecoder(r, settings) + + var stack stack[*Element] + stack.push(e) + for { + if pr != nil { + pr.PeekPrepare(dec.InputOffset(), len(cdataPrefix)) + } + + t, err := dec.RawToken() + + if settings.Permissive && settings.AutoClose != nil { + e.autoClose(&stack, t, settings.AutoClose) + } + + switch { + case err == io.EOF: + if len(stack.data) != 1 { + return r.Bytes(), ErrXML + } + return r.Bytes(), nil + case err != nil: + return r.Bytes(), err + case stack.empty(): + return r.Bytes(), ErrXML + } + + top := stack.peek() + + switch t := t.(type) { + case xml.StartElement: + e := newElement(t.Name.Space, t.Name.Local, top) + if settings.PreserveDuplicateAttrs || len(t.Attr) < 2 { + for _, a := range t.Attr { + e.addAttr(a.Name.Space, a.Name.Local, a.Value) + } + } else { + for _, a := range t.Attr { + if i, contains := attrCheck[a.Name]; contains { + e.Attr[i].Value = a.Value + } else { + attrCheck[a.Name] = e.addAttr(a.Name.Space, a.Name.Local, a.Value) + } + } + clear(attrCheck) + } + stack.push(e) + case xml.EndElement: + if top.Tag != t.Name.Local || top.Space != t.Name.Space { + return r.Bytes(), ErrXML + } + stack.pop() + case xml.CharData: + data := string(t) + var flags charDataFlags + if pr != nil { + peekBuf := pr.PeekFinalize() + if bytes.Equal(peekBuf, cdataPrefix) { + flags = cdataFlag + } else if isWhitespace(data) { + flags = whitespaceFlag + } + } else { + if isWhitespace(data) { + flags = whitespaceFlag + } + } + newCharData(data, flags, top) + case xml.Comment: + newComment(string(t), top) + case xml.Directive: + newDirective(string(t), top) + case xml.ProcInst: + newProcInst(t.Target, string(t.Inst), top) + } + } +} + +// SelectAttr finds an element attribute matching the requested 'key' and, if +// found, returns a pointer to the matching attribute. The function returns +// nil if no matching attribute is found. The key may include a namespace +// prefix followed by a colon. +func (e *Element) SelectAttr(key string) *Attr { + space, skey := spaceDecompose(key) + for i, a := range e.Attr { + if spaceMatch(space, a.Space) && skey == a.Key { + return &e.Attr[i] + } + } + return nil +} + +// SelectAttrValue finds an element attribute matching the requested 'key' and +// returns its value if found. If no matching attribute is found, the function +// returns the 'dflt' value instead. The key may include a namespace prefix +// followed by a colon. +func (e *Element) SelectAttrValue(key, dflt string) string { + space, skey := spaceDecompose(key) + for _, a := range e.Attr { + if spaceMatch(space, a.Space) && skey == a.Key { + return a.Value + } + } + return dflt +} + +// ChildElements returns all elements that are children of this element. +func (e *Element) ChildElements() []*Element { + return slices.Collect(e.ChildElementsSeq()) +} + +// ChildElementsSeq returns an iterator over all child elements of this +// element. +func (e *Element) ChildElementsSeq() iter.Seq[*Element] { + return func(yield func(*Element) bool) { + for _, t := range e.Child { + if c, ok := t.(*Element); ok { + if !yield(c) { + return + } + } + } + } +} + +// SelectElement returns the first child element with the given 'tag' (i.e., +// name). The function returns nil if no child element matching the tag is +// found. The tag may include a namespace prefix followed by a colon. +func (e *Element) SelectElement(tag string) *Element { + for element := range e.SelectElementsSeq(tag) { + return element + } + return nil +} + +// SelectElements returns a slice of all child elements with the given 'tag' +// (i.e., name). The tag may include a namespace prefix followed by a colon. +func (e *Element) SelectElements(tag string) []*Element { + return slices.Collect(e.SelectElementsSeq(tag)) +} + +// SelectElementsSeq returns an iterator over all child elements with the +// given 'tag' (i.e., name). The tag may include a namespace prefix followed +// by a colon. +func (e *Element) SelectElementsSeq(tag string) iter.Seq[*Element] { + return func(yield func(*Element) bool) { + space, stag := spaceDecompose(tag) + for _, t := range e.Child { + if c, ok := t.(*Element); ok && spaceMatch(space, c.Space) && stag == c.Tag { + if !yield(c) { + return + } + } + } + } +} + +// FindElement returns the first element matched by the XPath-like 'path' +// string. The function returns nil if no child element is found using the +// path. It panics if an invalid path string is supplied. +func (e *Element) FindElement(path string) *Element { + return e.FindElementPath(MustCompilePath(path)) +} + +// FindElementPath returns the first element matched by the 'path' object. The +// function returns nil if no element is found using the path. +func (e *Element) FindElementPath(path Path) *Element { + for element := range path.traverse(e) { + return element + } + return nil +} + +// FindElements returns a slice of elements matched by the XPath-like 'path' +// string. The function returns nil if no child element is found using the +// path. It panics if an invalid path string is supplied. +func (e *Element) FindElements(path string) []*Element { + return slices.Collect(e.FindElementsSeq(path)) +} + +// FindElementsSeq returns an iterator over elements matched by the XPath-like +// 'path' string. This function uses Go's iterator support for +// memory-efficient traversal. It panics if an invalid path string is +// supplied. +func (e *Element) FindElementsSeq(path string) iter.Seq[*Element] { + return e.FindElementsPathSeq(MustCompilePath(path)) +} + +// FindElementsPath returns a slice of elements matched by the 'path' object. +func (e *Element) FindElementsPath(path Path) []*Element { + return slices.Collect(e.FindElementsPathSeq(path)) +} + +// FindElementsPathSeq returns an iterator over elements matched by the 'path' +// object. +func (e *Element) FindElementsPathSeq(path Path) iter.Seq[*Element] { + return path.traverse(e) +} + +// NotNil returns the receiver element if it isn't nil; otherwise, it returns +// an unparented element with an empty string tag. This function simplifies +// the task of writing code to ignore not-found results from element queries. +// For example, instead of writing this: +// +// if e := doc.SelectElement("enabled"); e != nil { +// e.SetText("true") +// } +// +// You could write this: +// +// doc.SelectElement("enabled").NotNil().SetText("true") +func (e *Element) NotNil() *Element { + if e == nil { + return NewElement("") + } + return e +} + +// GetPath returns the absolute path of the element. The absolute path is the +// full path from the document's root. +func (e *Element) GetPath() string { + path := []string{} + for seg := e; seg != nil; seg = seg.Parent() { + if seg.Tag != "" { + path = append(path, seg.Tag) + } + } + + // Reverse the path. + for i, j := 0, len(path)-1; i < j; i, j = i+1, j-1 { + path[i], path[j] = path[j], path[i] + } + + return "/" + strings.Join(path, "/") +} + +// GetRelativePath returns the path of this element relative to the 'source' +// element. If the two elements are not part of the same element tree, then +// the function returns the empty string. +func (e *Element) GetRelativePath(source *Element) string { + var path []*Element + + if source == nil { + return "" + } + + // Build a reverse path from the element toward the root. Stop if the + // source element is encountered. + var seg *Element + for seg = e; seg != nil && seg != source; seg = seg.Parent() { + path = append(path, seg) + } + + // If we found the source element, reverse the path and compose the + // string. + if seg == source { + if len(path) == 0 { + return "." + } + parts := []string{} + for i := len(path) - 1; i >= 0; i-- { + parts = append(parts, path[i].Tag) + } + return "./" + strings.Join(parts, "/") + } + + // The source wasn't encountered, so climb from the source element toward + // the root of the tree until an element in the reversed path is + // encountered. + + findPathIndex := func(e *Element, path []*Element) int { + for i, ee := range path { + if e == ee { + return i + } + } + return -1 + } + + climb := 0 + for seg = source; seg != nil; seg = seg.Parent() { + i := findPathIndex(seg, path) + if i >= 0 { + path = path[:i] // truncate at found segment + break + } + climb++ + } + + // No element in the reversed path was encountered, so the two elements + // must not be part of the same tree. + if seg == nil { + return "" + } + + // Reverse the (possibly truncated) path and prepend ".." segments to + // climb. + parts := []string{} + for i := 0; i < climb; i++ { + parts = append(parts, "..") + } + for i := len(path) - 1; i >= 0; i-- { + parts = append(parts, path[i].Tag) + } + return strings.Join(parts, "/") +} + +// IndentWithSettings modifies the element and its child tree by inserting +// character data tokens containing newlines and indentation. The behavior of +// the indentation algorithm is configured by the indent settings. Because +// this function indents the element as if it were at the root of a document, +// it is most useful when called just before writing the element as an XML +// fragment using WriteTo. +func (e *Element) IndentWithSettings(s *IndentSettings) { + e.indent(1, getIndentFunc(s), s) +} + +// indent recursively inserts proper indentation between an XML element's +// child tokens. +func (e *Element) indent(depth int, indent indentFunc, s *IndentSettings) { + e.stripIndent(s) + n := len(e.Child) + if n == 0 { + return + } + + oldChild := e.Child + e.Child = make([]Token, 0, n*2+1) + isCharData, firstNonCharData := false, true + for _, c := range oldChild { + // Insert NL+indent before child if it's not character data. + // Exceptions: when it's the first non-character-data child, or when + // the child is at root depth. + _, isCharData = c.(*CharData) + if !isCharData { + if !firstNonCharData || depth > 0 { + s := indent(depth) + if s != "" { + newCharData(s, whitespaceFlag, e) + } + } + firstNonCharData = false + } + + e.addChild(c) + + // Recursively process child elements. + if ce, ok := c.(*Element); ok { + ce.indent(depth+1, indent, s) + } + } + + // Insert NL+indent before the last child. + if !isCharData { + if !firstNonCharData || depth > 0 { + s := indent(depth - 1) + if s != "" { + newCharData(s, whitespaceFlag, e) + } + } + } +} + +// stripIndent removes any previously inserted indentation. +func (e *Element) stripIndent(s *IndentSettings) { + // Count the number of non-indent child tokens + n := len(e.Child) + for _, c := range e.Child { + if cd, ok := c.(*CharData); ok && cd.IsWhitespace() { + n-- + } + } + if n == len(e.Child) { + return + } + if n == 0 && len(e.Child) == 1 && s.PreserveLeafWhitespace { + return + } + + // Strip out indent CharData + newChild := make([]Token, n) + j := 0 + for _, c := range e.Child { + if cd, ok := c.(*CharData); ok && cd.IsWhitespace() { + continue + } + newChild[j] = c + newChild[j].setIndex(j) + j++ + } + e.Child = newChild +} + +// stripTrailingWhitespace removes any trailing whitespace CharData tokens +// from the element's children. +func (e *Element) stripTrailingWhitespace() { + for i := len(e.Child) - 1; i >= 0; i-- { + if cd, ok := e.Child[i].(*CharData); !ok || !cd.IsWhitespace() { + e.Child = e.Child[:i+1] + return + } + } +} + +// dup duplicates the element. +func (e *Element) dup(parent *Element) Token { + ne := &Element{ + Space: e.Space, + Tag: e.Tag, + Attr: make([]Attr, len(e.Attr)), + Child: make([]Token, len(e.Child)), + parent: parent, + index: e.index, + } + for i, t := range e.Child { + ne.Child[i] = t.dup(ne) + } + copy(ne.Attr, e.Attr) + return ne +} + +// NextSibling returns this element's next sibling element. It returns nil if +// there is no next sibling element. +func (e *Element) NextSibling() *Element { + if e.parent == nil { + return nil + } + for i := e.index + 1; i < len(e.parent.Child); i++ { + if s, ok := e.parent.Child[i].(*Element); ok { + return s + } + } + return nil +} + +// PrevSibling returns this element's preceding sibling element. It returns +// nil if there is no preceding sibling element. +func (e *Element) PrevSibling() *Element { + if e.parent == nil { + return nil + } + for i := e.index - 1; i >= 0; i-- { + if s, ok := e.parent.Child[i].(*Element); ok { + return s + } + } + return nil +} + +// Parent returns this element's parent element. It returns nil if this +// element has no parent. +func (e *Element) Parent() *Element { + return e.parent +} + +// Index returns the index of this element within its parent element's +// list of child tokens. If this element has no parent, then the function +// returns -1. +func (e *Element) Index() int { + return e.index +} + +// WriteTo serializes the element to the writer w. +func (e *Element) WriteTo(w Writer, s *WriteSettings) { + w.WriteByte('<') + w.WriteString(e.FullTag()) + for _, a := range e.Attr { + w.WriteByte(' ') + a.WriteTo(w, s) + } + if len(e.Child) > 0 { + w.WriteByte('>') + for _, c := range e.Child { + c.WriteTo(w, s) + } + w.Write([]byte{'<', '/'}) + w.WriteString(e.FullTag()) + w.WriteByte('>') + } else { + if s.CanonicalEndTags { + w.Write([]byte{'>', '<', '/'}) + w.WriteString(e.FullTag()) + w.WriteByte('>') + } else { + w.Write([]byte{'/', '>'}) + } + } +} + +// setParent replaces this element token's parent. +func (e *Element) setParent(parent *Element) { + e.parent = parent +} + +// setIndex sets this element token's index within its parent's Child slice. +func (e *Element) setIndex(index int) { + e.index = index +} + +// addChild adds a child token to the element e. +func (e *Element) addChild(t Token) { + t.setParent(e) + t.setIndex(len(e.Child)) + e.Child = append(e.Child, t) +} + +// CreateAttr creates an attribute with the specified 'key' and 'value' and +// adds it to this element. If an attribute with same key already exists on +// this element, then its value is replaced. The key may include a namespace +// prefix followed by a colon. +func (e *Element) CreateAttr(key, value string) *Attr { + space, skey := spaceDecompose(key) + + for i, a := range e.Attr { + if space == a.Space && skey == a.Key { + e.Attr[i].Value = value + return &e.Attr[i] + } + } + + i := e.addAttr(space, skey, value) + return &e.Attr[i] +} + +// addAttr is a helper function that adds an attribute to an element. Returns +// the index of the added attribute. +func (e *Element) addAttr(space, key, value string) int { + a := Attr{ + Space: space, + Key: key, + Value: value, + element: e, + } + e.Attr = append(e.Attr, a) + return len(e.Attr) - 1 +} + +// RemoveAttr removes the first attribute of this element whose key matches +// 'key'. It returns a copy of the removed attribute if a match is found. If +// no match is found, it returns nil. The key may include a namespace prefix +// followed by a colon. +func (e *Element) RemoveAttr(key string) *Attr { + space, skey := spaceDecompose(key) + for i, a := range e.Attr { + if space == a.Space && skey == a.Key { + e.Attr = append(e.Attr[0:i], e.Attr[i+1:]...) + return &Attr{ + Space: a.Space, + Key: a.Key, + Value: a.Value, + element: nil, + } + } + } + return nil +} + +// SortAttrs sorts this element's attributes lexicographically by key. +func (e *Element) SortAttrs() { + slices.SortFunc(e.Attr, func(a, b Attr) int { + if v := strings.Compare(a.Space, b.Space); v != 0 { + return v + } + return strings.Compare(a.Key, b.Key) + }) +} + +// FullKey returns this attribute's complete key, including namespace prefix +// if present. +func (a *Attr) FullKey() string { + if a.Space == "" { + return a.Key + } + return a.Space + ":" + a.Key +} + +// Element returns a pointer to the element containing this attribute. +func (a *Attr) Element() *Element { + return a.element +} + +// NamespaceURI returns the XML namespace URI associated with this attribute. +// The function returns the empty string if the attribute is unprefixed or +// if the attribute is part of the XML default namespace. +func (a *Attr) NamespaceURI() string { + if a.Space == "" { + return "" + } + return a.element.findLocalNamespaceURI(a.Space) +} + +// WriteTo serializes the attribute to the writer. +func (a *Attr) WriteTo(w Writer, s *WriteSettings) { + w.WriteString(a.FullKey()) + if s.AttrSingleQuote { + w.WriteString(`='`) + } else { + w.WriteString(`="`) + } + var m escapeMode + if s.CanonicalAttrVal && !s.AttrSingleQuote { + m = escapeCanonicalAttr + } else { + m = escapeNormal + } + escapeString(w, a.Value, m) + if s.AttrSingleQuote { + w.WriteByte('\'') + } else { + w.WriteByte('"') + } +} + +// NewText creates an unparented CharData token containing simple text data. +func NewText(text string) *CharData { + return newCharData(text, 0, nil) +} + +// NewCData creates an unparented XML character CDATA section with 'data' as +// its content. +func NewCData(data string) *CharData { + return newCharData(data, cdataFlag, nil) +} + +// NewCharData creates an unparented CharData token containing simple text +// data. +// +// Deprecated: NewCharData is deprecated. Instead, use NewText, which does the +// same thing. +func NewCharData(data string) *CharData { + return newCharData(data, 0, nil) +} + +// newCharData creates a character data token and binds it to a parent +// element. If parent is nil, the CharData token remains unbound. +func newCharData(data string, flags charDataFlags, parent *Element) *CharData { + c := &CharData{ + Data: data, + parent: nil, + index: -1, + flags: flags, + } + if parent != nil { + parent.addChild(c) + } + return c +} + +// CreateText creates a CharData token containing simple text data and adds it +// to the end of this element's list of child tokens. +func (e *Element) CreateText(text string) *CharData { + return newCharData(text, 0, e) +} + +// CreateCData creates a CharData token containing a CDATA section with 'data' +// as its content and adds it to the end of this element's list of child +// tokens. +func (e *Element) CreateCData(data string) *CharData { + return newCharData(data, cdataFlag, e) +} + +// CreateCharData creates a CharData token containing simple text data and +// adds it to the end of this element's list of child tokens. +// +// Deprecated: CreateCharData is deprecated. Instead, use CreateText, which +// does the same thing. +func (e *Element) CreateCharData(data string) *CharData { + return e.CreateText(data) +} + +// SetData modifies the content of the CharData token. In the case of a +// CharData token containing simple text, the simple text is modified. In the +// case of a CharData token containing a CDATA section, the CDATA section's +// content is modified. +func (c *CharData) SetData(text string) { + c.Data = text + if isWhitespace(text) { + c.flags |= whitespaceFlag + } else { + c.flags &= ^whitespaceFlag + } +} + +// IsCData returns true if this CharData token is contains a CDATA section. It +// returns false if the CharData token contains simple text. +func (c *CharData) IsCData() bool { + return (c.flags & cdataFlag) != 0 +} + +// IsWhitespace returns true if this CharData token contains only whitespace. +func (c *CharData) IsWhitespace() bool { + return (c.flags & whitespaceFlag) != 0 +} + +// Parent returns this CharData token's parent element, or nil if it has no +// parent. +func (c *CharData) Parent() *Element { + return c.parent +} + +// Index returns the index of this CharData token within its parent element's +// list of child tokens. If this CharData token has no parent, then the +// function returns -1. +func (c *CharData) Index() int { + return c.index +} + +// WriteTo serializes character data to the writer. +func (c *CharData) WriteTo(w Writer, s *WriteSettings) { + if c.IsCData() { + w.WriteString(``) + } else { + var m escapeMode + if s.CanonicalText { + m = escapeCanonicalText + } else { + m = escapeNormal + } + escapeString(w, c.Data, m) + } +} + +// dup duplicates the character data. +func (c *CharData) dup(parent *Element) Token { + return &CharData{ + Data: c.Data, + flags: c.flags, + parent: parent, + index: c.index, + } +} + +// setParent replaces the character data token's parent. +func (c *CharData) setParent(parent *Element) { + c.parent = parent +} + +// setIndex sets the CharData token's index within its parent element's Child +// slice. +func (c *CharData) setIndex(index int) { + c.index = index +} + +// NewComment creates an unparented comment token. +func NewComment(comment string) *Comment { + return newComment(comment, nil) +} + +// NewComment creates a comment token and sets its parent element to 'parent'. +func newComment(comment string, parent *Element) *Comment { + c := &Comment{ + Data: comment, + parent: nil, + index: -1, + } + if parent != nil { + parent.addChild(c) + } + return c +} + +// CreateComment creates a comment token using the specified 'comment' string +// and adds it as the last child token of this element. +func (e *Element) CreateComment(comment string) *Comment { + return newComment(comment, e) +} + +// dup duplicates the comment. +func (c *Comment) dup(parent *Element) Token { + return &Comment{ + Data: c.Data, + parent: parent, + index: c.index, + } +} + +// Parent returns comment token's parent element, or nil if it has no parent. +func (c *Comment) Parent() *Element { + return c.parent +} + +// Index returns the index of this Comment token within its parent element's +// list of child tokens. If this Comment token has no parent, then the +// function returns -1. +func (c *Comment) Index() int { + return c.index +} + +// WriteTo serialies the comment to the writer. +func (c *Comment) WriteTo(w Writer, s *WriteSettings) { + w.WriteString("") +} + +// setParent replaces the comment token's parent. +func (c *Comment) setParent(parent *Element) { + c.parent = parent +} + +// setIndex sets the Comment token's index within its parent element's Child +// slice. +func (c *Comment) setIndex(index int) { + c.index = index +} + +// NewDirective creates an unparented XML directive token. +func NewDirective(data string) *Directive { + return newDirective(data, nil) +} + +// newDirective creates an XML directive and binds it to a parent element. If +// parent is nil, the Directive remains unbound. +func newDirective(data string, parent *Element) *Directive { + d := &Directive{ + Data: data, + parent: nil, + index: -1, + } + if parent != nil { + parent.addChild(d) + } + return d +} + +// CreateDirective creates an XML directive token with the specified 'data' +// value and adds it as the last child token of this element. +func (e *Element) CreateDirective(data string) *Directive { + return newDirective(data, e) +} + +// dup duplicates the directive. +func (d *Directive) dup(parent *Element) Token { + return &Directive{ + Data: d.Data, + parent: parent, + index: d.index, + } +} + +// Parent returns directive token's parent element, or nil if it has no +// parent. +func (d *Directive) Parent() *Element { + return d.parent +} + +// Index returns the index of this Directive token within its parent element's +// list of child tokens. If this Directive token has no parent, then the +// function returns -1. +func (d *Directive) Index() int { + return d.index +} + +// WriteTo serializes the XML directive to the writer. +func (d *Directive) WriteTo(w Writer, s *WriteSettings) { + w.WriteString("") +} + +// setParent replaces the directive token's parent. +func (d *Directive) setParent(parent *Element) { + d.parent = parent +} + +// setIndex sets the Directive token's index within its parent element's Child +// slice. +func (d *Directive) setIndex(index int) { + d.index = index +} + +// NewProcInst creates an unparented XML processing instruction. +func NewProcInst(target, inst string) *ProcInst { + return newProcInst(target, inst, nil) +} + +// newProcInst creates an XML processing instruction and binds it to a parent +// element. If parent is nil, the ProcInst remains unbound. +func newProcInst(target, inst string, parent *Element) *ProcInst { + p := &ProcInst{ + Target: target, + Inst: inst, + parent: nil, + index: -1, + } + if parent != nil { + parent.addChild(p) + } + return p +} + +// CreateProcInst creates an XML processing instruction token with the +// specified 'target' and instruction 'inst'. It is then added as the last +// child token of this element. +func (e *Element) CreateProcInst(target, inst string) *ProcInst { + return newProcInst(target, inst, e) +} + +// dup duplicates the procinst. +func (p *ProcInst) dup(parent *Element) Token { + return &ProcInst{ + Target: p.Target, + Inst: p.Inst, + parent: parent, + index: p.index, + } +} + +// Parent returns processing instruction token's parent element, or nil if it +// has no parent. +func (p *ProcInst) Parent() *Element { + return p.parent +} + +// Index returns the index of this ProcInst token within its parent element's +// list of child tokens. If this ProcInst token has no parent, then the +// function returns -1. +func (p *ProcInst) Index() int { + return p.index +} + +// WriteTo serializes the processing instruction to the writer. +func (p *ProcInst) WriteTo(w Writer, s *WriteSettings) { + w.WriteString("") +} + +// setParent replaces the processing instruction token's parent. +func (p *ProcInst) setParent(parent *Element) { + p.parent = parent +} + +// setIndex sets the processing instruction token's index within its parent +// element's Child slice. +func (p *ProcInst) setIndex(index int) { + p.index = index +} diff --git a/gen/test/go/vendor/github.com/beevik/etree/helpers.go b/gen/test/go/vendor/github.com/beevik/etree/helpers.go new file mode 100644 index 000000000..ea789b62a --- /dev/null +++ b/gen/test/go/vendor/github.com/beevik/etree/helpers.go @@ -0,0 +1,394 @@ +// Copyright 2015-2019 Brett Vickers. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package etree + +import ( + "io" + "strings" + "unicode/utf8" +) + +type stack[E any] struct { + data []E +} + +func (s *stack[E]) empty() bool { + return len(s.data) == 0 +} + +func (s *stack[E]) push(value E) { + s.data = append(s.data, value) +} + +func (s *stack[E]) pop() E { + value := s.data[len(s.data)-1] + var empty E + s.data[len(s.data)-1] = empty + s.data = s.data[:len(s.data)-1] + return value +} + +func (s *stack[E]) peek() E { + return s.data[len(s.data)-1] +} + +type queue[E any] struct { + data []E + head, tail int +} + +func (f *queue[E]) add(value E) { + if f.len()+1 >= len(f.data) { + f.grow() + } + f.data[f.tail] = value + if f.tail++; f.tail == len(f.data) { + f.tail = 0 + } +} + +func (f *queue[E]) remove() E { + value := f.data[f.head] + var empty E + f.data[f.head] = empty + if f.head++; f.head == len(f.data) { + f.head = 0 + } + return value +} + +func (f *queue[E]) len() int { + if f.tail >= f.head { + return f.tail - f.head + } + return len(f.data) - f.head + f.tail +} + +func (f *queue[E]) grow() { + c := len(f.data) * 2 + if c == 0 { + c = 4 + } + buf, count := make([]E, c), f.len() + if f.tail >= f.head { + copy(buf[:count], f.data[f.head:f.tail]) + } else { + hindex := len(f.data) - f.head + copy(buf[:hindex], f.data[f.head:]) + copy(buf[hindex:count], f.data[:f.tail]) + } + f.data, f.head, f.tail = buf, 0, count +} + +// xmlReader provides the interface by which an XML byte stream is +// processed and decoded. +type xmlReader interface { + Bytes() int64 + Read(p []byte) (n int, err error) +} + +// xmlSimpleReader implements a proxy reader that counts the number of +// bytes read from its encapsulated reader. +type xmlSimpleReader struct { + r io.Reader + bytes int64 +} + +func newXmlSimpleReader(r io.Reader) xmlReader { + return &xmlSimpleReader{r, 0} +} + +func (xr *xmlSimpleReader) Bytes() int64 { + return xr.bytes +} + +func (xr *xmlSimpleReader) Read(p []byte) (n int, err error) { + n, err = xr.r.Read(p) + xr.bytes += int64(n) + return n, err +} + +// xmlPeekReader implements a proxy reader that counts the number of +// bytes read from its encapsulated reader. It also allows the caller to +// "peek" at the previous portions of the buffer after they have been +// parsed. +type xmlPeekReader struct { + r io.Reader + bytes int64 // total bytes read by the Read function + buf []byte // internal read buffer + bufSize int // total bytes used in the read buffer + bufOffset int64 // total bytes read when buf was last filled + window []byte // current read buffer window + peekBuf []byte // buffer used to store data to be peeked at later + peekOffset int64 // total read offset of the start of the peek buffer +} + +func newXmlPeekReader(r io.Reader) *xmlPeekReader { + buf := make([]byte, 4096) + return &xmlPeekReader{ + r: r, + bytes: 0, + buf: buf, + bufSize: 0, + bufOffset: 0, + window: buf[0:0], + peekBuf: make([]byte, 0), + peekOffset: -1, + } +} + +func (xr *xmlPeekReader) Bytes() int64 { + return xr.bytes +} + +func (xr *xmlPeekReader) Read(p []byte) (n int, err error) { + if len(xr.window) == 0 { + err = xr.fill() + if err != nil { + return 0, err + } + if len(xr.window) == 0 { + return 0, nil + } + } + + if len(xr.window) < len(p) { + n = len(xr.window) + } else { + n = len(p) + } + + copy(p, xr.window) + xr.window = xr.window[n:] + xr.bytes += int64(n) + + return n, err +} + +func (xr *xmlPeekReader) PeekPrepare(offset int64, maxLen int) { + if maxLen > cap(xr.peekBuf) { + xr.peekBuf = make([]byte, 0, maxLen) + } + xr.peekBuf = xr.peekBuf[0:0] + xr.peekOffset = offset + xr.updatePeekBuf() +} + +func (xr *xmlPeekReader) PeekFinalize() []byte { + xr.updatePeekBuf() + return xr.peekBuf +} + +func (xr *xmlPeekReader) fill() error { + xr.bufOffset = xr.bytes + xr.bufSize = 0 + n, err := xr.r.Read(xr.buf) + if err != nil { + xr.window, xr.bufSize = xr.buf[0:0], 0 + return err + } + xr.window, xr.bufSize = xr.buf[:n], n + xr.updatePeekBuf() + return nil +} + +func (xr *xmlPeekReader) updatePeekBuf() { + peekRemain := cap(xr.peekBuf) - len(xr.peekBuf) + if xr.peekOffset >= 0 && peekRemain > 0 { + rangeMin := xr.peekOffset + rangeMax := xr.peekOffset + int64(cap(xr.peekBuf)) + bufMin := xr.bufOffset + bufMax := xr.bufOffset + int64(xr.bufSize) + if rangeMin < bufMin { + rangeMin = bufMin + } + if rangeMax > bufMax { + rangeMax = bufMax + } + if rangeMax > rangeMin { + rangeMin -= xr.bufOffset + rangeMax -= xr.bufOffset + if int(rangeMax-rangeMin) > peekRemain { + rangeMax = rangeMin + int64(peekRemain) + } + xr.peekBuf = append(xr.peekBuf, xr.buf[rangeMin:rangeMax]...) + } + } +} + +// xmlWriter implements a proxy writer that counts the number of +// bytes written by its encapsulated writer. +type xmlWriter struct { + w io.Writer + bytes int64 +} + +func newXmlWriter(w io.Writer) *xmlWriter { + return &xmlWriter{w: w} +} + +func (xw *xmlWriter) Write(p []byte) (n int, err error) { + n, err = xw.w.Write(p) + xw.bytes += int64(n) + return n, err +} + +// isWhitespace returns true if the byte slice contains only +// whitespace characters. +func isWhitespace(s string) bool { + for i := 0; i < len(s); i++ { + if c := s[i]; c != ' ' && c != '\t' && c != '\n' && c != '\r' { + return false + } + } + return true +} + +// spaceMatch returns true if namespace a is the empty string +// or if namespace a equals namespace b. +func spaceMatch(a, b string) bool { + switch { + case a == "": + return true + default: + return a == b + } +} + +// spaceDecompose breaks a namespace:tag identifier at the ':' +// and returns the two parts. +func spaceDecompose(str string) (space, key string) { + colon := strings.IndexByte(str, ':') + if colon == -1 { + return "", str + } + return str[:colon], str[colon+1:] +} + +// Strings used by indentCRLF and indentLF +const ( + indentSpaces = "\r\n " + indentTabs = "\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" +) + +// indentCRLF returns a CRLF newline followed by n copies of the first +// non-CRLF character in the source string. +func indentCRLF(n int, source string) string { + switch { + case n < 0: + return source[:2] + case n < len(source)-1: + return source[:n+2] + default: + return source + strings.Repeat(source[2:3], n-len(source)+2) + } +} + +// indentLF returns a LF newline followed by n copies of the first non-LF +// character in the source string. +func indentLF(n int, source string) string { + switch { + case n < 0: + return source[1:2] + case n < len(source)-1: + return source[1 : n+2] + default: + return source[1:] + strings.Repeat(source[2:3], n-len(source)+2) + } +} + +// nextIndex returns the index of the next occurrence of byte ch in s, +// starting from offset. It returns -1 if the byte is not found. +func nextIndex(s string, ch byte, offset int) int { + switch i := strings.IndexByte(s[offset:], ch); i { + case -1: + return -1 + default: + return offset + i + } +} + +// isInteger returns true if the string s contains an integer. +func isInteger(s string) bool { + for i := 0; i < len(s); i++ { + if (s[i] < '0' || s[i] > '9') && !(i == 0 && s[i] == '-') { + return false + } + } + return true +} + +type escapeMode byte + +const ( + escapeNormal escapeMode = iota + escapeCanonicalText + escapeCanonicalAttr +) + +// escapeString writes an escaped version of a string to the writer. +func escapeString(w Writer, s string, m escapeMode) { + var esc []byte + last := 0 + for i := 0; i < len(s); { + r, width := utf8.DecodeRuneInString(s[i:]) + i += width + switch r { + case '&': + esc = []byte("&") + case '<': + esc = []byte("<") + case '>': + if m == escapeCanonicalAttr { + continue + } + esc = []byte(">") + case '\'': + if m != escapeNormal { + continue + } + esc = []byte("'") + case '"': + if m == escapeCanonicalText { + continue + } + esc = []byte(""") + case '\t': + if m != escapeCanonicalAttr { + continue + } + esc = []byte(" ") + case '\n': + if m != escapeCanonicalAttr { + continue + } + esc = []byte(" ") + case '\r': + if m == escapeNormal { + continue + } + esc = []byte(" ") + default: + if !isInCharacterRange(r) || (r == 0xFFFD && width == 1) { + esc = []byte("\uFFFD") + break + } + continue + } + w.WriteString(s[last : i-width]) + w.Write(esc) + last = i + } + w.WriteString(s[last:]) +} + +func isInCharacterRange(r rune) bool { + return r == 0x09 || + r == 0x0A || + r == 0x0D || + r >= 0x20 && r <= 0xD7FF || + r >= 0xE000 && r <= 0xFFFD || + r >= 0x10000 && r <= 0x10FFFF +} diff --git a/gen/test/go/vendor/github.com/beevik/etree/path.go b/gen/test/go/vendor/github.com/beevik/etree/path.go new file mode 100644 index 000000000..21760d34d --- /dev/null +++ b/gen/test/go/vendor/github.com/beevik/etree/path.go @@ -0,0 +1,605 @@ +// Copyright 2015-2019 Brett Vickers. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package etree + +import ( + "iter" + "strconv" + "strings" +) + +/* +A Path is a string that represents a search path through an etree starting +from the document root or an arbitrary element. Paths are used with the +Element object's Find* methods to locate and return desired elements. + +A Path consists of a series of slash-separated "selectors", each of which may +be modified by one or more bracket-enclosed "filters". Selectors are used to +traverse the etree from element to element, while filters are used to narrow +the list of candidate elements at each node. + +Although etree Path strings are structurally and behaviorally similar to XPath +strings (https://www.w3.org/TR/1999/REC-xpath-19991116/), they have a more +limited set of selectors and filtering options. + +The following selectors are supported by etree paths: + + . Select the current element. + .. Select the parent of the current element. + * Select all child elements of the current element. + / Select the root element when used at the start of a path. + // Select all descendants of the current element. + tag Select all child elements with a name matching the tag. + +The following basic filters are supported: + + [@attrib] Keep elements with an attribute named attrib. + [@attrib='val'] Keep elements with an attribute named attrib and value matching val. + [tag] Keep elements with a child element named tag. + [tag='val'] Keep elements with a child element named tag and text matching val. + [n] Keep the n-th element, where n is a numeric index starting from 1. + +The following function-based filters are supported: + + [text()] Keep elements with non-empty text. + [text()='val'] Keep elements whose text matches val. + [local-name()='val'] Keep elements whose un-prefixed tag matches val. + [name()='val'] Keep elements whose full tag exactly matches val. + [namespace-prefix()] Keep elements with non-empty namespace prefixes. + [namespace-prefix()='val'] Keep elements whose namespace prefix matches val. + [namespace-uri()] Keep elements with non-empty namespace URIs. + [namespace-uri()='val'] Keep elements whose namespace URI matches val. + +Below are some examples of etree path strings. + +Select the bookstore child element of the root element: + + /bookstore + +Beginning from the root element, select the title elements of all descendant +book elements having a 'category' attribute of 'WEB': + + //book[@category='WEB']/title + +Beginning from the current element, select the first descendant book element +with a title child element containing the text 'Great Expectations': + + .//book[title='Great Expectations'][1] + +Beginning from the current element, select all child elements of book elements +with an attribute 'language' set to 'english': + + ./book/*[@language='english'] + +Beginning from the current element, select all child elements of book elements +containing the text 'special': + + ./book/*[text()='special'] + +Beginning from the current element, select all descendant book elements whose +title child element has a 'language' attribute of 'french': + + .//book/title[@language='french']/.. + +Beginning from the current element, select all descendant book elements +belonging to the http://www.w3.org/TR/html4/ namespace: + + .//book[namespace-uri()='http://www.w3.org/TR/html4/'] +*/ +type Path struct { + segments []segment +} + +// ErrPath is returned by path functions when an invalid etree path is provided. +type ErrPath string + +// Error returns the string describing a path error. +func (err ErrPath) Error() string { + return "etree: " + string(err) +} + +// CompilePath creates an optimized version of an XPath-like string that +// can be used to query elements in an element tree. +func CompilePath(path string) (Path, error) { + var comp compiler + segments := comp.parsePath(path) + if comp.err != ErrPath("") { + return Path{nil}, comp.err + } + return Path{segments}, nil +} + +// MustCompilePath creates an optimized version of an XPath-like string that +// can be used to query elements in an element tree. Panics if an error +// occurs. Use this function to create Paths when you know the path is +// valid (i.e., if it's hard-coded). +func MustCompilePath(path string) Path { + p, err := CompilePath(path) + if err != nil { + panic(err) + } + return p +} + +// traverse follows the path from the element e, yielding elements that match +// the path's selectors and filters using iterators. +func (p Path) traverse(e *Element) iter.Seq[*Element] { + pather := newPather() + return func(yield func(*Element) bool) { + pather.queue.add(node{e, p.segments}) + for pather.queue.len() > 0 { + if cont := pather.eval(pather.queue.remove(), yield); !cont { + return + } + } + } +} + +// A segment is a portion of a path between "/" characters. +// It contains one selector and zero or more [filters]. +type segment struct { + sel selector + filters []filter +} + +func (seg *segment) apply(e *Element, p *pather) { + seg.sel.apply(e, p) + for _, f := range seg.filters { + f.apply(p) + } +} + +// A selector selects XML elements for consideration by the +// path traversal. +type selector interface { + apply(e *Element, p *pather) +} + +// A filter pares down a list of candidate XML elements based +// on a path filter in [brackets]. +type filter interface { + apply(p *pather) +} + +// A node represents an element and the remaining path segments that +// should be applied against it by the pather. +type node struct { + e *Element + segments []segment +} + +// A pather is helper object that traverses an element tree using +// a Path object. It collects and deduplicates all elements matching +// the path query. +type pather struct { + queue queue[node] + results []*Element + inResults map[*Element]bool + candidates []*Element + scratch []*Element // used by filters +} + +// newPather creates a new pather instance. +func newPather() *pather { + return &pather{ + results: make([]*Element, 0), + inResults: make(map[*Element]bool), + candidates: make([]*Element, 0), + scratch: make([]*Element, 0), + } +} + +// eval evaluates the current path node by applying the remaining path's +// selector rules against the node's element, yielding results via iterator. +// Returns false if early termination is requested. +func (p *pather) eval(n node, yield func(*Element) bool) bool { + p.candidates = p.candidates[:0] + seg, remain := n.segments[0], n.segments[1:] + seg.apply(n.e, p) + + if len(remain) == 0 { + for _, c := range p.candidates { + if in := p.inResults[c]; !in { + p.inResults[c] = true + if !yield(c) { + return false + } + } + } + } else { + for _, c := range p.candidates { + p.queue.add(node{c, remain}) + } + } + return true +} + +// A compiler generates a compiled path from a path string. +type compiler struct { + err ErrPath +} + +// parsePath parses an XPath-like string describing a path +// through an element tree and returns a slice of segment +// descriptors. +func (c *compiler) parsePath(path string) []segment { + // If path ends with //, fix it + if strings.HasSuffix(path, "//") { + path += "*" + } + + var segments []segment + + // Check for an absolute path + if strings.HasPrefix(path, "/") { + segments = append(segments, segment{new(selectRoot), []filter{}}) + path = path[1:] + } + + // Split path into segments + for _, s := range splitPath(path) { + segments = append(segments, c.parseSegment(s)) + if c.err != ErrPath("") { + break + } + } + return segments +} + +func splitPath(path string) []string { + var pieces []string + start := 0 + inquote := false + var quote byte + for i := 0; i+1 <= len(path); i++ { + if !inquote { + if path[i] == '\'' || path[i] == '"' { + inquote, quote = true, path[i] + } else if path[i] == '/' { + pieces = append(pieces, path[start:i]) + start = i + 1 + } + } else if path[i] == quote { + inquote = false + } + } + return append(pieces, path[start:]) +} + +// parseSegment parses a path segment between / characters. +func (c *compiler) parseSegment(path string) segment { + pieces := strings.Split(path, "[") + seg := segment{ + sel: c.parseSelector(pieces[0]), + filters: []filter{}, + } + for i := 1; i < len(pieces); i++ { + fpath := pieces[i] + if len(fpath) == 0 || fpath[len(fpath)-1] != ']' { + c.err = ErrPath("path has invalid filter [brackets].") + break + } + seg.filters = append(seg.filters, c.parseFilter(fpath[:len(fpath)-1])) + } + return seg +} + +// parseSelector parses a selector at the start of a path segment. +func (c *compiler) parseSelector(path string) selector { + switch path { + case ".": + return new(selectSelf) + case "..": + return new(selectParent) + case "*": + return new(selectChildren) + case "": + return new(selectDescendants) + default: + return newSelectChildrenByTag(path) + } +} + +var fnTable = map[string]func(e *Element) string{ + "local-name": (*Element).name, + "name": (*Element).FullTag, + "namespace-prefix": (*Element).namespacePrefix, + "namespace-uri": (*Element).NamespaceURI, + "text": (*Element).Text, +} + +// parseFilter parses a path filter contained within [brackets]. +func (c *compiler) parseFilter(path string) filter { + if len(path) == 0 { + c.err = ErrPath("path contains an empty filter expression.") + return nil + } + + // Filter contains [@attr='val'], [@attr="val"], [fn()='val'], + // [fn()="val"], [tag='val'] or [tag="val"]? + eqindex := strings.IndexByte(path, '=') + if eqindex >= 0 && eqindex+1 < len(path) { + quote := path[eqindex+1] + if quote == '\'' || quote == '"' { + rindex := nextIndex(path, quote, eqindex+2) + if rindex != len(path)-1 { + c.err = ErrPath("path has mismatched filter quotes.") + return nil + } + + key := path[:eqindex] + value := path[eqindex+2 : rindex] + + switch { + case key[0] == '@': + return newFilterAttrVal(key[1:], value) + case strings.HasSuffix(key, "()"): + name := key[:len(key)-2] + if fn, ok := fnTable[name]; ok { + return newFilterFuncVal(fn, value) + } + c.err = ErrPath("path has unknown function " + name) + return nil + default: + return newFilterChildText(key, value) + } + } + } + + // Filter contains [@attr], [N], [tag] or [fn()] + switch { + case path[0] == '@': + return newFilterAttr(path[1:]) + case strings.HasSuffix(path, "()"): + name := path[:len(path)-2] + if fn, ok := fnTable[name]; ok { + return newFilterFunc(fn) + } + c.err = ErrPath("path has unknown function " + name) + return nil + case isInteger(path): + pos, _ := strconv.Atoi(path) + switch { + case pos > 0: + return newFilterPos(pos - 1) + default: + return newFilterPos(pos) + } + default: + return newFilterChild(path) + } +} + +// selectSelf selects the current element into the candidate list. +type selectSelf struct{} + +func (s *selectSelf) apply(e *Element, p *pather) { + p.candidates = append(p.candidates, e) +} + +// selectRoot selects the element's root node. +type selectRoot struct{} + +func (s *selectRoot) apply(e *Element, p *pather) { + root := e + for root.parent != nil { + root = root.parent + } + p.candidates = append(p.candidates, root) +} + +// selectParent selects the element's parent into the candidate list. +type selectParent struct{} + +func (s *selectParent) apply(e *Element, p *pather) { + if e.parent != nil { + p.candidates = append(p.candidates, e.parent) + } +} + +// selectChildren selects the element's child elements into the +// candidate list. +type selectChildren struct{} + +func (s *selectChildren) apply(e *Element, p *pather) { + for _, c := range e.Child { + if c, ok := c.(*Element); ok { + p.candidates = append(p.candidates, c) + } + } +} + +// selectDescendants selects all descendant child elements +// of the element into the candidate list. +type selectDescendants struct{} + +func (s *selectDescendants) apply(e *Element, p *pather) { + var queue queue[*Element] + for queue.add(e); queue.len() > 0; { + e := queue.remove() + p.candidates = append(p.candidates, e) + for _, c := range e.Child { + if c, ok := c.(*Element); ok { + queue.add(c) + } + } + } +} + +// selectChildrenByTag selects into the candidate list all child +// elements of the element having the specified tag. +type selectChildrenByTag struct { + space, tag string +} + +func newSelectChildrenByTag(path string) *selectChildrenByTag { + s, l := spaceDecompose(path) + return &selectChildrenByTag{s, l} +} + +func (s *selectChildrenByTag) apply(e *Element, p *pather) { + for _, c := range e.Child { + if c, ok := c.(*Element); ok && spaceMatch(s.space, c.Space) && s.tag == c.Tag { + p.candidates = append(p.candidates, c) + } + } +} + +// filterPos filters the candidate list, keeping only the +// candidate at the specified index. +type filterPos struct { + index int +} + +func newFilterPos(pos int) *filterPos { + return &filterPos{pos} +} + +func (f *filterPos) apply(p *pather) { + if f.index >= 0 { + if f.index < len(p.candidates) { + p.scratch = append(p.scratch, p.candidates[f.index]) + } + } else { + if -f.index <= len(p.candidates) { + p.scratch = append(p.scratch, p.candidates[len(p.candidates)+f.index]) + } + } + p.candidates, p.scratch = p.scratch, p.candidates[0:0] +} + +// filterAttr filters the candidate list for elements having +// the specified attribute. +type filterAttr struct { + space, key string +} + +func newFilterAttr(str string) *filterAttr { + s, l := spaceDecompose(str) + return &filterAttr{s, l} +} + +func (f *filterAttr) apply(p *pather) { + for _, c := range p.candidates { + for _, a := range c.Attr { + if spaceMatch(f.space, a.Space) && f.key == a.Key { + p.scratch = append(p.scratch, c) + break + } + } + } + p.candidates, p.scratch = p.scratch, p.candidates[0:0] +} + +// filterAttrVal filters the candidate list for elements having +// the specified attribute with the specified value. +type filterAttrVal struct { + space, key, val string +} + +func newFilterAttrVal(str, value string) *filterAttrVal { + s, l := spaceDecompose(str) + return &filterAttrVal{s, l, value} +} + +func (f *filterAttrVal) apply(p *pather) { + for _, c := range p.candidates { + for _, a := range c.Attr { + if spaceMatch(f.space, a.Space) && f.key == a.Key && f.val == a.Value { + p.scratch = append(p.scratch, c) + break + } + } + } + p.candidates, p.scratch = p.scratch, p.candidates[0:0] +} + +// filterFunc filters the candidate list for elements satisfying a custom +// boolean function. +type filterFunc struct { + fn func(e *Element) string +} + +func newFilterFunc(fn func(e *Element) string) *filterFunc { + return &filterFunc{fn} +} + +func (f *filterFunc) apply(p *pather) { + for _, c := range p.candidates { + if f.fn(c) != "" { + p.scratch = append(p.scratch, c) + } + } + p.candidates, p.scratch = p.scratch, p.candidates[0:0] +} + +// filterFuncVal filters the candidate list for elements containing a value +// matching the result of a custom function. +type filterFuncVal struct { + fn func(e *Element) string + val string +} + +func newFilterFuncVal(fn func(e *Element) string, value string) *filterFuncVal { + return &filterFuncVal{fn, value} +} + +func (f *filterFuncVal) apply(p *pather) { + for _, c := range p.candidates { + if f.fn(c) == f.val { + p.scratch = append(p.scratch, c) + } + } + p.candidates, p.scratch = p.scratch, p.candidates[0:0] +} + +// filterChild filters the candidate list for elements having +// a child element with the specified tag. +type filterChild struct { + space, tag string +} + +func newFilterChild(str string) *filterChild { + s, l := spaceDecompose(str) + return &filterChild{s, l} +} + +func (f *filterChild) apply(p *pather) { + for _, c := range p.candidates { + for _, cc := range c.Child { + if cc, ok := cc.(*Element); ok && + spaceMatch(f.space, cc.Space) && + f.tag == cc.Tag { + p.scratch = append(p.scratch, c) + } + } + } + p.candidates, p.scratch = p.scratch, p.candidates[0:0] +} + +// filterChildText filters the candidate list for elements having +// a child element with the specified tag and text. +type filterChildText struct { + space, tag, text string +} + +func newFilterChildText(str, text string) *filterChildText { + s, l := spaceDecompose(str) + return &filterChildText{s, l, text} +} + +func (f *filterChildText) apply(p *pather) { + for _, c := range p.candidates { + for _, cc := range c.Child { + if cc, ok := cc.(*Element); ok && + spaceMatch(f.space, cc.Space) && + f.tag == cc.Tag && + f.text == cc.Text() { + p.scratch = append(p.scratch, c) + } + } + } + p.candidates, p.scratch = p.scratch, p.candidates[0:0] +} diff --git a/gen/test/go/vendor/modules.txt b/gen/test/go/vendor/modules.txt new file mode 100644 index 000000000..79775cf7c --- /dev/null +++ b/gen/test/go/vendor/modules.txt @@ -0,0 +1,3 @@ +# github.com/beevik/etree v1.6.0 +## explicit; go 1.23.0 +github.com/beevik/etree From 6c0be4677c5453d7419abed4cb51b19986ed830a Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Mon, 8 Jun 2026 18:10:40 +0200 Subject: [PATCH 03/45] add a c generator stub --- gen/test/c/CMakeLists.txt | 25 ++++++ gen/test/c/config.toml | 6 ++ gen/test/c/src/compare.c | 128 ++++++++++++++++++++++++++++++ gen/test/c/src/compare.h | 13 ++++ gen/test/c/src/discover.c | 129 ++++++++++++++++++++++++++++++ gen/test/c/src/discover.h | 14 ++++ gen/test/c/src/fixer.c | 155 +++++++++++++++++++++++++++++++++++++ gen/test/c/src/fixer.h | 22 ++++++ gen/test/c/src/main.c | 45 +++++++++++ gen/test/c/src/normalize.c | 125 ++++++++++++++++++++++++++++++ gen/test/c/src/normalize.h | 8 ++ gen/test/c/src/roundtrip.c | 68 ++++++++++++++++ gen/test/c/src/roundtrip.h | 11 +++ gen/test/c/src/stub.c | 17 ++++ gen/test/c/src/stub.h | 12 +++ 15 files changed, 778 insertions(+) create mode 100644 gen/test/c/CMakeLists.txt create mode 100644 gen/test/c/config.toml create mode 100644 gen/test/c/src/compare.c create mode 100644 gen/test/c/src/compare.h create mode 100644 gen/test/c/src/discover.c create mode 100644 gen/test/c/src/discover.h create mode 100644 gen/test/c/src/fixer.c create mode 100644 gen/test/c/src/fixer.h create mode 100644 gen/test/c/src/main.c create mode 100644 gen/test/c/src/normalize.c create mode 100644 gen/test/c/src/normalize.h create mode 100644 gen/test/c/src/roundtrip.c create mode 100644 gen/test/c/src/roundtrip.h create mode 100644 gen/test/c/src/stub.c create mode 100644 gen/test/c/src/stub.h diff --git a/gen/test/c/CMakeLists.txt b/gen/test/c/CMakeLists.txt new file mode 100644 index 000000000..db820cf89 --- /dev/null +++ b/gen/test/c/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.13) +project(mx-corert-c C) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) + +find_package(LibXml2 REQUIRED) + +# Repo root is passed in at configure time so the test runner can find data/. +if(NOT DEFINED MX_REPO_ROOT) + get_filename_component(MX_REPO_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../.." ABSOLUTE) +endif() + +add_executable(corert-c + src/main.c + src/discover.c + src/fixer.c + src/normalize.c + src/compare.c + src/roundtrip.c + src/stub.c) + +target_include_directories(corert-c PRIVATE ${LIBXML2_INCLUDE_DIRS} src) +target_link_libraries(corert-c ${LIBXML2_LIBRARIES}) +target_compile_definitions(corert-c PRIVATE MX_REPO_ROOT="${MX_REPO_ROOT}") diff --git a/gen/test/c/config.toml b/gen/test/c/config.toml new file mode 100644 index 000000000..f04b1b240 --- /dev/null +++ b/gen/test/c/config.toml @@ -0,0 +1,6 @@ +# C generator target configuration. +# The generator reads this to know where and how to emit C code. + +[output] +# Directory for generated source files, relative to this config file. +dir = "mx" diff --git a/gen/test/c/src/compare.c b/gen/test/c/src/compare.c new file mode 100644 index 000000000..94ab4ec3e --- /dev/null +++ b/gen/test/c/src/compare.c @@ -0,0 +1,128 @@ +#include "compare.h" + +#include +#include +#include +#include + +static int is_equivalent(const char *a, const char *b) { + if (!a) a = ""; + if (!b) b = ""; + if (strcmp(a, b) == 0) return 1; + + char *ea, *eb; + long long la = strtoll(a, &ea, 10); + long long lb = strtoll(b, &eb, 10); + if (*ea == '\0' && *eb == '\0' && ea != a && eb != b && la == lb) + return 1; + + double da = strtod(a, &ea); + double db = strtod(b, &eb); + if (*ea == '\0' && *eb == '\0' && ea != a && eb != b && + !isnan(da) && !isnan(db) && !isinf(da) && !isinf(db) && + fabs(da - db) < 0.00000001) + return 1; + + return 0; +} + +static char *node_text(xmlNodePtr node) { + xmlChar *c = xmlNodeGetContent(node); + if (!c) return strdup(""); + char *s = strdup((const char *)c); + xmlFree(c); + return s; +} + +static CompareResult do_compare(xmlNodePtr expected, xmlNodePtr actual, + const char *path) { + CompareResult r = {0, ""}; + + if (!expected || !actual) { + r.failed = 1; + snprintf(r.detail, sizeof(r.detail), "nil element at %s", path); + return r; + } + + if (strcmp((const char *)expected->name, (const char *)actual->name) != 0) { + r.failed = 1; + snprintf(r.detail, sizeof(r.detail), + "element name mismatch at %s: expected '%s', actual '%s'", + path, expected->name, actual->name); + return r; + } + + char *et = node_text(expected); + char *at = node_text(actual); + if (!is_equivalent(et, at)) { + r.failed = 1; + snprintf(r.detail, sizeof(r.detail), + "mismatch at %s: expected '%s', actual '%s'", path, et, at); + free(et); + free(at); + return r; + } + free(et); + free(at); + + int ea_count = 0, aa_count = 0; + for (xmlAttrPtr a = expected->properties; a; a = a->next) ea_count++; + for (xmlAttrPtr a = actual->properties; a; a = a->next) aa_count++; + if (ea_count != aa_count) { + r.failed = 1; + snprintf(r.detail, sizeof(r.detail), + "attribute count mismatch at %s", path); + return r; + } + + xmlAttrPtr ea = expected->properties; + xmlAttrPtr aa = actual->properties; + while (ea && aa) { + xmlChar *ev = xmlGetProp(expected, ea->name); + xmlChar *av = xmlGetProp(actual, aa->name); + int name_eq = strcmp((const char *)ea->name, (const char *)aa->name) == 0; + int val_eq = is_equivalent((const char *)ev, (const char *)av); + if (!name_eq || !val_eq) { + r.failed = 1; + snprintf(r.detail, sizeof(r.detail), + "attribute mismatch at %s[@%s]", path, ea->name); + xmlFree(ev); + xmlFree(av); + return r; + } + xmlFree(ev); + xmlFree(av); + ea = ea->next; + aa = aa->next; + } + + xmlNodePtr ec = expected->children; + xmlNodePtr ac = actual->children; + int idx = 0; + while (ec || ac) { + while (ec && ec->type != XML_ELEMENT_NODE) ec = ec->next; + while (ac && ac->type != XML_ELEMENT_NODE) ac = ac->next; + if (!ec && !ac) break; + if (!ec || !ac) { + r.failed = 1; + snprintf(r.detail, sizeof(r.detail), + "child count mismatch at %s", path); + return r; + } + char child_path[2048]; + snprintf(child_path, sizeof(child_path), "%s/%s[%d]", + path, ec->name, idx); + r = do_compare(ec, ac, child_path); + if (r.failed) return r; + ec = ec->next; + ac = ac->next; + idx++; + } + + return r; +} + +CompareResult compare_elements(xmlNodePtr expected, xmlNodePtr actual) { + const char *root_name = expected ? (const char *)expected->name : "/"; + return do_compare(expected, actual, root_name); +} diff --git a/gen/test/c/src/compare.h b/gen/test/c/src/compare.h new file mode 100644 index 000000000..a6dc411cc --- /dev/null +++ b/gen/test/c/src/compare.h @@ -0,0 +1,13 @@ +#ifndef MX_CORERT_COMPARE_H +#define MX_CORERT_COMPARE_H + +#include + +typedef struct { + int failed; + char detail[1024]; +} CompareResult; + +CompareResult compare_elements(xmlNodePtr expected, xmlNodePtr actual); + +#endif diff --git a/gen/test/c/src/discover.c b/gen/test/c/src/discover.c new file mode 100644 index 000000000..d790f85a0 --- /dev/null +++ b/gen/test/c/src/discover.c @@ -0,0 +1,129 @@ +#include "discover.h" + +#include +#include +#include +#include +#include + +static const char *excluded_segments[] = { + "expected", "testOutput", "generalxml", "smufl", NULL}; + +static int is_excluded_path(const char *rel) { + for (const char **seg = excluded_segments; *seg; seg++) { + const char *p = rel; + while (*p) { + const char *slash = strchr(p, '/'); + int len = slash ? (int)(slash - p) : (int)strlen(p); + if (len == (int)strlen(*seg) && strncmp(p, *seg, len) == 0) + return 1; + if (!slash) break; + p = slash + 1; + } + } + return 0; +} + +static int has_xml_extension(const char *path) { + const char *dot = strrchr(path, '.'); + if (!dot) return 0; + return strcmp(dot, ".xml") == 0 || strcmp(dot, ".musicxml") == 0; +} + +static int is_fixup_sidecar(const char *path) { + const char *suffix = ".fixup.xml"; + size_t slen = strlen(suffix); + size_t plen = strlen(path); + if (plen < slen) return 0; + return strcmp(path + plen - slen, suffix) == 0; +} + +static int has_invalid_marker(const char *path) { + size_t len = strlen(path); + char *marker = malloc(len + 9); + if (!marker) return 0; + sprintf(marker, "%s.invalid", path); + struct stat st; + int exists = stat(marker, &st) == 0; + free(marker); + return exists; +} + +static int cmp_strings(const void *a, const void *b) { + return strcmp(*(const char **)a, *(const char **)b); +} + +static void walk_dir(const char *dir, const char *data_root, + char ***out, int *count, int *cap) { + DIR *d = opendir(dir); + if (!d) return; + + struct dirent *ent; + while ((ent = readdir(d)) != NULL) { + if (ent->d_name[0] == '.') continue; + + size_t path_len = strlen(dir) + 1 + strlen(ent->d_name) + 1; + char *path = malloc(path_len); + snprintf(path, path_len, "%s/%s", dir, ent->d_name); + + struct stat st; + if (stat(path, &st) != 0) { + free(path); + continue; + } + + if (S_ISDIR(st.st_mode)) { + walk_dir(path, data_root, out, count, cap); + free(path); + continue; + } + + if (!S_ISREG(st.st_mode)) { + free(path); + continue; + } + + size_t root_len = strlen(data_root); + const char *rel = path + root_len; + if (*rel == '/') rel++; + + if (is_excluded_path(rel) || !has_xml_extension(path) || + is_fixup_sidecar(path) || has_invalid_marker(path)) { + free(path); + continue; + } + + if (*count >= *cap) { + *cap = *cap ? *cap * 2 : 256; + *out = realloc(*out, sizeof(char *) * *cap); + } + (*out)[(*count)++] = path; + } + closedir(d); +} + +FileList discover_input_files(const char *data_root) { + FileList fl = {NULL, 0}; + int cap = 0; + walk_dir(data_root, data_root, &fl.paths, &fl.count, &cap); + if (fl.count > 1) + qsort(fl.paths, fl.count, sizeof(char *), cmp_strings); + return fl; +} + +void file_list_free(FileList *list) { + for (int i = 0; i < list->count; i++) + free(list->paths[i]); + free(list->paths); + list->paths = NULL; + list->count = 0; +} + +char *to_test_name(const char *abs_path, const char *data_root) { + size_t root_len = strlen(data_root); + if (strncmp(abs_path, data_root, root_len) != 0) + return strdup(abs_path); + const char *rel = abs_path + root_len; + if (*rel == '/') rel++; + return strdup(rel); +} diff --git a/gen/test/c/src/discover.h b/gen/test/c/src/discover.h new file mode 100644 index 000000000..7e12b04b6 --- /dev/null +++ b/gen/test/c/src/discover.h @@ -0,0 +1,14 @@ +#ifndef MX_CORERT_DISCOVER_H +#define MX_CORERT_DISCOVER_H + +typedef struct { + char **paths; + int count; +} FileList; + +FileList discover_input_files(const char *data_root); +void file_list_free(FileList *list); + +char *to_test_name(const char *abs_path, const char *data_root); + +#endif diff --git a/gen/test/c/src/fixer.c b/gen/test/c/src/fixer.c new file mode 100644 index 000000000..119b62a5b --- /dev/null +++ b/gen/test/c/src/fixer.c @@ -0,0 +1,155 @@ +#include "fixer.h" + +#include +#include +#include +#include + +static char *fixup_sidecar_path(const char *input_path) { + const char *dot = strrchr(input_path, '.'); + if (!dot) return NULL; + size_t base_len = dot - input_path; + const char *suffix = ".fixup.xml"; + size_t suf_len = strlen(suffix); + char *out = malloc(base_len + suf_len + 1); + if (!out) return NULL; + memcpy(out, input_path, base_len); + memcpy(out + base_len, suffix, suf_len + 1); + return out; +} + +static char *xml_node_text(xmlNodePtr node) { + xmlChar *content = xmlNodeGetContent(node); + if (!content) return strdup(""); + char *s = strdup((const char *)content); + xmlFree(content); + return s; +} + +static int parse_replace(xmlNodePtr replace, Fixup *out) { + int got_type = 0, got_name = 0, got_value = 0, got_repl = 0; + for (xmlNodePtr child = replace->children; child; child = child->next) { + if (child->type != XML_ELEMENT_NODE) continue; + char *text = xml_node_text(child); + if (strcmp((const char *)child->name, "type") == 0) { + if (strcmp(text, "element") == 0) { + out->type = 0; + got_type = 1; + } else if (strcmp(text, "attribute") == 0) { + out->type = 1; + got_type = 1; + } + } else if (strcmp((const char *)child->name, "name") == 0) { + out->name = strdup(text); + got_name = 1; + } else if (strcmp((const char *)child->name, "value") == 0) { + out->value = strdup(text); + got_value = 1; + } else if (strcmp((const char *)child->name, "replacement-value") == 0) { + out->replacement_value = strdup(text); + got_repl = 1; + } + free(text); + } + return got_type && got_name && got_value && got_repl; +} + +FixupList fixer_load(const char *input_file_path) { + FixupList fl = {NULL, 0}; + char *path = fixup_sidecar_path(input_file_path); + if (!path) return fl; + + FILE *f = fopen(path, "r"); + if (!f) { + free(path); + return fl; + } + fclose(f); + + xmlDocPtr doc = xmlReadFile(path, NULL, XML_PARSE_NONET); + free(path); + if (!doc) return fl; + + xmlNodePtr root = xmlDocGetRootElement(doc); + if (!root || strcmp((const char *)root->name, "fixups") != 0) { + xmlFreeDoc(doc); + return fl; + } + + int cap = 0; + for (xmlNodePtr child = root->children; child; child = child->next) { + if (child->type != XML_ELEMENT_NODE) continue; + if (strcmp((const char *)child->name, "replace") != 0) continue; + Fixup fx = {0, NULL, NULL, NULL}; + if (parse_replace(child, &fx)) { + if (fl.count >= cap) { + cap = cap ? cap * 2 : 8; + fl.items = realloc(fl.items, sizeof(Fixup) * cap); + } + fl.items[fl.count++] = fx; + } else { + free(fx.name); + free(fx.value); + free(fx.replacement_value); + } + } + xmlFreeDoc(doc); + return fl; +} + +void fixer_apply_to_expected(const FixupList *fl, xmlNodePtr node) { + if (!fl->count || !node) return; + if (node->type != XML_ELEMENT_NODE) return; + + int has_children = 0; + for (xmlNodePtr c = node->children; c; c = c->next) { + if (c->type == XML_ELEMENT_NODE) { + has_children = 1; + break; + } + } + + if (!has_children) { + char *text = xml_node_text(node); + for (int i = 0; i < fl->count; i++) { + if (fl->items[i].type != 0) continue; + if (strcmp((const char *)node->name, fl->items[i].name) != 0) continue; + if (strcmp(text, fl->items[i].value) == 0) { + xmlNodeSetContent(node, (const xmlChar *)fl->items[i].replacement_value); + break; + } + } + free(text); + } + + for (xmlAttrPtr attr = node->properties; attr; attr = attr->next) { + xmlChar *val = xmlGetProp(node, attr->name); + if (!val) continue; + for (int i = 0; i < fl->count; i++) { + if (fl->items[i].type != 1) continue; + if (strcmp((const char *)attr->name, fl->items[i].name) != 0) continue; + if (strcmp((const char *)val, fl->items[i].value) == 0) { + xmlSetProp(node, attr->name, + (const xmlChar *)fl->items[i].replacement_value); + break; + } + } + xmlFree(val); + } + + for (xmlNodePtr child = node->children; child; child = child->next) { + if (child->type == XML_ELEMENT_NODE) + fixer_apply_to_expected(fl, child); + } +} + +void fixer_free(FixupList *fl) { + for (int i = 0; i < fl->count; i++) { + free(fl->items[i].name); + free(fl->items[i].value); + free(fl->items[i].replacement_value); + } + free(fl->items); + fl->items = NULL; + fl->count = 0; +} diff --git a/gen/test/c/src/fixer.h b/gen/test/c/src/fixer.h new file mode 100644 index 000000000..e785b18b9 --- /dev/null +++ b/gen/test/c/src/fixer.h @@ -0,0 +1,22 @@ +#ifndef MX_CORERT_FIXER_H +#define MX_CORERT_FIXER_H + +#include + +typedef struct { + int type; /* 0 = element, 1 = attribute */ + char *name; + char *value; + char *replacement_value; +} Fixup; + +typedef struct { + Fixup *items; + int count; +} FixupList; + +FixupList fixer_load(const char *input_file_path); +void fixer_apply_to_expected(const FixupList *fl, xmlNodePtr node); +void fixer_free(FixupList *fl); + +#endif diff --git a/gen/test/c/src/main.c b/gen/test/c/src/main.c new file mode 100644 index 000000000..d176aa489 --- /dev/null +++ b/gen/test/c/src/main.c @@ -0,0 +1,45 @@ +#include "discover.h" +#include "roundtrip.h" + +#include +#include +#include +#include + +#ifndef MX_REPO_ROOT +#error "MX_REPO_ROOT must be defined at compile time" +#endif + +int main(int argc, char **argv) { + (void)argc; + (void)argv; + + LIBXML_TEST_VERSION + + char data_root[4096]; + snprintf(data_root, sizeof(data_root), "%s/data", MX_REPO_ROOT); + + FileList files = discover_input_files(data_root); + if (files.count == 0) { + fprintf(stderr, "no input files discovered under %s\n", data_root); + return 1; + } + + int pass = 0, fail = 0; + for (int i = 0; i < files.count; i++) { + char *name = to_test_name(files.paths[i], data_root); + RoundtripResult result = run_core_roundtrip(files.paths[i]); + if (result.ok) { + pass++; + } else { + fail++; + printf("FAIL %s: %s\n", name, result.message); + } + free(name); + } + + printf("\n%d tests: %d passed, %d failed\n", pass + fail, pass, fail); + file_list_free(&files); + xmlCleanupParser(); + return fail > 0 ? 1 : 0; +} diff --git a/gen/test/c/src/normalize.c b/gen/test/c/src/normalize.c new file mode 100644 index 000000000..f9e80b28f --- /dev/null +++ b/gen/test/c/src/normalize.c @@ -0,0 +1,125 @@ +#include "normalize.h" + +#include +#include +#include +#include +#include +#include + +#define MUSICXML_VERSION "3.0" + +static const char *decimal_fields[] = { + "top-system-distance", "dynamics", "left-margin", "right-margin", + "staff-distance", "system-distance", "default-y", "default-x", + "tenths", "width", NULL}; + +static int is_decimal_field(const char *name) { + for (const char **f = decimal_fields; *f; f++) { + if (strcmp(name, *f) == 0) return 1; + } + return 0; +} + +static char *strip_trailing_zeros(const char *s) { + if (!s || !*s) return strdup(s ? s : ""); + const char *dot = strrchr(s, '.'); + if (!dot) return strdup(s); + + size_t len = strlen(s); + char *buf = strdup(s); + size_t i = len - 1; + while (i > (size_t)(dot - s) && buf[i] == '0') i--; + if (buf[i] == '.') i--; + buf[i + 1] = '\0'; + + if (strcmp(buf, "-0") == 0) { + buf[0] = '0'; + buf[1] = '\0'; + } + return buf; +} + +static void strip_decimal_zeros(xmlNodePtr node) { + if (!node) return; + if (node->type != XML_ELEMENT_NODE) return; + + int has_element_children = 0; + for (xmlNodePtr c = node->children; c; c = c->next) { + if (c->type == XML_ELEMENT_NODE) { + has_element_children = 1; + break; + } + } + + if (!has_element_children && is_decimal_field((const char *)node->name)) { + xmlChar *text = xmlNodeGetContent(node); + if (text && text[0]) { + char *stripped = strip_trailing_zeros((const char *)text); + xmlNodeSetContent(node, (const xmlChar *)stripped); + free(stripped); + } + xmlFree(text); + } + + for (xmlAttrPtr attr = node->properties; attr; attr = attr->next) { + if (!is_decimal_field((const char *)attr->name)) continue; + xmlChar *val = xmlGetProp(node, attr->name); + if (val && val[0]) { + char *stripped = strip_trailing_zeros((const char *)val); + xmlSetProp(node, attr->name, (const xmlChar *)stripped); + free(stripped); + } + xmlFree(val); + } + + for (xmlNodePtr child = node->children; child; child = child->next) + strip_decimal_zeros(child); +} + +static int attr_cmp(const void *a, const void *b) { + const xmlAttrPtr *aa = a; + const xmlAttrPtr *bb = b; + return strcmp((const char *)(*aa)->name, (const char *)(*bb)->name); +} + +static void sort_attributes(xmlNodePtr node) { + if (!node || node->type != XML_ELEMENT_NODE) return; + + int count = 0; + for (xmlAttrPtr a = node->properties; a; a = a->next) count++; + + if (count > 1) { + xmlAttrPtr *arr = malloc(sizeof(xmlAttrPtr) * count); + int i = 0; + for (xmlAttrPtr a = node->properties; a; a = a->next) + arr[i++] = a; + qsort(arr, count, sizeof(xmlAttrPtr), attr_cmp); + + node->properties = arr[0]; + for (i = 0; i < count - 1; i++) { + arr[i]->next = arr[i + 1]; + arr[i + 1]->prev = arr[i]; + } + arr[0]->prev = NULL; + arr[count - 1]->next = NULL; + free(arr); + } + + for (xmlNodePtr child = node->children; child; child = child->next) + sort_attributes(child); +} + +static void set_root_version(xmlNodePtr root) { + if (!root) return; + xmlSetProp(root, (const xmlChar *)"version", + (const xmlChar *)MUSICXML_VERSION); +} + +void normalize(xmlDocPtr doc) { + if (!doc) return; + xmlNodePtr root = xmlDocGetRootElement(doc); + set_root_version(root); + strip_decimal_zeros(root); + sort_attributes(root); +} diff --git a/gen/test/c/src/normalize.h b/gen/test/c/src/normalize.h new file mode 100644 index 000000000..95743ef07 --- /dev/null +++ b/gen/test/c/src/normalize.h @@ -0,0 +1,8 @@ +#ifndef MX_CORERT_NORMALIZE_H +#define MX_CORERT_NORMALIZE_H + +#include + +void normalize(xmlDocPtr doc); + +#endif diff --git a/gen/test/c/src/roundtrip.c b/gen/test/c/src/roundtrip.c new file mode 100644 index 000000000..e7a0ce371 --- /dev/null +++ b/gen/test/c/src/roundtrip.c @@ -0,0 +1,68 @@ +#include "roundtrip.h" + +#include "compare.h" +#include "fixer.h" +#include "normalize.h" +#include "stub.h" + +#include +#include +#include + +RoundtripResult run_core_roundtrip(const char *abs_input_path) { + RoundtripResult r = {0, ""}; + + xmlDocPtr input_doc = xmlReadFile(abs_input_path, NULL, XML_PARSE_NONET); + if (!input_doc) { + snprintf(r.message, sizeof(r.message), "failed to load: %s", + abs_input_path); + return r; + } + + void *model = NULL; + if (mx_from_xdoc(input_doc, &model) != 0) { + snprintf(r.message, sizeof(r.message), + "from_xdoc: generated parser not implemented"); + xmlFreeDoc(input_doc); + return r; + } + + xmlDocPtr actual_doc = NULL; + if (mx_to_xdoc(model, &actual_doc) != 0) { + snprintf(r.message, sizeof(r.message), "to_xdoc failed"); + mx_model_free(model); + xmlFreeDoc(input_doc); + return r; + } + mx_model_free(model); + normalize(actual_doc); + + xmlDocPtr expected_doc = xmlReadFile(abs_input_path, NULL, XML_PARSE_NONET); + if (!expected_doc) { + snprintf(r.message, sizeof(r.message), "failed to reload expected"); + xmlFreeDoc(actual_doc); + xmlFreeDoc(input_doc); + return r; + } + normalize(expected_doc); + + FixupList fl = fixer_load(abs_input_path); + if (fl.count > 0) + fixer_apply_to_expected(&fl, xmlDocGetRootElement(expected_doc)); + fixer_free(&fl); + + CompareResult cmp = compare_elements( + xmlDocGetRootElement(expected_doc), + xmlDocGetRootElement(actual_doc)); + + if (cmp.failed) { + snprintf(r.message, sizeof(r.message), "%s", cmp.detail); + } else { + r.ok = 1; + } + + xmlFreeDoc(expected_doc); + xmlFreeDoc(actual_doc); + xmlFreeDoc(input_doc); + return r; +} diff --git a/gen/test/c/src/roundtrip.h b/gen/test/c/src/roundtrip.h new file mode 100644 index 000000000..6ec4a7d58 --- /dev/null +++ b/gen/test/c/src/roundtrip.h @@ -0,0 +1,11 @@ +#ifndef MX_CORERT_ROUNDTRIP_H +#define MX_CORERT_ROUNDTRIP_H + +typedef struct { + int ok; + char message[1024]; +} RoundtripResult; + +RoundtripResult run_core_roundtrip(const char *abs_input_path); + +#endif diff --git a/gen/test/c/src/stub.c b/gen/test/c/src/stub.c new file mode 100644 index 000000000..073e382b2 --- /dev/null +++ b/gen/test/c/src/stub.c @@ -0,0 +1,17 @@ +#include "stub.h" + +int mx_from_xdoc(xmlDocPtr input, void **model_out) { + (void)input; + *model_out = NULL; + return -1; /* not implemented */ +} + +int mx_to_xdoc(void *model, xmlDocPtr *output) { + (void)model; + *output = NULL; + return -1; /* not implemented */ +} + +void mx_model_free(void *model) { + (void)model; +} diff --git a/gen/test/c/src/stub.h b/gen/test/c/src/stub.h new file mode 100644 index 000000000..552db1b91 --- /dev/null +++ b/gen/test/c/src/stub.h @@ -0,0 +1,12 @@ +#ifndef MX_CORERT_STUB_H +#define MX_CORERT_STUB_H + +#include + +// Stub for the generated parser. Returns non-zero (failure) until the +// generator emits the C typed model. +int mx_from_xdoc(xmlDocPtr input, void **model_out); +int mx_to_xdoc(void *model, xmlDocPtr *output); +void mx_model_free(void *model); + +#endif From f7462424ff709e429c2dc983737023b46c010f79 Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Mon, 8 Jun 2026 18:11:03 +0200 Subject: [PATCH 04/45] add a cpp generator config stub --- gen/cpp/config.toml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 gen/cpp/config.toml diff --git a/gen/cpp/config.toml b/gen/cpp/config.toml new file mode 100644 index 000000000..05cacf7de --- /dev/null +++ b/gen/cpp/config.toml @@ -0,0 +1,6 @@ +# C++ generator target configuration. +# The generator reads this to know where and how to emit C++ code. + +[output] +# Directory for generated source files, relative to this config file. +dir = "../../src/private/mx/core" From 3e3baf9d8b794bc5239cc197cfb96c9449b06ac6 Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Mon, 8 Jun 2026 18:11:15 +0200 Subject: [PATCH 05/45] gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index d7418a892..a6de4d80b 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,7 @@ package-lock.json # Python bytecode cache __pycache__/ *.pyc + +# Gen test build artifacts +gen/test/c/build/ +gen/test/go/build/ From c6158983b406df6a6e936a717e11fcedd4220b4e Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Mon, 8 Jun 2026 18:11:25 +0200 Subject: [PATCH 06/45] prepare Dockerfile for gen work --- Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile b/Dockerfile index a1060a348..760e1aea4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ ccache \ python3 \ python3-venv \ + golang-go \ + libxml2-dev \ + pkg-config \ && rm -rf /var/lib/apt/lists/* # Unversioned name so the Makefile invokes the formatter without the suffix. From 357bcd8c2ba951de9d9105560292933028dd0400 Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Mon, 8 Jun 2026 18:11:41 +0200 Subject: [PATCH 07/45] prepare Makefile for gen work --- Makefile | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 81 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index c97c3f71b..d1bb891a4 100644 --- a/Makefile +++ b/Makefile @@ -7,9 +7,9 @@ # layer and the core roundtrip (corert) test harness. corert does not compile # until the new generator emits src/private/mx/core -- expected for now. # -# Native targets (ezxml/core-dev/test-core-dev) drive CMake directly. The fmt -# and check gates run inside the pinned `mx-sdk` Docker toolchain, built once -# and bind-mounting the workspace. Requires CMake >= 3.13. +# Native targets (ezxml/core-dev/test-core-dev) drive CMake directly. Docker- +# gated targets (fmt, check, gen, build-go, test-go, build-c, test-c) run +# inside the pinned `mx-sdk` toolchain. Requires CMake >= 3.13. # ============================================================================ CMAKE ?= cmake @@ -45,20 +45,40 @@ FIND_CPP := find src \ .DEFAULT_GOAL := help .PHONY: help sdk fmt check ezxml core-dev test-core-dev \ + gen gen-cpp gen-go gen-c \ + build-go build-c test-go test-c \ clean clean-docker check-docker docker-volume help: @echo 'mx targets (clean-slate spike -- see AGENTS.md):' + @echo '' + @echo ' C++ (native):' @echo ' make ezxml Build the embedded ezxml XML layer.' @echo ' make core-dev Build the corert binary (fails until mx/core is regenerated).' @echo " make test-core-dev Run the core roundtrip suite. Filter: ARGS='[core-roundtrip] lysuite/*'" + @echo '' + @echo ' Generator (via mx-sdk):' + @echo ' make gen Run the generator for all targets (cpp/go/c).' + @echo ' make gen-cpp Run the generator for the C++ target.' + @echo ' make gen-go Run the generator for the Go target.' + @echo ' make gen-c Run the generator for the C target.' + @echo '' + @echo ' Go test target (via mx-sdk):' + @echo ' make build-go Build Go corert tests.' + @echo ' make test-go Run Go corert tests.' + @echo '' + @echo ' C test target (via mx-sdk):' + @echo ' make build-c Build C corert test binary.' + @echo ' make test-c Run C corert tests.' + @echo '' + @echo ' Housekeeping:' @echo ' make fmt Format C++ under src/ via mx-sdk.' @echo ' make check fmt-check via mx-sdk.' @echo ' make sdk Build the mx-sdk Docker toolchain image.' - @echo ' make clean Remove the build/ tree.' + @echo ' make clean Remove the build/ tree and gen build artifacts.' @echo ' make clean-docker Remove the sdk image and build volume.' -# --- Native builds ---------------------------------------------------------- +# --- Native C++ builds (no Docker) ------------------------------------------ ezxml: $(CMAKE) -S . -B $(BUILD_ROOT)/dev -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) @@ -68,8 +88,6 @@ core-dev: $(CMAKE) -S . -B $(BUILD_ROOT)/core-dev -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) -DMX_CORE_DEV=on $(CMAKE) --build $(BUILD_ROOT)/core-dev --parallel $(JOBS) -# --allow-running-no-tests keeps the run green once corert builds but before any -# data/ case is wired up. (Until mx/core exists, core-dev above fails first.) test-core-dev: core-dev $(BUILD_ROOT)/core-dev/mxtest-core-dev --allow-running-no-tests $(ARGS) @@ -77,6 +95,8 @@ test-core-dev: core-dev clean: rm -rf $(BUILD_ROOT) + rm -rf gen/test/c/build + rm -rf gen/test/go/build clean-docker: -rm -f $(DOCKER_STAMP) @@ -89,9 +109,11 @@ check-docker: { echo "Docker not found. Install it to use the mx-sdk gates:"; \ echo " https://docs.docker.com/get-docker/"; exit 1; } +# === Docker-gated targets =================================================== + ifdef MX_RUNNING_IN_DOCKER -# ===== Inside the container: run the pinned tools directly ================== +# ----- Inside the container: run tools directly ----------------------------- fmt: @$(FIND_CPP) | xargs -r clang-format -i @@ -101,9 +123,35 @@ check: @$(FIND_CPP) | xargs -r clang-format --dry-run --Werror @echo "fmt-check passed." +gen: gen-cpp gen-go gen-c + +gen-cpp: + python3 -m gen gen/cpp/config.toml + +gen-go: + python3 -m gen gen/test/go/config.toml + +gen-c: + python3 -m gen gen/test/c/config.toml + +build-go: + cd gen/test/go && MX_REPO_ROOT=/workspace go test -c -o build/corert-test ./corert/ + +test-go: + cd gen/test/go && MX_REPO_ROOT=/workspace go test -count=1 -v ./corert/ $(ARGS) + +build-c: + $(CMAKE) -S gen/test/c -B gen/test/c/build \ + -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) \ + -DMX_REPO_ROOT=/workspace + $(CMAKE) --build gen/test/c/build --parallel $(JOBS) + +test-c: build-c + gen/test/c/build/corert-c + else -# ===== Outside the container: build the image once, then docker run ======== +# ----- Outside the container: build image, then docker run ------------------ $(DOCKER_STAMP): Dockerfile | check-docker @mkdir -p $(BUILD_ROOT) @@ -122,4 +170,28 @@ fmt: $(DOCKER_STAMP) docker-volume check: $(DOCKER_STAMP) docker-volume $(DOCKER_RUN) make check +gen: $(DOCKER_STAMP) docker-volume + $(DOCKER_RUN) make gen + +gen-cpp: $(DOCKER_STAMP) docker-volume + $(DOCKER_RUN) make gen-cpp + +gen-go: $(DOCKER_STAMP) docker-volume + $(DOCKER_RUN) make gen-go + +gen-c: $(DOCKER_STAMP) docker-volume + $(DOCKER_RUN) make gen-c + +build-go: $(DOCKER_STAMP) docker-volume + $(DOCKER_RUN) make build-go + +test-go: $(DOCKER_STAMP) docker-volume + $(DOCKER_RUN) make test-go + +build-c: $(DOCKER_STAMP) docker-volume + $(DOCKER_RUN) make build-c + +test-c: $(DOCKER_STAMP) docker-volume + $(DOCKER_RUN) make test-c + endif From c2c5aa05ed3bf91c97c3a896f91b5c499522a372 Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Mon, 8 Jun 2026 18:12:14 +0200 Subject: [PATCH 08/45] update AGENTS.md with project info --- AGENTS.md | 176 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 174 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index e269b1b87..99aa57a4d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,178 @@ # mx -We are implementing a generator from the MusicXML XSD specification. +A code generator that reads the MusicXML 4.0 XSD specification and emits typed document +serialization/deserialization libraries in multiple languages. C++ is the primary target; Go and C +are secondary targets that keep the generator architecture honest about extensibility. -Ignore git history prior to `b01288`. Reading anthing before that commit will only confuse you and +Ignore git history prior to `b01288`. Reading anything before that commit will only confuse you and degrade your performance. + +## Repository layout + +``` +mx/ + AGENTS.md <- you are here + Makefile <- top-level build driver (native + Docker-gated targets) + Dockerfile <- mx-sdk toolchain image (Ubuntu 24.04, GCC 14, Go, libxml2, Python 3) + CMakeLists.txt <- C++ project: ezxml library + corert test harness + data/ <- MusicXML test corpus (~830 files, see data/README.md) + src/private/ <- C++ source + mx/ezxml/ <- vendored pugixml-backed XML layer (builds today) + mx/core/ <- generated C++ typed model (empty until gen emits code) + mx/utility/ <- generated C++ utilities (empty) + mxtest/corert/ <- C++ core roundtrip test harness (Catch2, dynamic registration) + mxtest/import/ <- normalization helpers (sort attrs, strip decimal zeros) + mxtest/file/ <- PathRoot.h (CMake-generated, gitignored) + cpul/ <- vendored Catch2 test runner + gen/ <- code generator system + __main__.py <- generator entry point (stub, not yet implemented) + cpp/config.toml <- C++ target configuration + test/go/ <- Go corert test target + config.toml <- Go target configuration + go.mod, go.sum <- Go module (etree dependency, vendored) + vendor/ <- vendored Go deps + corert/ <- test package (discover, fixer, normalize, roundtrip, test) + stub/ <- placeholder parser stubs (always return error) + test/c/ <- C corert test target + config.toml <- C target configuration + CMakeLists.txt <- CMake project using libxml2 + src/ <- C source (main, discover, fixer, normalize, compare, roundtrip, stub) +``` + +## Build system + +### Docker (mx-sdk) + +All Docker-gated targets auto-build the `mx-sdk` image on first use. The workspace is bind-mounted +at `/workspace`. A named Docker volume `mx-build` persists CMake/ccache state across runs. + +The `MX_RUNNING_IN_DOCKER` env var switches the Makefile between in-container (direct tool +invocation) and outside-container (docker run wrapper) behavior. + +### Makefile targets + +**Native C++ (no Docker required):** +- `make ezxml` - build the ezxml XML layer +- `make core-dev` - build corert binary (fails until gen emits `mx/core`) +- `make test-core-dev` - run C++ corert suite + +**Generator (via mx-sdk):** +- `make gen` - run generator for all targets (cpp/go/c) +- `make gen-cpp`, `make gen-go`, `make gen-c` - single-target gen + +**Go test target (via mx-sdk):** +- `make build-go` - compile Go corert test binary +- `make test-go` - run Go corert tests + +**C test target (via mx-sdk):** +- `make build-c` - compile C corert test binary +- `make test-c` - build + run C corert tests + +**Housekeeping:** +- `make fmt` / `make check` - C++ formatting (via mx-sdk) +- `make sdk` - force-build the Docker image +- `make clean` - remove all build artifacts +- `make clean-docker` - remove Docker image and volume + +## The corert (core roundtrip) test + +The corert test is the primary correctness gate. It exercises the generated parser by round-tripping +every eligible XML file in `data/` through the typed model and comparing the output to a normalized +form of the input. + +### Flow (same in all three languages) + +1. **Discover** eligible `.xml`/`.musicxml` files under `data/`, excluding directories `expected`, + `testOutput`, `generalxml`, `smufl`, and files matching `*.fixup.xml` or having a `.invalid` + sibling marker. +2. For each file: + a. Load the XML into a DOM. + b. Set the root `version` attribute to `"3.0"`. + c. **Parse** into the typed model via `fromXDoc` (this is the generated code -- currently a stub + that always fails). + d. **Serialize** back to XML via `toXDoc`. + e. **Normalize** the actual output: set XML declaration, set DOCTYPE, set version, strip trailing + zeros from decimal fields, sort attributes alphabetically. + f. Load a fresh expected document from disk, apply the same normalization. + g. Apply **fixups** from `.fixup.xml` sidecars to the expected document. + h. **Compare** the two DOMs depth-first: element names, text content, attributes (with numeric + equivalence for ints/floats). +3. Report pass/fail per file. + +### Current state + +All three test targets (C++, Go, C) discover ~829 files and produce 100% failures because the +generated parser stubs always return "not implemented". This is the expected state until the +generator emits code. + +### Data directory conventions + +- `data/README.md` - documents marker file conventions. +- `*.xml.invalid` - sibling marker meaning the file is not valid MusicXML; skip it. +- `*.fixup.xml` - sidecar describing value substitutions for the expected document. Used when mx + clamps out-of-bounds values on import (e.g. MIDI channel 0 -> 1). Format: + ```xml + + + element + midi-channel + 0 + 1 + + + ``` +- `data/testOutput/corert/` - debug output directory for failure diffs (gitignored via build/). + +### Normalization pipeline + +Applied to both expected and actual documents before comparison: + +1. Set XML declaration: ``. +2. Set DOCTYPE based on root element name (`score-timewise` vs `score-partwise`). +3. Set root `version` attribute to `"3.0"`. +4. Strip trailing zeros from decimal fields: `top-system-distance`, `dynamics`, `left-margin`, + `right-margin`, `staff-distance`, `system-distance`, `default-y`, `default-x`, `tenths`, `width`. +5. Sort attributes alphabetically (must be last). + +### Numeric equivalence + +Text comparisons use numeric equivalence: if both strings parse as integers (or floats), compare +their values instead of their string representations. Float comparison uses epsilon `< 0.00000001`. + +## Generator architecture + +The generator (`gen/`) is a Python program invoked as `python3 -m gen `. It reads the +MusicXML XSD spec and emits code for a specific language target based on the config file. + +Each target has: +- `config.toml` - specifies the output directory (relative to the config file) and will eventually + hold language-specific settings. +- Template files (not yet created) - Jinja2 or similar templates for code generation. + +**Not yet implemented.** The `gen/__main__.py` stub exits with "not implemented". + +## Language targets + +### C++ (primary, `gen/cpp/`) + +The existing codebase. Generated code lands in `src/private/mx/core/`. The ezxml layer +(`src/private/mx/ezxml/`) provides the XML DOM that the generated code builds on. + +### Go (test target, `gen/test/go/`) + +Uses `github.com/beevik/etree` (vendored) for DOM-style XML. The generated code will land in +`gen/test/go/mx/`. Test runner uses Go's `testing` package with subtests. + +### C (test target, `gen/test/c/`) + +Uses libxml2 (apt package in Docker). The generated code will land in `gen/test/c/mx/`. Test runner +is a simple `main()` that prints pass/fail per file and a summary. + +## Key files to understand + +- `src/private/mxtest/corert/CoreRoundtripImpl.cpp` - the C++ roundtrip implementation (reference + for Go and C ports) +- `src/private/mxtest/corert/Fixer.cpp` - C++ fixup logic (reference for ports) +- `src/private/mxtest/import/DecimalFields.h` - list of decimal fields and zero-stripping logic +- `gen/test/go/corert/roundtrip.go` - Go roundtrip implementation +- `gen/test/c/src/roundtrip.c` - C roundtrip implementation From a69a58c921886a78456eff30efe626b0ebceed28 Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Mon, 8 Jun 2026 18:12:23 +0200 Subject: [PATCH 09/45] add gen program stub --- gen/__main__.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 gen/__main__.py diff --git a/gen/__main__.py b/gen/__main__.py new file mode 100644 index 000000000..66a6f0927 --- /dev/null +++ b/gen/__main__.py @@ -0,0 +1,19 @@ +"""mx code generator entry point. + +Usage: python3 -m gen + +Reads a MusicXML 4.0 XSD specification and generates typed document +serialization/deserialization code for the target described in the given +config file. +""" + +import sys + + +def main() -> int: + print("error: generator not implemented", file=sys.stderr) + return 1 + + +if __name__ == "__main__": + sys.exit(main()) From 93d8035076439b27a8223020516fbfcbb9b4c5f8 Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Mon, 8 Jun 2026 22:02:23 +0200 Subject: [PATCH 10/45] add XSD parser, IR, and generator analysis First stages of the gen pipeline (python3 -m gen): - gen/xsd: ElementTree parser into a model of the MusicXML XSD subset, plus a structural analysis report ("analyze" command). - gen/ir: lowers the XSD model to a resolved, dependency-ordered intermediate representation ("ir" command). Names and hoists anonymous types, collapses restriction chains, resolves refs, and drops unreferenced types. - gen/tests: unittest suite asserting the acyclic-graph and no-name-collision invariants and IR referential integrity across all MusicXML versions in docs (make test-gen). - gen/README.md: architecture, IR glossary, and XSD analysis. - AGENTS.md: reconcile the now-stale generator section. --- AGENTS.md | 71 +++---- Makefile | 6 +- gen/README.md | 222 ++++++++++++++++++++ gen/__init__.py | 1 + gen/__main__.py | 67 +++++- gen/ir/__init__.py | 6 + gen/ir/build.py | 390 +++++++++++++++++++++++++++++++++++ gen/ir/dump.py | 33 +++ gen/ir/model.py | 212 +++++++++++++++++++ gen/tests/__init__.py | 0 gen/tests/test_ir.py | 168 +++++++++++++++ gen/xsd/__init__.py | 6 + gen/xsd/analyze.py | 462 ++++++++++++++++++++++++++++++++++++++++++ gen/xsd/model.py | 229 +++++++++++++++++++++ gen/xsd/parser.py | 258 +++++++++++++++++++++++ 15 files changed, 2085 insertions(+), 46 deletions(-) create mode 100644 gen/README.md create mode 100644 gen/__init__.py create mode 100644 gen/ir/__init__.py create mode 100644 gen/ir/build.py create mode 100644 gen/ir/dump.py create mode 100644 gen/ir/model.py create mode 100644 gen/tests/__init__.py create mode 100644 gen/tests/test_ir.py create mode 100644 gen/xsd/__init__.py create mode 100644 gen/xsd/analyze.py create mode 100644 gen/xsd/model.py create mode 100644 gen/xsd/parser.py diff --git a/AGENTS.md b/AGENTS.md index 99aa57a4d..1c2f62361 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,17 +15,20 @@ mx/ Makefile <- top-level build driver (native + Docker-gated targets) Dockerfile <- mx-sdk toolchain image (Ubuntu 24.04, GCC 14, Go, libxml2, Python 3) CMakeLists.txt <- C++ project: ezxml library + corert test harness - data/ <- MusicXML test corpus (~830 files, see data/README.md) + data/ <- MusicXML test corpus (~1,347 files, see data/README.md) src/private/ <- C++ source - mx/ezxml/ <- vendored pugixml-backed XML layer (builds today) - mx/core/ <- generated C++ typed model (empty until gen emits code) - mx/utility/ <- generated C++ utilities (empty) + mx/ezxml/ <- vendored pugixml-backed XML layer + mx/core/ <- generated C++ typed model + mx/utility/ <- generated C++ utilities mxtest/corert/ <- C++ core roundtrip test harness (Catch2, dynamic registration) mxtest/import/ <- normalization helpers (sort attrs, strip decimal zeros) mxtest/file/ <- PathRoot.h (CMake-generated, gitignored) cpul/ <- vendored Catch2 test runner - gen/ <- code generator system - __main__.py <- generator entry point (stub, not yet implemented) + gen/ <- code generator system (see gen/README.md) + __main__.py <- CLI: analyze | ir | + README.md <- architecture, IR glossary, XSD analysis + xsd/ <- XSD parser + structural analysis + ir/ <- resolved intermediate representation (IR) cpp/config.toml <- C++ target configuration test/go/ <- Go corert test target config.toml <- Go target configuration @@ -51,34 +54,15 @@ invocation) and outside-container (docker run wrapper) behavior. ### Makefile targets -**Native C++ (no Docker required):** -- `make ezxml` - build the ezxml XML layer -- `make core-dev` - build corert binary (fails until gen emits `mx/core`) -- `make test-core-dev` - run C++ corert suite - -**Generator (via mx-sdk):** -- `make gen` - run generator for all targets (cpp/go/c) -- `make gen-cpp`, `make gen-go`, `make gen-c` - single-target gen - -**Go test target (via mx-sdk):** -- `make build-go` - compile Go corert test binary -- `make test-go` - run Go corert tests - -**C test target (via mx-sdk):** -- `make build-c` - compile C corert test binary -- `make test-c` - build + run C corert tests - -**Housekeeping:** -- `make fmt` / `make check` - C++ formatting (via mx-sdk) -- `make sdk` - force-build the Docker image -- `make clean` - remove all build artifacts -- `make clean-docker` - remove Docker image and volume +Run `make help` for the full, current target list (native C++, generator, Go/C test targets, +housekeeping). Docker-gated targets auto-build and run via mx-sdk. ## The corert (core roundtrip) test The corert test is the primary correctness gate. It exercises the generated parser by round-tripping every eligible XML file in `data/` through the typed model and comparing the output to a normalized -form of the input. +form of the input. The corpus is pinned to version `3.0` throughout (input and expected) even though +the generator targets the 4.0 schema, so comparison runs against a stable baseline. ### Flow (same in all three languages) @@ -88,11 +72,9 @@ form of the input. 2. For each file: a. Load the XML into a DOM. b. Set the root `version` attribute to `"3.0"`. - c. **Parse** into the typed model via `fromXDoc` (this is the generated code -- currently a stub - that always fails). + c. **Parse** into the typed model via `fromXDoc` (the generated code). d. **Serialize** back to XML via `toXDoc`. - e. **Normalize** the actual output: set XML declaration, set DOCTYPE, set version, strip trailing - zeros from decimal fields, sort attributes alphabetically. + e. **Normalize** the actual output (see Normalization pipeline below). f. Load a fresh expected document from disk, apply the same normalization. g. Apply **fixups** from `.fixup.xml` sidecars to the expected document. h. **Compare** the two DOMs depth-first: element names, text content, attributes (with numeric @@ -130,8 +112,7 @@ Applied to both expected and actual documents before comparison: 1. Set XML declaration: ``. 2. Set DOCTYPE based on root element name (`score-timewise` vs `score-partwise`). 3. Set root `version` attribute to `"3.0"`. -4. Strip trailing zeros from decimal fields: `top-system-distance`, `dynamics`, `left-margin`, - `right-margin`, `staff-distance`, `system-distance`, `default-y`, `default-x`, `tenths`, `width`. +4. Strip trailing zeros from decimal fields (the list lives in `DecimalFields.h`). 5. Sort attributes alphabetically (must be last). ### Numeric equivalence @@ -141,15 +122,21 @@ their values instead of their string representations. Float comparison uses epsi ## Generator architecture -The generator (`gen/`) is a Python program invoked as `python3 -m gen `. It reads the -MusicXML XSD spec and emits code for a specific language target based on the config file. +The generator (`gen/`) is a Python program structured as a three-stage pipeline: parse the MusicXML +XSD into a model (`gen/xsd/`), lower that into a resolved intermediate representation (`gen/ir/`), +then emit target code from the IR. See `gen/README.md` for the architecture, IR glossary, and a +structural analysis of the schema. + +Commands: +- `python3 -m gen analyze [xsd]` - print a structural analysis of the XSD. +- `python3 -m gen ir [--type NAME] [xsd]` - lower the XSD to the IR and print it as JSON. +- `python3 -m gen ` - emit code for the target in the config (not yet implemented). -Each target has: -- `config.toml` - specifies the output directory (relative to the config file) and will eventually - hold language-specific settings. -- Template files (not yet created) - Jinja2 or similar templates for code generation. +Each target has a `config.toml` specifying the output directory (relative to the config file) and, +eventually, language-specific settings. -**Not yet implemented.** The `gen/__main__.py` stub exits with "not implemented". +**Status.** The parse, IR, and analysis stages exist. The emit stage and its templates are not yet +implemented, so `python3 -m gen ` still exits with an error. ## Language targets diff --git a/Makefile b/Makefile index d1bb891a4..e4c8c4a4a 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ FIND_CPP := find src \ -type f \( -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \) -print .DEFAULT_GOAL := help -.PHONY: help sdk fmt check ezxml core-dev test-core-dev \ +.PHONY: help sdk fmt check ezxml core-dev test-core-dev test-gen \ gen gen-cpp gen-go gen-c \ build-go build-c test-go test-c \ clean clean-docker check-docker docker-volume @@ -56,6 +56,7 @@ help: @echo ' make ezxml Build the embedded ezxml XML layer.' @echo ' make core-dev Build the corert binary (fails until mx/core is regenerated).' @echo " make test-core-dev Run the core roundtrip suite. Filter: ARGS='[core-roundtrip] lysuite/*'" + @echo ' make test-gen Run the generator (parser + IR) Python tests.' @echo '' @echo ' Generator (via mx-sdk):' @echo ' make gen Run the generator for all targets (cpp/go/c).' @@ -91,6 +92,9 @@ core-dev: test-core-dev: core-dev $(BUILD_ROOT)/core-dev/mxtest-core-dev --allow-running-no-tests $(ARGS) +test-gen: + python3 -m unittest discover -s gen/tests -t . $(ARGS) + # --- Housekeeping ----------------------------------------------------------- clean: diff --git a/gen/README.md b/gen/README.md new file mode 100644 index 000000000..c031d7810 --- /dev/null +++ b/gen/README.md @@ -0,0 +1,222 @@ +# mx generator (`gen/`) + +Reads the MusicXML XSD and emits typed serialization/deserialization libraries. Runs as +`python3 -m gen`. This document covers the parsing and analysis layer that exists today; for the +build system, Docker toolchain, and corert test, see [`../AGENTS.md`](../AGENTS.md). + +## Background + +### Pipeline + +``` +XSD file --parse--> XSD model --lower--> IR --emit--> C++ / Go / C + (gen.xsd) (gen.ir) (templates, not yet built) +``` + +1. Parse (`gen/xsd/`) reads the XSD into a model mirroring it 1:1, still speaking XSD: restriction + chains, attribute-group references, anonymous inline types. +2. Lower (`gen/ir/`) resolves that into the intermediate representation (IR): a flat, fully-named, + dependency-ordered model in code-generation terms. A pure function of the XSD, no configurable + knobs (see Design principles). +3. Emit turns the IR into code via per-language templates. Not yet implemented. + +### Layout + +``` +gen/ + __main__.py CLI: analyze | ir | + xsd/ + model.py dataclasses mirroring the XSD subset MusicXML uses + parser.py ElementTree parser, no external dependencies + analyze.py structural analysis + reusable index helpers + ir/ + model.py the IR dataclasses + build.py lowering from the XSD model to the IR + dump.py IR to JSON + cpp/, test/go/, test/c/ per-target config and corert test harnesses +``` + +### Design principles + +- Generate by shape, not by element. Every type falls into one of 8 shapes (4 value + 4 complex, + defined in the Glossary). One template per shape; no per-element special casing. +- The IR is a pure, canonical function of the XSD. All schema-specific reasoning -- resolving + references, ordering, dead-code removal, naming -- happens once, in the IR, shared by every + target. Per-language choices (inheritance vs flattening, mixins vs inlined attributes) belong to + the emitter. The IR takes no configuration. +- Resolve, but preserve names. The IR computes every resolved answer (effective primitives, + cardinalities, dependency order) yet keeps the schema's named structure (aliases, inheritance + edges, model groups, attribute groups) so each emitter can decide how much to collapse. + +## Usage + +``` +python3 -m gen analyze [xsd] # structural analysis report (text) +python3 -m gen ir [--type NAME] [xsd] # lower to IR, print as JSON (whole IR, or one type) +python3 -m gen # emit code for a target (not yet implemented) +``` + +`xsd` defaults to `docs/musicxml-4.0-ed15c23.xsd`. Examples: + +``` +python3 -m gen ir --type note # one type +python3 -m gen ir > build/ir/musicxml-4.0.ir.json # whole IR (build/ is gitignored) +jq '.complex_types[] | select(.name=="note")' build/ir/musicxml-4.0.ir.json +python3 -m gen analyze docs/musicxml-3.1.xsd # analyze a different version +``` + +## Glossary + +### XSD source terms + +W3C XML Schema constructs MusicXML uses, as the parser sees them. + +- simpleType -- a type with no child elements and no attributes: just a constrained text value + (enumeration, number range, pattern, or union). Becomes an IR value type. +- complexType -- a type for an element with attributes and/or child elements. Becomes an IR complex + type. +- element -- a named node in the document. In MusicXML every element is declared inline as + `name="x" type="y"`; there are no global element refs. +- attribute -- a `name="value"` pair on an element. Its type is always a simpleType or builtin. +- group (`xs:group`) -- a named, reusable fragment of element content (a sequence/choice) spliced + into complex types by reference. No identity in the XML document. +- attributeGroup -- a named, reusable bundle of attributes referenced by complex types. +- restriction / extension -- the two ways one type derives from another: narrowing with facets, or + adding to it. +- simpleContent / complexContent -- a complex type whose body is a text value plus attributes + (simpleContent), or that derives from another complex type (complexContent). +- facet -- a constraint on a simpleType: `enumeration`, `pattern`, `minInclusive`, `maxInclusive`, + `minExclusive`, `maxExclusive`, `minLength`, `maxLength`, `length`. +- particle -- a piece of a content model: an element, `sequence`, `choice`, or group ref, each with + `minOccurs`/`maxOccurs`. +- anonymous (inline) type -- a type defined in place on an element rather than named at top level. + The IR names and hoists these (see synthesized type). + +### IR stats keys + +The `stats` block summarizes the lowered model. Every key: + +- value_types (143) -- IR value types: a single scalar value, no child elements. Lowered from XSD + simpleTypes (plus the text body of simpleContent complex types). +- value_kinds -- value types by kind: + - enum (96) -- a closed set of allowed string tokens, e.g. `step` = {A..G}. IR fields: `base` (the + primitive the tokens are drawn from, usually `token`/`string`) and `values`. Emits an enum class + plus string<->enum lookup tables. + - number (25) -- a numeric value whose resolved primitive is `decimal`/`integer`/ + `positive_integer`/`non_negative_integer`, with optional bounds. IR fields: `base`, and any of + `min_inclusive`/`max_inclusive`/`min_exclusive`/`max_exclusive`. Includes numeric aliases (a + named type that just renames a numeric primitive, e.g. `divisions`). Emits a numeric wrapper + that range-validates on assignment. + - string (18) -- a text value (primitive `string`/`token`/`nmtoken`/`date`) with optional + `patterns` and length constraints. Includes plain string aliases. Emits a string wrapper with an + optional pattern check. + - union (4) -- a value that may be any one of several member value types or inline literal sets, + e.g. `number-or-normal` = decimal | "normal". IR field: `members`. Emits a small tagged variant. +- complex_types (228) -- IR complex types: elements that carry attributes and/or child elements. + Lowered from XSD complexTypes (including synthesized ones). +- complex_kinds -- complex types by kind: + - value (82) -- a typed text body plus attributes (from XSD simpleContent), e.g. + `accidental-text`. IR fields: `value_type` (a Ref to the body's value type), `attributes`, + `attribute_groups`. Emits a class with a `value` field plus attribute fields. + - composite (96) -- child elements arranged in sequences/choices, plus attributes. The structural + workhorse, e.g. `note`. IR field: `content` (a particle tree). Emits a class with one member per + child element (cardinality required/optional/vector), order preserved. + - empty (45) -- an element with no child elements. Two sub-cases the IR unifies: presence-only (a + bare flag, `presence_only: true`) and attributes-only (attributes but no children, e.g. + `empty-placement`). Emits a bool or an attributes-only class. + - derived (5) -- extends another complex type and adds attributes (from XSD complexContent), e.g. + `metronome-tuplet` extends `time-modification`. IR field: `base` (parent type name). Emits + inheritance, or a flattened copy where the language has none. +- groups (27) -- XSD model groups carried into the IR as named, reusable content fragments. +- attribute_groups (45) -- XSD attribute groups carried into the IR as named, reusable attribute + bundles. +- synthesized_types (7) -- complex types the IR created by naming and hoisting anonymous XSD types: + `score-partwise`, `score-timewise`, `partwise-part`, `partwise-measure`, `timewise-part`, + `timewise-measure`, `directive`. The part/measure pairs are context-qualified because the partwise + and timewise hierarchies give them genuinely different shapes. +- dropped_dead_types (5) -- named XSD types nothing references, which the IR omits: + `empty-print-style`, `empty-print-style-align`, `formatted-symbol`, `positive-decimal`, + `start-stop-change-continue` (see XSD Analysis). + +### IR structural terms + +Terms used inside the lowered types, not in `stats`. + +- Ref `{ name, category }` -- a typed reference to another type. `category` is `complex` (a + generated element class), `value` (a generated value type), or `primitive` (a builtin, not + generated). +- primitive -- a builtin base type the generator does not emit. The IR canonicalizes XSD builtins + (`xs:decimal` -> `decimal`, `xs:token` -> `token`, ...) and the 10 `xml:`/`xlink:` attribute refs + into a small primitive set, listed in the `builtins` map. +- attribute -- IR fields: `name`, `type` (a Ref to a value type or primitive), `required`, and + optional `default`/`fixed`. +- particle -- a node in a complex type's `content`: an `element` (a child occurrence), a `sequence` + (ordered list), a `choice` (exactly one of), or a `group` reference. Each carries `min`/`max` (max + may be the string `"unbounded"`). +- cardinality -- the normalized occurrence of an element field: `required` (exactly 1), `optional` + (0 or 1), or `vector` (repeatable). Derived from min/max. +- presence_only -- true for an empty element with no attributes: its only information is whether it + appears, so it maps to a bool. +- base -- for a `derived` complex type, the parent complex type it extends. +- value_type -- for a `value` complex type, the Ref to the value type of its text body. +- deps -- the complex types a type structurally depends on (child element types + base), resolved + through groups. Drives the ordering below. +- roots -- the document root elements: `score-partwise` and `score-timewise`. +- builtins -- the map from XSD/external builtin names to canonical IR primitives. + +### Ordering + +Both `value_types` and `complex_types` are emitted **deps-first**: a type's dependencies always +precede it in the list. The list order *is* the topological order -- there is no separate rank field, +because the array index already encodes it and a duplicate integer would only risk drifting out of +sync. Value types never reference complex types, so concatenating `value_types` then `complex_types` +is a valid total order for a single-file emit. An emitter that wants a different order (alphabetical, +per-file, by shape) has `deps` and can compute its own. Within a value type, union members precede +the union; the only value-to-value dependency is a union referencing its members. + +## XSD Analysis + +Run `python3 -m gen analyze` for the full report. Key findings for MusicXML 4.0: + +### Inventory + +145 simpleTypes, 224 complexTypes, 27 model groups, 45 attribute groups, 2 document roots. 440 +distinct element names across 478 declaration sites. 351 attribute declarations (60 required). + +### Two load-bearing invariants + +These hold for 3.0, 3.1, 4.0, and 4.1 alike; the codegen design leans on both: + +1. The complex-type graph is a DAG. Zero cycles, zero self-references. So generated code can use + plain by-value members, emit types in topological order, and skip forward declarations and heap + indirection entirely -- removing the hardest problem in typed-XML codegen. +2. No element-name collisions. Every element name maps to exactly one type (no global element refs). + An element's type is fully determined by its name, so parse/serialize dispatch is a flat name -> + type table with no context-sensitive resolution. + +Because these are empirical, not guaranteed by XSD, `analyze` is worth keeping as a CI gate: fail +the build if a future schema introduces a cycle or name collision, before the value-type and +flat-dispatch assumptions silently break. + +### The part/measure wrinkle + +The one place name -> type is not 1:1 is the document-root scaffolding. `part` and `measure` each +appear as two anonymous types -- partwise nests `part > measure`, timewise nests `measure > part` -- +so the IR qualifies them as `partwise-part`/`timewise-part`/etc. + +### Dead types + +Five named types are defined but referenced by nothing, even indirectly: + +| Type | Why it is dead | +|------------------------------|--------------------------------------------------------------------| +| `positive-decimal` | orphan definition, never referenced | +| `start-stop-change-continue` | orphan; elements use `start-stop-change` / `start-stop-continue` | +| `formatted-symbol` | superseded by `formatted-symbol-id`, which is the one elements use | +| `empty-print-style` | superseded by the `-align` / `-id` / `-object` variants | +| `empty-print-style-align` | superseded by `empty-print-style-align-id` / `-object` | + +Verified by direct text search of the XSD (independent of the parser): each has exactly one +definition and zero references via `type`/`base`/`ref`/`itemType`/`memberTypes`. The IR drops them +and reports the count. Recognizing the families (print-style, formatted-symbol) is expected -- those +are heavily used; only the specific bare type names are vestigial. diff --git a/gen/__init__.py b/gen/__init__.py new file mode 100644 index 000000000..bd0866d7a --- /dev/null +++ b/gen/__init__.py @@ -0,0 +1 @@ +"""mx code generator package.""" diff --git a/gen/__main__.py b/gen/__main__.py index 66a6f0927..c664c3d20 100644 --- a/gen/__main__.py +++ b/gen/__main__.py @@ -1,6 +1,9 @@ """mx code generator entry point. -Usage: python3 -m gen +Usage: + python3 -m gen generate code for a target (not yet implemented) + python3 -m gen analyze [xsd] parse the XSD and print a structural analysis + python3 -m gen ir [--type N] [xsd] lower the XSD to the IR and print it as JSON Reads a MusicXML 4.0 XSD specification and generates typed document serialization/deserialization code for the target described in the given @@ -8,12 +11,70 @@ """ import sys +from pathlib import Path +# The MusicXML version this generator targets, used as the default for analyze. +DEFAULT_XSD = Path(__file__).resolve().parent.parent / "docs" / "musicxml-4.0-ed15c23.xsd" -def main() -> int: + +def _analyze(args: list[str]) -> int: + from gen.xsd.analyze import report + from gen.xsd.parser import parse + + xsd = Path(args[0]) if args else DEFAULT_XSD + if not xsd.exists(): + print(f"error: XSD not found: {xsd}", file=sys.stderr) + return 1 + print(report(parse(xsd))) + return 0 + + +def _ir(args: list[str]) -> int: + from gen.ir.build import build_ir + from gen.ir.dump import to_json + from gen.xsd.parser import parse + + type_name = None + rest = [] + i = 0 + while i < len(args): + if args[i] == "--type" and i + 1 < len(args): + type_name = args[i + 1] + i += 2 + else: + rest.append(args[i]) + i += 1 + + xsd = Path(rest[0]) if rest else DEFAULT_XSD + if not xsd.exists(): + print(f"error: XSD not found: {xsd}", file=sys.stderr) + return 1 + ir = build_ir(parse(xsd), source=xsd.stem) + + if type_name: + match = next((c for c in ir.complex_types if c.name == type_name), None) or next( + (v for v in ir.value_types if v.name == type_name), None + ) + if match is None: + print(f"error: type not found in IR: {type_name}", file=sys.stderr) + return 1 + print(to_json(match)) + else: + print(to_json(ir)) + return 0 + + +def main(argv: list[str]) -> int: + if not argv: + print(__doc__, file=sys.stderr) + return 2 + if argv[0] == "analyze": + return _analyze(argv[1:]) + if argv[0] == "ir": + return _ir(argv[1:]) print("error: generator not implemented", file=sys.stderr) return 1 if __name__ == "__main__": - sys.exit(main()) + sys.exit(main(sys.argv[1:])) diff --git a/gen/ir/__init__.py b/gen/ir/__init__.py new file mode 100644 index 000000000..294d8ee66 --- /dev/null +++ b/gen/ir/__init__.py @@ -0,0 +1,6 @@ +"""Intermediate representation for the mx generator.""" + +from gen.ir.build import build_ir +from gen.ir.model import Ir + +__all__ = ["Ir", "build_ir"] diff --git a/gen/ir/build.py b/gen/ir/build.py new file mode 100644 index 000000000..afd3690bd --- /dev/null +++ b/gen/ir/build.py @@ -0,0 +1,390 @@ +"""Lower a parsed XSD schema (gen.xsd.model) into the IR (gen.ir.model).""" + +from __future__ import annotations + +from collections import Counter + +from gen.xsd import model as xsd +from gen.xsd.analyze import content_particle, reachable_types +from gen.ir import model as ir + +# Map XSD builtin types to canonical IR primitive names. +_XS_PRIMITIVE = { + "xs:string": "string", + "xs:token": "token", + "xs:NMTOKEN": "nmtoken", + "xs:decimal": "decimal", + "xs:integer": "integer", + "xs:int": "integer", + "xs:positiveInteger": "positive_integer", + "xs:nonNegativeInteger": "non_negative_integer", + "xs:date": "date", + "xs:anyURI": "string", + "xs:language": "token", +} + +# The 10 attribute refs into the imported xml/xlink schemas, resolved to the +# primitive the emitter should use. This is the only place the IR reaches +# outside the main schema. +_EXTERNAL_ATTR = { + "xml:lang": "token", + "xml:space": "token", + "xlink:href": "string", + "xlink:type": "token", + "xlink:role": "string", + "xlink:title": "string", + "xlink:show": "token", + "xlink:actuate": "token", +} + +_NUMERIC = {"decimal", "integer", "positive_integer", "non_negative_integer"} + + +def _primitive(name: str) -> str: + if name in _XS_PRIMITIVE: + return _XS_PRIMITIVE[name] + if name in _EXTERNAL_ATTR: + return _EXTERNAL_ATTR[name] + return name.split(":")[-1] + + +def _occ(value: int) -> int | str: + return ir.UNBOUNDED if value == xsd.UNBOUNDED else value + + +def _cardinality(min_occurs: int, max_occurs: int) -> str: + if max_occurs == xsd.UNBOUNDED or max_occurs > 1: + return "vector" + return "optional" if min_occurs == 0 else "required" + + +def build_ir(schema: xsd.Schema, source: str) -> ir.Ir: + return _Builder(schema, source).build() + + +class _Builder: + def __init__(self, schema: xsd.Schema, source: str): + self.schema = schema + self.source = source + self.anon_names: dict[int, str] = {} # id(ComplexType) -> synthesized name + self.synth: list[tuple[str, xsd.ComplexType]] = [] + + # ----- top level ------------------------------------------------------- # + + def build(self) -> ir.Ir: + self._hoist_anonymous() + reachable = reachable_types(self.schema) + + value_types = [ + self._value_type(st) + for name, st in self.schema.simple_types.items() + if name in reachable + ] + value_types = self._topo_sort_values(value_types) + groups = [ + ir.Group(name, self._particle(g.particle), g.doc) + for name, g in self.schema.groups.items() + ] + attribute_groups = [ + ir.AttributeGroup( + name, + [self._attr(a) for a in ag.attributes], + [r.ref for r in ag.group_refs], + ag.doc, + ) + for name, ag in self.schema.attribute_groups.items() + ] + + complex_types = [ + self._complex_type(name, ct) + for name, ct in self.schema.complex_types.items() + if name in reachable + ] + complex_types += [self._complex_type(name, ct) for name, ct in self.synth] + + group_map = {g.name: g for g in groups} + for ct in complex_types: + ct.deps = sorted(self._deps(ct, group_map)) + complex_types = self._topo_sort(complex_types) + + all_named = set(self.schema.simple_types) | set(self.schema.complex_types) + dropped = sorted(all_named - reachable) + + return ir.Ir( + source=self.source, + builtins={**_XS_PRIMITIVE, **_EXTERNAL_ATTR}, + value_types=value_types, + groups=groups, + attribute_groups=attribute_groups, + complex_types=complex_types, + roots=[ir.Root(top.name, top.name) for top in self.schema.elements], + dropped_dead=dropped, + stats=self._stats(value_types, complex_types, dropped), + ) + + # ----- anonymous type hoisting ----------------------------------------- # + + def _hoist_anonymous(self) -> None: + used = set(self.schema.complex_types) | set(self.schema.simple_types) + # Document roots: the root element name is free; descendants are + # qualified by the partwise/timewise hierarchy to keep part/measure + # (which differ between the two) distinct. + for top in self.schema.elements: + if top.inline_type: + qualifier = top.name.replace("score-", "") + self._hoist(top.inline_type, top.name, qualifier, used) + # Anonymous types nested inside named types (e.g. directive). + for ct in self.schema.complex_types.values(): + self._scan(content_particle(ct), "", used) + for g in self.schema.groups.values(): + self._scan(g.particle, "", used) + + def _hoist(self, ct: xsd.ComplexType, name: str, qualifier: str, used: set) -> None: + self.anon_names[id(ct)] = name + self.synth.append((name, ct)) + used.add(name) + self._scan(content_particle(ct), qualifier, used) + + def _scan(self, particle, qualifier: str, used: set) -> None: + for ep in _iter_elements(particle): + if isinstance(ep.inline_type, xsd.ComplexType): + candidate = f"{qualifier}-{ep.name}" if qualifier else ep.name + if candidate in used: + candidate = f"{qualifier}-{ep.name}" if qualifier else f"{ep.name}-type" + self._hoist(ep.inline_type, candidate, qualifier, used) + + # ----- value types ----------------------------------------------------- # + + def _value_type(self, st: xsd.SimpleType) -> ir.ValueType: + if isinstance(st.content, xsd.Union): + return self._union(st) + if isinstance(st.content, xsd.ListType): + # MusicXML uses no xs:list; represent defensively as a token string. + return ir.StringType(st.name, "token", doc=st.doc) + primitive, facets = self._resolve_restriction(st.name) + if facets.enumerations: + return ir.EnumType( + st.name, primitive, [e.value for e in facets.enumerations], st.doc + ) + if primitive in _NUMERIC: + return ir.NumberType( + st.name, + primitive, + facets.min_inclusive, + facets.max_inclusive, + facets.min_exclusive, + facets.max_exclusive, + st.doc, + ) + return ir.StringType( + st.name, + primitive, + list(facets.patterns), + facets.min_length, + facets.max_length, + facets.length, + st.doc, + ) + + def _resolve_restriction(self, type_name: str) -> tuple[str, xsd.Facets]: + """Collapse a restriction chain to (primitive, merged facets). Child + facets override inherited ones; patterns accumulate.""" + st = self.schema.simple_types.get(type_name) + if st is None or not isinstance(st.content, xsd.Restriction): + return _primitive(type_name), xsd.Facets() + base = st.content.base + if base in self.schema.simple_types: + primitive, merged = self._resolve_restriction(base) + else: + primitive, merged = _primitive(base), xsd.Facets() + _merge_facets(merged, st.content.facets) + return primitive, merged + + def _union(self, st: xsd.SimpleType) -> ir.UnionType: + members: list[ir.UnionMember] = [] + for m in st.content.member_types: + if m in self.schema.simple_types: + members.append(ir.UnionMember(type=m, category="value")) + else: + members.append(ir.UnionMember(type=_primitive(m), category="primitive")) + for inline in st.content.inline_members: + if isinstance(inline.content, xsd.Restriction) and inline.content.facets.enumerations: + members.append( + ir.UnionMember( + literals=[e.value for e in inline.content.facets.enumerations] + ) + ) + return ir.UnionType(st.name, members, st.doc) + + # ----- attributes ------------------------------------------------------ # + + def _attr(self, a: xsd.Attribute) -> ir.Attr: + if a.ref: + ref = ir.Ref(_primitive(a.ref), "primitive") + name = a.ref + else: + ref = self._type_ref(a.type) if a.type else ir.Ref("string", "primitive") + name = a.name or "" + return ir.Attr(name, ref, a.use == "required", a.default, a.fixed, a.doc) + + # ----- complex types --------------------------------------------------- # + + def _complex_type(self, name: str, ct: xsd.ComplexType) -> ir.ComplexType: + c = ct.content + attrs = [self._attr(a) for a in c.attributes] + agrefs = [r.ref for r in c.attribute_group_refs] + + if isinstance(c, xsd.SimpleContent): + return ir.ComplexType( + name, "value", attrs, agrefs, value_type=self._type_ref(c.base), doc=ct.doc + ) + if isinstance(c, xsd.ComplexContent): + content = self._particle(c.particle) if c.particle else None + return ir.ComplexType( + name, "derived", attrs, agrefs, base=c.base, content=content, doc=ct.doc + ) + # ImplicitContent + if c.particle is not None: + return ir.ComplexType( + name, "composite", attrs, agrefs, content=self._particle(c.particle), doc=ct.doc + ) + presence = not attrs and not agrefs + return ir.ComplexType(name, "empty", attrs, agrefs, presence_only=presence, doc=ct.doc) + + # ----- particles ------------------------------------------------------- # + + def _particle(self, p) -> ir.Particle: + if isinstance(p, xsd.Sequence): + return ir.Sequence([self._particle(i) for i in p.items], p.min_occurs, _occ(p.max_occurs)) + if isinstance(p, xsd.Choice): + return ir.Choice([self._particle(i) for i in p.items], p.min_occurs, _occ(p.max_occurs)) + if isinstance(p, xsd.GroupRef): + return ir.GroupRef(p.ref, p.min_occurs, _occ(p.max_occurs)) + if isinstance(p, xsd.ElementParticle): + return ir.Element( + p.name, + self._element_ref(p), + _cardinality(p.min_occurs, p.max_occurs), + p.min_occurs, + _occ(p.max_occurs), + p.doc, + ) + raise ValueError(f"unexpected particle: {type(p).__name__}") + + def _element_ref(self, ep: xsd.ElementParticle) -> ir.Ref: + if isinstance(ep.inline_type, xsd.ComplexType): + return ir.Ref(self.anon_names[id(ep.inline_type)], "complex") + if ep.type: + return self._type_ref(ep.type) + return ir.Ref("string", "primitive") + + def _type_ref(self, type_name: str) -> ir.Ref: + if type_name in self.schema.complex_types: + return ir.Ref(type_name, "complex") + if type_name in self.schema.simple_types: + return ir.Ref(type_name, "value") + if type_name.startswith(("xs:", "xml:", "xlink:")): + return ir.Ref(_primitive(type_name), "primitive") + return ir.Ref(type_name, "complex") + + # ----- dependency ordering --------------------------------------------- # + + def _deps(self, ct: ir.ComplexType, groups: dict[str, ir.Group]) -> set[str]: + deps: set[str] = set() + if ct.base: + deps.add(ct.base) + self._collect_deps(ct.content, groups, set(), deps) + return deps + + def _collect_deps(self, node, groups, seen_groups, deps) -> None: + if isinstance(node, (ir.Sequence, ir.Choice)): + for item in node.items: + self._collect_deps(item, groups, seen_groups, deps) + elif isinstance(node, ir.GroupRef): + if node.name in groups and node.name not in seen_groups: + seen_groups.add(node.name) + self._collect_deps(groups[node.name].content, groups, seen_groups, deps) + elif isinstance(node, ir.Element) and node.type.category == "complex": + deps.add(node.type.name) + + def _topo_sort(self, types: list[ir.ComplexType]) -> list[ir.ComplexType]: + by_name = {t.name: t for t in types} + ordered: list[ir.ComplexType] = [] + state: dict[str, int] = {} # 0 visiting, 1 done + + def visit(name: str) -> None: + if state.get(name) == 1 or name not in by_name: + return + state[name] = 0 + for dep in by_name[name].deps: + visit(dep) + state[name] = 1 + ordered.append(by_name[name]) + + for name in sorted(by_name): + visit(name) + return ordered + + def _topo_sort_values(self, values: list[ir.ValueType]) -> list[ir.ValueType]: + """Order value types deps-first. Only unions reference other value + types (their members); every other kind resolves to a primitive.""" + by_name = {v.name: v for v in values} + ordered: list[ir.ValueType] = [] + state: dict[str, int] = {} + + def deps(v: ir.ValueType) -> list[str]: + if isinstance(v, ir.UnionType): + return [m.type for m in v.members if m.category == "value" and m.type in by_name] + return [] + + def visit(name: str) -> None: + if state.get(name) == 1 or name not in by_name: + return + state[name] = 1 + for dep in sorted(deps(by_name[name])): + visit(dep) + ordered.append(by_name[name]) + + for name in sorted(by_name): + visit(name) + return ordered + + # ----- stats ----------------------------------------------------------- # + + def _stats(self, value_types, complex_types, dropped) -> dict: + return { + "value_types": len(value_types), + "value_kinds": dict(Counter(v.kind for v in value_types)), + "complex_types": len(complex_types), + "complex_kinds": dict(Counter(c.kind for c in complex_types)), + "groups": len(self.schema.groups), + "attribute_groups": len(self.schema.attribute_groups), + "synthesized_types": len(self.synth), + "dropped_dead_types": len(dropped), + } + + +# --------------------------------------------------------------------------- # +# Helpers +# --------------------------------------------------------------------------- # + + +def _iter_elements(particle): + """Yield element particles directly contained in a particle (not inside + group refs, which are scanned at the group definition).""" + if isinstance(particle, (xsd.Sequence, xsd.Choice)): + for item in particle.items: + yield from _iter_elements(item) + elif isinstance(particle, xsd.ElementParticle): + yield particle + + +def _merge_facets(into: xsd.Facets, src: xsd.Facets) -> None: + if src.enumerations: + into.enumerations = src.enumerations + into.patterns = into.patterns + src.patterns + for f in ("min_inclusive", "max_inclusive", "min_exclusive", "max_exclusive", + "min_length", "max_length", "length"): + v = getattr(src, f) + if v is not None: + setattr(into, f, v) diff --git a/gen/ir/dump.py b/gen/ir/dump.py new file mode 100644 index 000000000..9f6fd3ea8 --- /dev/null +++ b/gen/ir/dump.py @@ -0,0 +1,33 @@ +"""Serialize the IR to JSON for inspection.""" + +from __future__ import annotations + +import json +from dataclasses import fields, is_dataclass + +# Discriminator fields are emitted first so each object announces what it is. +_FIRST = ("kind", "node", "name", "element") + + +def to_jsonable(obj): + """Convert IR dataclasses to plain JSON-able data, dropping None and empty + collections to keep the output readable.""" + if is_dataclass(obj): + names = [f.name for f in fields(obj)] + order = [n for n in _FIRST if n in names] + [n for n in names if n not in _FIRST] + result = {} + for name in order: + value = getattr(obj, name) + if value is None or (isinstance(value, (list, dict)) and not value): + continue + result[name] = to_jsonable(value) + return result + if isinstance(obj, list): + return [to_jsonable(x) for x in obj] + if isinstance(obj, dict): + return {k: to_jsonable(v) for k, v in obj.items()} + return obj + + +def to_json(obj) -> str: + return json.dumps(to_jsonable(obj), indent=2) diff --git a/gen/ir/model.py b/gen/ir/model.py new file mode 100644 index 000000000..cc1b75601 --- /dev/null +++ b/gen/ir/model.py @@ -0,0 +1,212 @@ +"""The intermediate representation: a resolved, language-agnostic model. + +The raw XSD model (gen.xsd.model) mirrors the schema 1:1 and still speaks in +XSD terms (restriction chains, attribute-group refs, anonymous inline types). +The IR is what the language emitters consume instead. It is a pure function of +the XSD with every cross-reference resolved: + + - all types are named (anonymous types are hoisted, with context-qualified + names for the partwise/timewise scaffolding); + - simple-type restriction chains are collapsed to one primitive plus merged + facets; + - element occurrence is normalized to a cardinality (required/optional/vector); + - dead types are dropped and complex types are emitted in dependency order. + +The IR deliberately *preserves* named structure (aliases keep their names, the +five inheritance edges stay as derivations, model groups and attribute groups +remain addressable) so emitters can choose how much to collapse. See the +analyze module's recommendations for the reasoning. +""" + +from __future__ import annotations + +from dataclasses import dataclass, field + +# Canonical maxOccurs="unbounded" marker in normalized particles. Kept as a +# string so it is self-describing in the JSON dump. +UNBOUNDED = "unbounded" + + +# --------------------------------------------------------------------------- # +# Type references +# --------------------------------------------------------------------------- # + + +@dataclass +class Ref: + """A reference to another type by name, tagged with where to resolve it.""" + + name: str + category: str # "complex" | "value" | "primitive" + + +# --------------------------------------------------------------------------- # +# Value types (lowered from simpleType and simpleContent bases) +# --------------------------------------------------------------------------- # + + +@dataclass +class EnumType: + name: str + base: str # primitive the tokens are drawn from (token/string) + values: list[str] + doc: str | None = None + kind: str = "enum" + + +@dataclass +class NumberType: + name: str + base: str # decimal/integer/positive_integer/non_negative_integer + min_inclusive: str | None = None + max_inclusive: str | None = None + min_exclusive: str | None = None + max_exclusive: str | None = None + doc: str | None = None + kind: str = "number" + + +@dataclass +class StringType: + name: str + base: str # string/token/nmtoken/date; a plain alias has no constraints + patterns: list[str] = field(default_factory=list) + min_length: str | None = None + max_length: str | None = None + length: str | None = None + doc: str | None = None + kind: str = "string" + + +@dataclass +class UnionMember: + type: str | None = None # ref to a value type or primitive + category: str | None = None # "value" | "primitive" + literals: list[str] | None = None # inline enumeration literals + + +@dataclass +class UnionType: + name: str + members: list[UnionMember] + doc: str | None = None + kind: str = "union" + + +ValueType = EnumType | NumberType | StringType | UnionType + + +# --------------------------------------------------------------------------- # +# Attributes +# --------------------------------------------------------------------------- # + + +@dataclass +class Attr: + name: str + type: Ref + required: bool = False + default: str | None = None + fixed: str | None = None + doc: str | None = None + + +@dataclass +class AttributeGroup: + name: str + attributes: list[Attr] = field(default_factory=list) + attribute_groups: list[str] = field(default_factory=list) # nested refs + doc: str | None = None + + +# --------------------------------------------------------------------------- # +# Content model (normalized particles) +# --------------------------------------------------------------------------- # + + +@dataclass +class Element: + name: str + type: Ref + card: str # "required" | "optional" | "vector" + min: int = 1 + max: int | str = 1 # int or UNBOUNDED + doc: str | None = None + node: str = "element" + + +@dataclass +class GroupRef: + name: str + min: int = 1 + max: int | str = 1 + node: str = "group" + + +@dataclass +class Sequence: + items: list + min: int = 1 + max: int | str = 1 + node: str = "sequence" + + +@dataclass +class Choice: + items: list + min: int = 1 + max: int | str = 1 + node: str = "choice" + + +Particle = Element | GroupRef | Sequence | Choice + + +@dataclass +class Group: + name: str + content: Particle + doc: str | None = None + + +# --------------------------------------------------------------------------- # +# Complex types +# --------------------------------------------------------------------------- # + + +@dataclass +class ComplexType: + name: str + kind: str # "value" | "composite" | "derived" | "empty" + attributes: list[Attr] = field(default_factory=list) + attribute_groups: list[str] = field(default_factory=list) + value_type: Ref | None = None # kind == "value" (text content type) + base: str | None = None # kind == "derived" (parent complex type) + content: Particle | None = None # composite/derived particle + presence_only: bool = False # empty element used as a boolean flag + deps: list[str] = field(default_factory=list) # complex types referenced + doc: str | None = None + + +# --------------------------------------------------------------------------- # +# Schema root +# --------------------------------------------------------------------------- # + + +@dataclass +class Root: + element: str + type: str + + +@dataclass +class Ir: + source: str + builtins: dict[str, str] + value_types: list[ValueType] + groups: list[Group] + attribute_groups: list[AttributeGroup] + complex_types: list[ComplexType] # dependency-ordered + roots: list[Root] + dropped_dead: list[str] + stats: dict diff --git a/gen/tests/__init__.py b/gen/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/gen/tests/test_ir.py b/gen/tests/test_ir.py new file mode 100644 index 000000000..0475e3cd1 --- /dev/null +++ b/gen/tests/test_ir.py @@ -0,0 +1,168 @@ +"""Integrity and invariant tests for the XSD parser and the IR. + +Most tests run against every MusicXML version in docs/, so the assumptions the +generator leans on are guarded for any schema it might be pointed at: + + - the complex-type graph is acyclic (lets the emitter use value types with no + indirection and emit in dependency order); + - element names do not collide (lets dispatch be a flat name -> type table); + - every reference in the lowered IR resolves, in dependency order. + +Run with: python3 -m unittest gen.tests.test_ir +""" + +from __future__ import annotations + +import sys +import unittest +from pathlib import Path + +from gen.ir import build_ir +from gen.ir import model as ir +from gen.xsd import parse +from gen.xsd.analyze import element_index, type_graph + +DOCS = Path(__file__).resolve().parents[2] / "docs" +XSDS = sorted(DOCS.glob("musicxml-*.xsd")) +XSD_40 = DOCS / "musicxml-4.0-ed15c23.xsd" + + +def _find_cycle(graph: dict[str, set[str]]) -> list[str]: + """Independent DFS cycle finder (does not reuse the production SCC code). + Returns a cycle as a node path, or [] if the graph is acyclic.""" + WHITE, GREY, BLACK = 0, 1, 2 + color = {n: WHITE for n in graph} + sys.setrecursionlimit(10000) + + def dfs(node: str, path: list[str]) -> list[str]: + color[node] = GREY + for nxt in graph[node]: + if color[nxt] == GREY: + return path[path.index(nxt):] + [nxt] + if color[nxt] == WHITE: + found = dfs(nxt, path + [nxt]) + if found: + return found + color[node] = BLACK + return [] + + for n in graph: + if color[n] == WHITE: + cycle = dfs(n, [n]) + if cycle: + return cycle + return [] + + +class SchemaInvariants(unittest.TestCase): + def test_xsds_present(self): + self.assertTrue(XSDS, f"no MusicXML XSDs found in {DOCS}") + + def test_acyclic_type_graph(self): + for xsd in XSDS: + with self.subTest(xsd=xsd.name): + cycle = _find_cycle(type_graph(parse(xsd))) + self.assertEqual(cycle, [], f"cycle: {' -> '.join(cycle)}") + + def test_no_element_name_collisions(self): + for xsd in XSDS: + with self.subTest(xsd=xsd.name): + index = element_index(parse(xsd)) + collisions = {n: sorted(set(t)) for n, t in index.items() if len(set(t)) > 1} + self.assertEqual(collisions, {}, f"name collisions: {collisions}") + + +class IRIntegrity(unittest.TestCase): + def test_references_resolve(self): + for xsd in XSDS: + with self.subTest(xsd=xsd.name): + self._check_references(build_ir(parse(xsd), xsd.stem)) + + def test_dependency_order(self): + """Both type lists are emitted deps-first: a dependency never follows + the type that uses it.""" + for xsd in XSDS: + with self.subTest(xsd=xsd.name): + m = build_ir(parse(xsd), xsd.stem) + cpos = {c.name: i for i, c in enumerate(m.complex_types)} + for c in m.complex_types: + for dep in c.deps: + self.assertLess(cpos[dep], cpos[c.name], f"{dep} after {c.name}") + vpos = {v.name: i for i, v in enumerate(m.value_types)} + for v in m.value_types: + if isinstance(v, ir.UnionType): + for mem in v.members: + if mem.category == "value" and mem.type in vpos: + self.assertLess( + vpos[mem.type], vpos[v.name], + f"union {v.name} member {mem.type} out of order", + ) + + def _check_references(self, m: ir.Ir) -> None: + complex_names = {c.name for c in m.complex_types} + value_names = {v.name for v in m.value_types} + group_names = {g.name for g in m.groups} + ag_names = {a.name for a in m.attribute_groups} + + def check_ref(ref: ir.Ref, where: str) -> None: + if ref.category == "complex": + self.assertIn(ref.name, complex_names, f"{where}: missing complex {ref.name}") + elif ref.category == "value": + self.assertIn(ref.name, value_names, f"{where}: missing value {ref.name}") + + def walk(node, where: str) -> None: + if isinstance(node, (ir.Sequence, ir.Choice)): + for item in node.items: + walk(item, where) + elif isinstance(node, ir.GroupRef): + self.assertIn(node.name, group_names, f"{where}: missing group {node.name}") + elif isinstance(node, ir.Element): + check_ref(node.type, f"{where} element {node.name}") + + for c in m.complex_types: + for a in c.attributes: + check_ref(a.type, f"{c.name} attr {a.name}") + for ref in c.attribute_groups: + self.assertIn(ref, ag_names, f"{c.name}: missing attribute group {ref}") + if c.value_type: + check_ref(c.value_type, f"{c.name} value") + if c.base: + self.assertIn(c.base, complex_names, f"{c.name}: missing base {c.base}") + if c.content: + walk(c.content, c.name) + for dep in c.deps: + self.assertIn(dep, complex_names, f"{c.name}: dep {dep} not a complex type") + + for ag in m.attribute_groups: + for ref in ag.attribute_groups: + self.assertIn(ref, ag_names, f"attribute group {ag.name}: missing {ref}") + for a in ag.attributes: + check_ref(a.type, f"attribute group {ag.name} attr {a.name}") + for g in m.groups: + walk(g.content, f"group {g.name}") + + +@unittest.skipUnless(XSD_40.exists(), "MusicXML 4.0 XSD not present") +class DeadTypeRegression(unittest.TestCase): + """Guards the reachability analysis against the bug where a type used only + via an inline type's attribute group was wrongly dropped.""" + + DEAD = { + "empty-print-style", + "empty-print-style-align", + "formatted-symbol", + "positive-decimal", + "start-stop-change-continue", + } + + def test_dead_dropped_live_kept(self): + m = build_ir(parse(XSD_40), "musicxml-4.0") + names = {c.name for c in m.complex_types} | {v.name for v in m.value_types} + self.assertEqual(set(m.dropped_dead), self.DEAD) + self.assertFalse(self.DEAD & names, "dead types leaked into the IR") + # measure-text is reachable via measure-attributes/@text, not dead. + self.assertIn("measure-text", names) + + +if __name__ == "__main__": + unittest.main() diff --git a/gen/xsd/__init__.py b/gen/xsd/__init__.py new file mode 100644 index 000000000..5c748e71d --- /dev/null +++ b/gen/xsd/__init__.py @@ -0,0 +1,6 @@ +"""XSD parsing and analysis for the mx generator.""" + +from gen.xsd.model import Schema +from gen.xsd.parser import parse + +__all__ = ["Schema", "parse"] diff --git a/gen/xsd/analyze.py b/gen/xsd/analyze.py new file mode 100644 index 000000000..c0e2a5e80 --- /dev/null +++ b/gen/xsd/analyze.py @@ -0,0 +1,462 @@ +"""Structural analysis of a parsed MusicXML schema. + +The generator does not need this to emit code, but the shape of the schema +drives every design decision in the generator, so it is worth measuring up +front. ``report`` produces a human-readable summary; the helper functions +(element_index, reachable_types, type_graph) compute structures the generator +itself will reuse later. +""" + +from __future__ import annotations + +from collections import Counter, defaultdict + +from gen.xsd.model import ( + Attribute, + AttributeGroup, + AttributeGroupRef, + Choice, + ComplexContent, + ComplexType, + ElementParticle, + GroupRef, + ImplicitContent, + ListType, + Restriction, + Schema, + Sequence, + SimpleContent, + SimpleType, + UNBOUNDED, + Union, +) + +# Builtin xs: types are not defined in the schema; treat any name with this +# prefix (or an external namespace prefix) as a leaf, not a dead reference. +_BUILTIN_PREFIXES = ("xs:", "xml:", "xlink:") + + +def _is_named(name: str | None) -> bool: + return bool(name) and not name.startswith(_BUILTIN_PREFIXES) + + +# --------------------------------------------------------------------------- # +# Particle walking +# --------------------------------------------------------------------------- # + + +def content_particle(ct: ComplexType): + """The particle of a complex type, or None for value/empty types.""" + if isinstance(ct.content, (ComplexContent, ImplicitContent)): + return ct.content.particle + return None + + +def _walk_local(particle): + """Yield element particles in a content model, recursing into nested + sequences/choices and inline complex types but NOT into group refs (group + contents are counted at the group definition to avoid double counting).""" + if isinstance(particle, (Sequence, Choice)): + for item in particle.items: + yield from _walk_local(item) + elif isinstance(particle, ElementParticle): + yield particle + if isinstance(particle.inline_type, ComplexType): + yield from _walk_local(content_particle(particle.inline_type)) + + +def _element_type_targets(particle, groups, _seen=None) -> set[str]: + """All element type names reachable from a particle, following group refs + and inline complex types. Used to build the type reference graph.""" + seen_groups = set() if _seen is None else _seen + targets: set[str] = set() + stack = [particle] + while stack: + p = stack.pop() + if isinstance(p, (Sequence, Choice)): + stack.extend(p.items) + elif isinstance(p, GroupRef): + if p.ref in groups and p.ref not in seen_groups: + seen_groups.add(p.ref) + stack.append(groups[p.ref].particle) + elif isinstance(p, ElementParticle): + if p.type: + targets.add(p.type) + if isinstance(p.inline_type, ComplexType): + stack.append(content_particle(p.inline_type)) + return targets + + +# --------------------------------------------------------------------------- # +# Reusable index structures +# --------------------------------------------------------------------------- # + + +def element_index(schema: Schema) -> dict[str, list[str]]: + """Map each element name to the list of type names it is declared with + across every content model. Inline types show as '(anonymous)'.""" + index: dict[str, list[str]] = defaultdict(list) + + def record(particle): + for ep in _walk_local(particle): + index[ep.name].append(ep.type or "(anonymous)") + + for ct in schema.complex_types.values(): + record(content_particle(ct)) + for g in schema.groups.values(): + record(g.particle) + for top in schema.elements: + if top.inline_type: + record(content_particle(top.inline_type)) + return index + + +def _attr_type_names(attrs, agrefs, attribute_groups, seen=None) -> set[str]: + """Type names referenced by a set of attributes, expanding attributeGroup + refs (deduplicated across nested groups).""" + seen = set() if seen is None else seen + names: set[str] = set() + for a in attrs: + if a.type: + names.add(a.type) + for ref in agrefs: + if ref.ref in attribute_groups and ref.ref not in seen: + seen.add(ref.ref) + ag = attribute_groups[ref.ref] + names |= _attr_type_names(ag.attributes, ag.group_refs, attribute_groups, seen) + return names + + +def _complex_refs(ct: ComplexType, schema: Schema) -> set[str]: + """All named type names a complex type references: base, element types, and + attribute types (through attribute groups). Recurses into anonymous inline + types so their attribute and element references are reachable too.""" + refs: set[str] = set() + _gather_complex_refs(ct, schema, refs) + return {r for r in refs if _is_named(r)} + + +def _gather_complex_refs(ct: ComplexType, schema: Schema, refs: set[str]) -> None: + c = ct.content + if isinstance(c, (SimpleContent, ComplexContent)): + refs.add(c.base) + if isinstance(c, (SimpleContent, ComplexContent, ImplicitContent)): + refs |= _attr_type_names(c.attributes, c.attribute_group_refs, schema.attribute_groups) + stack = [content_particle(ct)] + seen_groups: set[str] = set() + while stack: + p = stack.pop() + if isinstance(p, (Sequence, Choice)): + stack.extend(p.items) + elif isinstance(p, GroupRef): + if p.ref in schema.groups and p.ref not in seen_groups: + seen_groups.add(p.ref) + stack.append(schema.groups[p.ref].particle) + elif isinstance(p, ElementParticle): + if p.type: + refs.add(p.type) + if isinstance(p.inline_type, ComplexType): + _gather_complex_refs(p.inline_type, schema, refs) + + +def _simple_refs(st: SimpleType) -> set[str]: + if isinstance(st.content, Union): + return {m for m in st.content.member_types if _is_named(m)} + if isinstance(st.content, ListType): + return {st.content.item_type} if _is_named(st.content.item_type) else set() + if isinstance(st.content, Restriction) and _is_named(st.content.base): + return {st.content.base} + return set() + + +def reachable_types(schema: Schema) -> set[str]: + """Named types reachable from the document roots. The complement is dead.""" + seen: set[str] = set() + frontier: list[str] = [] + for top in schema.elements: + if top.type: + frontier.append(top.type) + if top.inline_type: + frontier += _complex_refs(top.inline_type, schema) + while frontier: + t = frontier.pop() + if t in seen or not _is_named(t): + continue + seen.add(t) + if t in schema.complex_types: + frontier += _complex_refs(schema.complex_types[t], schema) + elif t in schema.simple_types: + frontier += _simple_refs(schema.simple_types[t]) + return seen + + +def type_graph(schema: Schema) -> dict[str, set[str]]: + """Directed graph among complex types: X -> Y when X structurally contains + an element of complex type Y (or extends Y). Drives recursion analysis.""" + graph: dict[str, set[str]] = {} + cts = schema.complex_types + for name, ct in cts.items(): + targets = _element_type_targets(content_particle(ct), schema.groups) + c = ct.content + if isinstance(c, (SimpleContent, ComplexContent)): + targets.add(c.base) + graph[name] = {t for t in targets if t in cts} + return graph + + +def _sccs(graph: dict[str, set[str]]) -> list[list[str]]: + """Tarjan's strongly connected components (iterative).""" + index: dict[str, int] = {} + low: dict[str, int] = {} + on_stack: set[str] = set() + stack: list[str] = [] + result: list[list[str]] = [] + counter = 0 + + for root in graph: + if root in index: + continue + work = [(root, iter(graph[root]))] + while work: + node, children = work[-1] + if node not in index: + index[node] = low[node] = counter + counter += 1 + stack.append(node) + on_stack.add(node) + advanced = False + for child in children: + if child not in index: + work.append((child, iter(graph[child]))) + advanced = True + break + if child in on_stack: + low[node] = min(low[node], index[child]) + if advanced: + continue + if low[node] == index[node]: + comp = [] + while True: + w = stack.pop() + on_stack.discard(w) + comp.append(w) + if w == node: + break + result.append(comp) + work.pop() + if work: + parent = work[-1][0] + low[parent] = min(low[parent], low[node]) + return result + + +# --------------------------------------------------------------------------- # +# Categorization +# --------------------------------------------------------------------------- # + +_NUMERIC_BASES = { + "xs:decimal", + "xs:integer", + "xs:positiveInteger", + "xs:nonNegativeInteger", + "xs:int", +} + + +def _classify_simple(st: SimpleType) -> str: + c = st.content + if isinstance(c, Union): + return "union" + if isinstance(c, ListType): + return "list" + f = c.facets + if f.enumerations: + return "enumeration" + if f.patterns: + return "pattern" + if any((f.min_inclusive, f.max_inclusive, f.min_exclusive, f.max_exclusive)): + return "numeric range" + if any((f.min_length, f.max_length, f.length)): + return "length constrained" + return "alias" + + +def _classify_complex(ct: ComplexType) -> str: + c = ct.content + if isinstance(c, SimpleContent): + return "simple content (value + attrs)" + if isinstance(c, ComplexContent): + return "complex content (derived)" + # ImplicitContent + if c.particle is not None: + return "composite (sequence/choice)" + if c.attributes or c.attribute_group_refs: + return "attributes only (empty element)" + return "empty" + + +# --------------------------------------------------------------------------- # +# Report +# --------------------------------------------------------------------------- # + + +def _row(label: str, value) -> str: + return f" {label:<40} {value:>7}" + + +def _h(title: str) -> list[str]: + return ["", title, "-" * len(title)] + + +def report(schema: Schema) -> str: + out: list[str] = [] + out.append("=" * 60) + out.append("MusicXML XSD structural analysis") + out.append("=" * 60) + + # ---- component inventory ---- + out += _h("Component inventory") + out.append(_row("named simple types", len(schema.simple_types))) + out.append(_row("named complex types", len(schema.complex_types))) + out.append(_row("model groups", len(schema.groups))) + out.append(_row("attribute groups", len(schema.attribute_groups))) + out.append(_row("document root elements", len(schema.elements))) + out.append(_row("imported schemas", len(schema.imports))) + + # ---- simple types ---- + simple_kinds = Counter(_classify_simple(st) for st in schema.simple_types.values()) + out += _h("Simple types by kind") + for kind, n in simple_kinds.most_common(): + out.append(_row(kind, n)) + + enums = {n: st for n, st in schema.simple_types.items() if _classify_simple(st) == "enumeration"} + total_values = sum(len(st.content.facets.enumerations) for st in enums.values()) + out += _h("Enumerations") + out.append(_row("enumeration types", len(enums))) + out.append(_row("total enumeration values", total_values)) + largest = sorted(enums.items(), key=lambda kv: len(kv[1].content.facets.enumerations), reverse=True) + out.append(" largest:") + for name, st in largest[:8]: + out.append(_row(f" {name}", len(st.content.facets.enumerations))) + + bases = Counter( + st.content.base + for st in schema.simple_types.values() + if isinstance(st.content, Restriction) + ) + out += _h("Simple type restriction bases") + for base, n in bases.most_common(): + out.append(_row(base, n)) + + # ---- complex types ---- + complex_kinds = Counter(_classify_complex(ct) for ct in schema.complex_types.values()) + out += _h("Complex types by content model") + for kind, n in complex_kinds.most_common(): + out.append(_row(kind, n)) + + # ---- elements ---- + index = element_index(schema) + sites = sum(len(v) for v in index.values()) + collisions = {n: sorted(set(t)) for n, t in index.items() if len(set(t)) > 1} + same_name_type = sum( + 1 for n, types in index.items() for t in types if t == n + ) + out += _h("Elements") + out.append(_row("distinct element names", len(index))) + out.append(_row("element declaration sites", sites)) + out.append(_row("sites where element name == type name", same_name_type)) + out.append(_row("names declared with >1 distinct type", len(collisions))) + if collisions: + out.append(" collisions (same name, different types):") + for name in sorted(collisions)[:12]: + out.append(f" {name}: {', '.join(collisions[name])}") + + # type reuse (fan-in by element sites) + fan_in = Counter(t for types in index.values() for t in types if t != "(anonymous)") + out += _h("Most reused types (by element declaration sites)") + for name, n in fan_in.most_common(12): + out.append(_row(name, n)) + + # ---- recursion ---- + graph = type_graph(schema) + self_loops = sorted(n for n, deps in graph.items() if n in deps) + cycles = [c for c in _sccs(graph) if len(c) > 1] + out += _h("Recursion (complex type reference graph)") + out.append(_row("complex types in a cycle (SCC > 1)", sum(len(c) for c in cycles))) + out.append(_row("self-referential types", len(self_loops))) + if self_loops: + out.append(" self-referential: " + ", ".join(self_loops)) + for comp in sorted(cycles, key=len, reverse=True)[:6]: + out.append(f" cycle ({len(comp)}): {', '.join(sorted(comp))}") + + # ---- dead types ---- + reachable = reachable_types(schema) + all_named = set(schema.simple_types) | set(schema.complex_types) + dead = sorted(all_named - reachable) + out += _h("Reachability from document roots") + out.append(_row("named types reachable", len(reachable))) + out.append(_row("named types unreachable (dead)", len(dead))) + if dead: + out.append(" dead: " + ", ".join(dead[:20]) + (" ..." if len(dead) > 20 else "")) + + # ---- attributes ---- + out += _h("Attributes") + all_attrs: list[Attribute] = [] + for ct in schema.complex_types.values(): + c = ct.content + if isinstance(c, (SimpleContent, ComplexContent, ImplicitContent)): + all_attrs += c.attributes + for ag in schema.attribute_groups.values(): + all_attrs += ag.attributes + required = sum(1 for a in all_attrs if a.use == "required") + defaults = sum(1 for a in all_attrs if a.default is not None) + fixed = sum(1 for a in all_attrs if a.fixed is not None) + external = sum(1 for a in all_attrs if a.ref and a.ref.startswith(_BUILTIN_PREFIXES)) + out.append(_row("attribute declarations", len(all_attrs))) + out.append(_row("required", required)) + out.append(_row("optional", len(all_attrs) - required)) + out.append(_row("with default value", defaults)) + out.append(_row("with fixed value", fixed)) + out.append(_row("external refs (xml:/xlink:)", external)) + + # attribute group reuse + ag_fan_in: Counter[str] = Counter() + for ct in schema.complex_types.values(): + c = ct.content + if isinstance(c, (SimpleContent, ComplexContent, ImplicitContent)): + for r in c.attribute_group_refs: + ag_fan_in[r.ref] += 1 + for ag in schema.attribute_groups.values(): + for r in ag.group_refs: + ag_fan_in[r.ref] += 1 + out += _h("Most reused attribute groups") + for name, n in ag_fan_in.most_common(10): + out.append(_row(name, n)) + + # model group reuse + group_fan_in: Counter[str] = Counter() + + def count_group_refs(particle): + stack = [particle] + while stack: + p = stack.pop() + if isinstance(p, (Sequence, Choice)): + stack.extend(p.items) + elif isinstance(p, GroupRef): + group_fan_in[p.ref] += 1 + elif isinstance(p, ElementParticle) and isinstance(p.inline_type, ComplexType): + stack.append(content_particle(p.inline_type)) + + for ct in schema.complex_types.values(): + count_group_refs(content_particle(ct)) + for g in schema.groups.values(): + count_group_refs(g.particle) + for top in schema.elements: + if top.inline_type: + count_group_refs(content_particle(top.inline_type)) + out += _h("Most reused model groups") + for name, n in group_fan_in.most_common(10): + out.append(_row(name, n)) + + out.append("") + return "\n".join(out) diff --git a/gen/xsd/model.py b/gen/xsd/model.py new file mode 100644 index 000000000..fff8bae0a --- /dev/null +++ b/gen/xsd/model.py @@ -0,0 +1,229 @@ +"""In-memory model of the subset of W3C XML Schema that MusicXML uses. + +This is deliberately not a general XSD object model. It captures only the +constructs that actually appear in the MusicXML 4.0 schema (see the analyze +module for the inventory). Anything outside that subset -- xs:all, xs:any, +substitution groups, abstract types, mixed content -- is absent because the +schema does not use it. +""" + +from __future__ import annotations + +from dataclasses import dataclass, field + +# Sentinel for an unbounded maxOccurs. We keep occurrence bounds as ints so the +# rest of the generator can do arithmetic on them; unbounded is the one value +# that cannot be an int. +UNBOUNDED = -1 + + +# --------------------------------------------------------------------------- # +# Simple types +# --------------------------------------------------------------------------- # + + +@dataclass +class Enumeration: + value: str + doc: str | None = None + + +@dataclass +class Facets: + """Constraining facets attached to a restriction.""" + + enumerations: list[Enumeration] = field(default_factory=list) + patterns: list[str] = field(default_factory=list) + min_inclusive: str | None = None + max_inclusive: str | None = None + min_exclusive: str | None = None + max_exclusive: str | None = None + min_length: str | None = None + max_length: str | None = None + length: str | None = None + + +@dataclass +class Restriction: + base: str + facets: Facets + + +@dataclass +class Union: + # Named or builtin members listed in the memberTypes attribute. + member_types: list[str] + # Anonymous simpleType members nested inside the union. + inline_members: list[SimpleType] + + +@dataclass +class ListType: + item_type: str + + +@dataclass +class SimpleType: + name: str | None # None for anonymous (inline) simple types + doc: str | None + content: Restriction | Union | ListType + + +# --------------------------------------------------------------------------- # +# Attributes +# --------------------------------------------------------------------------- # + + +@dataclass +class Attribute: + """An xs:attribute, either a local declaration or a ref to an external one. + + A local declaration has a name and a type (or an inline simple type). A ref + (xml:lang, xlink:href, ...) carries only the qualified ref name; its type + lives in an imported schema. + """ + + name: str | None + ref: str | None + type: str | None + inline_type: SimpleType | None + use: str # "optional" | "required" | "prohibited" + default: str | None + fixed: str | None + doc: str | None + + +@dataclass +class AttributeGroupRef: + ref: str + + +@dataclass +class AttributeGroup: + name: str + doc: str | None + attributes: list[Attribute] + group_refs: list[AttributeGroupRef] + + +# --------------------------------------------------------------------------- # +# Particles (element content models) +# --------------------------------------------------------------------------- # + + +@dataclass +class ElementParticle: + """A local element declaration appearing inside a content model. + + MusicXML uses no global element refs, so every element is declared in place + with a name and either a named type or an anonymous inline type. + """ + + name: str + type: str | None + inline_type: ComplexType | SimpleType | None + min_occurs: int + max_occurs: int # UNBOUNDED for "unbounded" + doc: str | None + + +@dataclass +class GroupRef: + ref: str + min_occurs: int + max_occurs: int + + +@dataclass +class Sequence: + items: list[Particle] + min_occurs: int + max_occurs: int + + +@dataclass +class Choice: + items: list[Particle] + min_occurs: int + max_occurs: int + + +Particle = ElementParticle | GroupRef | Sequence | Choice + + +@dataclass +class Group: + name: str + doc: str | None + particle: Particle + + +# --------------------------------------------------------------------------- # +# Complex types +# --------------------------------------------------------------------------- # + + +@dataclass +class SimpleContent: + """A complex type whose body is text plus attributes (xs:simpleContent).""" + + base: str + derivation: str # "extension" | "restriction" + attributes: list[Attribute] + attribute_group_refs: list[AttributeGroupRef] + + +@dataclass +class ComplexContent: + """A complex type derived from another complex type (xs:complexContent).""" + + base: str + derivation: str + particle: Particle | None + attributes: list[Attribute] + attribute_group_refs: list[AttributeGroupRef] + + +@dataclass +class ImplicitContent: + """The shorthand form: a particle and/or attributes directly under + xs:complexType, with no simpleContent/complexContent wrapper.""" + + particle: Particle | None + attributes: list[Attribute] + attribute_group_refs: list[AttributeGroupRef] + + +ComplexContentModel = SimpleContent | ComplexContent | ImplicitContent + + +@dataclass +class ComplexType: + name: str | None # None for anonymous (inline) complex types + doc: str | None + content: ComplexContentModel + + +# --------------------------------------------------------------------------- # +# Schema root +# --------------------------------------------------------------------------- # + + +@dataclass +class TopLevelElement: + """A document root element (score-partwise, score-timewise).""" + + name: str + type: str | None + inline_type: ComplexType | None + doc: str | None + + +@dataclass +class Schema: + simple_types: dict[str, SimpleType] + complex_types: dict[str, ComplexType] + groups: dict[str, Group] + attribute_groups: dict[str, AttributeGroup] + elements: list[TopLevelElement] + imports: list[tuple[str, str]] # (namespace, schemaLocation) diff --git a/gen/xsd/parser.py b/gen/xsd/parser.py new file mode 100644 index 000000000..30799f176 --- /dev/null +++ b/gen/xsd/parser.py @@ -0,0 +1,258 @@ +"""Parse a MusicXML XSD file into the model in model.py. + +Uses the stdlib ElementTree. The parser is intentionally strict about the +MusicXML subset: it understands the element kinds the schema actually uses and +ignores annotations and XML comments. It does not resolve imported schemas +(xml.xsd, xlink.xsd); attribute refs into those namespaces are kept verbatim. +""" + +from __future__ import annotations + +import xml.etree.ElementTree as ET +from pathlib import Path + +from gen.xsd.model import ( + Attribute, + AttributeGroup, + AttributeGroupRef, + Choice, + ComplexContent, + ComplexType, + ElementParticle, + Enumeration, + Facets, + Group, + GroupRef, + ImplicitContent, + ListType, + Restriction, + Schema, + Sequence, + SimpleContent, + SimpleType, + TopLevelElement, + Union, + UNBOUNDED, +) + +XS = "{http://www.w3.org/2001/XMLSchema}" + + +def parse(path: str | Path) -> Schema: + root = ET.parse(path).getroot() + return _Parser().schema(root) + + +def _local(tag: object) -> str: + """Local name of an element tag; '' for comments/PIs (non-str tags).""" + return tag.rsplit("}", 1)[-1] if isinstance(tag, str) else "" + + +def _kids(elem: ET.Element) -> list[ET.Element]: + """Real child elements, skipping annotations and XML comments.""" + return [c for c in elem if isinstance(c.tag, str) and _local(c.tag) != "annotation"] + + +def _doc(elem: ET.Element) -> str | None: + for child in elem: + if _local(child.tag) == "annotation": + for d in child: + if _local(d.tag) == "documentation": + text = (d.text or "").strip() + return text or None + return None + + +def _min_occurs(elem: ET.Element) -> int: + return int(elem.get("minOccurs", "1")) + + +def _max_occurs(elem: ET.Element) -> int: + value = elem.get("maxOccurs", "1") + return UNBOUNDED if value == "unbounded" else int(value) + + +class _Parser: + def schema(self, root: ET.Element) -> Schema: + schema = Schema( + simple_types={}, + complex_types={}, + groups={}, + attribute_groups={}, + elements=[], + imports=[], + ) + for child in _kids(root): + kind = _local(child.tag) + if kind == "import": + schema.imports.append( + (child.get("namespace", ""), child.get("schemaLocation", "")) + ) + elif kind == "simpleType": + st = self.simple_type(child) + schema.simple_types[st.name] = st + elif kind == "complexType": + ct = self.complex_type(child) + schema.complex_types[ct.name] = ct + elif kind == "group": + g = self.group(child) + schema.groups[g.name] = g + elif kind == "attributeGroup": + ag = self.attribute_group(child) + schema.attribute_groups[ag.name] = ag + elif kind == "element": + schema.elements.append(self.top_level_element(child)) + return schema + + # ----- simple types ---------------------------------------------------- # + + def simple_type(self, elem: ET.Element) -> SimpleType: + body = _kids(elem)[0] + kind = _local(body.tag) + if kind == "restriction": + content = Restriction(body.get("base", ""), self.facets(body)) + elif kind == "union": + members = body.get("memberTypes", "").split() + inline = [self.simple_type(c) for c in _kids(body) if _local(c.tag) == "simpleType"] + content = Union(members, inline) + elif kind == "list": + content = ListType(body.get("itemType", "")) + else: + raise ValueError(f"unexpected simpleType body: {kind}") + return SimpleType(elem.get("name"), _doc(elem), content) + + def facets(self, restriction: ET.Element) -> Facets: + facets = Facets() + for f in _kids(restriction): + kind = _local(f.tag) + value = f.get("value", "") + if kind == "enumeration": + facets.enumerations.append(Enumeration(value, _doc(f))) + elif kind == "pattern": + facets.patterns.append(value) + elif kind == "minInclusive": + facets.min_inclusive = value + elif kind == "maxInclusive": + facets.max_inclusive = value + elif kind == "minExclusive": + facets.min_exclusive = value + elif kind == "maxExclusive": + facets.max_exclusive = value + elif kind == "minLength": + facets.min_length = value + elif kind == "maxLength": + facets.max_length = value + elif kind == "length": + facets.length = value + # whiteSpace and other facets are not needed by the generator. + return facets + + # ----- attributes ------------------------------------------------------ # + + def attribute(self, elem: ET.Element) -> Attribute: + inline = next((self.simple_type(c) for c in _kids(elem) if _local(c.tag) == "simpleType"), None) + return Attribute( + name=elem.get("name"), + ref=elem.get("ref"), + type=elem.get("type"), + inline_type=inline, + use=elem.get("use", "optional"), + default=elem.get("default"), + fixed=elem.get("fixed"), + doc=_doc(elem), + ) + + def _attributes(self, elem: ET.Element) -> tuple[list[Attribute], list[AttributeGroupRef]]: + """Collect direct xs:attribute and xs:attributeGroup ref children.""" + attrs: list[Attribute] = [] + refs: list[AttributeGroupRef] = [] + for child in _kids(elem): + kind = _local(child.tag) + if kind == "attribute": + attrs.append(self.attribute(child)) + elif kind == "attributeGroup": + refs.append(AttributeGroupRef(child.get("ref", ""))) + return attrs, refs + + def attribute_group(self, elem: ET.Element) -> AttributeGroup: + attrs, refs = self._attributes(elem) + return AttributeGroup(elem.get("name", ""), _doc(elem), attrs, refs) + + # ----- particles ------------------------------------------------------- # + + def particle(self, elem: ET.Element): + kind = _local(elem.tag) + if kind == "element": + return self.element_particle(elem) + if kind == "group": + return GroupRef(elem.get("ref", ""), _min_occurs(elem), _max_occurs(elem)) + if kind == "sequence": + return Sequence(self._particle_items(elem), _min_occurs(elem), _max_occurs(elem)) + if kind == "choice": + return Choice(self._particle_items(elem), _min_occurs(elem), _max_occurs(elem)) + raise ValueError(f"unexpected particle: {kind}") + + def _particle_items(self, container: ET.Element) -> list: + return [self.particle(c) for c in _kids(container)] + + def element_particle(self, elem: ET.Element) -> ElementParticle: + inline = None + for child in _kids(elem): + tag = _local(child.tag) + if tag == "complexType": + inline = self.complex_type(child) + elif tag == "simpleType": + inline = self.simple_type(child) + return ElementParticle( + name=elem.get("name", ""), + type=elem.get("type"), + inline_type=inline, + min_occurs=_min_occurs(elem), + max_occurs=_max_occurs(elem), + doc=_doc(elem), + ) + + def group(self, elem: ET.Element) -> Group: + return Group(elem.get("name", ""), _doc(elem), self.particle(_kids(elem)[0])) + + # ----- complex types --------------------------------------------------- # + + def complex_type(self, elem: ET.Element) -> ComplexType: + children = _kids(elem) + first = children[0] if children else None + first_kind = _local(first.tag) if first is not None else "" + + if first_kind == "simpleContent": + content = self._simple_content(first) + elif first_kind == "complexContent": + content = self._complex_content(first) + else: + content = self._implicit_content(elem) + return ComplexType(elem.get("name"), _doc(elem), content) + + def _simple_content(self, elem: ET.Element) -> SimpleContent: + body = _kids(elem)[0] # extension or restriction + attrs, refs = self._attributes(body) + return SimpleContent(body.get("base", ""), _local(body.tag), attrs, refs) + + def _complex_content(self, elem: ET.Element) -> ComplexContent: + body = _kids(elem)[0] # extension or restriction + attrs, refs = self._attributes(body) + particle = self._find_particle(body) + return ComplexContent(body.get("base", ""), _local(body.tag), particle, attrs, refs) + + def _implicit_content(self, elem: ET.Element) -> ImplicitContent: + attrs, refs = self._attributes(elem) + return ImplicitContent(self._find_particle(elem), attrs, refs) + + def _find_particle(self, container: ET.Element): + for child in _kids(container): + if _local(child.tag) in ("sequence", "choice", "group"): + return self.particle(child) + return None + + # ----- top-level element ----------------------------------------------- # + + def top_level_element(self, elem: ET.Element) -> TopLevelElement: + inline = next((self.complex_type(c) for c in _kids(elem) if _local(c.tag) == "complexType"), None) + return TopLevelElement(elem.get("name", ""), elem.get("type"), inline, _doc(elem)) From 862c1f24b23a9796c95881762b9cd37785b99e5f Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Mon, 8 Jun 2026 22:22:16 +0200 Subject: [PATCH 11/45] gen/ir: add resolution layer Collapse the IR's preserved named structure on demand instead of forcing every emitter to re-derive it. Resolver (gen/ir/resolve.py) expands attribute groups, splices model-group refs into content, and walks the base chain, exposing attributes/all_attributes/content/ elements. build now computes deps through it, removing the duplicated group walk. Also make UnionMember hold a Ref so every type reference in the IR has one shape, and add `ir --resolve` to dump the collapsed view. --- AGENTS.md | 10 ++-- gen/README.md | 53 ++++++++++++++++--- gen/__main__.py | 33 ++++++++---- gen/ir/__init__.py | 3 +- gen/ir/build.py | 33 ++++-------- gen/ir/dump.py | 23 ++++++++ gen/ir/model.py | 6 +-- gen/ir/resolve.py | 122 +++++++++++++++++++++++++++++++++++++++++++ gen/tests/test_ir.py | 72 +++++++++++++++++++++++-- 9 files changed, 305 insertions(+), 50 deletions(-) create mode 100644 gen/ir/resolve.py diff --git a/AGENTS.md b/AGENTS.md index 1c2f62361..36776381b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -124,12 +124,16 @@ their values instead of their string representations. Float comparison uses epsi The generator (`gen/`) is a Python program structured as a three-stage pipeline: parse the MusicXML XSD into a model (`gen/xsd/`), lower that into a resolved intermediate representation (`gen/ir/`), -then emit target code from the IR. See `gen/README.md` for the architecture, IR glossary, and a -structural analysis of the schema. +then emit target code from the IR. The IR data model preserves the schema's named structure (model +groups, attribute groups, inheritance edges); `gen/ir/resolve.py` collapses it on demand into the +flattened view an emitter consumes (attribute groups expanded, group refs spliced into content), so +that splicing-and-deduping reasoning lives once rather than once per language. See `gen/README.md` +for the architecture, IR glossary, the resolution layer, and a structural analysis of the schema. Commands: - `python3 -m gen analyze [xsd]` - print a structural analysis of the XSD. -- `python3 -m gen ir [--type NAME] [xsd]` - lower the XSD to the IR and print it as JSON. +- `python3 -m gen ir [--type NAME] [--resolve] [xsd]` - lower the XSD to the IR and print it as JSON; + `--resolve` prints the collapsed (group-spliced, attribute-flattened) view of complex types. - `python3 -m gen ` - emit code for the target in the config (not yet implemented). Each target has a `config.toml` specifying the output directory (relative to the config file) and, diff --git a/gen/README.md b/gen/README.md index c031d7810..e53c8aef1 100644 --- a/gen/README.md +++ b/gen/README.md @@ -32,6 +32,7 @@ gen/ ir/ model.py the IR dataclasses build.py lowering from the XSD model to the IR + resolve.py collapsed views (group + attribute-group resolution) for emitters dump.py IR to JSON cpp/, test/go/, test/c/ per-target config and corert test harnesses ``` @@ -44,22 +45,32 @@ gen/ references, ordering, dead-code removal, naming -- happens once, in the IR, shared by every target. Per-language choices (inheritance vs flattening, mixins vs inlined attributes) belong to the emitter. The IR takes no configuration. -- Resolve, but preserve names. The IR computes every resolved answer (effective primitives, - cardinalities, dependency order) yet keeps the schema's named structure (aliases, inheritance - edges, model groups, attribute groups) so each emitter can decide how much to collapse. +- Resolve, but preserve names. The IR data model computes every resolved answer (effective + primitives, cardinalities, dependency order) yet keeps the schema's named structure (aliases, + inheritance edges, model groups, attribute groups) so each emitter can decide how much to collapse. +- One resolution, shared. The collapsed form most emitters actually want -- attribute groups + flattened into a single ordered list, model-group refs spliced into the content, a derived type's + full attribute set including its base chain -- is *not* duplicated into the data (which would risk + drift). It is computed on demand by the resolution layer (`ir/resolve.py`), so the + splicing-and-deduping reasoning lives once and every emitter shares it rather than re-deriving it. ## Usage ``` -python3 -m gen analyze [xsd] # structural analysis report (text) -python3 -m gen ir [--type NAME] [xsd] # lower to IR, print as JSON (whole IR, or one type) -python3 -m gen # emit code for a target (not yet implemented) +python3 -m gen analyze [xsd] # structural analysis report (text) +python3 -m gen ir [--type NAME] [--resolve] [xsd] # lower to IR, print as JSON (whole IR, or one type) +python3 -m gen # emit code for a target (not yet implemented) ``` +`--resolve` prints the *collapsed* view of complex types (attribute groups flattened, model-group +refs spliced into the content, derived types carrying their full base-chain attribute set) -- the +form an emitter consumes. Without it, `ir` prints the IR verbatim, with the named structure intact. + `xsd` defaults to `docs/musicxml-4.0-ed15c23.xsd`. Examples: ``` python3 -m gen ir --type note # one type +python3 -m gen ir --type note --resolve # one type, collapsed for an emitter python3 -m gen ir > build/ir/musicxml-4.0.ir.json # whole IR (build/ is gitignored) jq '.complex_types[] | select(.name=="note")' build/ir/musicxml-4.0.ir.json python3 -m gen analyze docs/musicxml-3.1.xsd # analyze a different version @@ -111,7 +122,9 @@ The `stats` block summarizes the lowered model. Every key: `patterns` and length constraints. Includes plain string aliases. Emits a string wrapper with an optional pattern check. - union (4) -- a value that may be any one of several member value types or inline literal sets, - e.g. `number-or-normal` = decimal | "normal". IR field: `members`. Emits a small tagged variant. + e.g. `number-or-normal` = decimal | "normal". IR field: `members`, each a `UnionMember` holding + either a `ref` (a Ref to a value type or primitive) or inline `literals`. Emits a small tagged + variant. - complex_types (228) -- IR complex types: elements that carry attributes and/or child elements. Lowered from XSD complexTypes (including synthesized ones). - complex_kinds -- complex types by kind: @@ -157,13 +170,37 @@ Terms used inside the lowered types, not in `stats`. (0 or 1), or `vector` (repeatable). Derived from min/max. - presence_only -- true for an empty element with no attributes: its only information is whether it appears, so it maps to a bool. -- base -- for a `derived` complex type, the parent complex type it extends. +- base -- for a `derived` complex type, the parent complex type it extends. The IR stores only the + added attributes; inherited attributes are reached through `base`, or flattened in one call by + `resolve.all_attributes`. `ComplexType.content` is defined for derived types but is currently + always empty: every MusicXML derivation adds attributes only, never content. - value_type -- for a `value` complex type, the Ref to the value type of its text body. - deps -- the complex types a type structurally depends on (child element types + base), resolved through groups. Drives the ordering below. - roots -- the document root elements: `score-partwise` and `score-timewise`. - builtins -- the map from XSD/external builtin names to canonical IR primitives. +### Resolution layer + +The IR data model preserves the schema's named structure; `ir/resolve.py` collapses it on demand. +`Resolver.from_ir(ir)` exposes four read-only accessors over a complex type, none of which mutate the +IR: + +- `attributes(ct)` -- the type's own attributes with its `attribute_groups` expanded inline, in + declaration order, deduped by name. (`note`: 7 own + 5 groups -> 21 attributes.) +- `all_attributes(ct)` -- `attributes(ct)` plus the base chain's attributes, base-most first, for a + target with no inheritance to lean on. (`mordent`: 3 own -> 20 once `empty-trill-sound` is merged.) +- `content(ct)` -- `ct.content` with every model-group ref spliced in: a self-contained tree of + elements/sequences/choices with no `group` nodes left. Nesting and all min/max bounds are + preserved. +- `elements(ct)` -- every element occurrence in the resolved content, in document order, flattened + across sequences/choices/groups (drops the choice/sequence grouping; use `content` when that + matters). + +`python3 -m gen ir --resolve` dumps this view. `build` itself uses the resolver to compute each +complex type's `deps`, so the group-walking logic lives in exactly one place rather than once per +emitter. + ### Ordering Both `value_types` and `complex_types` are emitted **deps-first**: a type's dependencies always diff --git a/gen/__main__.py b/gen/__main__.py index c664c3d20..459849afb 100644 --- a/gen/__main__.py +++ b/gen/__main__.py @@ -1,9 +1,12 @@ """mx code generator entry point. Usage: - python3 -m gen generate code for a target (not yet implemented) - python3 -m gen analyze [xsd] parse the XSD and print a structural analysis - python3 -m gen ir [--type N] [xsd] lower the XSD to the IR and print it as JSON + python3 -m gen generate code for a target (not yet implemented) + python3 -m gen analyze [xsd] parse the XSD and print a structural analysis + python3 -m gen ir [--type N] [--resolve] [xsd] + lower the XSD to the IR and print it as JSON; + --resolve prints the collapsed (group-spliced, + attribute-flattened) view of complex types Reads a MusicXML 4.0 XSD specification and generates typed document serialization/deserialization code for the target described in the given @@ -31,16 +34,21 @@ def _analyze(args: list[str]) -> int: def _ir(args: list[str]) -> int: from gen.ir.build import build_ir - from gen.ir.dump import to_json + from gen.ir.dump import resolved_view, to_json + from gen.ir.resolve import Resolver from gen.xsd.parser import parse type_name = None + resolve = False rest = [] i = 0 while i < len(args): if args[i] == "--type" and i + 1 < len(args): type_name = args[i + 1] i += 2 + elif args[i] == "--resolve": + resolve = True + i += 1 else: rest.append(args[i]) i += 1 @@ -50,15 +58,22 @@ def _ir(args: list[str]) -> int: print(f"error: XSD not found: {xsd}", file=sys.stderr) return 1 ir = build_ir(parse(xsd), source=xsd.stem) + resolver = Resolver.from_ir(ir) if resolve else None if type_name: - match = next((c for c in ir.complex_types if c.name == type_name), None) or next( - (v for v in ir.value_types if v.name == type_name), None - ) - if match is None: + ct = next((c for c in ir.complex_types if c.name == type_name), None) + if ct is not None: + print(to_json(resolved_view(resolver, ct) if resolver else ct)) + return 0 + vt = next((v for v in ir.value_types if v.name == type_name), None) + if vt is None: print(f"error: type not found in IR: {type_name}", file=sys.stderr) return 1 - print(to_json(match)) + print(to_json(vt)) # value types are already fully resolved + return 0 + + if resolver: + print(to_json([resolved_view(resolver, c) for c in ir.complex_types])) else: print(to_json(ir)) return 0 diff --git a/gen/ir/__init__.py b/gen/ir/__init__.py index 294d8ee66..bf6796e55 100644 --- a/gen/ir/__init__.py +++ b/gen/ir/__init__.py @@ -2,5 +2,6 @@ from gen.ir.build import build_ir from gen.ir.model import Ir +from gen.ir.resolve import Resolver -__all__ = ["Ir", "build_ir"] +__all__ = ["Ir", "Resolver", "build_ir"] diff --git a/gen/ir/build.py b/gen/ir/build.py index afd3690bd..e47d81f8d 100644 --- a/gen/ir/build.py +++ b/gen/ir/build.py @@ -7,6 +7,7 @@ from gen.xsd import model as xsd from gen.xsd.analyze import content_particle, reachable_types from gen.ir import model as ir +from gen.ir.resolve import Resolver # Map XSD builtin types to canonical IR primitive names. _XS_PRIMITIVE = { @@ -102,9 +103,9 @@ def build(self) -> ir.Ir: ] complex_types += [self._complex_type(name, ct) for name, ct in self.synth] - group_map = {g.name: g for g in groups} + resolver = Resolver(groups, attribute_groups, complex_types) for ct in complex_types: - ct.deps = sorted(self._deps(ct, group_map)) + ct.deps = sorted(resolver.deps(ct)) complex_types = self._topo_sort(complex_types) all_named = set(self.schema.simple_types) | set(self.schema.complex_types) @@ -204,9 +205,9 @@ def _union(self, st: xsd.SimpleType) -> ir.UnionType: members: list[ir.UnionMember] = [] for m in st.content.member_types: if m in self.schema.simple_types: - members.append(ir.UnionMember(type=m, category="value")) + members.append(ir.UnionMember(ir.Ref(m, "value"))) else: - members.append(ir.UnionMember(type=_primitive(m), category="primitive")) + members.append(ir.UnionMember(ir.Ref(_primitive(m), "primitive"))) for inline in st.content.inline_members: if isinstance(inline.content, xsd.Restriction) and inline.content.facets.enumerations: members.append( @@ -289,24 +290,6 @@ def _type_ref(self, type_name: str) -> ir.Ref: # ----- dependency ordering --------------------------------------------- # - def _deps(self, ct: ir.ComplexType, groups: dict[str, ir.Group]) -> set[str]: - deps: set[str] = set() - if ct.base: - deps.add(ct.base) - self._collect_deps(ct.content, groups, set(), deps) - return deps - - def _collect_deps(self, node, groups, seen_groups, deps) -> None: - if isinstance(node, (ir.Sequence, ir.Choice)): - for item in node.items: - self._collect_deps(item, groups, seen_groups, deps) - elif isinstance(node, ir.GroupRef): - if node.name in groups and node.name not in seen_groups: - seen_groups.add(node.name) - self._collect_deps(groups[node.name].content, groups, seen_groups, deps) - elif isinstance(node, ir.Element) and node.type.category == "complex": - deps.add(node.type.name) - def _topo_sort(self, types: list[ir.ComplexType]) -> list[ir.ComplexType]: by_name = {t.name: t for t in types} ordered: list[ir.ComplexType] = [] @@ -334,7 +317,11 @@ def _topo_sort_values(self, values: list[ir.ValueType]) -> list[ir.ValueType]: def deps(v: ir.ValueType) -> list[str]: if isinstance(v, ir.UnionType): - return [m.type for m in v.members if m.category == "value" and m.type in by_name] + return [ + m.ref.name + for m in v.members + if m.ref and m.ref.category == "value" and m.ref.name in by_name + ] return [] def visit(name: str) -> None: diff --git a/gen/ir/dump.py b/gen/ir/dump.py index 9f6fd3ea8..d16c1c40b 100644 --- a/gen/ir/dump.py +++ b/gen/ir/dump.py @@ -31,3 +31,26 @@ def to_jsonable(obj): def to_json(obj) -> str: return json.dumps(to_jsonable(obj), indent=2) + + +def resolved_view(resolver, ct) -> dict: + """A complex type as an emitter consumes it: attribute groups flattened into + one list, model-group refs spliced into the content. The collapsed form the + Resolver computes, shaped for inspection via `ir --resolve`.""" + view: dict = {"kind": ct.kind, "name": ct.name} + attrs = resolver.attributes(ct) + if attrs: + view["attributes"] = attrs + if ct.kind == "derived": + view["base"] = ct.base + view["all_attributes"] = resolver.all_attributes(ct) + if ct.value_type: + view["value_type"] = ct.value_type + content = resolver.content(ct) + if content is not None: + view["content"] = content + if ct.presence_only: + view["presence_only"] = True + if ct.doc: + view["doc"] = ct.doc + return view diff --git a/gen/ir/model.py b/gen/ir/model.py index cc1b75601..f57e39eb4 100644 --- a/gen/ir/model.py +++ b/gen/ir/model.py @@ -80,9 +80,9 @@ class StringType: @dataclass class UnionMember: - type: str | None = None # ref to a value type or primitive - category: str | None = None # "value" | "primitive" - literals: list[str] | None = None # inline enumeration literals + # Exactly one is set: a Ref to another type, or inline enumeration literals. + ref: Ref | None = None + literals: list[str] | None = None @dataclass diff --git a/gen/ir/resolve.py b/gen/ir/resolve.py new file mode 100644 index 000000000..b6103c566 --- /dev/null +++ b/gen/ir/resolve.py @@ -0,0 +1,122 @@ +"""Collapsed views over the named structure the IR preserves. + +The IR keeps the schema's reusable structure addressable: a complex type lists +its attribute groups by name and leaves group references in its content tree, so +an emitter that wants mixins or shared structs can mirror them. Most emitters +instead want the collapsed view -- the full ordered attribute list, the content +with groups spliced in. Producing it means expanding attribute-group and +model-group references, deduping, and guarding cycles. That is schema reasoning, +so it lives here, once, rather than re-derived in every target's templates. + +Resolver is a pure read over the IR; it never mutates it. It depends only on the +three reusable tables (groups, attribute groups, complex types), not the whole +Ir, so build can use it mid-construction to compute dependencies. +""" + +from __future__ import annotations + +from gen.ir import model as ir + + +class Resolver: + """Collapsed views over an IR's preserved named structure.""" + + def __init__( + self, + groups: list[ir.Group], + attribute_groups: list[ir.AttributeGroup], + complex_types: list[ir.ComplexType], + ): + self._groups = {g.name: g for g in groups} + self._agroups = {a.name: a for a in attribute_groups} + self._complex = {c.name: c for c in complex_types} + + @classmethod + def from_ir(cls, m: ir.Ir) -> "Resolver": + return cls(m.groups, m.attribute_groups, m.complex_types) + + # ----- attributes ------------------------------------------------------ # + + def attributes(self, ct: ir.ComplexType) -> list[ir.Attr]: + """ct's own attributes with its attribute groups expanded inline, in + declaration order, deduped by name (first wins). Excludes the base.""" + out: list[ir.Attr] = [] + self._add_attrs(ct.attributes, ct.attribute_groups, out, set(), set()) + return out + + def all_attributes(self, ct: ir.ComplexType) -> list[ir.Attr]: + """attributes() plus the base chain's attributes (base-most first), for + the flattened set an emitter needs when the target has no inheritance.""" + chain: list[ir.ComplexType] = [] + cur: ir.ComplexType | None = ct + while cur is not None: + chain.append(cur) + cur = self._complex.get(cur.base) if cur.base else None + out: list[ir.Attr] = [] + seen: set[str] = set() + for c in reversed(chain): + self._add_attrs(c.attributes, c.attribute_groups, out, seen, set()) + return out + + def _add_attrs(self, attrs, group_names, out, seen, seen_groups) -> None: + for a in attrs: + if a.name not in seen: + seen.add(a.name) + out.append(a) + for name in group_names: + ag = self._agroups.get(name) + if ag is not None and name not in seen_groups: + seen_groups.add(name) + self._add_attrs(ag.attributes, ag.attribute_groups, out, seen, seen_groups) + + # ----- content --------------------------------------------------------- # + + def content(self, ct: ir.ComplexType) -> ir.Particle | None: + """ct.content with every group reference spliced in: a self-contained + tree of elements/sequences/choices with no GroupRef nodes. Nesting and + all min/max bounds are preserved. None for types with no content.""" + return None if ct.content is None else self._inline(ct.content, ()) + + def _inline(self, p: ir.Particle, path: tuple[str, ...]) -> ir.Particle: + if isinstance(p, ir.Sequence): + return ir.Sequence([self._inline(i, path) for i in p.items], p.min, p.max) + if isinstance(p, ir.Choice): + return ir.Choice([self._inline(i, path) for i in p.items], p.min, p.max) + if isinstance(p, ir.GroupRef): + g = self._groups.get(p.name) + if g is None or p.name in path: # unknown or cyclic: leave the leaf + return p + body = self._inline(g.content, path + (p.name,)) + # The ref's occurrence wraps the group body's own. Drop the wrapper + # when the ref is exactly-one and so contributes nothing. + if p.min == 1 and p.max == 1: + return body + return ir.Sequence([body], p.min, p.max) + return p # Element: a leaf with an already-resolved Ref + + # ----- elements -------------------------------------------------------- # + + def elements(self, ct: ir.ComplexType) -> list[ir.Element]: + """Every element occurrence in ct's resolved content, in document order, + flattened across sequences/choices/groups. Drops the choice/sequence + grouping; use content() when that structure matters.""" + out: list[ir.Element] = [] + self._collect_elements(self.content(ct), out) + return out + + def _collect_elements(self, p, out) -> None: + if isinstance(p, (ir.Sequence, ir.Choice)): + for i in p.items: + self._collect_elements(i, out) + elif isinstance(p, ir.Element): + out.append(p) + + # ----- dependencies ---------------------------------------------------- # + + def deps(self, ct: ir.ComplexType) -> set[str]: + """Complex types ct structurally depends on: its child element types + (groups resolved) plus its base. Drives the topological emit order.""" + d = {e.type.name for e in self.elements(ct) if e.type.category == "complex"} + if ct.base: + d.add(ct.base) + return d diff --git a/gen/tests/test_ir.py b/gen/tests/test_ir.py index 0475e3cd1..1425e6458 100644 --- a/gen/tests/test_ir.py +++ b/gen/tests/test_ir.py @@ -19,6 +19,7 @@ from gen.ir import build_ir from gen.ir import model as ir +from gen.ir.resolve import Resolver from gen.xsd import parse from gen.xsd.analyze import element_index, type_graph @@ -92,10 +93,10 @@ def test_dependency_order(self): for v in m.value_types: if isinstance(v, ir.UnionType): for mem in v.members: - if mem.category == "value" and mem.type in vpos: + if mem.ref and mem.ref.category == "value" and mem.ref.name in vpos: self.assertLess( - vpos[mem.type], vpos[v.name], - f"union {v.name} member {mem.type} out of order", + vpos[mem.ref.name], vpos[v.name], + f"union {v.name} member {mem.ref.name} out of order", ) def _check_references(self, m: ir.Ir) -> None: @@ -141,6 +142,12 @@ def walk(node, where: str) -> None: for g in m.groups: walk(g.content, f"group {g.name}") + for v in m.value_types: + if isinstance(v, ir.UnionType): + for mem in v.members: + if mem.ref is not None: + check_ref(mem.ref, f"union {v.name} member") + @unittest.skipUnless(XSD_40.exists(), "MusicXML 4.0 XSD not present") class DeadTypeRegression(unittest.TestCase): @@ -164,5 +171,64 @@ def test_dead_dropped_live_kept(self): self.assertIn("measure-text", names) +class ResolverIntegrity(unittest.TestCase): + """The resolution layer must produce a self-contained, resolvable view for + every complex type: groups fully spliced, attributes deduped and resolvable, + derived types carrying their base chain.""" + + def test_content_has_no_group_refs(self): + for xsd in XSDS: + with self.subTest(xsd=xsd.name): + m = build_ir(parse(xsd), xsd.stem) + r = Resolver.from_ir(m) + for c in m.complex_types: + self._assert_no_group_refs(r.content(c), c.name) + + def test_attributes_unique_and_resolvable(self): + for xsd in XSDS: + with self.subTest(xsd=xsd.name): + m = build_ir(parse(xsd), xsd.stem) + r = Resolver.from_ir(m) + value_names = {v.name for v in m.value_types} + for c in m.complex_types: + names = [a.name for a in r.attributes(c)] + self.assertEqual(len(names), len(set(names)), f"{c.name} dup attrs") + for a in r.attributes(c): + if a.type.category == "value": + self.assertIn(a.type.name, value_names, f"{c.name}/{a.name}") + + def test_elements_match_deps(self): + """elements() is the basis for deps: every complex element it surfaces + must appear in the type's recorded deps.""" + for xsd in XSDS: + with self.subTest(xsd=xsd.name): + m = build_ir(parse(xsd), xsd.stem) + r = Resolver.from_ir(m) + for c in m.complex_types: + for e in r.elements(c): + if e.type.category == "complex": + self.assertIn(e.type.name, c.deps, f"{c.name} -> {e.type.name}") + + def test_all_attributes_includes_base(self): + m = build_ir(parse(XSD_40), "musicxml-4.0") + r = Resolver.from_ir(m) + derived = [c for c in m.complex_types if c.kind == "derived"] + self.assertTrue(derived, "expected derived types in 4.0") + for c in derived: + own = {a.name for a in r.attributes(c)} + full = {a.name for a in r.all_attributes(c)} + base = next(b for b in m.complex_types if b.name == c.base) + base_attrs = {a.name for a in r.attributes(base)} + self.assertTrue(own <= full) + self.assertTrue(base_attrs <= full, f"{c.name} missing base {c.base} attrs") + + def _assert_no_group_refs(self, node, where: str) -> None: + if isinstance(node, (ir.Sequence, ir.Choice)): + for item in node.items: + self._assert_no_group_refs(item, where) + else: + self.assertNotIsInstance(node, ir.GroupRef, f"{where}: unresolved group ref") + + if __name__ == "__main__": unittest.main() From 5708ddd39b813f684c0f86a47d2ad97df8dff310 Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Wed, 10 Jun 2026 08:08:04 +0200 Subject: [PATCH 12/45] gen: per-target schemas + sounds.xml IR fold Fold the standard instrument-sound identifiers into the IR as a sound-id enum unioned with an open string (gen/ir/sounds.py): the XSD types instrument-sound as xs:string and lists the values only in the sounds.xml companion, not the schema. Opt-in per target. Vendor each MusicXML version's XSD and sounds.xml under docs/ with matching git-commit hash suffixes (3.0 5fd8eb3, 3.1 8bbe8e5, beside the existing 4.0 ed15c23 and 4.1 0d56097). Each target pins its schema and sounds policy via [input] xsd and [sounds] xml: C++ is 4.0 with sounds, C is 3.1 with sounds, Go is 3.1 without -- the C/Go pair differ only by the fold, keeping the generator honest about extensibility. --- AGENTS.md | 39 +- ...icxml-3.0.xsd => musicxml-3.0-5fd8eb3.xsd} | 8 +- ...icxml-3.1.xsd => musicxml-3.1-8bbe8e5.xsd} | 2 +- docs/{sounds.xml => sounds-3.0-5fd8eb3.xml} | 6 +- docs/sounds-3.1-8bbe8e5.xml | 924 +++++++++++++++++ docs/sounds-4.0-ed15c23.xml | 932 ++++++++++++++++++ docs/sounds-4.1-0d56097.xml | 932 ++++++++++++++++++ gen/README.md | 29 +- gen/__main__.py | 33 +- gen/config.py | 58 ++ gen/cpp/config.toml | 13 +- gen/ir/sounds.py | 101 ++ gen/test/c/config.toml | 14 +- gen/test/go/config.toml | 10 +- 14 files changed, 3072 insertions(+), 29 deletions(-) rename docs/{musicxml-3.0.xsd => musicxml-3.0-5fd8eb3.xsd} (98%) mode change 100755 => 100644 rename docs/{musicxml-3.1.xsd => musicxml-3.1-8bbe8e5.xsd} (98%) mode change 100755 => 100644 rename docs/{sounds.xml => sounds-3.0-5fd8eb3.xml} (96%) create mode 100644 docs/sounds-3.1-8bbe8e5.xml create mode 100644 docs/sounds-4.0-ed15c23.xml create mode 100644 docs/sounds-4.1-0d56097.xml create mode 100644 gen/config.py create mode 100644 gen/ir/sounds.py diff --git a/AGENTS.md b/AGENTS.md index 36776381b..123f15278 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -62,7 +62,8 @@ housekeeping). Docker-gated targets auto-build and run via mx-sdk. The corert test is the primary correctness gate. It exercises the generated parser by round-tripping every eligible XML file in `data/` through the typed model and comparing the output to a normalized form of the input. The corpus is pinned to version `3.0` throughout (input and expected) even though -the generator targets the 4.0 schema, so comparison runs against a stable baseline. +the generators target newer schemas (C++ 4.0, Go and C 3.1), so comparison runs against a stable +baseline. ### Flow (same in all three languages) @@ -132,12 +133,25 @@ for the architecture, IR glossary, the resolution layer, and a structural analys Commands: - `python3 -m gen analyze [xsd]` - print a structural analysis of the XSD. -- `python3 -m gen ir [--type NAME] [--resolve] [xsd]` - lower the XSD to the IR and print it as JSON; - `--resolve` prints the collapsed (group-spliced, attribute-flattened) view of complex types. +- `python3 -m gen ir [--type NAME] [--resolve] [--config C] [xsd]` - lower the XSD to the IR and + print it as JSON; `--resolve` prints the collapsed (group-spliced, attribute-flattened) view of + complex types; `--config` applies a target config's companion patches (the sounds.xml fold) first. - `python3 -m gen ` - emit code for the target in the config (not yet implemented). -Each target has a `config.toml` specifying the output directory (relative to the config file) and, -eventually, language-specific settings. +Each target has a `config.toml` specifying the MusicXML XSD it generates from (`[input] xsd`), the +output directory (`[output] dir`, relative to the config file), an optional `[sounds] xml` companion +file (see below), and, eventually, language-specific settings. Each path is relative to the config +file. The three targets deliberately span the matrix: C++ is 4.0 with sounds, C is 3.1 with sounds, +and Go is 3.1 without sounds (the C/Go pair differ only by the companion fold). + +### Companion data + +`instrument-sound` is `xs:string` in the XSD; the standard sound identifiers live only in the +separately versioned `sounds.xml` (vendored as `docs/sounds-.xml`). When a target's +`config.toml` sets `[sounds] xml`, `gen/ir/sounds.py` folds them into the IR as a `sound-id` enum +unioned with an open string (element `instrument-sound` retyped from `string` to that union). This is +the only place the IR depends on an input beyond the XSD; it is opt-in per target, so the base IR +stays a pure function of the schema. **Status.** The parse, IR, and analysis stages exist. The emit stage and its templates are not yet implemented, so `python3 -m gen ` still exits with an error. @@ -146,18 +160,21 @@ implemented, so `python3 -m gen ` still exits with an error. ### C++ (primary, `gen/cpp/`) -The existing codebase. Generated code lands in `src/private/mx/core/`. The ezxml layer -(`src/private/mx/ezxml/`) provides the XML DOM that the generated code builds on. +MusicXML 4.0 with the sounds companion. The existing codebase. Generated code lands in +`src/private/mx/core/`. The ezxml layer (`src/private/mx/ezxml/`) provides the XML DOM that the +generated code builds on. ### Go (test target, `gen/test/go/`) -Uses `github.com/beevik/etree` (vendored) for DOM-style XML. The generated code will land in -`gen/test/go/mx/`. Test runner uses Go's `testing` package with subtests. +MusicXML 3.1 *without* the sounds companion. Uses `github.com/beevik/etree` (vendored) for DOM-style +XML. The generated code will land in `gen/test/go/mx/`. Test runner uses Go's `testing` package with +subtests. ### C (test target, `gen/test/c/`) -Uses libxml2 (apt package in Docker). The generated code will land in `gen/test/c/mx/`. Test runner -is a simple `main()` that prints pass/fail per file and a summary. +MusicXML 3.1 *with* the sounds companion -- same schema as Go, so the two outputs differ only by the +fold. Uses libxml2 (apt package in Docker). The generated code will land in `gen/test/c/mx/`. Test +runner is a simple `main()` that prints pass/fail per file and a summary. ## Key files to understand diff --git a/docs/musicxml-3.0.xsd b/docs/musicxml-3.0-5fd8eb3.xsd old mode 100755 new mode 100644 similarity index 98% rename from docs/musicxml-3.0.xsd rename to docs/musicxml-3.0-5fd8eb3.xsd index 2e602eadf..76701dd43 --- a/docs/musicxml-3.0.xsd +++ b/docs/musicxml-3.0-5fd8eb3.xsd @@ -5,12 +5,12 @@ Version 3.0 -Copyright © 2004-2011 Recordare LLC. -http://www.recordare.com/ +Copyright © 2004-2011 MakeMusic, Inc. +http://www.makemusic.com/ This MusicXML™ work is being provided by the copyright holder under the MusicXML Public License Version 3.0, available from: - http://www.recordare.com/dtds/license.html + http://www.musicxml.org/dtds/license.html This is the W3C XML Schema (XSD) version of the MusicXML 3.0 language. Validation is tightened by moving MusicXML definitions from comments into schema data types and definitions. Character entities and other entity usages that are not supported in W3C XML Schema have been removed. The features of W3C XML Schema make it easier to define variations of the MusicXML format, either via extension or restriction. @@ -18,7 +18,7 @@ This file defines the MusicXML 3.0 XSD, including the score-partwise and score-t - The MusicXML 3.0 DTD has no namespace, so for compatibility the MusicXML 3.0 XSD has no namespace either. Those who need to import the MusicXML XSD into another schema are advised to create a new version that uses "http://www.musicxml.org/xsd/MusicXML" as the namespace. + The MusicXML 3.0 DTD has no namespace, so for compatibility the MusicXML 3.0 XSD has no namespace either. Those who need to import the MusicXML XSD into another schema are advised to create a new version that uses "MusicXML" as the namespace. diff --git a/docs/musicxml-3.1.xsd b/docs/musicxml-3.1-8bbe8e5.xsd old mode 100755 new mode 100644 similarity index 98% rename from docs/musicxml-3.1.xsd rename to docs/musicxml-3.1-8bbe8e5.xsd index 081d9c71c..c10bb3c56 --- a/docs/musicxml-3.1.xsd +++ b/docs/musicxml-3.1-8bbe8e5.xsd @@ -19,7 +19,7 @@ This file defines the MusicXML 3.1 XSD, including the score-partwise and score-t - The MusicXML 3.1 DTD has no namespace, so for compatibility the MusicXML 3.1 XSD has no namespace either. Those who need to import the MusicXML XSD into another schema are advised to create a new version that uses "http://www.musicxml.org/xsd/MusicXML" as the namespace. + The MusicXML 3.1 DTD has no namespace, so for compatibility the MusicXML 3.1 XSD has no namespace either. Those who need to import the MusicXML XSD into another schema are advised to create a new version that uses "MusicXML" as the namespace. diff --git a/docs/sounds.xml b/docs/sounds-3.0-5fd8eb3.xml similarity index 96% rename from docs/sounds.xml rename to docs/sounds-3.0-5fd8eb3.xml index b604c19a8..067ce566f 100644 --- a/docs/sounds.xml +++ b/docs/sounds-3.0-5fd8eb3.xml @@ -6,14 +6,14 @@ Version 3.0 - Copyright © 2004-2011 Recordare LLC. - http://www.recordare.com/ + Copyright © 2004-2011 MakeMusic, Inc. + http://www.makemusic.com/ This MusicXML™ work is being provided by the copyright holder under the MusicXML Public License Version 3.0, available from: - http://www.recordare.com/dtds/license.html + http://www.musicxml.org/dtds/license.html Starting with Version 3.0, the MusicXML format includes a standard set of instrument sounds to identify musical diff --git a/docs/sounds-3.1-8bbe8e5.xml b/docs/sounds-3.1-8bbe8e5.xml new file mode 100644 index 000000000..3191870a5 --- /dev/null +++ b/docs/sounds-3.1-8bbe8e5.xml @@ -0,0 +1,924 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/sounds-4.0-ed15c23.xml b/docs/sounds-4.0-ed15c23.xml new file mode 100644 index 000000000..bb09ef62f --- /dev/null +++ b/docs/sounds-4.0-ed15c23.xml @@ -0,0 +1,932 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/sounds-4.1-0d56097.xml b/docs/sounds-4.1-0d56097.xml new file mode 100644 index 000000000..f0bf95a94 --- /dev/null +++ b/docs/sounds-4.1-0d56097.xml @@ -0,0 +1,932 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gen/README.md b/gen/README.md index e53c8aef1..8c81ea682 100644 --- a/gen/README.md +++ b/gen/README.md @@ -54,18 +54,39 @@ gen/ drift). It is computed on demand by the resolution layer (`ir/resolve.py`), so the splicing-and-deduping reasoning lives once and every emitter shares it rather than re-deriving it. +### Companion data (sounds.xml) + +The one documented exception to "the IR is a pure function of the XSD." The schema types the +`instrument-sound` element as a bare `xs:string`; the ~900 standard timbre identifiers it expects +(`brass.alphorn`, ...) live only in `sounds.xml`, a separately versioned MusicXML file, not the +schema. `ir/sounds.py` reads that file and folds it into the IR: it adds a `sound-id` enum over the +identifiers and an `instrument-sound` union of that enum with an open string, then retypes the +element from `string` to the union. Known values become typed; any other string stays valid, because +the schema leaves the content open. This needs no new IR shape -- it is an ordinary enum plus an +ordinary union, the same shape as `font-size` (the `css-font-size` enum unioned with `decimal`). + +The patch runs only when a target's `config.toml` names a sounds file under `[sounds] xml` (vendored +in `docs/`, version-matched to the XSD), so it is opt-in per target and the base IR stays pure. +`python3 -m gen ir --config ` shows the patched view. It is config-gated, but not a +type-shaping knob: it selects an *input*, not how a type is emitted. + ## Usage ``` -python3 -m gen analyze [xsd] # structural analysis report (text) -python3 -m gen ir [--type NAME] [--resolve] [xsd] # lower to IR, print as JSON (whole IR, or one type) -python3 -m gen # emit code for a target (not yet implemented) +python3 -m gen analyze [xsd] # structural analysis report (text) +python3 -m gen ir [--type NAME] [--resolve] [--config C] [xsd] # lower to IR, print as JSON +python3 -m gen # emit code for a target (not yet implemented) ``` `--resolve` prints the *collapsed* view of complex types (attribute groups flattened, model-group refs spliced into the content, derived types carrying their full base-chain attribute set) -- the form an emitter consumes. Without it, `ir` prints the IR verbatim, with the named structure intact. +`--config C` lowers the IR exactly as that target's emitter will consume it: it reads the schema the +config pins (`[input] xsd`) and applies the companion patches the config enables (the sounds.xml +fold, see Companion data). An explicit positional `xsd` still overrides the config's. Without +`--config`, `ir` shows the pure-XSD IR of the default (or positional) schema. + `xsd` defaults to `docs/musicxml-4.0-ed15c23.xsd`. Examples: ``` @@ -73,7 +94,7 @@ python3 -m gen ir --type note # one type python3 -m gen ir --type note --resolve # one type, collapsed for an emitter python3 -m gen ir > build/ir/musicxml-4.0.ir.json # whole IR (build/ is gitignored) jq '.complex_types[] | select(.name=="note")' build/ir/musicxml-4.0.ir.json -python3 -m gen analyze docs/musicxml-3.1.xsd # analyze a different version +python3 -m gen analyze docs/musicxml-3.1-8bbe8e5.xsd # analyze a different version ``` ## Glossary diff --git a/gen/__main__.py b/gen/__main__.py index 459849afb..6f4dee3f0 100644 --- a/gen/__main__.py +++ b/gen/__main__.py @@ -3,10 +3,12 @@ Usage: python3 -m gen generate code for a target (not yet implemented) python3 -m gen analyze [xsd] parse the XSD and print a structural analysis - python3 -m gen ir [--type N] [--resolve] [xsd] + python3 -m gen ir [--type N] [--resolve] [--config C] [xsd] lower the XSD to the IR and print it as JSON; --resolve prints the collapsed (group-spliced, - attribute-flattened) view of complex types + attribute-flattened) view of complex types; + --config applies a target's companion patches + (e.g. the sounds.xml fold) before dumping Reads a MusicXML 4.0 XSD specification and generates typed document serialization/deserialization code for the target described in the given @@ -40,6 +42,7 @@ def _ir(args: list[str]) -> int: type_name = None resolve = False + config_path = None rest = [] i = 0 while i < len(args): @@ -49,15 +52,39 @@ def _ir(args: list[str]) -> int: elif args[i] == "--resolve": resolve = True i += 1 + elif args[i] == "--config" and i + 1 < len(args): + config_path = args[i + 1] + i += 2 else: rest.append(args[i]) i += 1 - xsd = Path(rest[0]) if rest else DEFAULT_XSD + cfg = None + if config_path is not None: + from gen.config import load as load_config + + cfg = load_config(config_path) + + # XSD precedence: an explicit positional argument wins, else the target + # config's pinned version, else the 4.0 default. + if rest: + xsd = Path(rest[0]) + elif cfg is not None and cfg.xsd is not None: + xsd = cfg.xsd + else: + xsd = DEFAULT_XSD if not xsd.exists(): print(f"error: XSD not found: {xsd}", file=sys.stderr) return 1 ir = build_ir(parse(xsd), source=xsd.stem) + + # A target config can fold companion data into the IR before it is consumed: + # today, the sounds.xml patch (instrument-sound -> open sound enum). + if cfg is not None and cfg.sounds_xml is not None: + from gen.ir.sounds import patch_sounds, read_sound_ids + + patch_sounds(ir, read_sound_ids(cfg.sounds_xml)) + resolver = Resolver.from_ir(ir) if resolve else None if type_name: diff --git a/gen/config.py b/gen/config.py new file mode 100644 index 000000000..5ed75dce2 --- /dev/null +++ b/gen/config.py @@ -0,0 +1,58 @@ +"""Load a target's config.toml into a typed Config. + +A target config describes one generation run: where generated code lands and +which optional companion patches to apply before emitting. The IR itself is a +pure function of the schema inputs (see gen.ir); config selects *which* inputs +and where the output goes, never how an individual type is shaped. +""" + +from __future__ import annotations + +import tomllib +from dataclasses import dataclass +from pathlib import Path + + +@dataclass +class Config: + path: Path # the config file itself, resolved + xsd: Path | None # the MusicXML XSD this target generates from, resolved + output_dir: Path | None # where generated code lands, resolved + sounds_xml: Path | None # companion sounds file to fold in, or None when off + + +def load(config_path) -> Config: + """Parse config.toml. Paths inside it are interpreted relative to the config + file's own directory, so a target's config stays self-contained.""" + path = Path(config_path).resolve() + if not path.exists(): + raise FileNotFoundError(f"config not found: {path}") + with open(path, "rb") as f: + data = tomllib.load(f) + base = path.parent + + # Each target pins its own MusicXML version: the schema it generates from is + # part of the target's identity, not a global default. + xsd = None + inp = data.get("input", {}) + if inp.get("xsd"): + xsd = (base / inp["xsd"]).resolve() + if not xsd.exists(): + raise FileNotFoundError(f"xsd not found: {xsd}") + + output_dir = None + out = data.get("output", {}) + if out.get("dir"): + output_dir = (base / out["dir"]).resolve() + + # Companion sounds patch is on iff [sounds] xml names a file (see + # gen.ir.sounds). Resolve and existence-check it here so a bad path fails at + # config load, not deep in the lowering. + sounds_xml = None + sounds = data.get("sounds", {}) + if sounds.get("xml"): + sounds_xml = (base / sounds["xml"]).resolve() + if not sounds_xml.exists(): + raise FileNotFoundError(f"sounds file not found: {sounds_xml}") + + return Config(path=path, xsd=xsd, output_dir=output_dir, sounds_xml=sounds_xml) diff --git a/gen/cpp/config.toml b/gen/cpp/config.toml index 05cacf7de..df4b5efe1 100644 --- a/gen/cpp/config.toml +++ b/gen/cpp/config.toml @@ -1,6 +1,17 @@ # C++ generator target configuration. -# The generator reads this to know where and how to emit C++ code. +# The generator reads this to know which schema to read and where/how to emit. +# C++ is the primary target: MusicXML 4.0, with the sounds.xml companion. + +[input] +# MusicXML XSD this target generates from, relative to this config file. +xsd = "../../docs/musicxml-4.0-ed15c23.xsd" [output] # Directory for generated source files, relative to this config file. dir = "../../src/private/mx/core" + +[sounds] +# Companion sounds file (vendored under docs/, version-matched to the XSD). +# Folds the standard instrument-sound identifiers into the IR as a sound-id +# enum unioned with an open string. Comment out to disable. +xml = "../../docs/sounds-4.0-ed15c23.xml" diff --git a/gen/ir/sounds.py b/gen/ir/sounds.py new file mode 100644 index 000000000..8b3a32bb4 --- /dev/null +++ b/gen/ir/sounds.py @@ -0,0 +1,101 @@ +"""Companion patch: fold sounds.xml into the IR as an open sound enum. + +The MusicXML XSD types the instrument-sound element as a bare xs:string. The +standard timbre identifiers it expects ("brass.alphorn", ...) live only in the +separately versioned sounds.xml companion file, not the schema. This patch +reads that file and rewrites the IR so instrument-sound resolves to a sound-id +enumeration unioned with an open string: the standard identifiers become typed +values, while any other string stays valid exactly as the schema allows. + +This is the one place the IR depends on an input beyond the XSD, and it runs +only when a target's config names a sounds file (see gen.config). The result +introduces no new IR shape -- it is an ordinary enum plus an ordinary union, +the same shape as font-size (the css-font-size enum unioned with decimal). +""" + +from __future__ import annotations + +import xml.etree.ElementTree as ET +from pathlib import Path + +from gen.ir import model as ir + +# The element keeps its name; its new type takes the element's name (the +# MusicXML convention, e.g. element note has type note), and the enumeration of +# identifiers gets a sub-name -- mirroring font-size over css-font-size. +ELEMENT = "instrument-sound" +UNION = "instrument-sound" +ENUM = "sound-id" + + +def read_sound_ids(path) -> list[str]: + """The id of every in a sounds.xml companion file, in document + order. The file's DOCTYPE points at an external DTD; ElementTree ignores it, + so this stays offline.""" + root = ET.parse(Path(path)).getroot() + return [s.get("id") for s in root.findall("sound") if s.get("id")] + + +def patch_sounds(m: ir.Ir, sound_ids: list[str]) -> int: + """Fold sound_ids into m in place: add the sound-id enum and instrument-sound + union, then retype every instrument-sound element from string to that union. + Returns the number of element occurrences retyped, which must be >= 1.""" + enum = ir.EnumType( + name=ENUM, + base="token", + values=list(sound_ids), + doc=( + "Standard MusicXML instrument sound identifiers. The XSD types " + "instrument-sound as xs:string and lists these values only in the " + "sounds.xml companion file; the generator injects them here." + ), + ) + union = ir.UnionType( + name=UNION, + members=[ + ir.UnionMember(ref=ir.Ref(ENUM, "value")), + ir.UnionMember(ref=ir.Ref("string", "primitive")), + ], + doc=( + "The instrument-sound value: one of the standard sound-id " + "identifiers, or any other string. The schema leaves the content " + "open (xs:string), so the string member is intrinsic, not a fallback." + ), + ) + # Deps-first invariant: the enum (no value deps) precedes the union that + # references it, and nothing already in the list references either, so + # appending the pair keeps value_types topologically ordered. + m.value_types.append(enum) + m.value_types.append(union) + + # instrument-sound is declared inside the virtual-instrument-data group, not + # a complex type's content, so retype across groups as well as complex types. + new_type = ir.Ref(UNION, "value") + retyped = sum( + _retype(ct.content, new_type) for ct in m.complex_types if ct.content is not None + ) + retyped += sum(_retype(g.content, new_type) for g in m.groups) + if retyped == 0: + raise ValueError(f"no {ELEMENT!r} element found to patch; schema changed?") + + _bump_stats(m.stats, len(sound_ids)) + return retyped + + +def _retype(particle: ir.Particle, new_type: ir.Ref) -> int: + """Retype every ELEMENT occurrence reachable in particle. GroupRef leaves are + left alone -- their target group is retyped where it is defined.""" + if isinstance(particle, (ir.Sequence, ir.Choice)): + return sum(_retype(i, new_type) for i in particle.items) + if isinstance(particle, ir.Element) and particle.name == ELEMENT: + particle.type = new_type + return 1 + return 0 + + +def _bump_stats(stats: dict, n_ids: int) -> None: + stats["value_types"] = stats.get("value_types", 0) + 2 + kinds = stats.setdefault("value_kinds", {}) + kinds["enum"] = kinds.get("enum", 0) + 1 + kinds["union"] = kinds.get("union", 0) + 1 + stats["companion_sound_ids"] = n_ids diff --git a/gen/test/c/config.toml b/gen/test/c/config.toml index f04b1b240..5a3804c1d 100644 --- a/gen/test/c/config.toml +++ b/gen/test/c/config.toml @@ -1,6 +1,18 @@ # C generator target configuration. -# The generator reads this to know where and how to emit C code. +# A test target that keeps the generator honest about extensibility: MusicXML +# 3.1, WITH the sounds.xml companion -- the counterpart to the Go target (same +# 3.1 schema, sounds off), so the two differ only by the fold. + +[input] +# MusicXML XSD this target generates from, relative to this config file. +xsd = "../../../docs/musicxml-3.1-8bbe8e5.xsd" [output] # Directory for generated source files, relative to this config file. dir = "mx" + +[sounds] +# Companion sounds file (vendored under docs/, version-matched to the XSD). +# Folds the standard instrument-sound identifiers into the IR as a sound-id +# enum unioned with an open string. Comment out to disable. +xml = "../../../docs/sounds-3.1-8bbe8e5.xml" diff --git a/gen/test/go/config.toml b/gen/test/go/config.toml index 5cb5a9c76..a21ce720d 100644 --- a/gen/test/go/config.toml +++ b/gen/test/go/config.toml @@ -1,6 +1,14 @@ # Go generator target configuration. -# The generator reads this to know where and how to emit Go code. +# A test target that keeps the generator honest about extensibility: MusicXML +# 3.1, and deliberately WITHOUT the sounds.xml companion -- the counterpart to +# the C target (same 3.1 schema, sounds on), so the two differ only by the fold. + +[input] +# MusicXML XSD this target generates from, relative to this config file. +xsd = "../../../docs/musicxml-3.1-8bbe8e5.xsd" [output] # Directory for generated source files, relative to this config file. dir = "mx" + +# No [sounds] section: instrument-sound stays a plain string for this target. From 9e6f6e37eca312eb9287b6067f2965b3ddb13b85 Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Wed, 10 Jun 2026 08:47:49 +0200 Subject: [PATCH 13/45] gen: design the Galley (template-facing layer) Design doc for the layer between the IR and the templates: the per-target projection that templates consume. The IR stays a pure, config-free function of the schema; the Galley is where config.toml meets it, so templates can stay dumb. Covers: the name and rejected alternatives; one-rich-layer-with-two- field-groups vs two passes, decided by a JSON Schema emitter contrast; the materialized, dumpable data shape built on the Resolver; automatic name-convention expansion with a precise tokenizer and a worked table (default-x, brass.alphorn, the empty value, midi/id); the wire string preserved separately; the two-tier rename/override system with scoped addressing, TOML schema, precedence, and IR validation; post-projection collision detection as a CI gate; the transformation catalog (representation, cardinality, primitives, derived types, default/fixed to variant, structure, files, docs, ordering); and a JSON Schema walkthrough proving the layer is neutral, not C++-shaped. --- docs/ai/design/galley.md | 638 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 638 insertions(+) create mode 100644 docs/ai/design/galley.md diff --git a/docs/ai/design/galley.md b/docs/ai/design/galley.md new file mode 100644 index 000000000..3b8f2eb81 --- /dev/null +++ b/docs/ai/design/galley.md @@ -0,0 +1,638 @@ +# The Galley: the template-facing, target-projected layer + +Status: design only. No code in this change. This document specifies the layer that sits between the +IR (`gen/ir`) and the per-language templates in the generator pipeline: + +``` +XSD file -> XSD model -> IR -> [ Galley ] -> templates -> C++ / Go / C / JSON Schema + (gen.xsd) (gen.ir) (dumb renderers) +``` + +The IR is a pure, language-agnostic, config-free function of the schema inputs. The Galley is its +opposite number: the per-target projection of that neutral model into a presentation-ready form a +template can print without thinking. It is where config.toml meets the IR. Everything a target needs +to decide -- what an identifier is called in each casing, what a `decimal` maps to, whether a +derived type uses inheritance or a flattened copy, which file a type lands in -- is decided here, +once, so the templates stay dumb: walk the structure, print text, no naming logic and no per-element +special casing. + +## 1. Name and rationale + +**Chosen name: the Galley** (Python package `gen/galley/`, CLI `python3 -m gen galley --config C`). + +In letterpress printing a *galley* is the shallow tray into which a compositor sets the actual sorts +(the metal type) after composing a manuscript: the words are now in concrete type, in final order +and layout, proofed (the "galley proof") before being locked into the press and inked. The metaphor +maps exactly onto this layer: + +- The IR is the abstract manuscript: neutral content, no typeface, no layout. +- The Galley is the composed tray: the same content rendered into a *specific* target's concrete + identifiers (the casing is the typeface), in that target's order and file layout, ready for the + press. +- The templates are the press: they ink and print what the galley already arranged. They add no + composition decisions of their own. +- `python3 -m gen galley --config C` is the galley proof: a dumpable, diffable preview of the + composed tray before any code is printed -- the same role `ir --resolve` plays for the IR. + +The name is evocative, thematically apt for a project about music engraving (itself a printing +discipline), and -- importantly -- collides with nothing already in this codebase: not `model`, not +`IR`, not `facet` (which already means an XSD constraint here), not `resolve`. It reads cleanly as a +noun, a module, and a command alongside `ir`. + +### Alternatives considered and rejected + +- **ViewModel** -- conceptually the most precise fit (MVVM's "presentation-ready projection of the + model that the view consumes" is exactly this). Rejected: the brief explicitly bars overloading + "model", and the term drags in web-framework baggage. +- **Projection / Project** -- accurate (the IR is "projected" onto a target), but `project` is badly + overloaded in this repo (there is a `/project` skill and a `docs/ai/projects/` tree), and `gen + project` reads as a noun command while colliding with the verb. The CLI ergonomics alone + disqualify it. +- **Binding** -- the brief's own framing ("target-binding stage") endorses it, and in compiler terms + binding-to-a-target is exactly this. Rejected for the audience: to a systems engineer "binding" + reads first as FFI / language bindings (bindgen, "Rust bindings to libfoo"). Since this project + literally emits C++/C/Go libraries, naming the layer "Binding" actively invites the wrong reading. + The term survives as the name of the *per-target field group* inside the Galley (see section 4), + where the FFI reading cannot intrude. +- **Facet** -- already a load-bearing XSD term in this codebase (enumeration/pattern/minInclusive). + Reusing it would be a genuine collision. +- **Dialect / Idiom** -- evocative of per-language flavor, but both connote *names only* and + undersell the layer's representation, layout, and structural work. + +## 2. Responsibilities and non-responsibilities + +The Galley owns the per-target projection and nothing else. + +It **is responsible for**: + +- Name expansion: every fundamental name gets all standard casings, automatically, plus its + immutable wire form preserved verbatim (section 5). +- Renames and per-convention overrides, with validation against the IR (section 6). +- Post-projection collision detection as a CI gate (section 7). +- Representation strategy: mapping each of the 8 shapes to an emit strategy, cardinality to the + target's optional/collection types, and IR primitives to target types via a config-overridable map + (section 8). +- Resolving `default`/`fixed` literals that name an enum variant to that variant's target + identifier, while keeping the wire literal (section 8). +- Exposing both the resolved content tree and a flat member list (section 8). +- File/layout partitioning and the per-file include/import graph (optional; section 8). +- Namespaces/packages/prefixes, reserved-word policy, identifier-validity enforcement, doc-comment + style, deterministic ordering (section 8). + +It **is not responsible for** (stays in the IR): + +- Schema resolution: collapsing restriction chains, normalizing cardinalities, hoisting anonymous + types, dropping dead types, dependency ordering, and the group / attribute-group structure. The + Galley consumes the IR and its `Resolver`; it never re-derives a schema fact. +- The wire names themselves (the Galley preserves them, the IR produces them). +- The sounds.xml fold (an IR-level, config-gated input selection, already done before the Galley + runs). + +It is **not responsible for** (stays in the templates): + +- The literal text: language grammar, punctuation, whitespace, file headers, the actual rendering of + a strategy tag into source lines. Templates contain no naming logic and no per-element + conditionals; they read Galley fields and print. + +## 3. One layer or two: decided by the JSON Schema contrast + +The forcing question (section 9 works it fully): a template that emits a JSON Schema version of the +MusicXML spec wants wire names (not casings), the resolved choice/sequence structure, enum wire +literals, union members, number facets, string patterns, the open-enum, and docs as `description`. +It wants **none** of the file partitioning, includes, reserved-word mangling, comment styling, or +casing machinery a code target needs. + +That split is real, and it has a sharp consequence: almost everything the JSON Schema target wants +is *already in the IR plus the Resolver*. The IR's names already are the wire names; +`Resolver.content` already splices groups into a choice/sequence tree; union members, number bounds, +patterns, enum values, the open-enum, and `doc` strings are all present. So the neutral half of this +layer is not new information -- it is the IR, re-presented. + +This drives the decision: + +**The Galley is one rich, materialized, template-facing object, internally partitioned into two +field groups on each node:** + +- a **neutral core** -- wire-faithful, target-independent facts (wire name, shape, resolved + structure, value lists, facets, docs), mirrored from the IR + Resolver; and +- a **target binding** -- the per-target overlay (the casing bundle, resolved target types, emit + strategy tags, file assignment, reserved-word resolution, doc style). + +Code targets read both groups. A neutral target like JSON Schema reads only the neutral core, leaves +the binding's optional pieces (partitioning, includes) unconfigured, and never touches the casings. + +**Why one object and not two passes.** Two separate artifacts -- a neutral enrichment layer plus a +detached per-target overlay -- would force every template to cross-reference the two by name and +re-walk the structure to stitch them, re-introducing exactly the per-emitter splicing the IR worked +to centralize. It would also split a wire name from its own casings across two objects. One object +with a disciplined neutral/bound field split gives the ergonomics of one (templates walk a single +tree) and the generality proof of two (the JSON Schema target demonstrably needs only the neutral +fields). The cost -- computing five casings per name and a file assignment even for a target that +ignores them -- is trivial (a few thousand names) and partitioning is opt-out, so neutral targets +pay nothing meaningful. + +## 4. Data shape: materialized, dumpable, built on the Resolver + +The IR's `Resolver` is computed-on-demand because it is pure over the IR and is needed *mid-build* +(to compute `deps`). The Galley has neither property: it depends on a config (a specific target), +and nothing consumes it mid-build. It is therefore **materialized** -- a plain dataclass tree built +once per target -- for three reasons: + +1. Collision detection (section 7) and rename validation (section 6) are global passes over all + projected identifiers; they are naturally build-then-check steps, which fit a materialized + result. +2. Inspectability and gating: a materialized tree dumps to JSON via the existing `gen/ir/dump.py` + machinery, giving `gen galley --config C` as a diffable artifact and a `--check` CI gate, + matching the project's analyze-as-gate ethos. +3. Templates want random-access to fully-resolved nodes, not recomputation. + +It is *built on* the Resolver: the Galley consumes `Resolver.attributes`, `all_attributes`, +`content`, and `elements` rather than re-deriving any splicing. + +Design sketch of the types (shapes and accessors, not implementation): + +``` +# --- the neutral/bound name bundle (R1, R3) --- +Name: + wire: str # immutable on-the-wire string (R3); never a code identifier + words: tuple[str, ...] # the tokenized word vector (section 5) + cased: dict[str, str] # convention-name -> identifier, e.g. {"pascal": "Note", ...} + # convenience accessors pascal/camel/snake/kebab/screaming read from `cased`. + # `cased` is filled by iterating a CONVENTION REGISTRY, so adding a convention + # later is registering one function -- zero changes elsewhere (R1). + +# --- value types (mirror the IR's 4 value shapes) --- +GalleyEnum: name: Name; base: str; variants: list[Variant]; doc: str|None +Variant: wire: str; name: Name; ident: str # ident = sanitized name.cased[variant-conv] +GalleyNumber: name: Name; base: str; bounds: NumberBounds; target_type: str; doc: str|None +GalleyString: name: Name; base: str; patterns; length; target_type: str; doc: str|None +GalleyUnion: name: Name; members: list[UnionMember]; doc: str|None # member -> Ref or literal set + +# --- complex types (mirror the IR's 4 complex shapes) --- +Member: name: Name; kind: str # "element" | "attribute" | "value" + type_ref: GalleyRef; cardinality: str # required|optional|vector + repr: MemberRepr # concrete optional/collection wrapper (section 8) + default: str|None; fixed: str|None + default_variant: str|None # variant ident when default/fixed names a variant + doc: str|None +GalleyType: name: Name; shape: str # value|composite|empty|derived (or value-type shape) + strategy: str # emit-strategy tag the template switches on + members: list[Member] # flat, deduped, ordered (code targets) + content: ContentNode|None # resolved sequence/choice tree (schema targets) + base: GalleyRef|None # derived: the inheritance edge + all_members: list[Member]|None # derived: flattened (base chain merged) + presence_only: bool + file: FileId|None # None when partition == single + doc: str|None + +# --- the whole projected target --- +TargetInfo: language: str; namespace: str; prefix: str + conventions: list[str]; doc_style: DocStyle; reserved: set[str]; partition: str +Galley: target: TargetInfo + value_types: list[GalleyEnum|GalleyNumber|GalleyString|GalleyUnion] # deps-ordered + complex_types: list[GalleyType] # deps-ordered + roots: list[GalleyRef] + files: list[FileSpec]|None # per-file include graph; None when not partitioned + type_map: dict[str, str] # primitive -> target type, after config overrides +``` + +Build entry point and CLI (mirrors `ir`): + +``` +build_galley(ir: Ir, config: Config) -> Galley # uses Resolver + a NameFactory + collision check +python3 -m gen galley --config C [--type N] [--check] +``` + +`--check` runs rename validation and collision detection and exits non-zero on any failure, so it +can gate CI exactly as `analyze` does for the DAG/no-collision invariants. Output serializes through +the existing `to_jsonable` in `gen/ir/dump.py`. + +## 5. The name-convention model + +### 5.1 Tokenizer + +A fundamental name is split into an ordered **word vector** of lowercase words, then recased. The +wire form is preserved untouched alongside (R3); tokenization feeds *only* the cased identifiers, +never serialization. + +Rules, applied in order: + +1. **Separators.** Split on and consume any of: hyphen `-`, dot `.`, underscore `_`, colon `:`, and + ASCII whitespace. (Hyphen covers ordinary kebab names; dot covers `brass.alphorn`; whitespace + covers space-separated enum values like `up down` and `bass drum`; colon covers external refs + like `xml:lang`, `xlink:type`.) +2. **Case-transition splits** (for any already-mixed-case input, rare in MusicXML but the tokenizer + must be total): split at a lower-to-upper boundary (`fooBar` -> `foo`, `bar`) and at an acronym + boundary, where an uppercase run is followed by an uppercase+lowercase (`MIDIChannel` -> `midi`, + `channel`): the last capital of the run begins the next word. +3. **Digits do not split.** A letter-digit or digit-letter boundary is *not* a word boundary, so + `default-x` -> `[default, x]` (split on the hyphen only), `midi-128` -> `[midi, 128]`, and the + enum value `1024th` -> `[1024th]` (one word). Digits ride with their adjacent letters. +4. **Lowercase.** Each resulting word is lowercased to its canonical form. Casing is reapplied per + convention. +5. **Degenerate input.** If the rules yield an empty vector -- the empty-string enum value `""` from + `positive-integer-or-empty` and a few `*-value` enums -- substitute the configured fallback word + vector, default `["empty"]`. The wire form stays `""`; only the identifier gets a name. + +### 5.2 Recasing + +Each convention is a function from the word vector (plus the acronym set) to a string. The five +standard conventions: + +- **PascalCase**: capitalize every word, concatenate. +- **camelCase**: the first word fully lowercased, every later word capitalized, concatenate. +- **snake_case**: words joined with `_`. +- **kebab-case**: words joined with `-`. +- **SCREAMING_SNAKE_CASE**: each word uppercased, joined with `_`. + +Where "capitalize a word" means: if the word is in the **acronym set**, uppercase it whole (`midi` +-> `MIDI`, `id` -> `ID`); else if its first character is a letter, uppercase that letter and +lowercase the rest; else (a digit-led word like `1024th`) leave it lowercased. The acronym set is +config-extensible (`[naming] acronyms = [...]`); the default is `{midi, id, xml, css, smufl, uri, +url}`. Acronyms affect only PascalCase and the non-leading words of camelCase; snake/kebab/screaming +are case-uniform and ignore the set. (The camelCase *leading* word is always fully lowercased, so a +leading acronym yields `midiChannel`, not `MIDIChannel`.) + +Because conventions live in a registry keyed by name, adding (say) `Train-Case` or `dot.case` later +is registering one function; `Name.cased` simply grows a key and templates opt in (R1). + +### 5.3 Worked conversion table + +| wire | words | PascalCase | camelCase | snake_case | kebab-case | SCREAMING_SNAKE_CASE | +|------------------|------------------|-------------------|-------------------|---------------------|---------------------|----------------------| +| `note` | [note] | `Note` | `note` | `note` | `note` | `NOTE` | +| `default-x` | [default, x] | `DefaultX` | `defaultX` | `default_x` | `default-x` | `DEFAULT_X` | +| `clef-octave-change` | [clef, octave, change] | `ClefOctaveChange` | `clefOctaveChange` | `clef_octave_change` | `clef-octave-change` | `CLEF_OCTAVE_CHANGE` | +| `midi-channel` | [midi, channel] | `MIDIChannel` | `midiChannel` | `midi_channel` | `midi-channel` | `MIDI_CHANNEL` | +| `optional-unique-id` | [optional, unique, id] | `OptionalUniqueID` | `optionalUniqueID` | `optional_unique_id` | `optional-unique-id` | `OPTIONAL_UNIQUE_ID` | +| `brass.alphorn` | [brass, alphorn] | `BrassAlphorn` | `brassAlphorn` | `brass_alphorn` | `brass-alphorn` | `BRASS_ALPHORN` | +| `up down` | [up, down] | `UpDown` | `upDown` | `up_down` | `up-down` | `UP_DOWN` | +| `1024th` | [1024th] | `1024th` | `1024th` | `1024th` | `1024th` | `1024TH` | +| `` (empty) | [empty] | `Empty` | `empty` | `empty` | `empty` | `EMPTY` | + +Notes on the hard rows: + +- `default-x` shows digit-free splitting on the hyphen and a single-letter trailing word; the wire + form `default-x` is preserved for the attribute on the wire. +- `midi-channel` and `optional-unique-id` show the acronym set producing `MIDI`/`ID` in PascalCase + while snake/kebab/screaming stay mechanical. +- `brass.alphorn` shows the dot as a separator while the wire keeps the dot for serialization (R3). +- `up down` shows a space-separated enum value tokenizing cleanly while the wire keeps the space. +- `1024th` shows a digit-led word: the casings are well-defined, but the result is not a legal + identifier in most code targets. That is fixed in the *binding's* identifier-validity step + (section 8.6), not here: the wire `1024th` and the recased `1024th` are both kept, and a code + target mangles to e.g. `_1024th`. The Galley never silently changes the casing to make it legal; + it records the ideal and lets the sanitizer (and the collision check) act on the result. +- the empty value shows the fallback word vector `["empty"]`; the wire form remains the empty + string, which is what a serializer must emit. + +A note on the dynamics elements `p`, `pp`, `ppp`, `f`, `ff`, ... and `sfz`: these tokenize to single +words and PascalCase to `Pp`, `Ppp`, `Sfz`, which is ugly. That is the textbook motivation for +per-convention overrides (section 6): a target can force `PascalCase = PP` per element without +disturbing the wire form. + +## 6. The override system + +### 6.1 Two tiers (R4) + +- **(a) Fundamental rename.** Rename the canonical root once; every convention re-expands from the + new root automatically. `attributes` -> `properties` makes PascalCase `Properties`, snake + `properties`, and so on, with no per-flavor work. +- **(b) Per-convention override.** When one flavor's auto-expansion is unacceptable, override that + single flavor and leave the rest auto-expanded. Keep fundamental `note`, force `PascalCase = + MusicNote`, and snake_case still resolves to `note`. + +Both tiers are available for any fundamental element name, attribute name, type name, enum type +name, and enum value/variant. + +### 6.2 Addressing scheme (R5) + +Override keys are namespaced by target-kind so they are unambiguous. Enum values are not globally +unique (`start`, `stop`, `up`, `down` recur across dozens of enums), so an enum-value key is scoped +to its enum type: + +| target kind | key path | notes | +|---------------------|-------------------------------------------------|----------------------------------------| +| type (cplx/val/enum)| `rename.type.` | one namespace; no collisions invariant | +| element | `rename.element.` | name -> type is 1:1 (invariant) | +| attribute (global) | `rename.attribute.` | applies on every owner | +| attribute (scoped) | `rename.attribute..`| more specific; wins over global | +| enum value | `rename.enum-value..` | scoped to the enum (R5) | +| group | `rename.group.` | for targets emitting shared fragments | +| attribute group | `rename.attribute-group.` | for targets emitting mixins | + +The empty enum value is addressed by the TOML empty-string key `"" = ...` under its enum's table. + +### 6.3 TOML schema + +Each override entry is a table. A bare `fundamental` key sets the root rename; convention keys +(`pascal`, `camel`, `snake`, `kebab`, `screaming`, or any registered convention) override individual +flavors. A string shorthand `type.note = "tone"` is sugar for a table with only `fundamental`. + +```toml +# ---- existing config, untouched ---- +[input] +xsd = "../../docs/musicxml-4.0-ed15c23.xsd" +[output] +dir = "../../src/private/mx/core" +[sounds] +xml = "../../docs/sounds-4.0-ed15c23.xml" + +# ---- new Galley config ---- +[target] +language = "cpp" +namespace = "mx::core" # Go: package; C: leave empty and use prefix +prefix = "" # global symbol prefix (C uses e.g. "Mx") + +[naming] +extends = "../naming.base.toml" # optional shared base (section 6.4) +acronyms = ["midi", "id", "xml", "css", "smufl"] +type-convention = "pascal" # which casing type identifiers use +field-convention = "snake" # which casing member identifiers use +variant-convention = "pascal" # which casing enum variants use +field-prefix = "" # e.g. "m_" for member fields (section 8.7) +empty-value-word = "empty" # fallback word vector for the "" wire value +pluralize-vectors = false # see section 8.7 + +[reserved] +words = ["class", "namespace", "for", "default", "operator"] # extends language defaults +policy = "suffix-underscore" # reserved word -> append "_" +invalid-prefix = "_" # leading-digit / empty identifier -> prepend "_" + +[types] # IR primitive -> target type (overrides defaults) +decimal = "Decimal" +integer = "int" +positive_integer = "unsigned" +non_negative_integer = "unsigned" +string = "std::string" +token = "std::string" +nmtoken = "std::string" +date = "std::string" + +[layout] +partition = "per-type" # "per-type" | "grouped" | "single" +include-style = "quoted" + +[docs] +style = "triple-slash" # "//" | "///" | "/** */" +wrap = 100 + +# ---- (a) fundamental rename: all flavors re-expand ---- +[rename.type.attributes] +fundamental = "properties" + +# shorthand form, identical effect: +# rename.element.default-x = "origin-x" + +# ---- (b) per-convention override: keep root, override one flavor ---- +[rename.type.note] +pascal = "MusicNote" # snake_case still resolves to "note" + +# ---- scoped enum-value rename (R5): key scoped to the enum type ---- +[rename.enum-value.up-down] +"up" = "upward" # variant 'up' of enum 'up-down' only +"down" = "downward" + +[rename.enum-value.breath-mark-value] +"" = "none" # the empty variant, scoped to this enum + +# ---- scoped vs global attribute rename ---- +[rename.attribute] +default-x = "origin-x" # every owner +[rename.attribute.note] +type = "kind" # only the 'type' attribute on 'note'; wins over global +``` + +### 6.4 Where overrides live and precedence (R6) + +Both per-target and shared: + +- **Per-target** (the common case): renames are almost always language-driven -- avoiding a C++ + keyword, a Go predeclared identifier -- so they live in each target's `config.toml`. +- **Shared base** (optional): a `naming.base.toml` referenced via `[naming] extends = "..."` holds + renames common to all targets (rare). A target's own entries win over the base on any conflict. + +Precedence, highest first: + +1. A per-convention override key (`pascal`, `snake`, ...) for the exact target kind. +2. A `fundamental` rename for that target kind. +3. Auto-expansion from the wire name. + +Orthogonally: per-target config beats the shared base; a scoped attribute key beats a global one. + +### 6.5 Validation (R6) + +Every rename key is validated against the IR at build time and the run **fails loud** on a miss: +`rename.type.` must name a type in the IR; `rename.element.` an element that occurs; +`rename.enum-value..` an enum `E` that actually lists value `V`; and so on. This matches the +analyze-as-gate ethos: a typo in a rename key (or a key left stale after a schema bump) is a build +error, not a silently ignored line. Chosen and recommended. + +## 7. Collision detection (R7) + +After tokenizing, recasing, applying renames, and reserved-word / validity mangling, two distinct +fundamental names can collapse to one identifier. The Galley detects these and reports them as +errors (`--check` exits non-zero), the way `analyze` guards the DAG and no-collision invariants +today. The IR's "no element-name collisions" invariant guarantees nothing here, because collisions +are *induced* by the projection (casing, mangling, prefixing), not present in the wire names. + +Scopes checked, each in the convention(s) the target actually uses: + +- **Type identifiers**: all emitted type identifiers (complex + value + enum) must be unique within + the target's namespace/package, in the type-convention. (`default-x` the element and `default_x` + some other name could both snake to `default_x`, etc.) +- **Enum variant identifiers**: unique within each enum type, in the variant-convention. (Distinct + wire values that mangle to the same identifier -- e.g. several empty/invalid values all sanitized + to the same fallback -- are caught here, per-enum.) +- **Member identifiers**: within a single complex type's flat member list (attributes + child + elements + the value body), the field identifiers must be unique in the field-convention. This is + where an attribute and a child element sharing a recased name, or a pluralized vector member + colliding with another member, would surface. +- **Group / attribute-group identifiers**: for targets that emit them as shared structs/mixins, + unique within the relevant namespace. +- **File stems** (when partitioning): unique within the output directory, checked + **case-insensitively** so `Note.h` and `note.h` are flagged -- a real hazard on macOS and Windows + filesystems. + +The report lists, per collision: the scope, the colliding fundamental (wire) names, and the +identifier they share -- enough to write a targeted rename to resolve it. + +## 8. The transformation catalog + +### 8.1 Shape -> emit strategy + +Each IR shape carries an explicit `strategy` tag the template switches on; the template never +re-derives the shape. The eight shapes and their default strategies: + +| IR shape (kind) | Galley strategy | Template emits (typical code target) | +|---------------------|--------------------------------|---------------------------------------------------------| +| value: enum | `enum-class` | enum class + wire<->variant lookup tables | +| value: number | `numeric-wrapper` | wrapper over a target numeric type, range-validating | +| value: string | `string-wrapper` | wrapper over the target string type, optional pattern | +| value: union | `tagged-variant` | a small tagged variant over the member types | +| complex: value | `value-class` | class with a `value` field (typed by `value_type`) + attrs | +| complex: composite | `composite-class` | class, one member per child element + attrs, order kept | +| complex: empty | `flag` or `attrs-class` | bool if `presence_only`, else an attributes-only class | +| complex: derived | `inherit` or `flatten` | base-class inheritance, or a flattened copy (8.4) | + +### 8.2 Cardinality -> optional/collection representation + +Each member's `cardinality` (required / optional / vector, already normalized by the IR) projects to +a `MemberRepr` describing the concrete wrapper, filled from a config mapping and the type map: + +- **required** -> a by-value member (the DAG invariant means no indirection is ever needed). +- **optional** -> the target's optional: C++ `std::optional`, Go a pointer `*T`, C a `bool has_x` + plus a value field. +- **vector** -> the target's collection: C++ `std::vector`, Go `[]T`, C a `T* xs; size_t n_xs`. + +The Galley computes the descriptor; the template prints the concrete spelling via the type map, so +the choice of wrapper is data, not template logic. + +### 8.3 IR primitive -> target type + +The IR's primitive set (`string`, `token`, `decimal`, `integer`, `positive_integer`, +`non_negative_integer`, `date`, `nmtoken`) maps to target types through `Galley.type_map`, seeded +with per-language defaults and overridable in `[types]` (section 6.3). The map is the single place a +target decides that `decimal` is a `Decimal` wrapper or that `token` is just `std::string`. + +### 8.4 Derived types: inheritance vs flattened + +The Galley exposes *both* the `base` edge (for a target with inheritance) and `all_members` (the +base chain merged via `Resolver.all_attributes`, for a target without it). A per-target switch +(`[target] inheritance = true|false`, default true for C++/Go-style structs that can embed, false +for C) selects which the `derived` strategy resolves to (`inherit` vs `flatten`). Templates read +whichever the strategy names; both are present so the choice is config, not a template fork. + +### 8.5 Enum variant identifiers and default/fixed resolution + +Enum variants are generated from arbitrary wire strings (dots, spaces, leading digits, empty), and +the variant identifier is always distinct from the wire literal, which is retained for serialization +(R3). A `Variant` carries `wire`, the `Name` bundle, and the sanitized `ident`. + +When an attribute's `default` or `fixed` value names an enum variant -- e.g. `strong-accent.type` +defaults to `up` against enum `up-down`, `barline.location` defaults to `right` against +`right-left-middle` -- the Galley resolves that wire literal to the variant's target `ident` and +stores it as `Member.default_variant`, so the emitter writes the enum member (`UpDown::Up`) rather +than a raw string, while the wire literal stays available for the serializer. A `default`/`fixed` on +a non-enum member (e.g. `beam.number` default `1`, or `xlink:type` fixed `simple`) is formatted as a +literal of the member's target type (section 8.8), not resolved to a variant. + +### 8.6 Identifier validity and reserved words + +After recasing and renames, the binding applies a sanitizer per the `[reserved]` policy: + +- **Reserved words** (language built-ins plus `[reserved] words`) are mangled by the configured + policy (default: append `_`, so `class` -> `class_`). +- **Invalid identifiers** -- leading digit (`1024th`), empty result, or any non-identifier character + that survived -- get the configured `invalid-prefix` (default `_`, so `1024th` -> `_1024th`). + +The pre-sanitized casing and the final identifier are both retained; collision detection (section 7) +runs on the *final* identifiers. + +### 8.7 Structure: resolved tree and flat member list + +Both are exposed, because emitters need different views: + +- `GalleyType.content` is the resolved sequence/choice tree (from `Resolver.content`, groups + spliced), for a target that cares about order and choice structure (a schema emitter). +- `GalleyType.members` is the flat, deduped, cardinality-tagged member list (attributes from + `Resolver.attributes`/`all_attributes` + elements from `Resolver.elements`), for a code target + that emits one field per member. + +### 8.8 File / layout partitioning (optional) + +`[layout] partition` selects the strategy: + +- `per-type` -- one type per file. Each `GalleyType` gets a `file`, and `Galley.files` carries, per + file, the include/import list derived from `deps`: each dependency's file, mapped through the same + assignment, deduped, self-excluded. +- `grouped` -- types grouped (by shape or by name prefix) into a fixed set of files; same include + graph, coarser. +- `single` -- one document, `file` is `None`, `Galley.files` is `None`, no include graph. This is + the JSON Schema case and the explicit reason partitioning is optional rather than assumed. + +### 8.9 Namespaces, docs, ordering + +- **Namespaces/packages/prefixes**: `TargetInfo.namespace` (C++ `mx::core`, Go package `mx`), and + `prefix` for languages without namespaces (C symbols `MxNote...`). +- **Doc comments**: the neutral core keeps the raw `doc` text (so JSON Schema can use it verbatim as + `description`); `TargetInfo.doc_style` carries the comment syntax, wrap column, and escape rules, + and the template applies them. The Galley does not pre-bake comment syntax into the doc string. +- **Ordering**: the Galley preserves the IR's deps-first order for types (so a single-file emit is a + valid total order) and document order for members and variants. All config-driven maps are + iterated deterministically. Determinism is a hard rule: the same IR + config always yields + byte-identical output. + +### 8.10 Optional niceties: accept / reject + +- **English pluralization of vector members** -- *rejected as a default*, available as opt-in + (`[naming] pluralize-vectors`, default `false`). Irregular plurals need a dictionary, the wire + name is singular, and a wrong plural is worse than a singular member name. Default leaves vector + members singular; a target that wants plurals enables the flag (naive `+s`) or renames the + offending member explicitly. +- **Prefix/suffix policy** (`m_` fields, `Enum`/`Type` suffixes) -- *accepted as config*, off by + default (`[naming] field-prefix`, and analogous type suffix keys). Applied after recasing and + before collision detection, so a prefix that induces a collision is still caught. +- **Numeric formatting of decimal defaults** -- *accepted*. A `default`/`fixed` literal on a numeric + member is normalized to the target's spelling for that primitive (e.g. `8` stays `8` for an + integer field, becomes `8.0` for a `decimal` field if the target wants explicit decimals), with + the wire literal retained. This reuses the corpus normalization spirit (trailing-zero handling) at + the identifier layer. + +## 9. Forcing function: a JSON Schema emitter + +A template emitting a JSON Schema (Draft 2020-12) version of the MusicXML spec reads only the +neutral core of the Galley, and configures `[layout] partition = "single"`. Walkthrough of what it +touches and what it ignores: + +What it reads (all neutral-core fields): + +- **Names: the wire form, never a casing.** `$defs` keys are the type wire names; object property + names are element/attribute wire names; enum members are wire values. JSON property names can be + any string, so `default-x`, `brass.alphorn`, and `up down` are used verbatim -- exactly the data + `Name.wire` preserves, and exactly why the wire form is a first-class field (R3). +- **Resolved structure.** `GalleyType.content` (the spliced sequence/choice tree) maps directly: a + `sequence` -> an `object` with ordered `properties` and a `required` list for required members; a + `choice` -> `oneOf`; a `vector` member -> `{ "type": "array", "items": ... }`; optional vs + required -> presence in `required`. No group references remain to chase (the Resolver already + spliced them). +- **Enum wire-literal lists.** `GalleyEnum.variants[*].wire` -> `{ "enum": [...] }`. The variant + identifiers (`ident`, casings) are not read at all -- proof the casing machinery is inert here. +- **Union members -> `anyOf`.** `GalleyUnion.members` -> `anyOf` of member schemas. +- **The open-enum** (`instrument-sound` = `sound-id` enum unioned with open string) -> `anyOf: [ { + "enum": [ ...sound ids... ] }, { "type": "string" } ]`. The Galley represents it as an ordinary + union with an enum member and a string member, so the schema falls out with no special case. +- **Number facets.** `GalleyNumber.bounds` -> `minimum` / `maximum` / `exclusiveMinimum` / + `exclusiveMaximum`. +- **String facets.** `GalleyString.patterns` -> `pattern`; length -> `minLength` / `maxLength`. +- **Docs -> `description`.** The raw `doc` text, used verbatim. + +What it never touches (the entire target binding): + +- casings (`Name.cased`), reserved-word and validity mangling (`ident` sanitization), the primitive + type map, namespaces/prefixes, doc comment style, file partitioning, and the include/import graph. + It sets `partition = single` and reads no `file` or `files`. + +This is the proof the layer is general, not C++-shaped: the JSON Schema target consumes a strict +subset of the same object every code target consumes, needing only the neutral core, while the code +targets layer their binding on top. It is the concrete justification for the one-object, +two-field-group decision in section 3: the neutral core is demonstrably self-sufficient (so the +split is real), but it is delivered as fields of one tree the template walks once (so templates stay +dumb). + +## 10. Open questions and future work + +- **Pluralization dictionary.** If natural plurals for vector members ever become desirable, a small + irregular-plural table would be needed; deferred behind the off-by-default flag. +- **Per-context attribute meaning.** The same attribute wire name (`type`, `number`) carries + different meaning across owners. Scoped attribute overrides handle it manually today; + auto-detecting divergent uses and warning could be future work. +- **`xs:list` support.** The IR currently maps the (unused) `xs:list` case defensively to a token + string; if a future schema uses real lists, the Galley would need a list `MemberRepr`. +- **Header/implementation split.** `FileId` is one file per type today; C++ may want a type to span + a header and a source file, making file assignment one-type-to-many-files. +- **Acronym splitting of arbitrary camel input.** The case-transition tokenizer rule is specified + but effectively unexercised by MusicXML's kebab names; it would need test coverage before relying + on it for a future mixed-case schema. +- **Configurable per-enum invalid-identifier prefix.** A single global `invalid-prefix` is assumed; + some targets might want it per enum (e.g. note-type values prefixed `N`). From 76b5cf552f5628ff1ae6293d7913a104303493d5 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 14:05:34 +0000 Subject: [PATCH 14/45] gen: rename the template-facing layer to Plates Each metadata object handed to a template (one per emitted type) is a Plate; the collection projected for a target is the Plates. Rename the design doc, rework its name-and-rationale section around the music engraving plate metaphor, rename the sketched dataclasses (EnumPlate, NumberPlate, StringPlate, UnionPlate, ComplexPlate, PlateRef, Plates) and the planned CLI to 'gen plates', and add the projection stage to the gen/README.md pipeline. The former layer name is removed entirely. https://claude.ai/code/session_01XUoGfETVUYbSedoEPx3mAV --- docs/ai/design/{galley.md => plates.md} | 164 ++++++++++++------------ gen/README.md | 12 +- 2 files changed, 94 insertions(+), 82 deletions(-) rename docs/ai/design/{galley.md => plates.md} (83%) diff --git a/docs/ai/design/galley.md b/docs/ai/design/plates.md similarity index 83% rename from docs/ai/design/galley.md rename to docs/ai/design/plates.md index 3b8f2eb81..42d7e3a66 100644 --- a/docs/ai/design/galley.md +++ b/docs/ai/design/plates.md @@ -1,43 +1,48 @@ -# The Galley: the template-facing, target-projected layer +# The Plates: the template-facing, target-projected layer Status: design only. No code in this change. This document specifies the layer that sits between the IR (`gen/ir`) and the per-language templates in the generator pipeline: ``` -XSD file -> XSD model -> IR -> [ Galley ] -> templates -> C++ / Go / C / JSON Schema +XSD file -> XSD model -> IR -> [ Plates ] -> templates -> C++ / Go / C / JSON Schema (gen.xsd) (gen.ir) (dumb renderers) ``` -The IR is a pure, language-agnostic, config-free function of the schema inputs. The Galley is its +The IR is a pure, language-agnostic, config-free function of the schema inputs. The Plates are its opposite number: the per-target projection of that neutral model into a presentation-ready form a -template can print without thinking. It is where config.toml meets the IR. Everything a target needs -to decide -- what an identifier is called in each casing, what a `decimal` maps to, whether a +template can print without thinking. This is where config.toml meets the IR. Everything a target +needs to decide -- what an identifier is called in each casing, what a `decimal` maps to, whether a derived type uses inheritance or a flattened copy, which file a type lands in -- is decided here, once, so the templates stay dumb: walk the structure, print text, no naming logic and no per-element special casing. ## 1. Name and rationale -**Chosen name: the Galley** (Python package `gen/galley/`, CLI `python3 -m gen galley --config C`). +**Chosen name: the Plates** (Python package `gen/plates/`, CLI `python3 -m gen plates --config C`). +Each metadata object handed to a template -- one per emitted type -- is a **plate**; the full +collection projected for a target is the **Plates**. -In letterpress printing a *galley* is the shallow tray into which a compositor sets the actual sorts -(the metal type) after composing a manuscript: the words are now in concrete type, in final order -and layout, proofed (the "galley proof") before being locked into the press and inked. The metaphor -maps exactly onto this layer: +In music engraving -- the discipline MusicXML exists to serve -- a publisher prepared an edition by +engraving the manuscript onto metal plates: every spelling, layout, and spacing decision committed +into the metal, one plate per page, ready for the press to ink and print. Published scores carry +plate numbers to this day. The metaphor maps exactly onto this layer: - The IR is the abstract manuscript: neutral content, no typeface, no layout. -- The Galley is the composed tray: the same content rendered into a *specific* target's concrete - identifiers (the casing is the typeface), in that target's order and file layout, ready for the - press. -- The templates are the press: they ink and print what the galley already arranged. They add no +- A plate is one type engraved for a *specific* target: the same content rendered into that target's + concrete identifiers (the casing is the engraving style), in that target's order and file layout, + ready for the press. +- The Plates are the complete set of plates for the edition; one target is one edition. +- The templates are the press: they ink and print what the plates already fixed. They add no composition decisions of their own. -- `python3 -m gen galley --config C` is the galley proof: a dumpable, diffable preview of the - composed tray before any code is printed -- the same role `ir --resolve` plays for the IR. +- `python3 -m gen plates --config C` is the proof pulled from the plates before the print run: a + dumpable, diffable preview of the engraved edition before any code is printed -- the same role + `ir --resolve` plays for the IR. -The name is evocative, thematically apt for a project about music engraving (itself a printing -discipline), and -- importantly -- collides with nothing already in this codebase: not `model`, not -`IR`, not `facet` (which already means an XSD constraint here), not `resolve`. It reads cleanly as a -noun, a module, and a command alongside `ir`. +The name is evocative, thematically exact for a project about music engraving, and collides with +nothing already in this codebase: not `model`, not `IR`, not `facet` (which already means an XSD +constraint here), not `resolve`. It reads cleanly as a noun, a module, and a command alongside `ir`, +and the singular/plural pair names the per-type object and the collection with one word -- a plate, +the Plates -- so there is no second term to learn. ### Alternatives considered and rejected @@ -52,7 +57,7 @@ noun, a module, and a command alongside `ir`. binding-to-a-target is exactly this. Rejected for the audience: to a systems engineer "binding" reads first as FFI / language bindings (bindgen, "Rust bindings to libfoo"). Since this project literally emits C++/C/Go libraries, naming the layer "Binding" actively invites the wrong reading. - The term survives as the name of the *per-target field group* inside the Galley (see section 4), + The term survives as the name of the *per-target field group* inside each plate (see section 4), where the FFI reading cannot intrude. - **Facet** -- already a load-bearing XSD term in this codebase (enumeration/pattern/minInclusive). Reusing it would be a genuine collision. @@ -61,7 +66,7 @@ noun, a module, and a command alongside `ir`. ## 2. Responsibilities and non-responsibilities -The Galley owns the per-target projection and nothing else. +The Plates layer owns the per-target projection and nothing else. It **is responsible for**: @@ -83,16 +88,16 @@ It **is not responsible for** (stays in the IR): - Schema resolution: collapsing restriction chains, normalizing cardinalities, hoisting anonymous types, dropping dead types, dependency ordering, and the group / attribute-group structure. The - Galley consumes the IR and its `Resolver`; it never re-derives a schema fact. -- The wire names themselves (the Galley preserves them, the IR produces them). -- The sounds.xml fold (an IR-level, config-gated input selection, already done before the Galley - runs). + Plates consume the IR and its `Resolver`; they never re-derive a schema fact. +- The wire names themselves (the plates preserve them, the IR produces them). +- The sounds.xml fold (an IR-level, config-gated input selection, already done before the Plates + are built). It is **not responsible for** (stays in the templates): - The literal text: language grammar, punctuation, whitespace, file headers, the actual rendering of a strategy tag into source lines. Templates contain no naming logic and no per-element - conditionals; they read Galley fields and print. + conditionals; they read plate fields and print. ## 3. One layer or two: decided by the JSON Schema contrast @@ -110,8 +115,8 @@ layer is not new information -- it is the IR, re-presented. This drives the decision: -**The Galley is one rich, materialized, template-facing object, internally partitioned into two -field groups on each node:** +**The Plates are one rich, materialized, template-facing object, and each plate in it is internally +partitioned into two field groups:** - a **neutral core** -- wire-faithful, target-independent facts (wire name, shape, resolved structure, value lists, facets, docs), mirrored from the IR + Resolver; and @@ -134,19 +139,19 @@ pay nothing meaningful. ## 4. Data shape: materialized, dumpable, built on the Resolver The IR's `Resolver` is computed-on-demand because it is pure over the IR and is needed *mid-build* -(to compute `deps`). The Galley has neither property: it depends on a config (a specific target), -and nothing consumes it mid-build. It is therefore **materialized** -- a plain dataclass tree built -once per target -- for three reasons: +(to compute `deps`). The Plates have neither property: they depend on a config (a specific target), +and nothing consumes them mid-build. They are therefore **materialized** -- a plain dataclass tree +built once per target -- for three reasons: 1. Collision detection (section 7) and rename validation (section 6) are global passes over all projected identifiers; they are naturally build-then-check steps, which fit a materialized result. 2. Inspectability and gating: a materialized tree dumps to JSON via the existing `gen/ir/dump.py` - machinery, giving `gen galley --config C` as a diffable artifact and a `--check` CI gate, + machinery, giving `gen plates --config C` as a diffable artifact and a `--check` CI gate, matching the project's analyze-as-gate ethos. -3. Templates want random-access to fully-resolved nodes, not recomputation. +3. Templates want random-access to fully-resolved plates, not recomputation. -It is *built on* the Resolver: the Galley consumes `Resolver.attributes`, `all_attributes`, +The Plates are *built on* the Resolver: they consume `Resolver.attributes`, `all_attributes`, `content`, and `elements` rather than re-deriving any splicing. Design sketch of the types (shapes and accessors, not implementation): @@ -161,25 +166,25 @@ Name: # `cased` is filled by iterating a CONVENTION REGISTRY, so adding a convention # later is registering one function -- zero changes elsewhere (R1). -# --- value types (mirror the IR's 4 value shapes) --- -GalleyEnum: name: Name; base: str; variants: list[Variant]; doc: str|None +# --- value plates (mirror the IR's 4 value shapes) --- +EnumPlate: name: Name; base: str; variants: list[Variant]; doc: str|None Variant: wire: str; name: Name; ident: str # ident = sanitized name.cased[variant-conv] -GalleyNumber: name: Name; base: str; bounds: NumberBounds; target_type: str; doc: str|None -GalleyString: name: Name; base: str; patterns; length; target_type: str; doc: str|None -GalleyUnion: name: Name; members: list[UnionMember]; doc: str|None # member -> Ref or literal set +NumberPlate: name: Name; base: str; bounds: NumberBounds; target_type: str; doc: str|None +StringPlate: name: Name; base: str; patterns; length; target_type: str; doc: str|None +UnionPlate: name: Name; members: list[UnionMember]; doc: str|None # member -> Ref or literal set -# --- complex types (mirror the IR's 4 complex shapes) --- +# --- complex plates (mirror the IR's 4 complex shapes) --- Member: name: Name; kind: str # "element" | "attribute" | "value" - type_ref: GalleyRef; cardinality: str # required|optional|vector + type_ref: PlateRef; cardinality: str # required|optional|vector repr: MemberRepr # concrete optional/collection wrapper (section 8) default: str|None; fixed: str|None default_variant: str|None # variant ident when default/fixed names a variant doc: str|None -GalleyType: name: Name; shape: str # value|composite|empty|derived (or value-type shape) +ComplexPlate: name: Name; shape: str # value|composite|empty|derived (or value-type shape) strategy: str # emit-strategy tag the template switches on members: list[Member] # flat, deduped, ordered (code targets) content: ContentNode|None # resolved sequence/choice tree (schema targets) - base: GalleyRef|None # derived: the inheritance edge + base: PlateRef|None # derived: the inheritance edge all_members: list[Member]|None # derived: flattened (base chain merged) presence_only: bool file: FileId|None # None when partition == single @@ -188,10 +193,10 @@ GalleyType: name: Name; shape: str # value|composite|empty|derived ( # --- the whole projected target --- TargetInfo: language: str; namespace: str; prefix: str conventions: list[str]; doc_style: DocStyle; reserved: set[str]; partition: str -Galley: target: TargetInfo - value_types: list[GalleyEnum|GalleyNumber|GalleyString|GalleyUnion] # deps-ordered - complex_types: list[GalleyType] # deps-ordered - roots: list[GalleyRef] +Plates: target: TargetInfo + value_types: list[EnumPlate|NumberPlate|StringPlate|UnionPlate] # deps-ordered + complex_types: list[ComplexPlate] # deps-ordered + roots: list[PlateRef] files: list[FileSpec]|None # per-file include graph; None when not partitioned type_map: dict[str, str] # primitive -> target type, after config overrides ``` @@ -199,8 +204,8 @@ Galley: target: TargetInfo Build entry point and CLI (mirrors `ir`): ``` -build_galley(ir: Ir, config: Config) -> Galley # uses Resolver + a NameFactory + collision check -python3 -m gen galley --config C [--type N] [--check] +build_plates(ir: Ir, config: Config) -> Plates # uses Resolver + a NameFactory + collision check +python3 -m gen plates --config C [--type N] [--check] ``` `--check` runs rename validation and collision detection and exits non-zero on any failure, so it @@ -281,8 +286,8 @@ Notes on the hard rows: - `1024th` shows a digit-led word: the casings are well-defined, but the result is not a legal identifier in most code targets. That is fixed in the *binding's* identifier-validity step (section 8.6), not here: the wire `1024th` and the recased `1024th` are both kept, and a code - target mangles to e.g. `_1024th`. The Galley never silently changes the casing to make it legal; - it records the ideal and lets the sanitizer (and the collision check) act on the result. + target mangles to e.g. `_1024th`. The casing is never silently changed to make it legal; the + plate records the ideal and lets the sanitizer (and the collision check) act on the result. - the empty value shows the fallback word vector `["empty"]`; the wire form remains the empty string, which is what a serializer must emit. @@ -338,7 +343,7 @@ dir = "../../src/private/mx/core" [sounds] xml = "../../docs/sounds-4.0-ed15c23.xml" -# ---- new Galley config ---- +# ---- new Plates config ---- [target] language = "cpp" namespace = "mx::core" # Go: package; C: leave empty and use prefix @@ -431,7 +436,7 @@ error, not a silently ignored line. Chosen and recommended. ## 7. Collision detection (R7) After tokenizing, recasing, applying renames, and reserved-word / validity mangling, two distinct -fundamental names can collapse to one identifier. The Galley detects these and reports them as +fundamental names can collapse to one identifier. The Plates build detects these and reports them as errors (`--check` exits non-zero), the way `analyze` guards the DAG and no-collision invariants today. The IR's "no element-name collisions" invariant guarantees nothing here, because collisions are *induced* by the projection (casing, mangling, prefixing), not present in the wire names. @@ -444,7 +449,7 @@ Scopes checked, each in the convention(s) the target actually uses: - **Enum variant identifiers**: unique within each enum type, in the variant-convention. (Distinct wire values that mangle to the same identifier -- e.g. several empty/invalid values all sanitized to the same fallback -- are caught here, per-enum.) -- **Member identifiers**: within a single complex type's flat member list (attributes + child +- **Member identifiers**: within a single complex plate's flat member list (attributes + child elements + the value body), the field identifiers must be unique in the field-convention. This is where an attribute and a child element sharing a recased name, or a pluralized vector member colliding with another member, would surface. @@ -464,7 +469,7 @@ identifier they share -- enough to write a targeted rename to resolve it. Each IR shape carries an explicit `strategy` tag the template switches on; the template never re-derives the shape. The eight shapes and their default strategies: -| IR shape (kind) | Galley strategy | Template emits (typical code target) | +| IR shape (kind) | Plate strategy | Template emits (typical code target) | |---------------------|--------------------------------|---------------------------------------------------------| | value: enum | `enum-class` | enum class + wire<->variant lookup tables | | value: number | `numeric-wrapper` | wrapper over a target numeric type, range-validating | @@ -485,20 +490,20 @@ a `MemberRepr` describing the concrete wrapper, filled from a config mapping and plus a value field. - **vector** -> the target's collection: C++ `std::vector`, Go `[]T`, C a `T* xs; size_t n_xs`. -The Galley computes the descriptor; the template prints the concrete spelling via the type map, so +The plate carries the descriptor; the template prints the concrete spelling via the type map, so the choice of wrapper is data, not template logic. ### 8.3 IR primitive -> target type The IR's primitive set (`string`, `token`, `decimal`, `integer`, `positive_integer`, -`non_negative_integer`, `date`, `nmtoken`) maps to target types through `Galley.type_map`, seeded +`non_negative_integer`, `date`, `nmtoken`) maps to target types through `Plates.type_map`, seeded with per-language defaults and overridable in `[types]` (section 6.3). The map is the single place a target decides that `decimal` is a `Decimal` wrapper or that `token` is just `std::string`. ### 8.4 Derived types: inheritance vs flattened -The Galley exposes *both* the `base` edge (for a target with inheritance) and `all_members` (the -base chain merged via `Resolver.all_attributes`, for a target without it). A per-target switch +A derived plate exposes *both* the `base` edge (for a target with inheritance) and `all_members` +(the base chain merged via `Resolver.all_attributes`, for a target without it). A per-target switch (`[target] inheritance = true|false`, default true for C++/Go-style structs that can embed, false for C) selects which the `derived` strategy resolves to (`inherit` vs `flatten`). Templates read whichever the strategy names; both are present so the choice is config, not a template fork. @@ -511,11 +516,12 @@ the variant identifier is always distinct from the wire literal, which is retain When an attribute's `default` or `fixed` value names an enum variant -- e.g. `strong-accent.type` defaults to `up` against enum `up-down`, `barline.location` defaults to `right` against -`right-left-middle` -- the Galley resolves that wire literal to the variant's target `ident` and -stores it as `Member.default_variant`, so the emitter writes the enum member (`UpDown::Up`) rather -than a raw string, while the wire literal stays available for the serializer. A `default`/`fixed` on -a non-enum member (e.g. `beam.number` default `1`, or `xlink:type` fixed `simple`) is formatted as a -literal of the member's target type (section 8.8), not resolved to a variant. +`right-left-middle` -- the Plates build resolves that wire literal to the variant's target `ident` +and stores it as `Member.default_variant`, so the emitter writes the enum member (`UpDown::Up`) +rather than a raw string, while the wire literal stays available for the serializer. A +`default`/`fixed` on a non-enum member (e.g. `beam.number` default `1`, or `xlink:type` fixed +`simple`) is formatted as a literal of the member's target type (section 8.8), not resolved to a +variant. ### 8.6 Identifier validity and reserved words @@ -533,9 +539,9 @@ runs on the *final* identifiers. Both are exposed, because emitters need different views: -- `GalleyType.content` is the resolved sequence/choice tree (from `Resolver.content`, groups +- `ComplexPlate.content` is the resolved sequence/choice tree (from `Resolver.content`, groups spliced), for a target that cares about order and choice structure (a schema emitter). -- `GalleyType.members` is the flat, deduped, cardinality-tagged member list (attributes from +- `ComplexPlate.members` is the flat, deduped, cardinality-tagged member list (attributes from `Resolver.attributes`/`all_attributes` + elements from `Resolver.elements`), for a code target that emits one field per member. @@ -543,12 +549,12 @@ Both are exposed, because emitters need different views: `[layout] partition` selects the strategy: -- `per-type` -- one type per file. Each `GalleyType` gets a `file`, and `Galley.files` carries, per +- `per-type` -- one type per file. Each plate gets a `file`, and `Plates.files` carries, per file, the include/import list derived from `deps`: each dependency's file, mapped through the same assignment, deduped, self-excluded. - `grouped` -- types grouped (by shape or by name prefix) into a fixed set of files; same include graph, coarser. -- `single` -- one document, `file` is `None`, `Galley.files` is `None`, no include graph. This is +- `single` -- one document, `file` is `None`, `Plates.files` is `None`, no include graph. This is the JSON Schema case and the explicit reason partitioning is optional rather than assumed. ### 8.9 Namespaces, docs, ordering @@ -557,8 +563,8 @@ Both are exposed, because emitters need different views: `prefix` for languages without namespaces (C symbols `MxNote...`). - **Doc comments**: the neutral core keeps the raw `doc` text (so JSON Schema can use it verbatim as `description`); `TargetInfo.doc_style` carries the comment syntax, wrap column, and escape rules, - and the template applies them. The Galley does not pre-bake comment syntax into the doc string. -- **Ordering**: the Galley preserves the IR's deps-first order for types (so a single-file emit is a + and the template applies them. The plate does not pre-bake comment syntax into the doc string. +- **Ordering**: the Plates preserve the IR's deps-first order for types (so a single-file emit is a valid total order) and document order for members and variants. All config-driven maps are iterated deterministically. Determinism is a hard rule: the same IR + config always yields byte-identical output. @@ -582,7 +588,7 @@ Both are exposed, because emitters need different views: ## 9. Forcing function: a JSON Schema emitter A template emitting a JSON Schema (Draft 2020-12) version of the MusicXML spec reads only the -neutral core of the Galley, and configures `[layout] partition = "single"`. Walkthrough of what it +neutral core of the Plates, and configures `[layout] partition = "single"`. Walkthrough of what it touches and what it ignores: What it reads (all neutral-core fields): @@ -591,20 +597,20 @@ What it reads (all neutral-core fields): names are element/attribute wire names; enum members are wire values. JSON property names can be any string, so `default-x`, `brass.alphorn`, and `up down` are used verbatim -- exactly the data `Name.wire` preserves, and exactly why the wire form is a first-class field (R3). -- **Resolved structure.** `GalleyType.content` (the spliced sequence/choice tree) maps directly: a +- **Resolved structure.** `ComplexPlate.content` (the spliced sequence/choice tree) maps directly: a `sequence` -> an `object` with ordered `properties` and a `required` list for required members; a `choice` -> `oneOf`; a `vector` member -> `{ "type": "array", "items": ... }`; optional vs required -> presence in `required`. No group references remain to chase (the Resolver already spliced them). -- **Enum wire-literal lists.** `GalleyEnum.variants[*].wire` -> `{ "enum": [...] }`. The variant +- **Enum wire-literal lists.** `EnumPlate.variants[*].wire` -> `{ "enum": [...] }`. The variant identifiers (`ident`, casings) are not read at all -- proof the casing machinery is inert here. -- **Union members -> `anyOf`.** `GalleyUnion.members` -> `anyOf` of member schemas. +- **Union members -> `anyOf`.** `UnionPlate.members` -> `anyOf` of member schemas. - **The open-enum** (`instrument-sound` = `sound-id` enum unioned with open string) -> `anyOf: [ { - "enum": [ ...sound ids... ] }, { "type": "string" } ]`. The Galley represents it as an ordinary + "enum": [ ...sound ids... ] }, { "type": "string" } ]`. The plate represents it as an ordinary union with an enum member and a string member, so the schema falls out with no special case. -- **Number facets.** `GalleyNumber.bounds` -> `minimum` / `maximum` / `exclusiveMinimum` / +- **Number facets.** `NumberPlate.bounds` -> `minimum` / `maximum` / `exclusiveMinimum` / `exclusiveMaximum`. -- **String facets.** `GalleyString.patterns` -> `pattern`; length -> `minLength` / `maxLength`. +- **String facets.** `StringPlate.patterns` -> `pattern`; length -> `minLength` / `maxLength`. - **Docs -> `description`.** The raw `doc` text, used verbatim. What it never touches (the entire target binding): @@ -628,7 +634,7 @@ dumb). different meaning across owners. Scoped attribute overrides handle it manually today; auto-detecting divergent uses and warning could be future work. - **`xs:list` support.** The IR currently maps the (unused) `xs:list` case defensively to a token - string; if a future schema uses real lists, the Galley would need a list `MemberRepr`. + string; if a future schema uses real lists, the Plates would need a list `MemberRepr`. - **Header/implementation split.** `FileId` is one file per type today; C++ may want a type to span a header and a source file, making file assignment one-type-to-many-files. - **Acronym splitting of arbitrary camel input.** The case-transition tokenizer rule is specified diff --git a/gen/README.md b/gen/README.md index 8c81ea682..5836af059 100644 --- a/gen/README.md +++ b/gen/README.md @@ -9,8 +9,8 @@ build system, Docker toolchain, and corert test, see [`../AGENTS.md`](../AGENTS. ### Pipeline ``` -XSD file --parse--> XSD model --lower--> IR --emit--> C++ / Go / C - (gen.xsd) (gen.ir) (templates, not yet built) +XSD file --parse--> XSD model --lower--> IR --project--> Plates --emit--> C++ / Go / C + (gen.xsd) (gen.ir) (gen.plates, designed) (templates, not yet built) ``` 1. Parse (`gen/xsd/`) reads the XSD into a model mirroring it 1:1, still speaking XSD: restriction @@ -18,7 +18,13 @@ XSD file --parse--> XSD model --lower--> IR --emit--> C++ / Go / C 2. Lower (`gen/ir/`) resolves that into the intermediate representation (IR): a flat, fully-named, dependency-ordered model in code-generation terms. A pure function of the XSD, no configurable knobs (see Design principles). -3. Emit turns the IR into code via per-language templates. Not yet implemented. +3. Project (`gen/plates/`) binds the IR to one target: every per-target decision -- identifier + casings, renames, primitive type mappings, emit strategies, file layout -- is made here, once, + producing one **plate** per emitted type. The collection projected for a target is the + **Plates**. Designed in [`../docs/ai/design/plates.md`](../docs/ai/design/plates.md); not yet + implemented. +4. Emit renders each plate through per-language templates ("dumb renderers": walk the plate, print + text, no naming logic). Not yet implemented. ### Layout From 2f9ca18b60bea9f393c0a44292e04f0b6914d1e0 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 14:24:04 +0000 Subject: [PATCH 15/45] AGENTS.md: document the Plates vocabulary and design doc Add the projection stage to the generator pipeline description, define plate (per-type, template-facing metadata object) and the Plates (the per-target collection), point to docs/ai/design/plates.md from the architecture section, the status note, and the repository layout tree. https://claude.ai/code/session_01XUoGfETVUYbSedoEPx3mAV --- AGENTS.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 123f15278..203ad3f23 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,6 +16,7 @@ mx/ Dockerfile <- mx-sdk toolchain image (Ubuntu 24.04, GCC 14, Go, libxml2, Python 3) CMakeLists.txt <- C++ project: ezxml library + corert test harness data/ <- MusicXML test corpus (~1,347 files, see data/README.md) + docs/ai/design/ <- design docs (plates.md: the Plates, the template-facing layer) src/private/ <- C++ source mx/ezxml/ <- vendored pugixml-backed XML layer mx/core/ <- generated C++ typed model @@ -123,14 +124,20 @@ their values instead of their string representations. Float comparison uses epsi ## Generator architecture -The generator (`gen/`) is a Python program structured as a three-stage pipeline: parse the MusicXML -XSD into a model (`gen/xsd/`), lower that into a resolved intermediate representation (`gen/ir/`), -then emit target code from the IR. The IR data model preserves the schema's named structure (model -groups, attribute groups, inheritance edges); `gen/ir/resolve.py` collapses it on demand into the -flattened view an emitter consumes (attribute groups expanded, group refs spliced into content), so -that splicing-and-deduping reasoning lives once rather than once per language. See `gen/README.md` +The generator (`gen/`) is a Python program structured as a pipeline: parse the MusicXML XSD into a +model (`gen/xsd/`), lower that into a resolved intermediate representation (`gen/ir/`), project the +IR onto a target as the Plates (`gen/plates/`, designed but not yet implemented), then emit code +from the plates via per-language templates. The IR data model preserves the schema's named structure +(model groups, attribute groups, inheritance edges); `gen/ir/resolve.py` collapses it on demand into +the flattened view an emitter consumes (attribute groups expanded, group refs spliced into content), +so that splicing-and-deduping reasoning lives once rather than once per language. See `gen/README.md` for the architecture, IR glossary, the resolution layer, and a structural analysis of the schema. +Vocabulary: a **plate** is the per-type metadata object handed to a template -- one per emitted +type, carrying the target's identifier casings, type mappings, emit strategy, and file assignment. +The **Plates** is the full collection projected for one target; it is where config.toml meets the +IR, so templates stay dumb renderers. Specified in `docs/ai/design/plates.md`. + Commands: - `python3 -m gen analyze [xsd]` - print a structural analysis of the XSD. - `python3 -m gen ir [--type NAME] [--resolve] [--config C] [xsd]` - lower the XSD to the IR and @@ -153,7 +160,8 @@ unioned with an open string (element `instrument-sound` retyped from `string` to the only place the IR depends on an input beyond the XSD; it is opt-in per target, so the base IR stays a pure function of the schema. -**Status.** The parse, IR, and analysis stages exist. The emit stage and its templates are not yet +**Status.** The parse, IR, and analysis stages exist. The Plates stage is designed +(`docs/ai/design/plates.md`) but not yet implemented; the emit stage and its templates are not yet implemented, so `python3 -m gen ` still exits with an error. ## Language targets From 49a7c84ead3c8afc6f94479be5925b492a4cd68d Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 15:43:56 +0000 Subject: [PATCH 16/45] gen: implement the Plates projection layer Implement docs/ai/design/plates.md as gen/plates/: the per-target projection that sits between the IR and the templates. One plate per emitted type, internally split into a neutral core (wire names, resolved structure, facets, docs) and a target binding (casings, sanitized identifiers, type mappings, strategy tags, file assignment). - gen/plates/names.py: tokenizer (separators, case transitions, digits ride), the convention registry (pascal/camel/snake/kebab/screaming), acronym set, and the reserved-word/validity sanitizer. - gen/plates/model.py: the plate dataclasses; every plate, member, and variant carries both its Name bundle and its final sanitized identifier. - gen/plates/build.py: the projection. Rename validation (fail loud on stale keys), member flattening over the Resolver (effective cardinality computed from the resolved particle tree), default/fixed literals resolved to enum variant identifiers, per-type file assignment with an include graph. - gen/plates/check.py: post-projection collision detection (type, variant, member, and case-insensitive file-stem scopes). - gen/plates/languages.py: per-language seeds (type maps, reserved words, doc styles) that config overrides. - gen/config.py: parse the new [target]/[naming]/[reserved]/[types]/[layout]/ [docs]/[rename.*] sections, including the two-tier rename scheme, scoped addressing, and the [naming] extends shared base. - gen/naming.base.toml: first real shared rename -- barline carries both elements and attributes named segno/coda, which any field casing collapses; the attributes become segno-sound/coda-sound for every target. - gen/ir/build.py: canonicalize xs:ID/xs:IDREF to token so the IR primitive set stays the documented eight. - CLI: python3 -m gen plates --config C [--type N] [--check]; --check is a CI gate like analyze. - gen/tests/test_plates.py: the design's worked conversion table, override tiers and precedence, fail-loud validation, collision detection, and spot-checked projections of all three shipped target configs. Design deltas recorded in plates.md section 11 (MemberRepr dropped, named convention fields, grouped partition deferred, group renames reserved). --- AGENTS.md | 7 +- docs/ai/design/plates.md | 35 ++- gen/README.md | 19 +- gen/__main__.py | 80 +++++++ gen/config.py | 359 ++++++++++++++++++++++++++-- gen/cpp/config.toml | 10 + gen/ir/build.py | 5 + gen/naming.base.toml | 12 + gen/plates/__init__.py | 10 + gen/plates/build.py | 490 +++++++++++++++++++++++++++++++++++++++ gen/plates/check.py | 101 ++++++++ gen/plates/languages.py | 106 +++++++++ gen/plates/model.py | 292 +++++++++++++++++++++++ gen/plates/names.py | 132 +++++++++++ gen/test/c/config.toml | 14 ++ gen/test/go/config.toml | 14 ++ gen/tests/test_plates.py | 417 +++++++++++++++++++++++++++++++++ 17 files changed, 2080 insertions(+), 23 deletions(-) create mode 100644 gen/naming.base.toml create mode 100644 gen/plates/__init__.py create mode 100644 gen/plates/build.py create mode 100644 gen/plates/check.py create mode 100644 gen/plates/languages.py create mode 100644 gen/plates/model.py create mode 100644 gen/plates/names.py create mode 100644 gen/tests/test_plates.py diff --git a/AGENTS.md b/AGENTS.md index 203ad3f23..4c7d43c18 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -143,6 +143,9 @@ Commands: - `python3 -m gen ir [--type NAME] [--resolve] [--config C] [xsd]` - lower the XSD to the IR and print it as JSON; `--resolve` prints the collapsed (group-spliced, attribute-flattened) view of complex types; `--config` applies a target config's companion patches (the sounds.xml fold) first. +- `python3 -m gen plates --config C [--type NAME] [--check]` - project the IR onto the target the + config describes and print the Plates as JSON; `--check` validates renames and detects identifier + collisions (a CI gate, like analyze). - `python3 -m gen ` - emit code for the target in the config (not yet implemented). Each target has a `config.toml` specifying the MusicXML XSD it generates from (`[input] xsd`), the @@ -160,8 +163,8 @@ unioned with an open string (element `instrument-sound` retyped from `string` to the only place the IR depends on an input beyond the XSD; it is opt-in per target, so the base IR stays a pure function of the schema. -**Status.** The parse, IR, and analysis stages exist. The Plates stage is designed -(`docs/ai/design/plates.md`) but not yet implemented; the emit stage and its templates are not yet +**Status.** The parse, IR, analysis, and Plates stages exist (`python3 -m gen plates --config C +[--check]` dumps or gates the projection); the emit stage and its templates are not yet implemented, so `python3 -m gen ` still exits with an error. ## Language targets diff --git a/docs/ai/design/plates.md b/docs/ai/design/plates.md index 42d7e3a66..76fb326a4 100644 --- a/docs/ai/design/plates.md +++ b/docs/ai/design/plates.md @@ -1,7 +1,8 @@ # The Plates: the template-facing, target-projected layer -Status: design only. No code in this change. This document specifies the layer that sits between the -IR (`gen/ir`) and the per-language templates in the generator pipeline: +Status: implemented in `gen/plates/` (see Implementation notes, section 11, for the deltas between +this design and the code). This document specifies the layer that sits between the IR (`gen/ir`) +and the per-language templates in the generator pipeline: ``` XSD file -> XSD model -> IR -> [ Plates ] -> templates -> C++ / Go / C / JSON Schema @@ -642,3 +643,33 @@ dumb). on it for a future mixed-case schema. - **Configurable per-enum invalid-identifier prefix.** A single global `invalid-prefix` is assumed; some targets might want it per enum (e.g. note-type values prefixed `N`). + +## 11. Implementation notes + +The implementation (`gen/plates/`: `model.py`, `names.py`, `languages.py`, `build.py`, `check.py`; +config parsing in `gen/config.py`; tests in `gen/tests/test_plates.py`) follows this document with +these deliberate deltas: + +- **`MemberRepr` dropped.** A member's `cardinality` plus the target type map already fully + determine the wrapper spelling (by-value / optional / collection); a descriptor object in between + carried no information. The spelling is the template's grammar. +- **Named convention fields.** `TargetInfo` carries `type_convention` / `field_convention` / + `variant_convention` / `file_convention` rather than an anonymous `conventions` list -- the four + roles are load-bearing and deserve names. +- **Final identifiers are materialized.** Every plate, member, and variant carries `ident`: the + recased, renamed, prefix-applied, sanitized identifier the target uses. Collision detection runs + on these. The pre-sanitized casings stay available in `Name.cased` (section 8.6's both-retained + rule). +- **`grouped` partition deferred.** Config accepts the value; the build rejects it with a clear + message until a target needs it. `per-type` and `single` are implemented. +- **`group` / `attribute-group` rename kinds reserved.** No current target emits shared fragments + or mixins, so configuring those kinds is a config error rather than a silently dead table. +- **One reserved-word policy.** Only `suffix-underscore` is implemented; the config key exists and + validates so a future policy is an addition, not a migration. +- **First contact found a real collision.** `barline` carries both elements and attributes named + `segno`/`coda`; every code target's field casing collapses each pair. The shared + `gen/naming.base.toml` (the section 6.4 mechanism) renames the attributes' fundamentals to + `segno-sound`/`coda-sound`, and all three target configs extend it. +- **`xs:ID`/`xs:IDREF` canonicalized in the IR.** They surfaced as accidental ninth and tenth + primitives via the builtin fallback; the IR now folds them to `token`, keeping the primitive set + the eight this document assumes. diff --git a/gen/README.md b/gen/README.md index 5836af059..6233eec04 100644 --- a/gen/README.md +++ b/gen/README.md @@ -21,8 +21,7 @@ XSD file --parse--> XSD model --lower--> IR --project--> Plates --emit--> 3. Project (`gen/plates/`) binds the IR to one target: every per-target decision -- identifier casings, renames, primitive type mappings, emit strategies, file layout -- is made here, once, producing one **plate** per emitted type. The collection projected for a target is the - **Plates**. Designed in [`../docs/ai/design/plates.md`](../docs/ai/design/plates.md); not yet - implemented. + **Plates**. Designed in [`../docs/ai/design/plates.md`](../docs/ai/design/plates.md). 4. Emit renders each plate through per-language templates ("dumb renderers": walk the plate, print text, no naming logic). Not yet implemented. @@ -30,7 +29,9 @@ XSD file --parse--> XSD model --lower--> IR --project--> Plates --emit--> ``` gen/ - __main__.py CLI: analyze | ir | + __main__.py CLI: analyze | ir | plates | + config.py typed config.toml loader (inputs, output, plates sections) + naming.base.toml schema-forced renames shared by all targets xsd/ model.py dataclasses mirroring the XSD subset MusicXML uses parser.py ElementTree parser, no external dependencies @@ -40,6 +41,12 @@ gen/ build.py lowering from the XSD model to the IR resolve.py collapsed views (group + attribute-group resolution) for emitters dump.py IR to JSON + plates/ + model.py the plate dataclasses (neutral core + target binding) + names.py tokenizer, convention registry, sanitizer + languages.py per-language defaults (type maps, reserved words, doc styles) + build.py the projection: IR + config -> Plates + check.py post-projection collision detection cpp/, test/go/, test/c/ per-target config and corert test harnesses ``` @@ -81,6 +88,7 @@ type-shaping knob: it selects an *input*, not how a type is emitted. ``` python3 -m gen analyze [xsd] # structural analysis report (text) python3 -m gen ir [--type NAME] [--resolve] [--config C] [xsd] # lower to IR, print as JSON +python3 -m gen plates --config C [--type NAME] [--check] # project the IR onto a target, print as JSON python3 -m gen # emit code for a target (not yet implemented) ``` @@ -93,6 +101,11 @@ config pins (`[input] xsd`) and applies the companion patches the config enables fold, see Companion data). An explicit positional `xsd` still overrides the config's. Without `--config`, `ir` shows the pure-XSD IR of the default (or positional) schema. +`plates --config C` projects that same IR onto the target: identifier casings, renames, sanitized +identifiers, type mappings, strategies, file assignment -- the proof pulled from the plates before +any code is printed. `--check` runs rename validation and collision detection and exits non-zero on +any failure, so it can gate CI the way `analyze` guards the DAG and no-collision invariants. + `xsd` defaults to `docs/musicxml-4.0-ed15c23.xsd`. Examples: ``` diff --git a/gen/__main__.py b/gen/__main__.py index 6f4dee3f0..4a3469caf 100644 --- a/gen/__main__.py +++ b/gen/__main__.py @@ -9,6 +9,12 @@ attribute-flattened) view of complex types; --config applies a target's companion patches (e.g. the sounds.xml fold) before dumping + python3 -m gen plates --config C [--type N] [--check] + project the IR onto the target the config + describes and print the Plates as JSON; + --check validates renames and detects + identifier collisions, exiting non-zero on + any failure (a CI gate, like analyze) Reads a MusicXML 4.0 XSD specification and generates typed document serialization/deserialization code for the target described in the given @@ -106,6 +112,78 @@ def _ir(args: list[str]) -> int: return 0 +def _build_target_plates(config_path: str): + """Shared loading path for plates-consuming commands: read the target's + config, lower its pinned XSD to the IR, apply companion patches, project.""" + from gen.config import load as load_config + from gen.ir.build import build_ir + from gen.plates import build_plates + from gen.xsd.parser import parse + + cfg = load_config(config_path) + if cfg.xsd is None: + raise FileNotFoundError(f"config has no [input] xsd: {cfg.path}") + ir = build_ir(parse(cfg.xsd), source=cfg.xsd.stem) + if cfg.sounds_xml is not None: + from gen.ir.sounds import patch_sounds, read_sound_ids + + patch_sounds(ir, read_sound_ids(cfg.sounds_xml)) + return build_plates(ir, cfg), cfg + + +def _plates(args: list[str]) -> int: + from gen.ir.dump import to_json + from gen.plates import PlatesError + + config_path = None + type_name = None + check = False + i = 0 + while i < len(args): + if args[i] == "--config" and i + 1 < len(args): + config_path = args[i + 1] + i += 2 + elif args[i] == "--type" and i + 1 < len(args): + type_name = args[i + 1] + i += 2 + elif args[i] == "--check": + check = True + i += 1 + else: + print(f"error: unexpected argument: {args[i]}", file=sys.stderr) + return 2 + if config_path is None: + print("error: plates requires --config ", file=sys.stderr) + return 2 + + try: + plates, _ = _build_target_plates(config_path) + except PlatesError as e: + for line in e.errors: + print(f"error: {line}", file=sys.stderr) + return 1 + + if check: + # Rename validation and collision detection already ran in the build; + # reaching here means the projection is clean. + n_files = len(plates.files) if plates.files is not None else 0 + print( + f"plates ok: {len(plates.value_types)} value types, " + f"{len(plates.complex_types)} complex types, {n_files} files" + ) + return 0 + + if type_name: + if not plates.has_plate(type_name): + print(f"error: type not found in plates: {type_name}", file=sys.stderr) + return 1 + print(to_json(plates.plate(type_name))) + return 0 + + print(to_json(plates)) + return 0 + + def main(argv: list[str]) -> int: if not argv: print(__doc__, file=sys.stderr) @@ -114,6 +192,8 @@ def main(argv: list[str]) -> int: return _analyze(argv[1:]) if argv[0] == "ir": return _ir(argv[1:]) + if argv[0] == "plates": + return _plates(argv[1:]) print("error: generator not implemented", file=sys.stderr) return 1 diff --git a/gen/config.py b/gen/config.py index 5ed75dce2..c557921f7 100644 --- a/gen/config.py +++ b/gen/config.py @@ -1,29 +1,127 @@ """Load a target's config.toml into a typed Config. -A target config describes one generation run: where generated code lands and -which optional companion patches to apply before emitting. The IR itself is a -pure function of the schema inputs (see gen.ir); config selects *which* inputs -and where the output goes, never how an individual type is shaped. +A target config describes one generation run: which schema inputs to read, +where generated code lands, which optional companion patches to apply before +emitting, and how the IR is projected onto the target (the Plates: naming +conventions, renames, type mappings, layout). The IR itself stays a pure +function of the schema inputs (see gen.ir); config selects *which* inputs and +how the result is presented, never what the schema means. + +Parsing is structural only: key shapes, types, and the rename addressing +scheme. Semantic validation (does a rename key name something in the IR, do +projected identifiers collide) happens in gen.plates.build, which has the IR +in hand and fails loud there. """ from __future__ import annotations import tomllib -from dataclasses import dataclass +from dataclasses import dataclass, field from pathlib import Path +from gen.plates.names import CONVENTIONS + +# Keys allowed in a rename entry table: a fundamental rename (all casings +# re-expand from the new root) or per-convention overrides (pin one flavor). +_ENTRY_KEYS = frozenset(CONVENTIONS) | {"fundamental"} + +# Rename kinds the Plates build consumes today. `group` and `attribute-group` +# are reserved by the design for targets that emit shared fragments/mixins; +# none of ours does, so configuring them is an error rather than a silently +# dead table. +_RENAME_KINDS = ("type", "element", "attribute", "enum-value") + + +class ConfigError(ValueError): + """A malformed config file. Always raised with the offending key path.""" + + +@dataclass +class RenameEntry: + """One rename: an optional fundamental root plus per-convention pins.""" + + fundamental: str | None = None + cased: dict[str, str] = field(default_factory=dict) + + +@dataclass +class Renames: + """Parsed [rename.*] tables, keyed by the design's addressing scheme.""" + + types: dict[str, RenameEntry] = field(default_factory=dict) + elements: dict[str, RenameEntry] = field(default_factory=dict) + attributes: dict[str, RenameEntry] = field(default_factory=dict) # global + scoped_attributes: dict[tuple[str, str], RenameEntry] = field(default_factory=dict) + enum_values: dict[tuple[str, str], RenameEntry] = field(default_factory=dict) + + def __bool__(self) -> bool: + return bool( + self.types + or self.elements + or self.attributes + or self.scoped_attributes + or self.enum_values + ) + + +@dataclass +class TargetSection: + language: str = "neutral" + namespace: str = "" + prefix: str = "" + inheritance: bool = True # derived types: inherit (True) or flatten + + +@dataclass +class NamingSection: + acronyms: tuple[str, ...] | None = None # None -> the built-in default set + type_convention: str = "pascal" + field_convention: str = "snake" + variant_convention: str = "pascal" + file_convention: str = "snake" + field_prefix: str = "" + empty_value_word: str = "empty" + pluralize_vectors: bool = False + + +@dataclass +class ReservedSection: + words: tuple[str, ...] = () # extends the language defaults + policy: str = "suffix-underscore" + invalid_prefix: str = "_" + + +@dataclass +class LayoutSection: + partition: str = "single" # "per-type" | "single" ("grouped" reserved) + include_style: str = "quoted" + file_prefix: str = "" + + +@dataclass +class DocsSection: + style: str | None = None # None -> the language default + wrap: int = 100 + @dataclass class Config: - path: Path # the config file itself, resolved - xsd: Path | None # the MusicXML XSD this target generates from, resolved - output_dir: Path | None # where generated code lands, resolved - sounds_xml: Path | None # companion sounds file to fold in, or None when off + path: Path = Path(".") # the config file itself, resolved + xsd: Path | None = None # the MusicXML XSD this target generates from + output_dir: Path | None = None # where generated code lands, resolved + sounds_xml: Path | None = None # companion sounds file to fold in, or None + target: TargetSection = field(default_factory=TargetSection) + naming: NamingSection = field(default_factory=NamingSection) + reserved: ReservedSection = field(default_factory=ReservedSection) + types: dict[str, str] = field(default_factory=dict) # primitive overrides + layout: LayoutSection = field(default_factory=LayoutSection) + docs: DocsSection = field(default_factory=DocsSection) + renames: Renames = field(default_factory=Renames) def load(config_path) -> Config: - """Parse config.toml. Paths inside it are interpreted relative to the config - file's own directory, so a target's config stays self-contained.""" + """Parse config.toml. Paths inside it are interpreted relative to the + config file's own directory, so a target's config stays self-contained.""" path = Path(config_path).resolve() if not path.exists(): raise FileNotFoundError(f"config not found: {path}") @@ -31,8 +129,12 @@ def load(config_path) -> Config: data = tomllib.load(f) base = path.parent - # Each target pins its own MusicXML version: the schema it generates from is - # part of the target's identity, not a global default. + # A shared naming base (design: [naming] extends) contributes [naming] + # keys and [rename.*] entries; the target's own win on any conflict. + data = _apply_extends(data, base) + + # Each target pins its own MusicXML version: the schema it generates from + # is part of the target's identity, not a global default. xsd = None inp = data.get("input", {}) if inp.get("xsd"): @@ -46,8 +148,8 @@ def load(config_path) -> Config: output_dir = (base / out["dir"]).resolve() # Companion sounds patch is on iff [sounds] xml names a file (see - # gen.ir.sounds). Resolve and existence-check it here so a bad path fails at - # config load, not deep in the lowering. + # gen.ir.sounds). Resolve and existence-check it here so a bad path fails + # at config load, not deep in the lowering. sounds_xml = None sounds = data.get("sounds", {}) if sounds.get("xml"): @@ -55,4 +157,229 @@ def load(config_path) -> Config: if not sounds_xml.exists(): raise FileNotFoundError(f"sounds file not found: {sounds_xml}") - return Config(path=path, xsd=xsd, output_dir=output_dir, sounds_xml=sounds_xml) + return Config( + path=path, + xsd=xsd, + output_dir=output_dir, + sounds_xml=sounds_xml, + target=_target(data.get("target", {})), + naming=_naming(data.get("naming", {})), + reserved=_reserved(data.get("reserved", {})), + types=_types(data.get("types", {})), + layout=_layout(data.get("layout", {})), + docs=_docs(data.get("docs", {})), + renames=_renames(data.get("rename", {})), + ) + + +# --------------------------------------------------------------------------- # +# Section parsers. Each takes the raw TOML table and fails loud on unknown +# keys, so a typo is a config error, not a silently ignored line. +# --------------------------------------------------------------------------- # + + +def _check_keys(table: dict, allowed: set[str], where: str) -> None: + unknown = set(table) - allowed + if unknown: + raise ConfigError(f"unknown key(s) in [{where}]: {', '.join(sorted(unknown))}") + + +def _target(t: dict) -> TargetSection: + _check_keys(t, {"language", "namespace", "prefix", "inheritance"}, "target") + return TargetSection( + language=t.get("language", "neutral"), + namespace=t.get("namespace", ""), + prefix=t.get("prefix", ""), + inheritance=bool(t.get("inheritance", True)), + ) + + +def _naming(t: dict) -> NamingSection: + _check_keys( + t, + { + "extends", "acronyms", "type-convention", "field-convention", + "variant-convention", "file-convention", "field-prefix", + "empty-value-word", "pluralize-vectors", + }, + "naming", + ) + section = NamingSection( + acronyms=tuple(t["acronyms"]) if "acronyms" in t else None, + type_convention=t.get("type-convention", "pascal"), + field_convention=t.get("field-convention", "snake"), + variant_convention=t.get("variant-convention", "pascal"), + file_convention=t.get("file-convention", "snake"), + field_prefix=t.get("field-prefix", ""), + empty_value_word=t.get("empty-value-word", "empty"), + pluralize_vectors=bool(t.get("pluralize-vectors", False)), + ) + for key in ("type_convention", "field_convention", "variant_convention", "file_convention"): + value = getattr(section, key) + if value not in CONVENTIONS: + raise ConfigError( + f"[naming] {key.replace('_', '-')} = {value!r} is not a " + f"registered convention ({', '.join(sorted(CONVENTIONS))})" + ) + return section + + +def _reserved(t: dict) -> ReservedSection: + _check_keys(t, {"words", "policy", "invalid-prefix"}, "reserved") + section = ReservedSection( + words=tuple(t.get("words", ())), + policy=t.get("policy", "suffix-underscore"), + invalid_prefix=t.get("invalid-prefix", "_"), + ) + if section.policy != "suffix-underscore": + raise ConfigError( + f"[reserved] policy = {section.policy!r}: only 'suffix-underscore' is implemented" + ) + return section + + +def _types(t: dict) -> dict[str, str]: + for k, v in t.items(): + if not isinstance(v, str): + raise ConfigError(f"[types] {k} must be a string target type") + return dict(t) + + +def _layout(t: dict) -> LayoutSection: + _check_keys(t, {"partition", "include-style", "file-prefix"}, "layout") + section = LayoutSection( + partition=t.get("partition", "single"), + include_style=t.get("include-style", "quoted"), + file_prefix=t.get("file-prefix", ""), + ) + if section.partition not in ("per-type", "single", "grouped"): + raise ConfigError( + f"[layout] partition = {section.partition!r}: expected per-type, single, or grouped" + ) + return section + + +def _docs(t: dict) -> DocsSection: + _check_keys(t, {"style", "wrap"}, "docs") + return DocsSection(style=t.get("style"), wrap=int(t.get("wrap", 100))) + + +# --------------------------------------------------------------------------- # +# Renames (design 6.2/6.3): two tiers (fundamental + per-convention), four +# addressable kinds, with enum values scoped to their enum and attributes +# optionally scoped to their owner type. +# --------------------------------------------------------------------------- # + + +def _entry(value, where: str) -> RenameEntry: + """A rename value is either the string shorthand (sugar for a table with + only `fundamental`) or a table of fundamental/convention keys.""" + if isinstance(value, str): + return RenameEntry(fundamental=value) + if isinstance(value, dict): + unknown = set(value) - _ENTRY_KEYS + if unknown: + raise ConfigError( + f"unknown key(s) in [{where}]: {', '.join(sorted(unknown))} " + f"(expected fundamental or a convention: {', '.join(sorted(CONVENTIONS))})" + ) + bad = [k for k, v in value.items() if not isinstance(v, str)] + if bad: + raise ConfigError(f"[{where}] {bad[0]} must be a string") + return RenameEntry( + fundamental=value.get("fundamental"), + cased={k: v for k, v in value.items() if k != "fundamental"}, + ) + raise ConfigError(f"[{where}] must be a string or a table") + + +def _is_entry_table(value) -> bool: + return isinstance(value, dict) and set(value) <= _ENTRY_KEYS + + +def _renames(t: dict) -> Renames: + unknown = set(t) - set(_RENAME_KINDS) - {"group", "attribute-group"} + if unknown: + raise ConfigError(f"unknown rename kind(s): {', '.join(sorted(unknown))}") + for reserved_kind in ("group", "attribute-group"): + if reserved_kind in t: + raise ConfigError( + f"rename kind '{reserved_kind}' is reserved for targets that emit " + f"shared fragments; no current target does" + ) + + r = Renames() + for wire, value in t.get("type", {}).items(): + r.types[wire] = _entry(value, f"rename.type.{wire}") + for wire, value in t.get("element", {}).items(): + r.elements[wire] = _entry(value, f"rename.element.{wire}") + + # [rename.attribute] mixes global entries (string, or a table of entry + # keys) with owner scopes (a table keyed by attribute names). The key sets + # are disjoint: entry keys are fundamental/conventions, never wire names. + for key, value in t.get("attribute", {}).items(): + if isinstance(value, str) or _is_entry_table(value): + r.attributes[key] = _entry(value, f"rename.attribute.{key}") + elif isinstance(value, dict): + for attr, sub in value.items(): + r.scoped_attributes[(key, attr)] = _entry( + sub, f"rename.attribute.{key}.{attr}" + ) + else: + raise ConfigError(f"[rename.attribute] {key} must be a string or a table") + + for enum, table in t.get("enum-value", {}).items(): + if not isinstance(table, dict): + raise ConfigError(f"[rename.enum-value.{enum}] must be a table of values") + for wire, value in table.items(): + r.enum_values[(enum, wire)] = _entry(value, f"rename.enum-value.{enum}.{wire}") + return r + + +# --------------------------------------------------------------------------- # +# Shared naming base ([naming] extends) +# --------------------------------------------------------------------------- # + + +def _apply_extends(data: dict, base_dir: Path) -> dict: + """Merge a shared base file under the target's config: the base + contributes [naming] keys and whole [rename] entries; the target's own + win per key/entry. Other sections never come from the base.""" + extends = data.get("naming", {}).get("extends") + if not extends: + return data + base_path = (base_dir / extends).resolve() + if not base_path.exists(): + raise FileNotFoundError(f"naming base not found: {base_path}") + with open(base_path, "rb") as f: + shared = tomllib.load(f) + + merged = dict(data) + naming = dict(shared.get("naming", {})) + naming.pop("extends", None) # a base may not chain to another base + naming.update(data.get("naming", {})) + naming.pop("extends", None) + merged["naming"] = naming + + rename: dict = {} + for kind in set(shared.get("rename", {})) | set(data.get("rename", {})): + base_table = shared.get("rename", {}).get(kind, {}) + own_table = data.get("rename", {}).get(kind, {}) + table: dict = {} + for key in list(base_table) + [k for k in own_table if k not in base_table]: + b, o = base_table.get(key), own_table.get(key) + if ( + isinstance(b, dict) + and isinstance(o, dict) + and not _is_entry_table(b) + and not _is_entry_table(o) + ): + # A nested scope (an enum's value table, an owner's attribute + # table): merge per inner entry, target winning. + table[key] = {**b, **o} + else: + table[key] = o if key in own_table else b + rename[kind] = table + if rename: + merged["rename"] = rename + return merged diff --git a/gen/cpp/config.toml b/gen/cpp/config.toml index df4b5efe1..22bf6eff7 100644 --- a/gen/cpp/config.toml +++ b/gen/cpp/config.toml @@ -15,3 +15,13 @@ dir = "../../src/private/mx/core" # Folds the standard instrument-sound identifiers into the IR as a sound-id # enum unioned with an open string. Comment out to disable. xml = "../../docs/sounds-4.0-ed15c23.xml" + +[target] +language = "cpp" +namespace = "mx::core" + +[naming] +extends = "../naming.base.toml" # schema-forced renames shared by all targets + +[layout] +partition = "per-type" diff --git a/gen/ir/build.py b/gen/ir/build.py index e47d81f8d..14990d6e6 100644 --- a/gen/ir/build.py +++ b/gen/ir/build.py @@ -22,6 +22,11 @@ "xs:date": "date", "xs:anyURI": "string", "xs:language": "token", + # The identity builtins are NCName-derived (whitespace-collapsed tokens); + # no target gives them identity semantics, so they canonicalize to token + # rather than leaking ID/IDREF as accidental extra primitives. + "xs:ID": "token", + "xs:IDREF": "token", } # The 10 attribute refs into the imported xml/xlink schemas, resolved to the diff --git a/gen/naming.base.toml b/gen/naming.base.toml new file mode 100644 index 000000000..88137c7b0 --- /dev/null +++ b/gen/naming.base.toml @@ -0,0 +1,12 @@ +# Shared naming base for every target ([naming] extends in each config.toml). +# Holds renames that are forced by the schema itself rather than by any one +# language, so they stay identical across targets. A target's own entries win +# over these on any conflict. + +# `barline` carries both child ELEMENTS segno/coda (the visible symbols) and +# ATTRIBUTES segno/coda (sound/playback jump markers). Any field casing +# collapses each pair to one identifier, so every code target collides. The +# elements keep their wire-true names; the attributes are qualified. +[rename.attribute.barline] +segno = "segno-sound" +coda = "coda-sound" diff --git a/gen/plates/__init__.py b/gen/plates/__init__.py new file mode 100644 index 000000000..144347605 --- /dev/null +++ b/gen/plates/__init__.py @@ -0,0 +1,10 @@ +"""The Plates: the template-facing, per-target projection of the IR. + +See gen.plates.model for the data shape, gen.plates.build for the projection, +and docs/ai/design/plates.md for the design. +""" + +from gen.plates.build import PlatesError, build_plates +from gen.plates.model import Plates + +__all__ = ["Plates", "PlatesError", "build_plates"] diff --git a/gen/plates/build.py b/gen/plates/build.py new file mode 100644 index 000000000..313b58faa --- /dev/null +++ b/gen/plates/build.py @@ -0,0 +1,490 @@ +"""Project the IR onto one target: build the Plates. + +The build consumes the IR and its Resolver (it never re-derives a schema +fact) plus a Config, and produces the materialized Plates tree. Three phases, +each failing loud: + + 1. Rename validation: every [rename.*] key must name something the IR + actually contains (a stale or misspelled key is a build error). + 2. Projection: names are tokenized and recased, renames and overrides + applied, identifiers sanitized, types mapped, strategies and files + assigned. + 3. Collision detection (gen.plates.check): distinct wire names that + collapsed to one identifier under the projection are reported together. +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from gen.ir import model as ir +from gen.ir.resolve import Resolver +from gen.plates import languages +from gen.plates.check import run_checks +from gen.plates.model import ( + ComplexPlate, + EnumPlate, + FileSpec, + Member, + Name, + NumberBounds, + NumberPlate, + PlateRef, + Plates, + StringPlate, + TargetInfo, + UnionPlate, + UnionPlateMember, + Variant, +) +from gen.plates.names import DEFAULT_ACRONYMS, NameFactory, sanitize_identifier + +if TYPE_CHECKING: + # Imported for annotations only: gen.config imports the convention + # registry from gen.plates.names, so a runtime import here would cycle. + from gen.config import Config + + +class PlatesError(Exception): + """One or more projection failures, collected so a run reports every + problem at once rather than the first.""" + + def __init__(self, errors: list[str]): + self.errors = errors + super().__init__("\n".join(errors)) + + +def build_plates(m: ir.Ir, config: Config) -> Plates: + plates = _Builder(m, config).build() + errors = run_checks(plates) + if errors: + raise PlatesError(errors) + return plates + + +class _Builder: + def __init__(self, m: ir.Ir, config: Config): + self.m = m + self.cfg = config + self.resolver = Resolver.from_ir(m) + self.values_by_name: dict[str, ir.ValueType] = {v.name: v for v in m.value_types} + self.complex_by_name = {c.name: c for c in m.complex_types} + + naming = config.naming + self.factory = NameFactory( + naming.acronyms if naming.acronyms is not None else DEFAULT_ACRONYMS, + naming.empty_value_word, + ) + self.reserved = frozenset(languages.reserved_for(config.target.language)) | set( + config.reserved.words + ) + self.invalid_prefix = config.reserved.invalid_prefix + self.type_map = languages.type_map_for(config.target.language) + self.type_map.update(config.types) + + # Every type's Name and final identifier, computed up front so any + # reference can be resolved to its target spelling in one lookup. + self.type_names: dict[str, Name] = {} + self.type_idents: dict[str, str] = {} + for type_wire in list(self.values_by_name) + list(self.complex_by_name): + name = self._type_name(type_wire) + self.type_names[type_wire] = name + self.type_idents[type_wire] = self._sanitize( + config.target.prefix + name.cased[naming.type_convention] + ) + + # ----- entry ------------------------------------------------------------ # + + def build(self) -> Plates: + errors = self._validate_renames() + if errors: + raise PlatesError(errors) + + plates = Plates( + source=self.m.source, + target=self._target_info(), + value_types=[self._value_plate(v) for v in self.m.value_types], + complex_types=[self._complex_plate(c) for c in self.m.complex_types], + roots=[self._plate_ref(ir.Ref(r.type, "complex")) for r in self.m.roots], + type_map=dict(self.type_map), + ) + plates.files = self._assign_files(plates) + return plates + + def _target_info(self) -> TargetInfo: + t, n = self.cfg.target, self.cfg.naming + doc_style = languages.doc_style_for(t.language) + if self.cfg.docs.style is not None: + doc_style.style = self.cfg.docs.style + doc_style.wrap = self.cfg.docs.wrap + return TargetInfo( + language=t.language, + namespace=t.namespace, + prefix=t.prefix, + type_convention=n.type_convention, + field_convention=n.field_convention, + variant_convention=n.variant_convention, + file_convention=n.file_convention, + inheritance=t.inheritance, + doc_style=doc_style, + reserved=sorted(self.reserved), + partition=self.cfg.layout.partition, + ) + + # ----- names and references ---------------------------------------------- # + + def _sanitize(self, raw: str) -> str: + return sanitize_identifier(raw, self.reserved, self.invalid_prefix) + + def _type_name(self, wire: str) -> Name: + entry = self.cfg.renames.types.get(wire) + return self.factory.make( + wire, + fundamental=entry.fundamental if entry else None, + overrides=entry.cased if entry else None, + ) + + def _element_name(self, wire: str, pluralize: bool) -> Name: + entry = self.cfg.renames.elements.get(wire) + return self.factory.make( + wire, + fundamental=entry.fundamental if entry else None, + overrides=entry.cased if entry else None, + pluralize=pluralize, + ) + + def _attribute_name(self, owner: str, wire: str) -> Name: + # A scoped key (this attribute on this owner) wins over a global one. + entry = self.cfg.renames.scoped_attributes.get((owner, wire)) + if entry is None: + entry = self.cfg.renames.attributes.get(wire) + return self.factory.make( + wire, + fundamental=entry.fundamental if entry else None, + overrides=entry.cased if entry else None, + ) + + def _variant(self, scope_wire: str, value_wire: str) -> Variant: + entry = self.cfg.renames.enum_values.get((scope_wire, value_wire)) + name = self.factory.make( + value_wire, + fundamental=entry.fundamental if entry else None, + overrides=entry.cased if entry else None, + ) + ident = self._sanitize(name.cased[self.cfg.naming.variant_convention]) + return Variant(wire=value_wire, name=name, ident=ident) + + def _field_ident(self, name: Name) -> str: + raw = self.cfg.naming.field_prefix + name.cased[self.cfg.naming.field_convention] + return self._sanitize(raw) + + def _plate_ref(self, ref: ir.Ref) -> PlateRef: + if ref.category == "primitive": + ident = self.type_map.get(ref.name, ref.name) + else: + ident = self.type_idents[ref.name] + return PlateRef(wire=ref.name, category=ref.category, ident=ident) + + # ----- value plates -------------------------------------------------------- # + + def _value_plate(self, v: ir.ValueType): + name = self.type_names[v.name] + ident = self.type_idents[v.name] + if isinstance(v, ir.EnumType): + return EnumPlate( + name=name, + ident=ident, + base=v.base, + variants=[self._variant(v.name, value) for value in v.values], + doc=v.doc, + ) + if isinstance(v, ir.NumberType): + return NumberPlate( + name=name, + ident=ident, + base=v.base, + bounds=NumberBounds( + v.min_inclusive, v.max_inclusive, v.min_exclusive, v.max_exclusive + ), + target_type=self.type_map.get(v.base, v.base), + doc=v.doc, + ) + if isinstance(v, ir.StringType): + return StringPlate( + name=name, + ident=ident, + base=v.base, + patterns=list(v.patterns), + min_length=v.min_length, + max_length=v.max_length, + length=v.length, + target_type=self.type_map.get(v.base, v.base), + doc=v.doc, + ) + members = [] + for m in v.members: + if m.ref is not None: + members.append(UnionPlateMember(ref=self._plate_ref(m.ref))) + else: + # An inline literal set projects like a tiny anonymous enum; + # its variants are addressable for renames under the union's + # own type name. + members.append( + UnionPlateMember( + literals=[self._variant(v.name, lit) for lit in m.literals or []] + ) + ) + return UnionPlate(name=name, ident=ident, members=members, doc=v.doc) + + # ----- complex plates ------------------------------------------------------ # + + def _complex_plate(self, ct: ir.ComplexType) -> ComplexPlate: + strategy = { + "value": "value-class", + "composite": "composite-class", + "empty": "flag" if ct.presence_only else "attrs-class", + "derived": "inherit" if self.cfg.target.inheritance else "flatten", + }[ct.kind] + + members = self._members(ct, self.resolver.attributes(ct)) + all_members = None + if ct.kind == "derived": + all_members = self._members(ct, self.resolver.all_attributes(ct), flatten=True) + + return ComplexPlate( + name=self.type_names[ct.name], + ident=self.type_idents[ct.name], + shape=ct.kind, + strategy=strategy, + members=members, + content=self.resolver.content(ct), + base=self._plate_ref(ir.Ref(ct.base, "complex")) if ct.base else None, + all_members=all_members, + presence_only=ct.presence_only, + doc=ct.doc, + ) + + def _members(self, ct: ir.ComplexType, attrs: list[ir.Attr], flatten: bool = False) -> list[Member]: + """The flat field list: attributes first, then the text value body, + then child elements in document order (deduped by wire name). When + flattening a derived type, the base chain's value body and elements + are merged in (base-most first), mirroring all_attributes.""" + members = [self._attr_member(ct.name, a) for a in attrs] + + chain = [ct] + if flatten: + cur = ct + while cur.base and cur.base in self.complex_by_name: + cur = self.complex_by_name[cur.base] + chain.append(cur) + chain.reverse() # base-most first + + for c in chain: + if c.value_type is not None: + members.append(self._value_member(c.value_type)) + seen: set[str] = set() + for c in chain: + for occ_name, element, cardinality in self._element_occurrences(c): + if occ_name in seen: + continue + seen.add(occ_name) + members.append(self._element_member(element, cardinality)) + return members + + def _attr_member(self, owner_wire: str, a: ir.Attr) -> Member: + name = self._attribute_name(owner_wire, a.name) + literal = a.fixed if a.fixed is not None else a.default + return Member( + name=name, + ident=self._field_ident(name), + kind="attribute", + type_ref=self._plate_ref(a.type), + cardinality="required" if a.required else "optional", + default=a.default, + fixed=a.fixed, + default_variant=self._default_variant(a.type, literal), + doc=a.doc, + ) + + def _value_member(self, value_type: ir.Ref) -> Member: + # The text body of a value-shaped type has no wire name of its own; + # it is projected under the fixed root "value". + name = self.factory.make("", fundamental="value") + return Member( + name=name, + ident=self._field_ident(name), + kind="value", + type_ref=self._plate_ref(value_type), + cardinality="required", + ) + + def _element_member(self, element: ir.Element, cardinality: str) -> Member: + pluralize = self.cfg.naming.pluralize_vectors and cardinality == "vector" + name = self._element_name(element.name, pluralize) + return Member( + name=name, + ident=self._field_ident(name), + kind="element", + type_ref=self._plate_ref(element.type), + cardinality=cardinality, + doc=element.doc, + ) + + def _element_occurrences(self, ct: ir.ComplexType) -> list[tuple[str, ir.Element, str]]: + """Walk the resolved content tree computing each element's effective + cardinality for a flat member list: an element under a repeated + sequence/choice is a vector; an element under a choice (or an optional + wrapper) is at most optional; only an element required along a spine + of exactly-once sequences stays required. Occurrences of the same + name (e.g. the same element in two choice branches) are merged by the + caller: vector beats optional beats required, first name wins order.""" + out: list[tuple[str, ir.Element, str]] = [] + merged: dict[str, int] = {} # name -> index in out + rank = {"required": 0, "optional": 1, "vector": 2} + + def walk(node, forced: bool, repeated: bool) -> None: + if node is None: + return + if isinstance(node, ir.Element): + if repeated: + card = "vector" + elif node.card == "vector": + card = "vector" + elif node.card == "required" and forced: + card = "required" + else: + card = "optional" + if node.name in merged: + i = merged[node.name] + prev_name, prev_el, prev_card = out[i] + # A second occurrence means alternative branches: the + # member cannot be statically required. + card = max(card, prev_card, key=lambda c: rank[c]) + if card == "required": + card = "optional" + out[i] = (prev_name, prev_el, card) + else: + merged[node.name] = len(out) + out.append((node.name, node, card)) + return + once = node.min >= 1 and node.max == 1 + again = repeated or node.max == ir.UNBOUNDED or (node.max != 1) + if isinstance(node, ir.Sequence): + for item in node.items: + walk(item, forced and once, again) + elif isinstance(node, ir.Choice): + for item in node.items: + walk(item, False, again) + # GroupRef leaves cannot appear: the Resolver spliced them. + + walk(self.resolver.content(ct), True, False) + return out + + def _default_variant(self, type_ref: ir.Ref, literal: str | None) -> str | None: + """When a default/fixed literal names a variant of the member's enum + type, resolve it to the variant's target identifier (the wire literal + stays in `default`/`fixed` for the serializer).""" + if literal is None or type_ref.category != "value": + return None + vt = self.values_by_name.get(type_ref.name) + if isinstance(vt, ir.EnumType) and literal in vt.values: + return self._variant(vt.name, literal).ident + return None + + # ----- file assignment ------------------------------------------------------ # + + def _assign_files(self, plates: Plates) -> list[FileSpec] | None: + partition = self.cfg.layout.partition + if partition == "single": + return None + if partition == "grouped": + raise PlatesError( + ["[layout] partition = 'grouped' is not yet implemented; " + "use 'per-type' or 'single'"] + ) + + all_plates = list(plates.value_types) + list(plates.complex_types) + stem_of = { + p.name.wire: self.cfg.layout.file_prefix + + p.name.cased[self.cfg.naming.file_convention] + for p in all_plates + } + specs: list[FileSpec] = [] + for p in all_plates: + p.file = stem_of[p.name.wire] + deps = sorted( + {stem_of[d] for d in self._type_deps(p) if d in stem_of} - {p.file} + ) + specs.append(FileSpec(file=p.file, types=[p.name.wire], includes=deps)) + return specs + + def _type_deps(self, plate) -> set[str]: + """Wire names of the types a plate's emitted code references: member + and value types, union members, and the base edge. Primitives are not + plates and are excluded by the caller's stem lookup.""" + deps: set[str] = set() + if isinstance(plate, UnionPlate): + deps.update(m.ref.wire for m in plate.members if m.ref is not None) + if isinstance(plate, ComplexPlate): + for member_list in (plate.members, plate.all_members or []): + deps.update(m.type_ref.wire for m in member_list) + if plate.base is not None: + deps.add(plate.base.wire) + return deps + + # ----- rename validation ------------------------------------------------------ # + + def _validate_renames(self) -> list[str]: + """Every rename key must address something in the IR (design 6.5): + a typo or a key left stale after a schema bump is a build error, not + a silently ignored line.""" + r = self.cfg.renames + errors: list[str] = [] + + type_wires = set(self.values_by_name) | set(self.complex_by_name) + for wire in r.types: + if wire not in type_wires: + errors.append(f"rename.type.{wire}: no such type in the IR") + + element_wires: set[str] = set() + for ct in self.m.complex_types: + for e in self.resolver.elements(ct): + element_wires.add(e.name) + element_wires.update(root.element for root in self.m.roots) + for wire in r.elements: + if wire not in element_wires: + errors.append(f"rename.element.{wire}: no element by that name occurs") + + attribute_wires = { + a.name for ct in self.m.complex_types for a in self.resolver.attributes(ct) + } + for wire in r.attributes: + if wire not in attribute_wires: + errors.append(f"rename.attribute.{wire}: no attribute by that name occurs") + + for owner, attr in r.scoped_attributes: + ct = self.complex_by_name.get(owner) + if ct is None: + errors.append(f"rename.attribute.{owner}.{attr}: no such complex type") + elif attr not in {a.name for a in self.resolver.all_attributes(ct)}: + errors.append( + f"rename.attribute.{owner}.{attr}: type '{owner}' has no such attribute" + ) + + for enum, value in r.enum_values: + vt = self.values_by_name.get(enum) + if isinstance(vt, ir.EnumType): + if value not in vt.values: + errors.append( + f"rename.enum-value.{enum}.{value!r}: enum has no such value" + ) + elif isinstance(vt, ir.UnionType): + literals = {lit for m in vt.members for lit in (m.literals or [])} + if value not in literals: + errors.append( + f"rename.enum-value.{enum}.{value!r}: union has no such literal" + ) + else: + errors.append(f"rename.enum-value.{enum}: no such enum type") + + return errors diff --git a/gen/plates/check.py b/gen/plates/check.py new file mode 100644 index 000000000..0a5cda771 --- /dev/null +++ b/gen/plates/check.py @@ -0,0 +1,101 @@ +"""Post-projection collision detection (design section 7). + +After tokenizing, recasing, renames, and reserved-word/validity mangling, two +distinct wire names can collapse to one identifier. The IR's "no element-name +collisions" invariant guarantees nothing here, because these collisions are +induced by the projection. Each scope is checked in the convention the target +actually uses (the identifiers were already produced in it); every report +names the scope, the colliding wire names, and the shared identifier -- +enough to write a targeted rename to resolve it. +""" + +from __future__ import annotations + +from gen.plates.model import ComplexPlate, EnumPlate, Plates, UnionPlate + + +def run_checks(plates: Plates) -> list[str]: + errors: list[str] = [] + _check_type_idents(plates, errors) + _check_variants(plates, errors) + _check_members(plates, errors) + _check_file_stems(plates, errors) + return errors + + +def _collisions(pairs: list[tuple[str, str]]) -> list[tuple[str, list[str]]]: + """Group (identifier, wire) pairs; return identifiers claimed by more + than one distinct wire name, with their claimants.""" + by_ident: dict[str, list[str]] = {} + for ident, wire in pairs: + by_ident.setdefault(ident, []).append(wire) + return [ + (ident, wires) + for ident, wires in by_ident.items() + if len(set(wires)) > 1 + ] + + +def _check_type_idents(plates: Plates, errors: list[str]) -> None: + pairs = [ + (p.ident, p.name.wire) + for p in list(plates.value_types) + list(plates.complex_types) + ] + for ident, wires in _collisions(pairs): + errors.append( + f"type identifier collision: {sorted(set(wires))} all project to '{ident}'" + ) + + +def _check_variants(plates: Plates, errors: list[str]) -> None: + for p in plates.value_types: + if isinstance(p, EnumPlate): + pairs = [(v.ident, repr(v.wire)) for v in p.variants] + elif isinstance(p, UnionPlate): + pairs = [ + (v.ident, repr(v.wire)) + for m in p.members + if m.literals + for v in m.literals + ] + else: + continue + for ident, wires in _collisions(pairs): + errors.append( + f"variant identifier collision in '{p.name.wire}': " + f"{sorted(set(wires))} all project to '{ident}'" + ) + + +def _check_members(plates: Plates, errors: list[str]) -> None: + for p in plates.complex_types: + for label, members in (("members", p.members), ("all_members", p.all_members)): + if not members: + continue + pairs = [(m.ident, f"{m.kind} {m.name.wire!r}") for m in members] + for ident, wires in _collisions(pairs): + errors.append( + f"member identifier collision in '{p.name.wire}' ({label}): " + f"{sorted(set(wires))} all project to '{ident}'" + ) + + +def _check_file_stems(plates: Plates, errors: list[str]) -> None: + """File stems are compared case-insensitively: `Note` and `note` are + distinct identifiers but the same file on macOS/Windows filesystems.""" + if not plates.files: + return + pairs = [(spec.file.lower(), spec.file) for spec in plates.files] + by_lower: dict[str, set[str]] = {} + for lower, stem in pairs: + by_lower.setdefault(lower, set()).add(stem) + seen: dict[str, list[str]] = {} + for spec in plates.files: + seen.setdefault(spec.file.lower(), []).extend(spec.types) + for lower, stems in by_lower.items(): + types = seen[lower] + if len(types) > 1: + errors.append( + f"file stem collision (case-insensitive): types {sorted(types)} " + f"all land in '{sorted(stems)[0]}'" + ) diff --git a/gen/plates/languages.py b/gen/plates/languages.py new file mode 100644 index 000000000..1fed85cca --- /dev/null +++ b/gen/plates/languages.py @@ -0,0 +1,106 @@ +"""Per-language defaults the Plates build seeds before config overrides. + +Everything here is a default, not a decision: `[types]` entries override the +type map, `[reserved] words` extends the reserved set, `[docs]` overrides the +doc style. An unknown language starts from the neutral defaults (empty type +map, no reserved words), which is what a schema-style target wants. +""" + +from __future__ import annotations + +from gen.plates.model import DocStyle + +# IR primitive -> target type. The single place a target decides what a +# `decimal` or a `token` is spelled like; overridable per-primitive in +# [types]. The IR primitive set is fixed by gen.ir.build. +TYPE_MAPS: dict[str, dict[str, str]] = { + "go": { + "string": "string", + "token": "string", + "nmtoken": "string", + "date": "string", + "decimal": "float64", + "integer": "int", + "positive_integer": "int", + "non_negative_integer": "int", + }, + "c": { + "string": "char *", + "token": "char *", + "nmtoken": "char *", + "date": "char *", + "decimal": "double", + "integer": "long", + "positive_integer": "long", + "non_negative_integer": "long", + }, + "cpp": { + "string": "std::string", + "token": "std::string", + "nmtoken": "std::string", + "date": "std::string", + "decimal": "Decimal", + "integer": "int", + "positive_integer": "int", + "non_negative_integer": "int", + }, +} + +# Identifiers the sanitizer must not emit bare, per language: keywords plus +# predeclared/builtin names a generated identifier could shadow disastrously. +# [reserved] words extends these. +RESERVED: dict[str, tuple[str, ...]] = { + "go": ( + # keywords + "break", "case", "chan", "const", "continue", "default", "defer", + "else", "fallthrough", "for", "func", "go", "goto", "if", "import", + "interface", "map", "package", "range", "return", "select", "struct", + "switch", "type", "var", + # predeclared identifiers + "any", "append", "bool", "byte", "cap", "clear", "close", "comparable", + "complex", "complex64", "complex128", "copy", "delete", "error", + "false", "float32", "float64", "imag", "int", "int8", "int16", "int32", + "int64", "iota", "len", "make", "max", "min", "new", "nil", "panic", + "print", "println", "real", "recover", "rune", "string", "true", + "uint", "uint8", "uint16", "uint32", "uint64", "uintptr", + ), + "c": ( + "auto", "break", "case", "char", "const", "continue", "default", "do", + "double", "else", "enum", "extern", "float", "for", "goto", "if", + "inline", "int", "long", "register", "restrict", "return", "short", + "signed", "sizeof", "static", "struct", "switch", "typedef", "union", + "unsigned", "void", "volatile", "while", + "bool", "true", "false", # is assumed by generated code + ), + "cpp": ( + "alignas", "alignof", "and", "asm", "auto", "bool", "break", "case", + "catch", "char", "class", "const", "constexpr", "continue", "decltype", + "default", "delete", "do", "double", "else", "enum", "explicit", + "export", "extern", "false", "float", "for", "friend", "goto", "if", + "inline", "int", "long", "mutable", "namespace", "new", "noexcept", + "not", "nullptr", "operator", "or", "private", "protected", "public", + "register", "return", "short", "signed", "sizeof", "static", "struct", + "switch", "template", "this", "throw", "true", "try", "typedef", + "typeid", "typename", "union", "unsigned", "using", "virtual", "void", + "volatile", "while", + ), +} + +DOC_STYLES: dict[str, DocStyle] = { + "go": DocStyle(style="//"), + "c": DocStyle(style="/* */"), + "cpp": DocStyle(style="///"), +} + + +def type_map_for(language: str) -> dict[str, str]: + return dict(TYPE_MAPS.get(language, {})) + + +def reserved_for(language: str) -> tuple[str, ...]: + return RESERVED.get(language, ()) + + +def doc_style_for(language: str) -> DocStyle: + style = DOC_STYLES.get(language) + return DocStyle(style=style.style, wrap=style.wrap) if style else DocStyle(style="") diff --git a/gen/plates/model.py b/gen/plates/model.py new file mode 100644 index 000000000..49d91af5a --- /dev/null +++ b/gen/plates/model.py @@ -0,0 +1,292 @@ +"""The Plates: the template-facing, per-target projection of the IR. + +The IR (gen.ir) is a pure, language-agnostic function of the schema inputs. +The Plates are its opposite number: one plate per emitted type, carrying +everything a template needs to print code without thinking -- identifier +casings, resolved target types, emit strategy tags, file assignment. This is +where config.toml meets the IR; templates stay dumb renderers. + +Each plate is internally partitioned into two field groups: + + - a neutral core: wire-faithful, target-independent facts (wire name, shape, + resolved structure, value lists, facets, docs), mirrored from the IR and + its Resolver; and + - a target binding: the per-target overlay (casings, sanitized identifiers, + resolved target types, strategy tags, file assignment). + +A code target reads both groups. A neutral target (e.g. a JSON Schema +emitter) reads only the neutral core and configures `[layout] partition = +"single"`, paying nothing for the binding it ignores. + +The Plates are materialized (built once per target, dumpable via +gen.ir.dump.to_jsonable) rather than computed on demand: collision detection +and rename validation are global build-then-check passes, and templates want +random access to fully resolved plates. Design: docs/ai/design/plates.md. +""" + +from __future__ import annotations + +from dataclasses import dataclass, field + +from gen.ir import model as ir + + +@dataclass +class Name: + """The neutral/bound name bundle. `wire` is the immutable on-the-wire + string (never a code identifier); `words` is the tokenized vector the + casings expand from; `cased` maps convention name -> identifier, filled + by iterating the convention registry (gen.plates.names.CONVENTIONS).""" + + wire: str + words: tuple[str, ...] + cased: dict[str, str] + + @property + def pascal(self) -> str: + return self.cased["pascal"] + + @property + def camel(self) -> str: + return self.cased["camel"] + + @property + def snake(self) -> str: + return self.cased["snake"] + + @property + def kebab(self) -> str: + return self.cased["kebab"] + + @property + def screaming(self) -> str: + return self.cased["screaming"] + + +@dataclass +class PlateRef: + """A reference to another type, resolved for the target: `wire` and + `category` mirror the IR Ref; `ident` is the spelling a template prints -- + the referenced plate's type identifier, or the mapped target type when the + category is `primitive`.""" + + wire: str + category: str # "complex" | "value" | "primitive" + ident: str + + +@dataclass +class DocStyle: + """How the target writes doc comments. The plate keeps raw doc text (so a + neutral target can use it verbatim); the template applies the syntax.""" + + style: str # e.g. "//", "///", "/* */"; "" for targets without comments + wrap: int = 100 + + +@dataclass +class TargetInfo: + """The per-target facts that are global to the projection, not per-type.""" + + language: str + namespace: str # C++ namespace / Go package; empty when prefix is used + prefix: str # global symbol prefix for languages without namespaces + type_convention: str + field_convention: str + variant_convention: str + file_convention: str + inheritance: bool # derived strategy: True -> inherit, False -> flatten + doc_style: DocStyle + reserved: list[str] # language defaults + [reserved] words, sorted + partition: str # "per-type" | "single" + + +# --------------------------------------------------------------------------- # +# Value plates (mirror the IR's 4 value shapes) +# --------------------------------------------------------------------------- # + + +@dataclass +class Variant: + """One enum value. `wire` is retained for serialization; `ident` is the + sanitized identifier in the variant convention. Both are kept: a target + whose enum constants are scoped by composition (Go `NoteTypeValue1024th`, + C `MX_NOTE_TYPE_VALUE_1024TH`) composes from `name.cased`, while a target + whose constants stand alone uses `ident` (where `1024th` -> `_1024th`).""" + + wire: str + name: Name + ident: str + + +@dataclass +class NumberBounds: + """Numeric facets, verbatim from the schema (strings, not parsed).""" + + min_inclusive: str | None = None + max_inclusive: str | None = None + min_exclusive: str | None = None + max_exclusive: str | None = None + + +@dataclass +class EnumPlate: + name: Name + ident: str + base: str # IR primitive the tokens are drawn from + variants: list[Variant] + doc: str | None = None + file: str | None = None + kind: str = "enum" + strategy: str = "enum-class" + + +@dataclass +class NumberPlate: + name: Name + ident: str + base: str # IR primitive: decimal/integer/positive_integer/non_negative_integer + bounds: NumberBounds = field(default_factory=NumberBounds) + target_type: str = "" # type_map[base]: what the wrapper wraps + doc: str | None = None + file: str | None = None + kind: str = "number" + strategy: str = "numeric-wrapper" + + +@dataclass +class StringPlate: + name: Name + ident: str + base: str # IR primitive: string/token/nmtoken/date + patterns: list[str] = field(default_factory=list) + min_length: str | None = None + max_length: str | None = None + length: str | None = None + target_type: str = "" # type_map[base] + doc: str | None = None + file: str | None = None + kind: str = "string" + strategy: str = "string-wrapper" + + +@dataclass +class UnionPlateMember: + """Exactly one is set: a resolved reference to a member type, or an inline + literal set projected like a tiny anonymous enum (each literal carries its + wire form and a variant identifier).""" + + ref: PlateRef | None = None + literals: list[Variant] | None = None + + +@dataclass +class UnionPlate: + name: Name + ident: str + members: list[UnionPlateMember] = field(default_factory=list) + doc: str | None = None + file: str | None = None + kind: str = "union" + strategy: str = "tagged-variant" + + +ValuePlate = EnumPlate | NumberPlate | StringPlate | UnionPlate + + +# --------------------------------------------------------------------------- # +# Complex plates (mirror the IR's 4 complex shapes) +# --------------------------------------------------------------------------- # + + +@dataclass +class Member: + """One field of a complex plate: an attribute, a child element, or the + text value body of a `value`-shaped type. `cardinality` (required / + optional / vector) plus the target's type map fully determine the concrete + wrapper spelling (by-value, optional, collection); the template prints it. + + `default`/`fixed` keep the wire literal. When that literal names a variant + of the member's enum type, `default_variant` carries the variant's target + identifier so an emitter writes the enum member, not a raw string.""" + + name: Name + ident: str + kind: str # "attribute" | "element" | "value" + type_ref: PlateRef + cardinality: str # "required" | "optional" | "vector" + default: str | None = None + fixed: str | None = None + default_variant: str | None = None + doc: str | None = None + + +@dataclass +class ComplexPlate: + """One complex type, projected. `members` is the flat, deduped, ordered + field list a code target emits (attributes, then the value body, then + child elements in document order); `content` is the resolved + sequence/choice particle tree (groups spliced; IR node types) for a + target that cares about order and choice structure. + + A derived plate exposes both the `base` edge (for a target with + inheritance) and `all_members` (the base chain merged, for one without); + `strategy` says which one this target uses.""" + + name: Name + ident: str + shape: str # "value" | "composite" | "empty" | "derived" + strategy: str # value-class | composite-class | flag | attrs-class | inherit | flatten + members: list[Member] = field(default_factory=list) + content: ir.Particle | None = None + base: PlateRef | None = None + all_members: list[Member] | None = None + presence_only: bool = False + doc: str | None = None + file: str | None = None + kind: str = "complex" + + +# --------------------------------------------------------------------------- # +# The whole projected target +# --------------------------------------------------------------------------- # + + +@dataclass +class FileSpec: + """One output file when partitioning: its stem (extension is the + template's business), the wire names of the types assigned to it (in emit + order), and the stems it must include/import (dependencies' files, + deduped, self-excluded, sorted).""" + + file: str + types: list[str] = field(default_factory=list) + includes: list[str] = field(default_factory=list) + + +@dataclass +class Plates: + """The complete projection of one target: every plate, in the IR's + deps-first order (value types never reference complex types, so + `value_types + complex_types` is a valid total emit order).""" + + source: str # provenance: the XSD stem the IR was lowered from + target: TargetInfo + value_types: list[ValuePlate] = field(default_factory=list) + complex_types: list[ComplexPlate] = field(default_factory=list) + roots: list[PlateRef] = field(default_factory=list) + files: list[FileSpec] | None = None # None when partition == "single" + type_map: dict[str, str] = field(default_factory=dict) + + def __post_init__(self): + # Random-access index for templates; a plain attribute (not a + # dataclass field) so JSON dumps stay free of the duplication. + self._index = {p.name.wire: p for p in self.value_types} + self._index.update({p.name.wire: p for p in self.complex_types}) + + def plate(self, wire: str) -> ValuePlate | ComplexPlate: + """Look up any plate by its wire type name.""" + return self._index[wire] + + def has_plate(self, wire: str) -> bool: + return wire in self._index diff --git a/gen/plates/names.py b/gen/plates/names.py new file mode 100644 index 000000000..52ed9b006 --- /dev/null +++ b/gen/plates/names.py @@ -0,0 +1,132 @@ +"""Name expansion: tokenize wire names, recase per convention, sanitize. + +A fundamental (wire) name is split into an ordered word vector of lowercase +words, then recased by each registered convention. The wire form is preserved +untouched alongside the casings -- tokenization feeds only the cased +identifiers, never serialization (design R3). + +Conventions live in a registry keyed by name, so adding one later is +registering one function; every Name simply grows a key (design R1). +""" + +from __future__ import annotations + +from gen.plates.model import Name + +# Word separators, split on and consumed. Hyphen covers ordinary kebab names; +# dot covers sound ids like `brass.alphorn`; whitespace covers space-separated +# enum values like `up down`; colon covers external refs like `xml:lang`. +_SEPARATORS = set("-._: \t\n\r\v\f") + +# Words uppercased whole by capitalizing conventions (Pascal, non-leading +# camel). Config-extensible via [naming] acronyms. +DEFAULT_ACRONYMS = ("midi", "id", "xml", "css", "smufl", "uri", "url") + +# Fallback word vector for wire names that tokenize to nothing (the empty enum +# value of positive-integer-or-empty and a few *-value enums). The wire form +# stays ""; only the identifier gets a name. Config: [naming] empty-value-word. +DEFAULT_EMPTY_WORD = "empty" + + +def tokenize(wire: str, empty_word: str = DEFAULT_EMPTY_WORD) -> tuple[str, ...]: + """Split a wire name into its canonical lowercase word vector.""" + tokens: list[str] = [] + current: list[str] = [] + for ch in wire: + if ch in _SEPARATORS: + if current: + tokens.append("".join(current)) + current = [] + else: + current.append(ch) + if current: + tokens.append("".join(current)) + + words: list[str] = [] + for token in tokens: + words.extend(w.lower() for w in _split_case_transitions(token)) + return tuple(words) if words else (empty_word,) + + +def _split_case_transitions(token: str) -> list[str]: + """Split an already-mixed-case token at a lower-to-upper boundary + (`fooBar` -> foo, Bar) and at an acronym boundary, where an uppercase run + is followed by uppercase+lowercase (`MIDIChannel` -> MIDI, Channel): the + last capital of the run begins the next word. Letter-digit boundaries do + not split; digits ride with their adjacent letters.""" + if not token: + return [] + starts = [0] + for i in range(1, len(token)): + prev, cur = token[i - 1], token[i] + if cur.isupper() and prev.islower(): + starts.append(i) + elif ( + cur.isupper() + and prev.isupper() + and i + 1 < len(token) + and token[i + 1].islower() + ): + starts.append(i) + return [token[a:b] for a, b in zip(starts, starts[1:] + [len(token)])] + + +def _capitalize(word: str, acronyms: frozenset[str]) -> str: + if word in acronyms: + return word.upper() + if word and word[0].isalpha(): + return word[0].upper() + word[1:] + return word # digit-led words like `1024th` stay lowercase + + +# Each convention maps (word vector, acronym set) -> identifier string. The +# camelCase leading word is always fully lowercased, so a leading acronym +# yields `midiChannel`, never `MIDIChannel`. snake/kebab/screaming are +# case-uniform and ignore the acronym set. +CONVENTIONS = { + "pascal": lambda ws, ac: "".join(_capitalize(w, ac) for w in ws), + "camel": lambda ws, ac: ws[0] + "".join(_capitalize(w, ac) for w in ws[1:]), + "snake": lambda ws, ac: "_".join(ws), + "kebab": lambda ws, ac: "-".join(ws), + "screaming": lambda ws, ac: "_".join(w.upper() for w in ws), +} + + +def sanitize_identifier(ident: str, reserved: frozenset[str], invalid_prefix: str = "_") -> str: + """Make a recased identifier legal for a code target: non-identifier + characters become underscores, a leading digit or empty result gets the + configured prefix, and reserved words get a trailing underscore (the + `suffix-underscore` policy). The pre-sanitized casing stays available on + the Name; collision detection runs on the sanitized result.""" + out = "".join(ch if ch.isalnum() or ch == "_" else "_" for ch in ident) + if not out or out[0].isdigit(): + out = invalid_prefix + out + if out in reserved: + out += "_" + return out + + +class NameFactory: + """Builds Name bundles: tokenize once, expand every registered convention, + honoring a fundamental rename (re-expands all casings from the new root) + and per-convention overrides (pin one flavor, leave the rest expanded).""" + + def __init__(self, acronyms=DEFAULT_ACRONYMS, empty_word: str = DEFAULT_EMPTY_WORD): + self.acronyms = frozenset(acronyms) + self.empty_word = empty_word + + def make( + self, + wire: str, + fundamental: str | None = None, + overrides: dict[str, str] | None = None, + pluralize: bool = False, + ) -> Name: + words = tokenize(fundamental if fundamental is not None else wire, self.empty_word) + if pluralize: + words = words[:-1] + (words[-1] + "s",) + cased = {conv: fn(words, self.acronyms) for conv, fn in CONVENTIONS.items()} + if overrides: + for conv, value in overrides.items(): + cased[conv] = value + return Name(wire=wire, words=words, cased=cased) diff --git a/gen/test/c/config.toml b/gen/test/c/config.toml index 5a3804c1d..e5b0d8a0e 100644 --- a/gen/test/c/config.toml +++ b/gen/test/c/config.toml @@ -16,3 +16,17 @@ dir = "mx" # Folds the standard instrument-sound identifiers into the IR as a sound-id # enum unioned with an open string. Comment out to disable. xml = "../../../docs/sounds-3.1-8bbe8e5.xml" + +[target] +language = "c" +prefix = "Mx" # C has no namespaces; every type is prefixed +inheritance = false # and no inheritance; derived types flatten the base chain + +[naming] +extends = "../../naming.base.toml" # schema-forced renames shared by all targets +field-convention = "snake" +variant-convention = "screaming" + +[layout] +partition = "per-type" +file-prefix = "mx_" # generated files: mx_.h / mx_.c diff --git a/gen/test/go/config.toml b/gen/test/go/config.toml index a21ce720d..037b99a5d 100644 --- a/gen/test/go/config.toml +++ b/gen/test/go/config.toml @@ -12,3 +12,17 @@ xsd = "../../../docs/musicxml-3.1-8bbe8e5.xsd" dir = "mx" # No [sounds] section: instrument-sound stays a plain string for this target. + +[target] +language = "go" +namespace = "mx" # the Go package name + +[naming] +extends = "../../naming.base.toml" # schema-forced renames shared by all targets +# Exported Go identifiers: types, fields, and enum constants are all Pascal. +field-convention = "pascal" +variant-convention = "pascal" + +[layout] +# One generated .go file per type; same-package files need no import graph. +partition = "per-type" diff --git a/gen/tests/test_plates.py b/gen/tests/test_plates.py new file mode 100644 index 000000000..5546ff8bf --- /dev/null +++ b/gen/tests/test_plates.py @@ -0,0 +1,417 @@ +"""Tests for the Plates projection (gen.plates) and its config surface. + +Covers the four pillars of the design (docs/ai/design/plates.md): + + - the name-convention model: tokenizer, recasing, acronyms, sanitization + (the worked conversion table from design section 5.3 is asserted row by + row); + - the override system: both tiers, the addressing scheme, precedence, and + fail-loud validation of stale keys; + - collision detection across the projected scopes; + - the projection itself, built against the real target configs so the + shipped configurations stay green. + +Run with: python3 -m unittest gen.tests.test_plates +""" + +from __future__ import annotations + +import tempfile +import unittest +from pathlib import Path + +from gen import config as cfg +from gen.config import Config, ConfigError, RenameEntry +from gen.ir import model as ir +from gen.ir.build import build_ir +from gen.ir.dump import to_json +from gen.plates import PlatesError, build_plates +from gen.plates.names import NameFactory, sanitize_identifier, tokenize +from gen.xsd import parse + +REPO = Path(__file__).resolve().parents[2] +GO_CONFIG = REPO / "gen" / "test" / "go" / "config.toml" +C_CONFIG = REPO / "gen" / "test" / "c" / "config.toml" +CPP_CONFIG = REPO / "gen" / "cpp" / "config.toml" + + +def build_for(config_path: Path): + config = cfg.load(config_path) + m = build_ir(parse(config.xsd), source=config.xsd.stem) + if config.sounds_xml is not None: + from gen.ir.sounds import patch_sounds, read_sound_ids + + patch_sounds(m, read_sound_ids(config.sounds_xml)) + return build_plates(m, config), config + + +class TokenizerAndConventions(unittest.TestCase): + """The worked conversion table from design section 5.3.""" + + TABLE = [ + # wire, words, pascal, camel, snake, kebab, screaming + ("note", ("note",), "Note", "note", "note", "note", "NOTE"), + ("default-x", ("default", "x"), "DefaultX", "defaultX", "default_x", + "default-x", "DEFAULT_X"), + ("clef-octave-change", ("clef", "octave", "change"), "ClefOctaveChange", + "clefOctaveChange", "clef_octave_change", "clef-octave-change", + "CLEF_OCTAVE_CHANGE"), + ("midi-channel", ("midi", "channel"), "MIDIChannel", "midiChannel", + "midi_channel", "midi-channel", "MIDI_CHANNEL"), + ("optional-unique-id", ("optional", "unique", "id"), "OptionalUniqueID", + "optionalUniqueID", "optional_unique_id", "optional-unique-id", + "OPTIONAL_UNIQUE_ID"), + ("brass.alphorn", ("brass", "alphorn"), "BrassAlphorn", "brassAlphorn", + "brass_alphorn", "brass-alphorn", "BRASS_ALPHORN"), + ("up down", ("up", "down"), "UpDown", "upDown", "up_down", "up-down", + "UP_DOWN"), + ("1024th", ("1024th",), "1024th", "1024th", "1024th", "1024th", "1024TH"), + ("", ("empty",), "Empty", "empty", "empty", "empty", "EMPTY"), + ] + + def test_worked_table(self): + factory = NameFactory() + for wire, words, pascal, camel, snake, kebab, screaming in self.TABLE: + with self.subTest(wire=wire): + name = factory.make(wire) + self.assertEqual(name.words, words) + self.assertEqual(name.pascal, pascal) + self.assertEqual(name.camel, camel) + self.assertEqual(name.snake, snake) + self.assertEqual(name.kebab, kebab) + self.assertEqual(name.screaming, screaming) + + def test_case_transition_splits(self): + self.assertEqual(tokenize("fooBar"), ("foo", "bar")) + self.assertEqual(tokenize("MIDIChannel"), ("midi", "channel")) + self.assertEqual(tokenize("xml:lang"), ("xml", "lang")) + self.assertEqual(tokenize("midi-128"), ("midi", "128")) + + def test_leading_camel_acronym_is_lowercased(self): + # The camelCase leading word is always fully lowercased, even when it + # is in the acronym set. + name = NameFactory().make("midi-channel") + self.assertEqual(name.camel, "midiChannel") + + def test_pluralize_appends_to_last_word(self): + name = NameFactory().make("midi-channel", pluralize=True) + self.assertEqual(name.pascal, "MIDIChannels") + self.assertEqual(name.wire, "midi-channel") + + +class Sanitizer(unittest.TestCase): + def test_reserved_word_gets_suffix(self): + self.assertEqual(sanitize_identifier("class", frozenset({"class"})), "class_") + + def test_leading_digit_gets_prefix(self): + self.assertEqual(sanitize_identifier("1024th", frozenset()), "_1024th") + + def test_empty_gets_prefix(self): + self.assertEqual(sanitize_identifier("", frozenset()), "_") + + def test_non_identifier_chars_become_underscores(self): + self.assertEqual(sanitize_identifier("a-b", frozenset()), "a_b") + + def test_clean_identifier_unchanged(self): + self.assertEqual(sanitize_identifier("Note", frozenset()), "Note") + + +def tiny_ir() -> ir.Ir: + """A minimal IR exercising renames, defaults, and collisions without + parsing a schema.""" + up_down = ir.EnumType("up-down", "token", ["up", "down"]) + pitch = ir.ComplexType("pitch", "empty", presence_only=True, deps=[]) + note = ir.ComplexType( + "note", + "composite", + attributes=[ + ir.Attr("type", ir.Ref("up-down", "value"), default="up"), + ir.Attr("default-x", ir.Ref("decimal", "primitive")), + ], + content=ir.Sequence( + [ + ir.Element("pitch", ir.Ref("pitch", "complex"), "required"), + ir.Element("tie", ir.Ref("up-down", "value"), "vector", 0, ir.UNBOUNDED), + ] + ), + deps=["pitch"], + ) + return ir.Ir( + source="tiny", + builtins={}, + value_types=[up_down], + groups=[], + attribute_groups=[], + complex_types=[pitch, note], + roots=[ir.Root("note", "note")], + dropped_dead=[], + stats={}, + ) + + +class Overrides(unittest.TestCase): + def test_fundamental_rename_reexpands_every_convention(self): + config = Config() + config.renames.types["note"] = RenameEntry(fundamental="music-note") + plates = build_plates(tiny_ir(), config) + plate = plates.plate("note") + self.assertEqual(plate.name.wire, "note") # wire form untouched + self.assertEqual(plate.name.pascal, "MusicNote") + self.assertEqual(plate.name.snake, "music_note") + + def test_per_convention_override_pins_one_flavor(self): + config = Config() + config.renames.types["note"] = RenameEntry(cased={"pascal": "MusicNote"}) + plates = build_plates(tiny_ir(), config) + plate = plates.plate("note") + self.assertEqual(plate.name.pascal, "MusicNote") + self.assertEqual(plate.name.snake, "note") # others still auto-expand + + def test_scoped_attribute_rename_beats_global(self): + config = Config() + config.renames.attributes["type"] = RenameEntry(fundamental="global-kind") + config.renames.scoped_attributes[("note", "type")] = RenameEntry( + fundamental="note-kind" + ) + plates = build_plates(tiny_ir(), config) + members = {m.name.wire: m for m in plates.plate("note").members} + self.assertEqual(members["type"].name.snake, "note_kind") + + def test_enum_value_rename_is_scoped_to_its_enum(self): + config = Config() + config.renames.enum_values[("up-down", "up")] = RenameEntry(fundamental="upward") + plates = build_plates(tiny_ir(), config) + enum = plates.plate("up-down") + self.assertEqual(enum.variants[0].wire, "up") + self.assertEqual(enum.variants[0].ident, "Upward") + self.assertEqual(enum.variants[1].ident, "Down") + + def test_stale_rename_keys_fail_loud(self): + config = Config() + config.renames.types["no-such-type"] = RenameEntry(fundamental="x") + config.renames.elements["no-such-element"] = RenameEntry(fundamental="x") + config.renames.attributes["no-such-attr"] = RenameEntry(fundamental="x") + config.renames.scoped_attributes[("note", "no-such")] = RenameEntry(fundamental="x") + config.renames.enum_values[("up-down", "sideways")] = RenameEntry(fundamental="x") + with self.assertRaises(PlatesError) as caught: + build_plates(tiny_ir(), config) + text = "\n".join(caught.exception.errors) + for key in ("no-such-type", "no-such-element", "no-such-attr", + "note.no-such", "sideways"): + self.assertIn(key, text) + + +class Projection(unittest.TestCase): + def test_default_naming_an_enum_variant_resolves(self): + plates = build_plates(tiny_ir(), Config()) + members = {m.name.wire: m for m in plates.plate("note").members} + self.assertEqual(members["type"].default, "up") + self.assertEqual(members["type"].default_variant, "Up") + self.assertIsNone(members["default-x"].default_variant) + + def test_member_order_attributes_then_elements(self): + plates = build_plates(tiny_ir(), Config()) + kinds = [m.kind for m in plates.plate("note").members] + self.assertEqual(kinds, ["attribute", "attribute", "element", "element"]) + + def test_presence_only_empty_projects_to_flag(self): + plates = build_plates(tiny_ir(), Config()) + self.assertEqual(plates.plate("pitch").strategy, "flag") + + def test_vector_cardinality_survives(self): + plates = build_plates(tiny_ir(), Config()) + members = {m.name.wire: m for m in plates.plate("note").members} + self.assertEqual(members["tie"].cardinality, "vector") + self.assertEqual(members["pitch"].cardinality, "required") + + def test_collision_detection_reports_induced_collisions(self): + config = Config() + # Force the enum's two variants onto one identifier. + config.renames.enum_values[("up-down", "down")] = RenameEntry(fundamental="up") + with self.assertRaises(PlatesError) as caught: + build_plates(tiny_ir(), config) + self.assertIn("variant identifier collision", caught.exception.errors[0]) + + def test_unmapped_language_passes_primitives_through(self): + plates = build_plates(tiny_ir(), Config()) + members = {m.name.wire: m for m in plates.plate("note").members} + self.assertEqual(members["default-x"].type_ref.ident, "decimal") + + +class RealTargets(unittest.TestCase): + """The shipped configs must project cleanly, deterministically, and with + the spot-checkable facts the emitters will lean on.""" + + @classmethod + def setUpClass(cls): + cls.go, cls.go_config = build_for(GO_CONFIG) + cls.c, cls.c_config = build_for(C_CONFIG) + cls.cpp, cls.cpp_config = build_for(CPP_CONFIG) + + def test_determinism(self): + again, _ = build_for(GO_CONFIG) + self.assertEqual(to_json(self.go), to_json(again)) + + def test_go_idents_are_exported_pascal(self): + plate = self.go.plate("midi-instrument") + self.assertEqual(plate.ident, "MIDIInstrument") + self.assertEqual(plate.file, "midi_instrument") + + def test_c_idents_carry_prefix(self): + plate = self.c.plate("midi-instrument") + self.assertEqual(plate.ident, "MxMIDIInstrument") + self.assertEqual(plate.file, "mx_midi_instrument") + + def test_shared_base_resolves_barline_collision(self): + for plates in (self.go, self.c, self.cpp): + members = plates.plate("barline").members + idents = [m.ident for m in members] + self.assertEqual(len(idents), len(set(idents))) + # Both the attribute and the element keep the wire name "segno"; + # only their identifiers diverge (the shared base renames the + # attribute's fundamental root). + kinds = {m.kind for m in members if m.name.wire == "segno"} + self.assertEqual(kinds, {"attribute", "element"}) + + def test_empty_enum_value_gets_fallback_identifier(self): + enum = self.go.plate("breath-mark-value") + empty = next(v for v in enum.variants if v.wire == "") + self.assertEqual(empty.ident, "Empty") + + def test_digit_led_variant_sanitized_but_casing_kept(self): + enum = self.go.plate("note-type-value") + v1024 = next(v for v in enum.variants if v.wire == "1024th") + self.assertEqual(v1024.name.pascal, "1024th") # the ideal is recorded + self.assertEqual(v1024.ident, "_1024th") # the sanitized result + + def test_default_variant_resolution_in_real_schema(self): + plate = self.go.plate("strong-accent") + member = next(m for m in plate.members if m.name.wire == "type") + self.assertEqual(member.default, "up") + self.assertEqual(member.default_variant, "Up") + + def test_sound_id_fold_present_only_with_sounds(self): + self.assertTrue(self.c.has_plate("sound-id")) + self.assertFalse(self.go.has_plate("sound-id")) + union = self.c.plate("instrument-sound") + kinds = [(m.ref.category if m.ref else "literals") for m in union.members] + self.assertEqual(kinds, ["value", "primitive"]) # enum + open string + + def test_files_unique_and_self_excluded(self): + for plates in (self.go, self.c, self.cpp): + stems = [f.file for f in plates.files] + self.assertEqual(len(stems), len(set(stems))) + for spec in plates.files: + self.assertNotIn(spec.file, spec.includes) + + def test_derived_plates_expose_both_views(self): + derived = [p for p in self.c.complex_types if p.shape == "derived"] + self.assertTrue(derived) + for plate in derived: + self.assertEqual(plate.strategy, "flatten") # C: inheritance = false + self.assertIsNotNone(plate.base) + self.assertIsNotNone(plate.all_members) + self.assertGreaterEqual(len(plate.all_members), len(plate.members)) + go_derived = [p for p in self.go.complex_types if p.shape == "derived"] + self.assertTrue(all(p.strategy == "inherit" for p in go_derived)) + + def test_neutral_core_keeps_wire_facts(self): + # What a JSON Schema target would read: wire names, enum literals, + # the resolved content tree -- all present regardless of binding. + enum = self.go.plate("step") + self.assertIn("A", [v.wire for v in enum.variants]) + note = self.go.plate("note") + self.assertIsNotNone(note.content) + self.assertEqual(note.name.wire, "note") + + +class ConfigParsing(unittest.TestCase): + def _load(self, text: str) -> Config: + with tempfile.TemporaryDirectory() as d: + path = Path(d) / "config.toml" + path.write_text(text) + return cfg.load(path) + + def test_rename_tables_parse(self): + config = self._load( + """ + [rename.type.attributes] + fundamental = "properties" + [rename.type.note] + pascal = "MusicNote" + [rename.attribute] + default-x = "origin-x" + [rename.attribute.note] + type = "kind" + [rename.enum-value.up-down] + "up" = "upward" + "" = "none" + """ + ) + self.assertEqual(config.renames.types["attributes"].fundamental, "properties") + self.assertEqual(config.renames.types["note"].cased, {"pascal": "MusicNote"}) + self.assertEqual(config.renames.attributes["default-x"].fundamental, "origin-x") + self.assertEqual( + config.renames.scoped_attributes[("note", "type")].fundamental, "kind" + ) + self.assertEqual( + config.renames.enum_values[("up-down", "")].fundamental, "none" + ) + + def test_unknown_keys_fail(self): + with self.assertRaises(ConfigError): + self._load("[naming]\nconventions = ['pascal']\n") + with self.assertRaises(ConfigError): + self._load("[rename.kind.x]\nfundamental = 'y'\n") + with self.assertRaises(ConfigError): + self._load("[rename.type.note]\npasta = 'MusicNote'\n") + + def test_unsupported_values_fail(self): + with self.assertRaises(ConfigError): + self._load("[layout]\npartition = 'sharded'\n") + with self.assertRaises(ConfigError): + self._load("[reserved]\npolicy = 'rename'\n") + with self.assertRaises(ConfigError): + self._load("[naming]\ntype-convention = 'dot'\n") + + def test_extends_merges_with_target_precedence(self): + with tempfile.TemporaryDirectory() as d: + base = Path(d) / "base.toml" + base.write_text( + """ + [naming] + acronyms = ["midi"] + [rename.type.note] + fundamental = "base-note" + [rename.type.pitch] + fundamental = "base-pitch" + [rename.enum-value.up-down] + "up" = "base-up" + "down" = "base-down" + """ + ) + target = Path(d) / "config.toml" + target.write_text( + """ + [naming] + extends = "base.toml" + [rename.type.note] + fundamental = "target-note" + [rename.enum-value.up-down] + "up" = "target-up" + """ + ) + config = cfg.load(target) + self.assertEqual(config.naming.acronyms, ("midi",)) + self.assertEqual(config.renames.types["note"].fundamental, "target-note") + self.assertEqual(config.renames.types["pitch"].fundamental, "base-pitch") + self.assertEqual( + config.renames.enum_values[("up-down", "up")].fundamental, "target-up" + ) + self.assertEqual( + config.renames.enum_values[("up-down", "down")].fundamental, "base-down" + ) + + +if __name__ == "__main__": + unittest.main() From 7dc120efefb0be6ee6631e00c5e84e09b1971296 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 16:14:04 +0000 Subject: [PATCH 17/45] gen: incorporate plates review feedback (code review + architecture review) Two independent reviews of 49a7c84 (one correctness-focused, one architectural). The accepted findings, by theme: Correctness (the critical find): - Resolver.flat_elements now owns the effective-cardinality view, with a co-occurrence-aware duplicate merge: occurrences of one element name in different branches of one choice are exclusive (optional), anything else can co-occur in a single instance and must be a vector. The old rule demoted every duplicate to optional, which mis-projected metronome's beat-unit (legally twice in one instance; the corpus exercises this). The flattening and the base-chain merge (all_flat_elements, base_chain) moved from the projection into gen/ir/resolve.py where schema reasoning lives once; never-occurring particles (max=0) are skipped. Naming and the collision gate: - Variant.ident is now the FINAL emitted constant. Constant scoping is a language fact (gen/plates/languages.py): bare for C++ enum class, composed for Go/C flat namespaces (NoteTypeValue1024th, MX_NOTE_TYPE_VALUE_1024TH); the projection composes, sanitizes, and the collision gate checks the namespace the target actually has (type idents and constants mutually, for composed scopes). Templates print idents verbatim, restoring 'templates do no naming'. - gen/names.py is now a leaf vocabulary module (tokenizer, conventions, Name, sanitizer) below both config and plates, removing the latent config->plates import cycle and the TYPE_CHECKING workaround. Acronym sets are case-normalized. Config tightening: - Unknown top-level sections rejected (a [renames] typo used to silently drop every rename); [input]/[output]/[sounds] keys checked too. - Removed dead surface: [layout] include-style, [reserved] policy, [naming] empty-value-word. The cpp decimal=Decimal mapping moved from language seeds to gen/cpp/config.toml ([types]). - extends hardened: no chained bases, naming/rename sections only, and a base/target scope-vs-entry shape disagreement is an error instead of a silent wholesale replacement. String-list keys reject bare strings; [types] keys must name real IR primitives; empty rename entries and non-table rename kinds fail loud. Plates model: - all_members is always built for derived plates so the gate covers the merged chain under either strategy; ComplexPlate.member(wire, kind) joins content occurrences to fields; the content tree's IR particle types are declared part of the neutral contract; Plates.type_map dropped (PlateRef.ident and target_type are the published spellings); UnionPlateMember.name added so primitive members have a field name; complete C++ reserved-word list; PlateRef primitive-wire docstring. - build_for_config in gen/plates is the one pipeline shared by CLI and tests. Documented as review-round deltas in plates.md section 11, including the always-on gate (a plain plates dump fails loud too; --check is the quiet CI entry). New tests: a particle-tree cardinality suite (exclusive vs co-occurring duplicates, wrappers, never-occurring particles), the real metronome assertion across schemas, composed-scope constants and their flat-namespace collisions, element-rename application, case-insensitive stem collisions, derived all_members under inherit, and the config rejection paths. --- docs/ai/design/plates.md | 42 +++++++++ gen/README.md | 17 ++-- gen/config.py | 73 ++++++++++------ gen/cpp/config.toml | 6 ++ gen/ir/build.py | 5 ++ gen/ir/resolve.py | 115 +++++++++++++++++++++++-- gen/{plates => }/names.py | 72 +++++++++++++--- gen/plates/__init__.py | 21 ++++- gen/plates/build.py | 167 +++++++++++++++--------------------- gen/plates/check.py | 53 +++++++++--- gen/plates/languages.py | 42 ++++++--- gen/plates/model.py | 83 +++++++++--------- gen/tests/test_ir.py | 96 +++++++++++++++++++++ gen/tests/test_plates.py | 173 ++++++++++++++++++++++++++++++++++---- 14 files changed, 732 insertions(+), 233 deletions(-) rename gen/{plates => }/names.py (69%) diff --git a/docs/ai/design/plates.md b/docs/ai/design/plates.md index 76fb326a4..06f5ce934 100644 --- a/docs/ai/design/plates.md +++ b/docs/ai/design/plates.md @@ -673,3 +673,45 @@ these deliberate deltas: - **`xs:ID`/`xs:IDREF` canonicalized in the IR.** They surfaced as accidental ninth and tenth primitives via the builtin fallback; the IR now folds them to `token`, keeping the primitive set the eight this document assumes. + +Revised after the first implementation review (one code review, one architecture review): + +- **`Variant.ident` is the final emitted constant.** How enum constants are scoped is a language + fact seeded in `gen/plates/languages.py`: `bare` where the language scopes them inside the type + (C++ `enum class` -> `_1024th`), `composed` where they share one flat namespace (Go + `NoteTypeValue1024th`, C `MX_NOTE_TYPE_VALUE_1024TH`). The projection composes (prefix + type + casing + variant casing, joined in the variant convention's style), sanitizes, and stores the + result; templates print it verbatim. The collision gate runs in the namespace the target actually + has: for `composed`, type identifiers and all constants are checked mutually; for `bare`, + per-enum. (Originally the composition was left to templates, which both broke "templates do no + naming" and blinded the gate to the real namespace.) +- **Effective cardinality lives in the Resolver.** `Resolver.flat_elements` / + `all_flat_elements` / `base_chain` (gen/ir/resolve.py) own the flattened field view: repeated + wrappers make vectors, choices demote to optional, and duplicate occurrences of one name merge by + co-occurrence analysis -- occurrences in different branches of one choice are exclusive + (optional), anything else can co-occur in a single instance and must be a vector. The review + caught a real bug here: `metronome`'s `beat-unit` appears on a branch's spine and again inside + that branch's inner choice, so it must merge to vector, not optional (the corpus exercises this). + Schema reasoning of this kind belongs in the resolution layer, not the projection. +- **The naming vocabulary is a leaf module.** Tokenizer, convention registry, `Name`, and the + sanitizer live in `gen/names.py`, below both `gen/config.py` (which validates convention names) + and `gen/plates/` -- removing a latent config -> plates import cycle. +- **Config surface cuts.** `[layout] include-style` (consumed by nothing), `[reserved] policy` (one + legal value), and `[naming] empty-value-word` (strictly weaker than a scoped enum-value rename) + were removed. Unknown top-level sections are rejected, as are unknown keys in + `[input]`/`[output]`/`[sounds]`. `extends` is hardened: a base may not chain, may hold only + `[naming]`/`[rename]`, and a scope/entry shape disagreement between base and target is an error. + String-list keys reject bare strings. `[types]` keys must name real IR primitives. The cpp + `decimal = "Decimal"` mapping moved from the language seeds to `gen/cpp/config.toml` (it is an + mx::core decision, not a C++ fact). +- **`Plates.type_map` dropped from the public object.** Members and value plates already carry + their resolved spellings (`PlateRef.ident`, `target_type`); publishing the raw map a second way + was drift surface. +- **`all_members` is always built for derived plates**, so the collision gate covers the merged + chain under both the inherit and flatten strategies. +- **The build always gates.** `build_plates` runs validation and collision detection + unconditionally, so a plain `plates` dump fails loud too; `--check` is the quiet CI entry point, + not the only gate. +- **`UnionPlateMember.name` added.** A union member referencing a primitive (`decimal`) has no + plate to take a field name from; the member carries its own name bundle so templates invent + nothing. diff --git a/gen/README.md b/gen/README.md index 6233eec04..f8a6cd98c 100644 --- a/gen/README.md +++ b/gen/README.md @@ -31,6 +31,7 @@ XSD file --parse--> XSD model --lower--> IR --project--> Plates --emit--> gen/ __main__.py CLI: analyze | ir | plates | config.py typed config.toml loader (inputs, output, plates sections) + names.py naming vocabulary: tokenizer, convention registry, sanitizer naming.base.toml schema-forced renames shared by all targets xsd/ model.py dataclasses mirroring the XSD subset MusicXML uses @@ -39,11 +40,10 @@ gen/ ir/ model.py the IR dataclasses build.py lowering from the XSD model to the IR - resolve.py collapsed views (group + attribute-group resolution) for emitters + resolve.py collapsed views (groups, attributes, flat element fields) for emitters dump.py IR to JSON plates/ model.py the plate dataclasses (neutral core + target binding) - names.py tokenizer, convention registry, sanitizer languages.py per-language defaults (type maps, reserved words, doc styles) build.py the projection: IR + config -> Plates check.py post-projection collision detection @@ -223,7 +223,7 @@ Terms used inside the lowered types, not in `stats`. ### Resolution layer The IR data model preserves the schema's named structure; `ir/resolve.py` collapses it on demand. -`Resolver.from_ir(ir)` exposes four read-only accessors over a complex type, none of which mutate the +`Resolver.from_ir(ir)` exposes read-only accessors over a complex type, none of which mutate the IR: - `attributes(ct)` -- the type's own attributes with its `attribute_groups` expanded inline, in @@ -234,8 +234,15 @@ IR: elements/sequences/choices with no `group` nodes left. Nesting and all min/max bounds are preserved. - `elements(ct)` -- every element occurrence in the resolved content, in document order, flattened - across sequences/choices/groups (drops the choice/sequence grouping; use `content` when that - matters). + across sequences/choices/groups (drops the choice/sequence grouping and keeps local cardinality; + use `content` when structure matters and `flat_elements` for a field view). +- `flat_elements(ct)` -- each distinct element name with its *effective* cardinality for a flat + one-field-per-name view: repeated wrappers make vectors, choices demote to optional, and + duplicate occurrences of one name merge by co-occurrence analysis (occurrences in different + branches of one choice are exclusive -> optional; anything else can co-occur in one instance -> + vector, e.g. `metronome`'s `beat-unit`). +- `all_flat_elements(ct)` / `base_chain(ct)` -- the flattened view merged across the derivation + chain (base-most first), mirroring `all_attributes`. `python3 -m gen ir --resolve` dumps this view. `build` itself uses the resolver to compute each complex type's `deps`, so the group-walking logic lives in exactly one place rather than once per diff --git a/gen/config.py b/gen/config.py index c557921f7..56d0cb5fa 100644 --- a/gen/config.py +++ b/gen/config.py @@ -19,7 +19,7 @@ from dataclasses import dataclass, field from pathlib import Path -from gen.plates.names import CONVENTIONS +from gen.names import CONVENTIONS # Keys allowed in a rename entry table: a fundamental rename (all casings # re-expand from the new root) or per-convention overrides (pin one flavor). @@ -80,21 +80,18 @@ class NamingSection: variant_convention: str = "pascal" file_convention: str = "snake" field_prefix: str = "" - empty_value_word: str = "empty" pluralize_vectors: bool = False @dataclass class ReservedSection: words: tuple[str, ...] = () # extends the language defaults - policy: str = "suffix-underscore" invalid_prefix: str = "_" @dataclass class LayoutSection: partition: str = "single" # "per-type" | "single" ("grouped" reserved) - include_style: str = "quoted" file_prefix: str = "" @@ -128,6 +125,15 @@ def load(config_path) -> Config: with open(path, "rb") as f: data = tomllib.load(f) base = path.parent + _check_keys( + data, + {"input", "output", "sounds", "target", "naming", "reserved", "types", + "layout", "docs", "rename"}, + "top level", + ) + _check_keys(data.get("input", {}), {"xsd"}, "input") + _check_keys(data.get("output", {}), {"dir"}, "output") + _check_keys(data.get("sounds", {}), {"xml"}, "sounds") # A shared naming base (design: [naming] extends) contributes [naming] # keys and [rename.*] entries; the target's own win on any conflict. @@ -194,24 +200,31 @@ def _target(t: dict) -> TargetSection: ) +def _string_list(value, where: str) -> tuple[str, ...]: + """A TOML array of strings. A bare string is rejected rather than being + silently exploded into characters.""" + if not isinstance(value, list) or not all(isinstance(x, str) for x in value): + raise ConfigError(f"[{where}] must be an array of strings") + return tuple(value) + + def _naming(t: dict) -> NamingSection: _check_keys( t, { "extends", "acronyms", "type-convention", "field-convention", "variant-convention", "file-convention", "field-prefix", - "empty-value-word", "pluralize-vectors", + "pluralize-vectors", }, "naming", ) section = NamingSection( - acronyms=tuple(t["acronyms"]) if "acronyms" in t else None, + acronyms=_string_list(t["acronyms"], "naming.acronyms") if "acronyms" in t else None, type_convention=t.get("type-convention", "pascal"), field_convention=t.get("field-convention", "snake"), variant_convention=t.get("variant-convention", "pascal"), file_convention=t.get("file-convention", "snake"), field_prefix=t.get("field-prefix", ""), - empty_value_word=t.get("empty-value-word", "empty"), pluralize_vectors=bool(t.get("pluralize-vectors", False)), ) for key in ("type_convention", "field_convention", "variant_convention", "file_convention"): @@ -225,17 +238,11 @@ def _naming(t: dict) -> NamingSection: def _reserved(t: dict) -> ReservedSection: - _check_keys(t, {"words", "policy", "invalid-prefix"}, "reserved") - section = ReservedSection( - words=tuple(t.get("words", ())), - policy=t.get("policy", "suffix-underscore"), + _check_keys(t, {"words", "invalid-prefix"}, "reserved") + return ReservedSection( + words=_string_list(t["words"], "reserved.words") if "words" in t else (), invalid_prefix=t.get("invalid-prefix", "_"), ) - if section.policy != "suffix-underscore": - raise ConfigError( - f"[reserved] policy = {section.policy!r}: only 'suffix-underscore' is implemented" - ) - return section def _types(t: dict) -> dict[str, str]: @@ -246,10 +253,9 @@ def _types(t: dict) -> dict[str, str]: def _layout(t: dict) -> LayoutSection: - _check_keys(t, {"partition", "include-style", "file-prefix"}, "layout") + _check_keys(t, {"partition", "file-prefix"}, "layout") section = LayoutSection( partition=t.get("partition", "single"), - include_style=t.get("include-style", "quoted"), file_prefix=t.get("file-prefix", ""), ) if section.partition not in ("per-type", "single", "grouped"): @@ -286,6 +292,8 @@ def _entry(value, where: str) -> RenameEntry: bad = [k for k, v in value.items() if not isinstance(v, str)] if bad: raise ConfigError(f"[{where}] {bad[0]} must be a string") + if not value: + raise ConfigError(f"[{where}] is empty: set fundamental or a convention") return RenameEntry( fundamental=value.get("fundamental"), cased={k: v for k, v in value.items() if k != "fundamental"}, @@ -308,6 +316,10 @@ def _renames(t: dict) -> Renames: f"shared fragments; no current target does" ) + for kind in _RENAME_KINDS: + if kind in t and not isinstance(t[kind], dict): + raise ConfigError(f"[rename.{kind}] must be a table") + r = Renames() for wire, value in t.get("type", {}).items(): r.types[wire] = _entry(value, f"rename.type.{wire}") @@ -343,8 +355,8 @@ def _renames(t: dict) -> Renames: def _apply_extends(data: dict, base_dir: Path) -> dict: """Merge a shared base file under the target's config: the base - contributes [naming] keys and whole [rename] entries; the target's own - win per key/entry. Other sections never come from the base.""" + contributes [naming] keys and [rename] entries; the target's own win per + key/entry. Anything else in the base is an error, as is chaining bases.""" extends = data.get("naming", {}).get("extends") if not extends: return data @@ -353,10 +365,12 @@ def _apply_extends(data: dict, base_dir: Path) -> dict: raise FileNotFoundError(f"naming base not found: {base_path}") with open(base_path, "rb") as f: shared = tomllib.load(f) + _check_keys(shared, {"naming", "rename"}, f"naming base {base_path.name}") + if "extends" in shared.get("naming", {}): + raise ConfigError(f"naming base {base_path.name} may not chain to another base") merged = dict(data) naming = dict(shared.get("naming", {})) - naming.pop("extends", None) # a base may not chain to another base naming.update(data.get("naming", {})) naming.pop("extends", None) merged["naming"] = naming @@ -368,12 +382,17 @@ def _apply_extends(data: dict, base_dir: Path) -> dict: table: dict = {} for key in list(base_table) + [k for k in own_table if k not in base_table]: b, o = base_table.get(key), own_table.get(key) - if ( - isinstance(b, dict) - and isinstance(o, dict) - and not _is_entry_table(b) - and not _is_entry_table(o) - ): + b_scope = isinstance(b, dict) and not _is_entry_table(b) + o_scope = isinstance(o, dict) and not _is_entry_table(o) + if b is not None and o is not None and b_scope != o_scope: + # One side addresses a scope table, the other a single entry: + # a silent wholesale replacement would quietly drop the + # base's renames, so the disagreement is an error. + raise ConfigError( + f"[rename.{kind}.{key}]: the target and its naming base " + f"disagree on whether this is a scope or an entry" + ) + if b_scope and o_scope: # A nested scope (an enum's value table, an owner's attribute # table): merge per inner entry, target winning. table[key] = {**b, **o} diff --git a/gen/cpp/config.toml b/gen/cpp/config.toml index 22bf6eff7..a6ab41876 100644 --- a/gen/cpp/config.toml +++ b/gen/cpp/config.toml @@ -23,5 +23,11 @@ namespace = "mx::core" [naming] extends = "../naming.base.toml" # schema-forced renames shared by all targets +[types] +# mx::core wraps xs:decimal in its own Decimal class; that is this target's +# decision, not a C++ language fact, so it lives here rather than in the +# language defaults. +decimal = "Decimal" + [layout] partition = "per-type" diff --git a/gen/ir/build.py b/gen/ir/build.py index 14990d6e6..80caec677 100644 --- a/gen/ir/build.py +++ b/gen/ir/build.py @@ -45,6 +45,11 @@ _NUMERIC = {"decimal", "integer", "positive_integer", "non_negative_integer"} +# The closed set of canonical IR primitives (the values of the builtins map): +# everything a Ref with category "primitive" can name, and the keys a target +# type map may override. +PRIMITIVES = frozenset(_XS_PRIMITIVE.values()) | frozenset(_EXTERNAL_ATTR.values()) + def _primitive(name: str) -> str: if name in _XS_PRIMITIVE: diff --git a/gen/ir/resolve.py b/gen/ir/resolve.py index b6103c566..674dc7ef1 100644 --- a/gen/ir/resolve.py +++ b/gen/ir/resolve.py @@ -47,14 +47,9 @@ def attributes(self, ct: ir.ComplexType) -> list[ir.Attr]: def all_attributes(self, ct: ir.ComplexType) -> list[ir.Attr]: """attributes() plus the base chain's attributes (base-most first), for the flattened set an emitter needs when the target has no inheritance.""" - chain: list[ir.ComplexType] = [] - cur: ir.ComplexType | None = ct - while cur is not None: - chain.append(cur) - cur = self._complex.get(cur.base) if cur.base else None out: list[ir.Attr] = [] seen: set[str] = set() - for c in reversed(chain): + for c in self.base_chain(ct): self._add_attrs(c.attributes, c.attribute_groups, out, seen, set()) return out @@ -99,7 +94,9 @@ def _inline(self, p: ir.Particle, path: tuple[str, ...]) -> ir.Particle: def elements(self, ct: ir.ComplexType) -> list[ir.Element]: """Every element occurrence in ct's resolved content, in document order, flattened across sequences/choices/groups. Drops the choice/sequence - grouping; use content() when that structure matters.""" + grouping and keeps each occurrence's LOCAL cardinality; use content() + when the structure matters and flat_elements() for the effective, + deduplicated field view an emitter wants.""" out: list[ir.Element] = [] self._collect_elements(self.content(ct), out) return out @@ -111,6 +108,110 @@ def _collect_elements(self, p, out) -> None: elif isinstance(p, ir.Element): out.append(p) + def flat_elements(self, ct: ir.ComplexType) -> list[tuple[ir.Element, str]]: + """Each distinct element name in ct's resolved content, in document + order of first occurrence, with its EFFECTIVE cardinality for a flat + one-field-per-name view: + + - an element under any repeated particle (max != 1) is a vector; + - an element under a choice, or under an optional wrapper, is at + most optional; + - only an element required along a spine of exactly-once sequences + stays required. + + Occurrences of the same name merge by co-occurrence analysis: if two + occurrences sit in different branches of one choice they are mutually + exclusive (at most one per instance: optional), but otherwise both + can appear in a single instance and the merged field must be a vector + (e.g. metronome's beat-unit, which appears on a branch's spine and + again inside that same branch's inner choice).""" + merged: dict[str, int] = {} # name -> index into out + paths: dict[str, list[tuple]] = {} # name -> choice paths seen + out: list[tuple[ir.Element, str]] = [] + rank = {"required": 0, "optional": 1, "vector": 2} + + def exclusive(a: tuple, b: tuple) -> bool: + """True when the two occurrence paths diverge at two different + branches of one choice node, so they can never co-occur.""" + i = 0 + while i < len(a) and i < len(b) and a[i] == b[i]: + i += 1 + return ( + i < len(a) + and i < len(b) + and a[i][0] == b[i][0] # same choice node + and a[i][1] != b[i][1] # different branches + ) + + def walk(node, forced: bool, repeated: bool, path: tuple) -> None: + if node is None: + return + if isinstance(node, ir.Element): + if repeated or node.card == "vector": + card = "vector" + elif forced and node.card == "required": + card = "required" + else: + card = "optional" + if node.name not in merged: + merged[node.name] = len(out) + paths[node.name] = [path] + out.append((node, card)) + return + i = merged[node.name] + prev_el, prev_card = out[i] + if all(exclusive(path, seen) for seen in paths[node.name]): + # Alternative branches: at most one occurs, but none is + # statically guaranteed. + card = max(card, prev_card, key=lambda c: rank[c]) + if card == "required": + card = "optional" + else: + # The occurrences can co-occur in one instance. + card = "vector" + paths[node.name].append(path) + out[i] = (prev_el, card) + return + if node.max == 0: + return # a never-occurring particle contributes nothing + once = node.min >= 1 and node.max == 1 + again = repeated or node.max != 1 + if isinstance(node, ir.Sequence): + for item in node.items: + walk(item, forced and once, again, path) + elif isinstance(node, ir.Choice): + for branch, item in enumerate(node.items): + walk(item, False, again, path + ((id(node), branch),)) + # GroupRef leaves cannot appear: content() spliced them. + + walk(self.content(ct), True, False, ()) + return out + + def all_flat_elements(self, ct: ir.ComplexType) -> list[tuple[ir.Element, str]]: + """flat_elements() merged across the base chain (base-most first, + first occurrence of a name wins), mirroring all_attributes, for the + flattened view a target without inheritance emits.""" + out: list[tuple[ir.Element, str]] = [] + seen: set[str] = set() + for c in self.base_chain(ct): + for element, card in self.flat_elements(c): + if element.name not in seen: + seen.add(element.name) + out.append((element, card)) + return out + + # ----- derivation ------------------------------------------------------ # + + def base_chain(self, ct: ir.ComplexType) -> list[ir.ComplexType]: + """ct's derivation chain, base-most first, ending with ct itself.""" + chain: list[ir.ComplexType] = [] + cur: ir.ComplexType | None = ct + while cur is not None: + chain.append(cur) + cur = self._complex.get(cur.base) if cur.base else None + chain.reverse() + return chain + # ----- dependencies ---------------------------------------------------- # def deps(self, ct: ir.ComplexType) -> set[str]: diff --git a/gen/plates/names.py b/gen/names.py similarity index 69% rename from gen/plates/names.py rename to gen/names.py index 52ed9b006..40b716c80 100644 --- a/gen/plates/names.py +++ b/gen/names.py @@ -7,11 +7,15 @@ Conventions live in a registry keyed by name, so adding one later is registering one function; every Name simply grows a key (design R1). + +This module is deliberately a leaf: it is shared vocabulary for the config +loader (which validates convention names and rename-entry keys) and for the +Plates projection, so it sits below both and imports neither. """ from __future__ import annotations -from gen.plates.model import Name +from dataclasses import dataclass # Word separators, split on and consumed. Hyphen covers ordinary kebab names; # dot covers sound ids like `brass.alphorn`; whitespace covers space-separated @@ -24,11 +28,44 @@ # Fallback word vector for wire names that tokenize to nothing (the empty enum # value of positive-integer-or-empty and a few *-value enums). The wire form -# stays ""; only the identifier gets a name. Config: [naming] empty-value-word. -DEFAULT_EMPTY_WORD = "empty" +# stays ""; only the identifier gets a name. A target wanting a different word +# for a particular enum renames it: [rename.enum-value.] "" = "none". +EMPTY_WORD = "empty" + + +@dataclass +class Name: + """The neutral/bound name bundle. `wire` is the immutable on-the-wire + string (never a code identifier); `words` is the tokenized vector the + casings expand from; `cased` maps convention name -> identifier, filled + by iterating the convention registry.""" + + wire: str + words: tuple[str, ...] + cased: dict[str, str] + + @property + def pascal(self) -> str: + return self.cased["pascal"] + + @property + def camel(self) -> str: + return self.cased["camel"] + @property + def snake(self) -> str: + return self.cased["snake"] -def tokenize(wire: str, empty_word: str = DEFAULT_EMPTY_WORD) -> tuple[str, ...]: + @property + def kebab(self) -> str: + return self.cased["kebab"] + + @property + def screaming(self) -> str: + return self.cased["screaming"] + + +def tokenize(wire: str, empty_word: str = EMPTY_WORD) -> tuple[str, ...]: """Split a wire name into its canonical lowercase word vector.""" tokens: list[str] = [] current: list[str] = [] @@ -91,13 +128,25 @@ def _capitalize(word: str, acronyms: frozenset[str]) -> str: "screaming": lambda ws, ac: "_".join(w.upper() for w in ws), } +# How a convention joins two already-cased parts when an identifier is +# composed from a scope plus a member (a type name plus a variant name, for +# targets whose enum constants share one namespace). Concatenating +# conventions join with nothing; delimited conventions reuse their delimiter. +JOINERS = { + "pascal": "", + "camel": "", + "snake": "_", + "kebab": "-", + "screaming": "_", +} + def sanitize_identifier(ident: str, reserved: frozenset[str], invalid_prefix: str = "_") -> str: """Make a recased identifier legal for a code target: non-identifier characters become underscores, a leading digit or empty result gets the - configured prefix, and reserved words get a trailing underscore (the - `suffix-underscore` policy). The pre-sanitized casing stays available on - the Name; collision detection runs on the sanitized result.""" + configured prefix, and reserved words get a trailing underscore. The + pre-sanitized casing stays available on the Name; collision detection + runs on the sanitized result.""" out = "".join(ch if ch.isalnum() or ch == "_" else "_" for ch in ident) if not out or out[0].isdigit(): out = invalid_prefix + out @@ -111,9 +160,10 @@ class NameFactory: honoring a fundamental rename (re-expands all casings from the new root) and per-convention overrides (pin one flavor, leave the rest expanded).""" - def __init__(self, acronyms=DEFAULT_ACRONYMS, empty_word: str = DEFAULT_EMPTY_WORD): - self.acronyms = frozenset(acronyms) - self.empty_word = empty_word + def __init__(self, acronyms=DEFAULT_ACRONYMS): + # The acronym set matches against already-lowercased words, so it is + # normalized here: acronyms = ["MIDI"] must behave like ["midi"]. + self.acronyms = frozenset(a.lower() for a in acronyms) def make( self, @@ -122,7 +172,7 @@ def make( overrides: dict[str, str] | None = None, pluralize: bool = False, ) -> Name: - words = tokenize(fundamental if fundamental is not None else wire, self.empty_word) + words = tokenize(fundamental if fundamental is not None else wire) if pluralize: words = words[:-1] + (words[-1] + "s",) cased = {conv: fn(words, self.acronyms) for conv, fn in CONVENTIONS.items()} diff --git a/gen/plates/__init__.py b/gen/plates/__init__.py index 144347605..a3fd8d2ae 100644 --- a/gen/plates/__init__.py +++ b/gen/plates/__init__.py @@ -7,4 +7,23 @@ from gen.plates.build import PlatesError, build_plates from gen.plates.model import Plates -__all__ = ["Plates", "PlatesError", "build_plates"] +__all__ = ["Plates", "PlatesError", "build_plates", "build_for_config"] + + +def build_for_config(config_path): + """The whole pipeline for one target, shared by the CLI and tests: load + the config, lower its pinned XSD to the IR, apply companion patches, and + project. Returns (plates, config).""" + from gen.config import load as load_config + from gen.ir.build import build_ir + from gen.xsd.parser import parse + + cfg = load_config(config_path) + if cfg.xsd is None: + raise FileNotFoundError(f"config has no [input] xsd: {cfg.path}") + m = build_ir(parse(cfg.xsd), source=cfg.xsd.stem) + if cfg.sounds_xml is not None: + from gen.ir.sounds import patch_sounds, read_sound_ids + + patch_sounds(m, read_sound_ids(cfg.sounds_xml)) + return build_plates(m, cfg), cfg diff --git a/gen/plates/build.py b/gen/plates/build.py index 313b58faa..8b5d3e5dc 100644 --- a/gen/plates/build.py +++ b/gen/plates/build.py @@ -1,24 +1,27 @@ """Project the IR onto one target: build the Plates. The build consumes the IR and its Resolver (it never re-derives a schema -fact) plus a Config, and produces the materialized Plates tree. Three phases, -each failing loud: +fact: splicing, base-chain merging, and effective cardinality all come from +gen.ir.resolve) plus a Config, and produces the materialized Plates tree. +Three phases, each failing loud: - 1. Rename validation: every [rename.*] key must name something the IR - actually contains (a stale or misspelled key is a build error). + 1. Config-against-IR validation: every [rename.*] key must name something + the IR actually contains, and every [types] key a real primitive (a + stale or misspelled key is a build error). 2. Projection: names are tokenized and recased, renames and overrides - applied, identifiers sanitized, types mapped, strategies and files - assigned. + applied, identifiers composed per the target's scoping and sanitized, + types mapped, strategies and files assigned. 3. Collision detection (gen.plates.check): distinct wire names that collapsed to one identifier under the projection are reported together. """ from __future__ import annotations -from typing import TYPE_CHECKING - +from gen.config import Config from gen.ir import model as ir +from gen.ir.build import PRIMITIVES from gen.ir.resolve import Resolver +from gen.names import DEFAULT_ACRONYMS, JOINERS, NameFactory, sanitize_identifier from gen.plates import languages from gen.plates.check import run_checks from gen.plates.model import ( @@ -37,12 +40,6 @@ UnionPlateMember, Variant, ) -from gen.plates.names import DEFAULT_ACRONYMS, NameFactory, sanitize_identifier - -if TYPE_CHECKING: - # Imported for annotations only: gen.config imports the convention - # registry from gen.plates.names, so a runtime import here would cycle. - from gen.config import Config class PlatesError(Exception): @@ -72,8 +69,7 @@ def __init__(self, m: ir.Ir, config: Config): naming = config.naming self.factory = NameFactory( - naming.acronyms if naming.acronyms is not None else DEFAULT_ACRONYMS, - naming.empty_value_word, + naming.acronyms if naming.acronyms is not None else DEFAULT_ACRONYMS ) self.reserved = frozenset(languages.reserved_for(config.target.language)) | set( config.reserved.words @@ -81,6 +77,7 @@ def __init__(self, m: ir.Ir, config: Config): self.invalid_prefix = config.reserved.invalid_prefix self.type_map = languages.type_map_for(config.target.language) self.type_map.update(config.types) + self.variant_scope = languages.variant_scope_for(config.target.language) # Every type's Name and final identifier, computed up front so any # reference can be resolved to its target spelling in one lookup. @@ -96,7 +93,7 @@ def __init__(self, m: ir.Ir, config: Config): # ----- entry ------------------------------------------------------------ # def build(self) -> Plates: - errors = self._validate_renames() + errors = self._validate_config_against_ir() if errors: raise PlatesError(errors) @@ -106,7 +103,6 @@ def build(self) -> Plates: value_types=[self._value_plate(v) for v in self.m.value_types], complex_types=[self._complex_plate(c) for c in self.m.complex_types], roots=[self._plate_ref(ir.Ref(r.type, "complex")) for r in self.m.roots], - type_map=dict(self.type_map), ) plates.files = self._assign_files(plates) return plates @@ -126,6 +122,7 @@ def _target_info(self) -> TargetInfo: variant_convention=n.variant_convention, file_convention=n.file_convention, inheritance=t.inheritance, + variant_scope=self.variant_scope, doc_style=doc_style, reserved=sorted(self.reserved), partition=self.cfg.layout.partition, @@ -165,14 +162,35 @@ def _attribute_name(self, owner: str, wire: str) -> Name: ) def _variant(self, scope_wire: str, value_wire: str) -> Variant: + """Project one enum value (or union literal). The final constant + identifier follows the target's variant scope: `bare` sanitizes the + variant casing alone; `composed` joins the owning type's casing (and + symbol prefix) in the variant convention's join style, because the + constant will live in a flat namespace.""" entry = self.cfg.renames.enum_values.get((scope_wire, value_wire)) name = self.factory.make( value_wire, fundamental=entry.fundamental if entry else None, overrides=entry.cased if entry else None, ) - ident = self._sanitize(name.cased[self.cfg.naming.variant_convention]) - return Variant(wire=value_wire, name=name, ident=ident) + conv = self.cfg.naming.variant_convention + if self.variant_scope == "composed": + joiner = JOINERS.get(conv, "_") + if joiner: + parts = [] + if self.cfg.target.prefix: + prefix_name = self.factory.make(self.cfg.target.prefix) + parts.append(prefix_name.cased[conv]) + parts.append(self.type_names[scope_wire].cased[conv]) + parts.append(name.cased[conv]) + raw = joiner.join(parts) + else: + # Concatenating conventions: the type identifier (which + # already carries the prefix) plus the variant casing. + raw = self.type_idents[scope_wire] + name.cased[conv] + else: + raw = name.cased[conv] + return Variant(wire=value_wire, name=name, ident=self._sanitize(raw)) def _field_ident(self, name: Name) -> str: raw = self.cfg.naming.field_prefix + name.cased[self.cfg.naming.field_convention] @@ -224,7 +242,10 @@ def _value_plate(self, v: ir.ValueType): members = [] for m in v.members: if m.ref is not None: - members.append(UnionPlateMember(ref=self._plate_ref(m.ref))) + member_name = self.type_names.get(m.ref.name) or self.factory.make(m.ref.name) + members.append( + UnionPlateMember(ref=self._plate_ref(m.ref), name=member_name) + ) else: # An inline literal set projects like a tiny anonymous enum; # its variants are addressable for renames under the union's @@ -246,10 +267,12 @@ def _complex_plate(self, ct: ir.ComplexType) -> ComplexPlate: "derived": "inherit" if self.cfg.target.inheritance else "flatten", }[ct.kind] - members = self._members(ct, self.resolver.attributes(ct)) + members = self._members(ct, flatten=False) all_members = None if ct.kind == "derived": - all_members = self._members(ct, self.resolver.all_attributes(ct), flatten=True) + # Built under either strategy, so the collision gate covers the + # merged chain even for inheriting targets. + all_members = self._members(ct, flatten=True) return ComplexPlate( name=self.type_names[ct.name], @@ -264,31 +287,24 @@ def _complex_plate(self, ct: ir.ComplexType) -> ComplexPlate: doc=ct.doc, ) - def _members(self, ct: ir.ComplexType, attrs: list[ir.Attr], flatten: bool = False) -> list[Member]: + def _members(self, ct: ir.ComplexType, flatten: bool) -> list[Member]: """The flat field list: attributes first, then the text value body, - then child elements in document order (deduped by wire name). When - flattening a derived type, the base chain's value body and elements - are merged in (base-most first), mirroring all_attributes.""" - members = [self._attr_member(ct.name, a) for a in attrs] - - chain = [ct] + then child elements in document order. The flattened variant merges + the base chain (base-most first) via the Resolver's chain views.""" if flatten: - cur = ct - while cur.base and cur.base in self.complex_by_name: - cur = self.complex_by_name[cur.base] - chain.append(cur) - chain.reverse() # base-most first + attrs = self.resolver.all_attributes(ct) + elements = self.resolver.all_flat_elements(ct) + chain = self.resolver.base_chain(ct) + else: + attrs = self.resolver.attributes(ct) + elements = self.resolver.flat_elements(ct) + chain = [ct] + members = [self._attr_member(ct.name, a) for a in attrs] for c in chain: if c.value_type is not None: members.append(self._value_member(c.value_type)) - seen: set[str] = set() - for c in chain: - for occ_name, element, cardinality in self._element_occurrences(c): - if occ_name in seen: - continue - seen.add(occ_name) - members.append(self._element_member(element, cardinality)) + members += [self._element_member(e, card) for e, card in elements] return members def _attr_member(self, owner_wire: str, a: ir.Attr) -> Member: @@ -330,56 +346,6 @@ def _element_member(self, element: ir.Element, cardinality: str) -> Member: doc=element.doc, ) - def _element_occurrences(self, ct: ir.ComplexType) -> list[tuple[str, ir.Element, str]]: - """Walk the resolved content tree computing each element's effective - cardinality for a flat member list: an element under a repeated - sequence/choice is a vector; an element under a choice (or an optional - wrapper) is at most optional; only an element required along a spine - of exactly-once sequences stays required. Occurrences of the same - name (e.g. the same element in two choice branches) are merged by the - caller: vector beats optional beats required, first name wins order.""" - out: list[tuple[str, ir.Element, str]] = [] - merged: dict[str, int] = {} # name -> index in out - rank = {"required": 0, "optional": 1, "vector": 2} - - def walk(node, forced: bool, repeated: bool) -> None: - if node is None: - return - if isinstance(node, ir.Element): - if repeated: - card = "vector" - elif node.card == "vector": - card = "vector" - elif node.card == "required" and forced: - card = "required" - else: - card = "optional" - if node.name in merged: - i = merged[node.name] - prev_name, prev_el, prev_card = out[i] - # A second occurrence means alternative branches: the - # member cannot be statically required. - card = max(card, prev_card, key=lambda c: rank[c]) - if card == "required": - card = "optional" - out[i] = (prev_name, prev_el, card) - else: - merged[node.name] = len(out) - out.append((node.name, node, card)) - return - once = node.min >= 1 and node.max == 1 - again = repeated or node.max == ir.UNBOUNDED or (node.max != 1) - if isinstance(node, ir.Sequence): - for item in node.items: - walk(item, forced and once, again) - elif isinstance(node, ir.Choice): - for item in node.items: - walk(item, False, again) - # GroupRef leaves cannot appear: the Resolver spliced them. - - walk(self.resolver.content(ct), True, False) - return out - def _default_variant(self, type_ref: ir.Ref, literal: str | None) -> str | None: """When a default/fixed literal names a variant of the member's enum type, resolve it to the variant's target identifier (the wire literal @@ -432,15 +398,22 @@ def _type_deps(self, plate) -> set[str]: deps.add(plate.base.wire) return deps - # ----- rename validation ------------------------------------------------------ # + # ----- config-against-IR validation ----------------------------------------- # - def _validate_renames(self) -> list[str]: - """Every rename key must address something in the IR (design 6.5): - a typo or a key left stale after a schema bump is a build error, not - a silently ignored line.""" + def _validate_config_against_ir(self) -> list[str]: + """Every rename key must address something in the IR, and every + [types] key a real primitive (design 6.5): a typo or a key left stale + after a schema bump is a build error, not a silently ignored line.""" r = self.cfg.renames errors: list[str] = [] + for primitive in self.cfg.types: + if primitive not in PRIMITIVES: + errors.append( + f"[types] {primitive}: not an IR primitive " + f"({', '.join(sorted(PRIMITIVES))})" + ) + type_wires = set(self.values_by_name) | set(self.complex_by_name) for wire in r.types: if wire not in type_wires: diff --git a/gen/plates/check.py b/gen/plates/check.py index 0a5cda771..94647a563 100644 --- a/gen/plates/check.py +++ b/gen/plates/check.py @@ -16,13 +16,46 @@ def run_checks(plates: Plates) -> list[str]: errors: list[str] = [] - _check_type_idents(plates, errors) - _check_variants(plates, errors) + if plates.target.variant_scope == "composed": + _check_flat_namespace(plates, errors) + else: + _check_type_idents(plates, errors) + _check_variants_per_type(plates, errors) _check_members(plates, errors) _check_file_stems(plates, errors) return errors +def _variant_pairs(plate) -> list[tuple[str, str]]: + """(ident, claimant description) for every constant a value plate emits.""" + if isinstance(plate, EnumPlate): + return [(v.ident, f"{plate.name.wire}.{v.wire!r}") for v in plate.variants] + if isinstance(plate, UnionPlate): + return [ + (v.ident, f"{plate.name.wire}.{v.wire!r}") + for m in plate.members + if m.literals + for v in m.literals + ] + return [] + + +def _check_flat_namespace(plates: Plates, errors: list[str]) -> None: + """For a composed variant scope, the target has one identifier namespace: + type identifiers and every (already composed) enum/literal constant must + be mutually unique -- this is the namespace the compiler actually sees.""" + pairs = [ + (p.ident, f"type {p.name.wire!r}") + for p in list(plates.value_types) + list(plates.complex_types) + ] + for p in plates.value_types: + pairs.extend(_variant_pairs(p)) + for ident, claimants in _collisions(pairs): + errors.append( + f"identifier collision: {sorted(set(claimants))} all project to '{ident}'" + ) + + def _collisions(pairs: list[tuple[str, str]]) -> list[tuple[str, list[str]]]: """Group (identifier, wire) pairs; return identifiers claimed by more than one distinct wire name, with their claimants.""" @@ -47,19 +80,11 @@ def _check_type_idents(plates: Plates, errors: list[str]) -> None: ) -def _check_variants(plates: Plates, errors: list[str]) -> None: +def _check_variants_per_type(plates: Plates, errors: list[str]) -> None: + """For a bare variant scope, constants live inside their type: uniqueness + is per enum (or per union's literal set).""" for p in plates.value_types: - if isinstance(p, EnumPlate): - pairs = [(v.ident, repr(v.wire)) for v in p.variants] - elif isinstance(p, UnionPlate): - pairs = [ - (v.ident, repr(v.wire)) - for m in p.members - if m.literals - for v in m.literals - ] - else: - continue + pairs = _variant_pairs(p) for ident, wires in _collisions(pairs): errors.append( f"variant identifier collision in '{p.name.wire}': " diff --git a/gen/plates/languages.py b/gen/plates/languages.py index 1fed85cca..7af7f324f 100644 --- a/gen/plates/languages.py +++ b/gen/plates/languages.py @@ -39,13 +39,24 @@ "token": "std::string", "nmtoken": "std::string", "date": "std::string", - "decimal": "Decimal", + "decimal": "double", "integer": "int", "positive_integer": "int", "non_negative_integer": "int", }, } +# How enum constants are scoped, per language: `bare` when the language +# scopes them inside the type (C++ enum class), `composed` when they share +# one flat namespace and carry the type's name (Go package-level constants, +# C's single global namespace). This is a language fact, not configuration; +# the composition itself happens in the projection so Variant.ident is final. +VARIANT_SCOPES: dict[str, str] = { + "go": "composed", + "c": "composed", + "cpp": "bare", +} + # Identifiers the sanitizer must not emit bare, per language: keywords plus # predeclared/builtin names a generated identifier could shadow disastrously. # [reserved] words extends these. @@ -73,16 +84,21 @@ "bool", "true", "false", # is assumed by generated code ), "cpp": ( - "alignas", "alignof", "and", "asm", "auto", "bool", "break", "case", - "catch", "char", "class", "const", "constexpr", "continue", "decltype", - "default", "delete", "do", "double", "else", "enum", "explicit", - "export", "extern", "false", "float", "for", "friend", "goto", "if", - "inline", "int", "long", "mutable", "namespace", "new", "noexcept", - "not", "nullptr", "operator", "or", "private", "protected", "public", - "register", "return", "short", "signed", "sizeof", "static", "struct", - "switch", "template", "this", "throw", "true", "try", "typedef", - "typeid", "typename", "union", "unsigned", "using", "virtual", "void", - "volatile", "while", + "alignas", "alignof", "and", "and_eq", "asm", "auto", "bitand", + "bitor", "bool", "break", "case", "catch", "char", "char8_t", + "char16_t", "char32_t", "class", "compl", "concept", "const", + "consteval", "constexpr", "constinit", "const_cast", "continue", + "co_await", "co_return", "co_yield", "decltype", "default", "delete", + "do", "double", "dynamic_cast", "else", "enum", "explicit", "export", + "extern", "false", "float", "for", "friend", "goto", "if", "inline", + "int", "long", "mutable", "namespace", "new", "noexcept", "not", + "not_eq", "nullptr", "operator", "or", "or_eq", "private", + "protected", "public", "register", "reinterpret_cast", "requires", + "return", "short", "signed", "sizeof", "static", "static_assert", + "static_cast", "struct", "switch", "template", "this", "thread_local", + "throw", "true", "try", "typedef", "typeid", "typename", "union", + "unsigned", "using", "virtual", "void", "volatile", "wchar_t", + "while", "xor", "xor_eq", ), } @@ -104,3 +120,7 @@ def reserved_for(language: str) -> tuple[str, ...]: def doc_style_for(language: str) -> DocStyle: style = DOC_STYLES.get(language) return DocStyle(style=style.style, wrap=style.wrap) if style else DocStyle(style="") + + +def variant_scope_for(language: str) -> str: + return VARIANT_SCOPES.get(language, "bare") diff --git a/gen/plates/model.py b/gen/plates/model.py index 49d91af5a..866633dd4 100644 --- a/gen/plates/model.py +++ b/gen/plates/model.py @@ -29,38 +29,9 @@ from dataclasses import dataclass, field from gen.ir import model as ir +from gen.names import Name - -@dataclass -class Name: - """The neutral/bound name bundle. `wire` is the immutable on-the-wire - string (never a code identifier); `words` is the tokenized vector the - casings expand from; `cased` maps convention name -> identifier, filled - by iterating the convention registry (gen.plates.names.CONVENTIONS).""" - - wire: str - words: tuple[str, ...] - cased: dict[str, str] - - @property - def pascal(self) -> str: - return self.cased["pascal"] - - @property - def camel(self) -> str: - return self.cased["camel"] - - @property - def snake(self) -> str: - return self.cased["snake"] - - @property - def kebab(self) -> str: - return self.cased["kebab"] - - @property - def screaming(self) -> str: - return self.cased["screaming"] +__all__ = ["Name"] # re-exported: templates reach all plate vocabulary here @dataclass @@ -68,7 +39,9 @@ class PlateRef: """A reference to another type, resolved for the target: `wire` and `category` mirror the IR Ref; `ident` is the spelling a template prints -- the referenced plate's type identifier, or the mapped target type when the - category is `primitive`.""" + category is `primitive`. For primitives, `wire` carries the IR's canonical + primitive name (e.g. `non_negative_integer`), not an XSD spelling: builtins + never appear on the wire themselves.""" wire: str category: str # "complex" | "value" | "primitive" @@ -96,6 +69,7 @@ class TargetInfo: variant_convention: str file_convention: str inheritance: bool # derived strategy: True -> inherit, False -> flatten + variant_scope: str # "bare" | "composed" (see Variant) doc_style: DocStyle reserved: list[str] # language defaults + [reserved] words, sorted partition: str # "per-type" | "single" @@ -109,10 +83,12 @@ class TargetInfo: @dataclass class Variant: """One enum value. `wire` is retained for serialization; `ident` is the - sanitized identifier in the variant convention. Both are kept: a target - whose enum constants are scoped by composition (Go `NoteTypeValue1024th`, - C `MX_NOTE_TYPE_VALUE_1024TH`) composes from `name.cased`, while a target - whose constants stand alone uses `ident` (where `1024th` -> `_1024th`).""" + FINAL emitted constant identifier -- templates print it verbatim, and the + collision gate certifies it. Its shape follows the target's variant scope + (a language fact seeded in gen.plates.languages): `bare` for languages + whose enum constants live inside the type (C++ `enum class` -> `_1024th`), + `composed` for languages where they share one flat namespace (Go + `NoteTypeValue1024th`, C `MX_NOTE_TYPE_VALUE_1024TH`).""" wire: str name: Name @@ -172,11 +148,15 @@ class StringPlate: @dataclass class UnionPlateMember: - """Exactly one is set: a resolved reference to a member type, or an inline - literal set projected like a tiny anonymous enum (each literal carries its - wire form and a variant identifier).""" + """Exactly one of ref/literals is set: a resolved reference to a member + type, or an inline literal set projected like a tiny anonymous enum (each + literal carries its wire form and a variant identifier). A ref member also + carries `name`, the referenced type's name bundle, so a template can spell + the member's tag/field without inventing a name (a primitive member like + `positive_integer` has no plate to look it up on).""" ref: PlateRef | None = None + name: Name | None = None literals: list[Variant] | None = None @@ -226,12 +206,21 @@ class ComplexPlate: """One complex type, projected. `members` is the flat, deduped, ordered field list a code target emits (attributes, then the value body, then child elements in document order); `content` is the resolved - sequence/choice particle tree (groups spliced; IR node types) for a - target that cares about order and choice structure. + sequence/choice particle tree for a target that cares about order and + choice structure. + + `content` deliberately re-presents the IR's particle node types + (Sequence/Choice/Element from gen.ir.model, groups already spliced): the + neutral core IS the IR re-presented, and a parallel node hierarchy would + only drift. Those node types are therefore part of this layer's public + contract. A template joining a content occurrence back to the field it + populates uses `member(wire, kind="element")` rather than re-walking. A derived plate exposes both the `base` edge (for a target with inheritance) and `all_members` (the base chain merged, for one without); - `strategy` says which one this target uses.""" + `strategy` says which one this target uses. Both views are always + populated for derived plates so the collision gate covers them under + either strategy.""" name: Name ident: str @@ -246,6 +235,15 @@ class ComplexPlate: file: str | None = None kind: str = "complex" + def member(self, wire: str, kind: str | None = None) -> Member: + """The member a content occurrence or attribute wire name populates. + `kind` disambiguates the rare wire name carried by both an attribute + and an element (e.g. barline's segno).""" + for m in self.members: + if m.name.wire == wire and (kind is None or m.kind == kind): + return m + raise KeyError(f"{self.name.wire}: no member {wire!r} (kind={kind})") + # --------------------------------------------------------------------------- # # The whole projected target @@ -276,7 +274,6 @@ class Plates: complex_types: list[ComplexPlate] = field(default_factory=list) roots: list[PlateRef] = field(default_factory=list) files: list[FileSpec] | None = None # None when partition == "single" - type_map: dict[str, str] = field(default_factory=dict) def __post_init__(self): # Random-access index for templates; a plain attribute (not a diff --git a/gen/tests/test_ir.py b/gen/tests/test_ir.py index 1425e6458..e816cd3d5 100644 --- a/gen/tests/test_ir.py +++ b/gen/tests/test_ir.py @@ -230,5 +230,101 @@ def _assert_no_group_refs(self, node, where: str) -> None: self.assertNotIsInstance(node, ir.GroupRef, f"{where}: unresolved group ref") +class FlatElements(unittest.TestCase): + """flat_elements computes the effective cardinality a flat field view + needs. The merge must distinguish exclusive duplicates (two branches of + one choice: at most one occurs) from co-occurring duplicates (anything + else: both can appear in one instance, so the field is a vector).""" + + def _resolver(self, complex_types, groups=()): + return Resolver(list(groups), [], list(complex_types)) + + def _ct(self, content): + return ir.ComplexType("t", "composite", content=content) + + def _el(self, name, card="required", min=1, max=1): + return ir.Element(name, ir.Ref(name, "complex"), card, min, max) + + def _flat(self, content) -> dict[str, str]: + ct = self._ct(content) + r = self._resolver([ct]) + return {e.name: card for e, card in r.flat_elements(ct)} + + def test_spine_of_exactly_once_sequences_keeps_required(self): + flat = self._flat(ir.Sequence([self._el("a"), self._el("b", "optional", 0)])) + self.assertEqual(flat, {"a": "required", "b": "optional"}) + + def test_choice_demotes_required_to_optional(self): + flat = self._flat(ir.Choice([self._el("a"), self._el("b")])) + self.assertEqual(flat, {"a": "optional", "b": "optional"}) + + def test_optional_sequence_wrapper_demotes(self): + flat = self._flat(ir.Sequence([self._el("a")], min=0)) + self.assertEqual(flat, {"a": "optional"}) + + def test_repeated_wrapper_makes_vectors(self): + inner = ir.Choice([self._el("a"), self._el("b")], 0, ir.UNBOUNDED) + flat = self._flat(ir.Sequence([inner])) + self.assertEqual(flat, {"a": "vector", "b": "vector"}) + + def test_exclusive_duplicates_merge_to_optional(self): + # The same element heads two branches of one choice: never co-occurs. + flat = self._flat( + ir.Choice( + [ + ir.Sequence([self._el("a"), self._el("b", "optional", 0)]), + ir.Sequence([self._el("a"), self._el("c")]), + ] + ) + ) + self.assertEqual(flat["a"], "optional") + + def test_co_occurring_duplicates_merge_to_vector(self): + # One occurrence on a branch's spine, another inside that same + # branch's inner choice: both can appear in one instance (the + # metronome beat-unit shape). + flat = self._flat( + ir.Choice( + [ + ir.Sequence( + [ + self._el("a"), + ir.Choice([self._el("p"), ir.Sequence([self._el("a")])]), + ] + ), + ir.Sequence([self._el("m")]), + ] + ) + ) + self.assertEqual(flat["a"], "vector") + + def test_same_sequence_duplicates_merge_to_vector(self): + flat = self._flat(ir.Sequence([self._el("a"), self._el("a")])) + self.assertEqual(flat["a"], "vector") + + def test_never_occurring_particle_is_skipped(self): + flat = self._flat(ir.Sequence([self._el("a"), ir.Sequence([self._el("z")], 0, 0)])) + self.assertEqual(flat, {"a": "required"}) + + def test_metronome_beat_unit_is_vector_in_real_schemas(self): + for xsd in XSDS: + with self.subTest(xsd=xsd.name): + m = build_ir(parse(xsd), xsd.stem) + r = Resolver.from_ir(m) + metronome = next(c for c in m.complex_types if c.name == "metronome") + flat = {e.name: card for e, card in r.flat_elements(metronome)} + self.assertEqual(flat["beat-unit"], "vector") + + def test_all_flat_elements_merges_base_chain(self): + base = ir.ComplexType( + "base", "composite", content=ir.Sequence([self._el("a")]) + ) + derived = ir.ComplexType("derived", "derived", base="base") + r = self._resolver([base, derived]) + flat = {e.name: card for e, card in r.all_flat_elements(derived)} + self.assertEqual(flat, {"a": "required"}) + self.assertEqual([c.name for c in r.base_chain(derived)], ["base", "derived"]) + + if __name__ == "__main__": unittest.main() diff --git a/gen/tests/test_plates.py b/gen/tests/test_plates.py index 5546ff8bf..6a70a0dc8 100644 --- a/gen/tests/test_plates.py +++ b/gen/tests/test_plates.py @@ -25,8 +25,8 @@ from gen.ir import model as ir from gen.ir.build import build_ir from gen.ir.dump import to_json -from gen.plates import PlatesError, build_plates -from gen.plates.names import NameFactory, sanitize_identifier, tokenize +from gen.names import NameFactory, sanitize_identifier, tokenize +from gen.plates import PlatesError, build_for_config, build_plates from gen.xsd import parse REPO = Path(__file__).resolve().parents[2] @@ -36,13 +36,8 @@ def build_for(config_path: Path): - config = cfg.load(config_path) - m = build_ir(parse(config.xsd), source=config.xsd.stem) - if config.sounds_xml is not None: - from gen.ir.sounds import patch_sounds, read_sound_ids - - patch_sounds(m, read_sound_ids(config.sounds_xml)) - return build_plates(m, config), config + # The CLI and the tests share one pipeline so they cannot drift. + return build_for_config(config_path) class TokenizerAndConventions(unittest.TestCase): @@ -237,6 +232,80 @@ def test_unmapped_language_passes_primitives_through(self): members = {m.name.wire: m for m in plates.plate("note").members} self.assertEqual(members["default-x"].type_ref.ident, "decimal") + def test_element_rename_applies_to_members(self): + config = Config() + config.renames.elements["tie"] = RenameEntry(fundamental="tie-mark") + plates = build_plates(tiny_ir(), config) + member = plates.plate("note").member("tie", kind="element") + self.assertEqual(member.name.snake, "tie_mark") + self.assertEqual(member.name.wire, "tie") + + def test_composed_scope_builds_flat_namespace_constants(self): + config = Config() + config.target.language = "go" + config.naming.field_convention = "pascal" + config.naming.variant_convention = "pascal" + plates = build_plates(tiny_ir(), config) + enum = plates.plate("up-down") + self.assertEqual([v.ident for v in enum.variants], ["UpDownUp", "UpDownDown"]) + # A composed constant colliding with a TYPE identifier is caught: pin + # casings so type "no" + variant "te" composes to the type ident + # "Note" of the existing note type. + config.renames.types["up-down"] = RenameEntry(fundamental="no") + config.renames.enum_values[("up-down", "up")] = RenameEntry( + cased={"pascal": "te"} + ) + with self.assertRaises(PlatesError) as caught: + build_plates(tiny_ir(), config) + self.assertIn("identifier collision", caught.exception.errors[0]) + self.assertIn("Note", caught.exception.errors[0]) + + def test_composed_scope_with_prefix_screams(self): + config = Config() + config.target.language = "c" + config.target.prefix = "Mx" + config.naming.variant_convention = "screaming" + plates = build_plates(tiny_ir(), config) + enum = plates.plate("up-down") + self.assertEqual(enum.variants[0].ident, "MX_UP_DOWN_UP") + + def test_file_stem_collision_checked_case_insensitively(self): + config = Config() + config.layout.partition = "per-type" + # Pin pitch's snake (the file convention) to differ from note's only + # by case: distinct identifiers, same file on macOS/Windows. + config.renames.types["pitch"] = RenameEntry(cased={"snake": "Note"}) + with self.assertRaises(PlatesError) as caught: + build_plates(tiny_ir(), config) + self.assertIn("file stem collision", "\n".join(caught.exception.errors)) + + def test_unknown_types_key_fails_loud(self): + config = Config(types={"decmial": "double"}) + with self.assertRaises(PlatesError) as caught: + build_plates(tiny_ir(), config) + self.assertIn("not an IR primitive", caught.exception.errors[0]) + + def test_derived_all_members_built_for_inheriting_targets(self): + base = ir.ComplexType( + "base-type", "empty", + attributes=[ir.Attr("color", ir.Ref("token", "primitive"))], + ) + derived = ir.ComplexType( + "derived-type", "derived", base="base-type", + attributes=[ir.Attr("size", ir.Ref("token", "primitive"))], + deps=["base-type"], + ) + m = tiny_ir() + m.complex_types += [base, derived] + config = Config() # inheritance defaults to True + plates = build_plates(m, config) + plate = plates.plate("derived-type") + self.assertEqual(plate.strategy, "inherit") + self.assertIsNotNone(plate.all_members) # gate coverage either way + self.assertEqual( + [mm.name.wire for mm in plate.all_members], ["color", "size"] + ) + class RealTargets(unittest.TestCase): """The shipped configs must project cleanly, deterministically, and with @@ -276,19 +345,43 @@ def test_shared_base_resolves_barline_collision(self): def test_empty_enum_value_gets_fallback_identifier(self): enum = self.go.plate("breath-mark-value") empty = next(v for v in enum.variants if v.wire == "") - self.assertEqual(empty.ident, "Empty") + self.assertEqual(empty.ident, "BreathMarkValueEmpty") # composed scope - def test_digit_led_variant_sanitized_but_casing_kept(self): - enum = self.go.plate("note-type-value") - v1024 = next(v for v in enum.variants if v.wire == "1024th") + def test_variant_idents_are_final_per_scope(self): + # Go and C compose into their flat constant namespaces; the digit-led + # value needs no sanitizer mangling once composed. + go_enum = self.go.plate("note-type-value") + v1024 = next(v for v in go_enum.variants if v.wire == "1024th") self.assertEqual(v1024.name.pascal, "1024th") # the ideal is recorded - self.assertEqual(v1024.ident, "_1024th") # the sanitized result + self.assertEqual(v1024.ident, "NoteTypeValue1024th") + c_enum = self.c.plate("note-type-value") + c1024 = next(v for v in c_enum.variants if v.wire == "1024th") + self.assertEqual(c1024.ident, "MX_NOTE_TYPE_VALUE_1024TH") + self.assertEqual(self.go.target.variant_scope, "composed") + self.assertEqual(self.cpp.target.variant_scope, "bare") + cpp_enum = self.cpp.plate("note-type-value") + cpp1024 = next(v for v in cpp_enum.variants if v.wire == "1024th") + self.assertEqual(cpp1024.ident, "_1024th") # bare: sanitizer applies def test_default_variant_resolution_in_real_schema(self): plate = self.go.plate("strong-accent") member = next(m for m in plate.members if m.name.wire == "type") self.assertEqual(member.default, "up") - self.assertEqual(member.default_variant, "Up") + self.assertEqual(member.default_variant, "UpDownUp") + + def test_flat_cardinality_in_real_schema(self): + # metronome's beat-unit occurs on a branch spine AND inside that same + # branch's inner choice: the occurrences co-occur, so the flat member + # must be a vector (the corpus exercises this: beat-unit-tied). + metronome = {m.name.wire: m for m in self.go.plate("metronome").members} + self.assertEqual(metronome["beat-unit"].cardinality, "vector") + # pitch's spine is exactly-once sequences: step stays required. + pitch = {m.name.wire: m for m in self.go.plate("pitch").members} + self.assertEqual(pitch["step"].cardinality, "required") + # note's cue occurs in two exclusive branches of one choice. + note = {m.name.wire: m for m in self.go.plate("note").members} + self.assertEqual(note["cue"].cardinality, "optional") + self.assertEqual(note["tie"].cardinality, "vector") # maxOccurs=2 def test_sound_id_fold_present_only_with_sounds(self): self.assertTrue(self.c.has_plate("sound-id")) @@ -366,14 +459,34 @@ def test_unknown_keys_fail(self): with self.assertRaises(ConfigError): self._load("[rename.type.note]\npasta = 'MusicNote'\n") + def test_unknown_top_level_sections_fail(self): + # The flagship fail-loud guarantee must hold at the outermost level: + # [renames] (typo) silently dropping every rename would be the worst + # silent misconfiguration in the system. + with self.assertRaises(ConfigError): + self._load("[renames.type]\nnote = 'tone'\n") + with self.assertRaises(ConfigError): + self._load("[targets]\nlanguage = 'go'\n") + with self.assertRaises(ConfigError): + self._load("[input]\nxsd_file = 'x.xsd'\n") + def test_unsupported_values_fail(self): with self.assertRaises(ConfigError): self._load("[layout]\npartition = 'sharded'\n") - with self.assertRaises(ConfigError): - self._load("[reserved]\npolicy = 'rename'\n") with self.assertRaises(ConfigError): self._load("[naming]\ntype-convention = 'dot'\n") + def test_string_lists_reject_bare_strings(self): + # A bare TOML string would silently explode into characters. + with self.assertRaises(ConfigError): + self._load("[naming]\nacronyms = 'midi'\n") + with self.assertRaises(ConfigError): + self._load("[reserved]\nwords = 'class'\n") + + def test_empty_rename_entry_fails(self): + with self.assertRaises(ConfigError): + self._load("[rename.type.note]\n") + def test_extends_merges_with_target_precedence(self): with tempfile.TemporaryDirectory() as d: base = Path(d) / "base.toml" @@ -412,6 +525,32 @@ def test_extends_merges_with_target_precedence(self): config.renames.enum_values[("up-down", "down")].fundamental, "base-down" ) + def _load_with_base(self, base_text: str, target_text: str) -> Config: + with tempfile.TemporaryDirectory() as d: + (Path(d) / "base.toml").write_text(base_text) + target = Path(d) / "config.toml" + target.write_text('[naming]\nextends = "base.toml"\n' + target_text) + return cfg.load(target) + + def test_extends_rejects_chained_bases(self): + with self.assertRaises(ConfigError): + self._load_with_base("[naming]\nextends = 'grand.toml'\n", "") + + def test_extends_rejects_foreign_sections_in_base(self): + # A [reserved] or [types] in the base would be silently dead. + with self.assertRaises(ConfigError): + self._load_with_base("[reserved]\nwords = ['class']\n", "") + + def test_extends_rejects_scope_shape_disagreement(self): + # The base addresses an owner scope; the target a global entry of the + # same name. Wholesale replacement would quietly drop the base's + # renames, so the disagreement must be loud. + with self.assertRaises(ConfigError): + self._load_with_base( + "[rename.attribute.barline]\nsegno = 'segno-sound'\n", + "[rename.attribute]\nbarline = 'bar-line'\n", + ) + if __name__ == "__main__": unittest.main() From 4b6d165c34b77242d4da048ea8f6b245ca8a788c Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 16:14:26 +0000 Subject: [PATCH 18/45] gen: add the emit stage (template processing layer) The fourth pipeline stage: python3 -m gen now projects the target's Plates and renders them through a per-language backend. - gen/emit/__init__.py: backend dispatch. A backend is a module exposing render(plates) -> {relative path: content} -- the dumb-renderer contract: identifiers, casings, type mappings, file stems, and structure all arrive resolved on the plates; a backend owns only its language's grammar and support files. Backends register by language name; no backend ships yet, so emitting any target fails loud with the known-backend list. - gen/emit/writer.py: deterministic output. Files are written only when their content changed (stable mtimes for build systems); files the generator wrote previously but that left the manifest are pruned, but only files carrying the generated-code marker are ever deleted -- unmarked files in the output directory are reported and left alone. Backends must mark every file (checked), paths are validated against escapes, and the marker satisfies Go's generated-code convention. - gen/__main__.py: wire the emit command; fold the duplicated load-lower-patch pipeline onto gen.plates.build_for_config and one _lower helper (a review finding: the sounds fold existed in two places). - gen/tests/test_emit.py: writer contract (idempotence, marker-gated pruning, foreign-file safety, unsafe paths) and dispatch failure modes. --- gen/__main__.py | 55 +++++++++++--------- gen/emit/__init__.py | 49 ++++++++++++++++++ gen/emit/writer.py | 99 +++++++++++++++++++++++++++++++++++ gen/tests/test_emit.py | 114 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 292 insertions(+), 25 deletions(-) create mode 100644 gen/emit/__init__.py create mode 100644 gen/emit/writer.py create mode 100644 gen/tests/test_emit.py diff --git a/gen/__main__.py b/gen/__main__.py index 4a3469caf..9a9266d89 100644 --- a/gen/__main__.py +++ b/gen/__main__.py @@ -1,7 +1,7 @@ """mx code generator entry point. Usage: - python3 -m gen generate code for a target (not yet implemented) + python3 -m gen emit code for the target the config describes python3 -m gen analyze [xsd] parse the XSD and print a structural analysis python3 -m gen ir [--type N] [--resolve] [--config C] [xsd] lower the XSD to the IR and print it as JSON; @@ -41,10 +41,8 @@ def _analyze(args: list[str]) -> int: def _ir(args: list[str]) -> int: - from gen.ir.build import build_ir from gen.ir.dump import resolved_view, to_json from gen.ir.resolve import Resolver - from gen.xsd.parser import parse type_name = None resolve = False @@ -82,14 +80,7 @@ def _ir(args: list[str]) -> int: if not xsd.exists(): print(f"error: XSD not found: {xsd}", file=sys.stderr) return 1 - ir = build_ir(parse(xsd), source=xsd.stem) - - # A target config can fold companion data into the IR before it is consumed: - # today, the sounds.xml patch (instrument-sound -> open sound enum). - if cfg is not None and cfg.sounds_xml is not None: - from gen.ir.sounds import patch_sounds, read_sound_ids - - patch_sounds(ir, read_sound_ids(cfg.sounds_xml)) + ir = _lower(xsd, cfg) resolver = Resolver.from_ir(ir) if resolve else None @@ -112,23 +103,18 @@ def _ir(args: list[str]) -> int: return 0 -def _build_target_plates(config_path: str): - """Shared loading path for plates-consuming commands: read the target's - config, lower its pinned XSD to the IR, apply companion patches, project.""" - from gen.config import load as load_config +def _lower(xsd: Path, cfg): + """Lower an XSD to the IR, applying a config's companion patches (today: + the sounds.xml fold). One definition, shared by every command.""" from gen.ir.build import build_ir - from gen.plates import build_plates from gen.xsd.parser import parse - cfg = load_config(config_path) - if cfg.xsd is None: - raise FileNotFoundError(f"config has no [input] xsd: {cfg.path}") - ir = build_ir(parse(cfg.xsd), source=cfg.xsd.stem) - if cfg.sounds_xml is not None: + ir = build_ir(parse(xsd), source=xsd.stem) + if cfg is not None and cfg.sounds_xml is not None: from gen.ir.sounds import patch_sounds, read_sound_ids patch_sounds(ir, read_sound_ids(cfg.sounds_xml)) - return build_plates(ir, cfg), cfg + return ir def _plates(args: list[str]) -> int: @@ -156,8 +142,10 @@ def _plates(args: list[str]) -> int: print("error: plates requires --config ", file=sys.stderr) return 2 + from gen.plates import build_for_config + try: - plates, _ = _build_target_plates(config_path) + plates, _ = build_for_config(config_path) except PlatesError as e: for line in e.errors: print(f"error: {line}", file=sys.stderr) @@ -184,6 +172,21 @@ def _plates(args: list[str]) -> int: return 0 +def _emit(config_path: str) -> int: + from gen.emit import EmitError, emit + from gen.plates import PlatesError, build_for_config + + try: + plates, cfg = build_for_config(config_path) + result = emit(plates, cfg) + except (PlatesError, EmitError) as e: + message = "\n".join(e.errors) if isinstance(e, PlatesError) else str(e) + print(f"error: {message}", file=sys.stderr) + return 1 + print(result.summary()) + return 0 + + def main(argv: list[str]) -> int: if not argv: print(__doc__, file=sys.stderr) @@ -194,8 +197,10 @@ def main(argv: list[str]) -> int: return _ir(argv[1:]) if argv[0] == "plates": return _plates(argv[1:]) - print("error: generator not implemented", file=sys.stderr) - return 1 + if argv[0].endswith(".toml"): + return _emit(argv[0]) + print(f"error: unknown command: {argv[0]}", file=sys.stderr) + return 2 if __name__ == "__main__": diff --git a/gen/emit/__init__.py b/gen/emit/__init__.py new file mode 100644 index 000000000..6b19537b3 --- /dev/null +++ b/gen/emit/__init__.py @@ -0,0 +1,49 @@ +"""The emit stage: render the Plates through a per-language backend. + +A backend is a module exposing one function: + + render(plates: gen.plates.Plates) -> dict[str, str] + +mapping output paths (relative to the target's [output] dir) to file +contents. Backends are the templates of the pipeline -- dumb renderers that +walk plates and print text. They make no naming decisions (identifiers, +casings, file stems all arrive resolved on the plates) and no schema +decisions (structure and cardinality arrive resolved from the IR); what they +own is the target language's grammar: declarations, parse/serialize bodies, +comment syntax, and which support files accompany the generated types. + +Backends register here by language name; the language comes from the +target's config ([target] language), so `python3 -m gen ` is +the whole user interface. +""" + +from __future__ import annotations + +import importlib + +from gen.config import Config +from gen.emit.writer import EmitResult, write_files +from gen.plates.model import Plates + +# language -> backend module path, imported lazily so a target can be +# projected (`gen plates`) without every backend being importable. +BACKENDS: dict[str, str] = {} + + +class EmitError(Exception): + pass + + +def emit(plates: Plates, config: Config) -> EmitResult: + language = plates.target.language + module_path = BACKENDS.get(language) + if module_path is None: + known = ", ".join(sorted(BACKENDS)) or "none yet" + raise EmitError( + f"no emit backend for language '{language}' (backends: {known})" + ) + if config.output_dir is None: + raise EmitError(f"config has no [output] dir: {config.path}") + backend = importlib.import_module(module_path) + rendered = backend.render(plates) + return write_files(config.output_dir, rendered) diff --git a/gen/emit/writer.py b/gen/emit/writer.py new file mode 100644 index 000000000..21246abce --- /dev/null +++ b/gen/emit/writer.py @@ -0,0 +1,99 @@ +"""Deterministic file output for the emit stage. + +A backend renders a target into a manifest (relative path -> content); the +writer makes the output directory match it exactly: + + - files are written only when their content actually changed, so build + systems see stable mtimes across no-op regenerations; + - files the generator wrote on a previous run but that left the manifest + (a type was renamed, a partition changed) are pruned -- but only files + carrying the generated-code marker are ever deleted, so a hand-written + file accidentally placed in the output directory is reported, never + removed. + +Every generated file must carry the marker near the top; render() output is +checked here so a backend cannot silently emit unmarked (unprunable) files. +""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from pathlib import Path + +# The substrings that identify a file as generator-owned. The Go convention +# (^// Code generated .* DO NOT EDIT\.$) is satisfied when a backend puts +# banner() in a line comment, and the prune check matches any comment syntax. +_MARK_PREFIX = "Code generated by gen" +_MARK_SUFFIX = "DO NOT EDIT." + +# How far into a file the marker must appear (license headers may precede it). +_MARK_WINDOW = 512 + + +def banner(source: str) -> str: + """The marker sentence a backend wraps in its comment syntax, e.g. + `// {banner(plates.source)}` for Go.""" + return f"{_MARK_PREFIX} from {source}. {_MARK_SUFFIX}" + + +def is_generated(content: str) -> bool: + head = content[:_MARK_WINDOW] + return _MARK_PREFIX in head and _MARK_SUFFIX in head + + +@dataclass +class EmitResult: + out_dir: Path + written: list[str] = field(default_factory=list) # created or changed + unchanged: list[str] = field(default_factory=list) + pruned: list[str] = field(default_factory=list) # stale generated files + foreign: list[str] = field(default_factory=list) # unmarked files left alone + + def summary(self) -> str: + parts = [f"{len(self.written)} written", f"{len(self.unchanged)} unchanged"] + if self.pruned: + parts.append(f"{len(self.pruned)} pruned") + if self.foreign: + parts.append(f"{len(self.foreign)} foreign file(s) left alone") + return f"{self.out_dir}: " + ", ".join(parts) + + +def write_files(out_dir: Path, files: dict[str, str]) -> EmitResult: + """Make out_dir contain exactly the manifest's generated files.""" + for rel, content in files.items(): + path = Path(rel) + if path.is_absolute() or ".." in path.parts: + raise ValueError(f"backend produced an unsafe output path: {rel}") + if not is_generated(content): + raise ValueError(f"backend produced a file without the generated marker: {rel}") + + result = EmitResult(out_dir=out_dir) + out_dir.mkdir(parents=True, exist_ok=True) + + for rel in sorted(files): + target = out_dir / rel + content = files[rel] + if target.exists() and target.read_text(encoding="utf-8") == content: + result.unchanged.append(rel) + continue + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(content, encoding="utf-8") + result.written.append(rel) + + keep = {str(Path(rel)) for rel in files} + for path in sorted(out_dir.rglob("*")): + if not path.is_file(): + continue + rel = str(path.relative_to(out_dir)) + if rel in keep: + continue + try: + owned = is_generated(path.read_text(encoding="utf-8")) + except (UnicodeDecodeError, OSError): + owned = False + if owned: + path.unlink() + result.pruned.append(rel) + else: + result.foreign.append(rel) + return result diff --git a/gen/tests/test_emit.py b/gen/tests/test_emit.py new file mode 100644 index 000000000..8514f5ee8 --- /dev/null +++ b/gen/tests/test_emit.py @@ -0,0 +1,114 @@ +"""Tests for the emit stage's writer and backend dispatch. + +The writer owns the contract that makes regeneration safe and repeatable: +write-if-changed, prune only generator-marked files, refuse unsafe paths and +unmarked content. Backends themselves are exercised by their own tests. + +Run with: python3 -m unittest gen.tests.test_emit +""" + +from __future__ import annotations + +import sys +import tempfile +import types +import unittest +from pathlib import Path + +from gen import emit +from gen.config import Config +from gen.emit.writer import banner, is_generated, write_files +from gen.plates.model import DocStyle, Plates, TargetInfo + + +def _mark(text: str) -> str: + return f"// {banner('test')}\n{text}\n" + + +class Writer(unittest.TestCase): + def setUp(self): + self._tmp = tempfile.TemporaryDirectory() + self.out = Path(self._tmp.name) + self.addCleanup(self._tmp.cleanup) + + def test_writes_then_reports_unchanged(self): + files = {"a.go": _mark("package mx"), "sub/b.go": _mark("package mx")} + first = write_files(self.out, files) + self.assertEqual(sorted(first.written), ["a.go", "sub/b.go"]) + second = write_files(self.out, files) + self.assertEqual(second.written, []) + self.assertEqual(sorted(second.unchanged), ["a.go", "sub/b.go"]) + + def test_prunes_stale_generated_files_only(self): + write_files(self.out, {"old.go": _mark("package mx")}) + (self.out / "handwritten.go").write_text("package mx // mine\n") + result = write_files(self.out, {"new.go": _mark("package mx")}) + self.assertEqual(result.pruned, ["old.go"]) + self.assertEqual(result.foreign, ["handwritten.go"]) + self.assertTrue((self.out / "handwritten.go").exists()) + self.assertFalse((self.out / "old.go").exists()) + + def test_rejects_unmarked_content(self): + with self.assertRaises(ValueError): + write_files(self.out, {"a.go": "package mx\n"}) + + def test_rejects_unsafe_paths(self): + for bad in ("../escape.go", "/abs.go"): + with self.assertRaises(ValueError): + write_files(self.out, {bad: _mark("x")}) + + def test_marker_must_be_near_the_top(self): + buried = ("//x\n" * 300) + f"// {banner('test')}\n" + self.assertFalse(is_generated(buried)) + self.assertTrue(is_generated(_mark("body"))) + + +class Dispatch(unittest.TestCase): + def _plates(self, language: str) -> Plates: + return Plates( + source="test", + target=TargetInfo( + language=language, + namespace="", + prefix="", + type_convention="pascal", + field_convention="snake", + variant_convention="pascal", + file_convention="snake", + inheritance=True, + variant_scope="bare", + doc_style=DocStyle(style="//"), + reserved=[], + partition="single", + ), + ) + + def test_unknown_language_fails_loud(self): + with self.assertRaises(emit.EmitError) as caught: + emit.emit(self._plates("cobol"), Config(output_dir=Path("."))) + self.assertIn("cobol", str(caught.exception)) + + def test_backend_render_is_written(self): + module = types.ModuleType("gen.tests._fake_backend") + module.render = lambda plates: {"out.txt": _mark(plates.target.language)} + sys.modules["gen.tests._fake_backend"] = module + emit.BACKENDS["fake"] = "gen.tests._fake_backend" + self.addCleanup(emit.BACKENDS.pop, "fake") + self.addCleanup(sys.modules.pop, "gen.tests._fake_backend") + + with tempfile.TemporaryDirectory() as d: + out = Path(d) / "generated" + result = emit.emit(self._plates("fake"), Config(output_dir=out)) + self.assertEqual(result.written, ["out.txt"]) + self.assertIn("fake", (out / "out.txt").read_text()) + + def test_missing_output_dir_fails_loud(self): + emit.BACKENDS["fake2"] = "gen.tests._fake_backend2" + self.addCleanup(emit.BACKENDS.pop, "fake2") + with self.assertRaises(emit.EmitError) as caught: + emit.emit(self._plates("fake2"), Config(output_dir=None)) + self.assertIn("[output] dir", str(caught.exception)) + + +if __name__ == "__main__": + unittest.main() From 98d46b14af9da4b50ac8962f8259a8df77795ab4 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 16:15:46 +0000 Subject: [PATCH 19/45] gen/emit: Go templates for the leaf node types (value shapes) The Go backend (gen/emit/go/) renders the four value shapes -- enum-class, numeric-wrapper, string-wrapper, tagged-variant -- one template per shape, plus the static runtime support file. Every generated type exposes the same surface so the complex-type templates can call them uniformly: TryParse(s) (T, bool) strict membership Parse(s) T lenient (corpus fixup policies) (T) String() string the wire spelling Leniency policies, in one place (runtime.go) and per-type clamps generated from the plates' bounds: unknown enum literal -> first variant; unparseable number -> 0; decimal-looking integers truncate; every number clamps into its declared range, with primitive-implied lower bounds (positive_integer >= 1, non_negative_integer >= 0) and exclusive bounds clamping to the nearest representable value (decimal: +/- 1e-6, matching the corpus duration fixup). Unions try members strictly in schema order; literal members become payload-free kinds; an unmatched input is absorbed by the first member's lenient parse. Backends own only Go grammar: identifiers (including the composed enum constants), casings, stems, and structure arrive final on the plates. Rendered output is piped through gofmt once (the formatter owns tabwriter alignment, per Go codegen convention; required on PATH, fail loud), and the emit is byte-idempotent. gen/test/go/mx/ now holds the 132 generated files (131 value types + runtime.go), committed per project convention. They compile (go build) and vet clean. gen/test/go/corert/values_smoke_test.go exercises the generated types end to end: wire -> typed -> wire across the policies above (run: go test -run TestValueSmoke ./corert/). --- AGENTS.md | 8 +- gen/README.md | 6 +- gen/emit/__init__.py | 4 +- gen/emit/c/common.py | 103 +++++ gen/emit/go/__init__.py | 46 +++ gen/emit/go/common.py | 63 +++ gen/emit/go/runtime.py | 65 +++ gen/emit/go/values.py | 380 ++++++++++++++++++ gen/test/go/corert/values_smoke_test.go | 50 +++ gen/test/go/mx/above_below.go | 45 +++ gen/test/go/mx/accidental_value.go | 171 ++++++++ gen/test/go/mx/accordion_middle.go | 38 ++ gen/test/go/mx/arrow_direction.go | 78 ++++ gen/test/go/mx/arrow_style.go | 62 +++ gen/test/go/mx/backward_forward.go | 45 +++ gen/test/go/mx/bar_style.go | 73 ++++ gen/test/go/mx/beam_level.go | 38 ++ gen/test/go/mx/beam_value.go | 54 +++ gen/test/go/mx/beater_value.go | 100 +++++ gen/test/go/mx/breath_mark_value.go | 53 +++ gen/test/go/mx/caesura_value.go | 56 +++ gen/test/go/mx/cancel_location.go | 50 +++ gen/test/go/mx/circular_arrow.go | 45 +++ gen/test/go/mx/clef_sign.go | 62 +++ gen/test/go/mx/color.go | 25 ++ gen/test/go/mx/comma_separated_text.go | 21 + gen/test/go/mx/css_font_size.go | 60 +++ gen/test/go/mx/degree_symbol_value.go | 54 +++ gen/test/go/mx/degree_type_value.go | 48 +++ gen/test/go/mx/distance_type.go | 22 + gen/test/go/mx/divisions.go | 28 ++ gen/test/go/mx/effect.go | 87 ++++ gen/test/go/mx/enclosure_shape.go | 82 ++++ gen/test/go/mx/ending_number.go | 24 ++ gen/test/go/mx/fan.go | 48 +++ gen/test/go/mx/fermata_shape.go | 66 +++ gen/test/go/mx/fifths.go | 28 ++ gen/test/go/mx/font_size.go | 46 +++ gen/test/go/mx/font_style.go | 44 ++ gen/test/go/mx/font_weight.go | 44 ++ gen/test/go/mx/glass_value.go | 47 +++ gen/test/go/mx/glyph_type.go | 31 ++ gen/test/go/mx/group_barline_value.go | 47 +++ gen/test/go/mx/group_symbol_value.go | 54 +++ gen/test/go/mx/handbell_value.go | 74 ++++ gen/test/go/mx/harmon_closed_location.go | 51 +++ gen/test/go/mx/harmon_closed_value.go | 47 +++ gen/test/go/mx/harmony_type.go | 49 +++ gen/test/go/mx/hole_closed_location.go | 51 +++ gen/test/go/mx/hole_closed_value.go | 47 +++ gen/test/go/mx/kind_value.go | 153 +++++++ gen/test/go/mx/left_center_right.go | 47 +++ gen/test/go/mx/left_right.go | 45 +++ gen/test/go/mx/line_end.go | 54 +++ gen/test/go/mx/line_length.go | 48 +++ gen/test/go/mx/line_shape.go | 44 ++ gen/test/go/mx/line_type.go | 50 +++ gen/test/go/mx/line_width_type.go | 24 ++ gen/test/go/mx/margin_type.go | 47 +++ gen/test/go/mx/measure_numbering_value.go | 48 +++ gen/test/go/mx/measure_text.go | 22 + gen/test/go/mx/membrane.go | 89 ++++ gen/test/go/mx/metal.go | 135 +++++++ gen/test/go/mx/midi_128.go | 36 ++ gen/test/go/mx/midi_16.go | 36 ++ gen/test/go/mx/midi_16384.go | 36 ++ gen/test/go/mx/millimeters.go | 27 ++ gen/test/go/mx/mode.go | 21 + gen/test/go/mx/mute.go | 85 ++++ gen/test/go/mx/non_negative_decimal.go | 33 ++ gen/test/go/mx/note_size_type.go | 54 +++ gen/test/go/mx/note_type_value.go | 81 ++++ gen/test/go/mx/notehead_value.go | 134 ++++++ gen/test/go/mx/number_level.go | 40 ++ gen/test/go/mx/number_of_lines.go | 36 ++ gen/test/go/mx/number_or_normal.go | 46 +++ gen/test/go/mx/octave.go | 36 ++ gen/test/go/mx/on_off.go | 44 ++ gen/test/go/mx/over_under.go | 45 +++ gen/test/go/mx/pedal_type.go | 57 +++ gen/test/go/mx/percent.go | 36 ++ gen/test/go/mx/pitched_value.go | 72 ++++ gen/test/go/mx/positive_divisions.go | 33 ++ gen/test/go/mx/positive_integer_or_empty.go | 45 +++ gen/test/go/mx/principal_voice_symbol.go | 53 +++ gen/test/go/mx/right_left_middle.go | 47 +++ gen/test/go/mx/rotation_degrees.go | 37 ++ gen/test/go/mx/runtime.go | 57 +++ gen/test/go/mx/semi_pitched.go | 56 +++ gen/test/go/mx/semitones.go | 28 ++ gen/test/go/mx/show_frets.go | 45 +++ gen/test/go/mx/show_tuplet.go | 48 +++ gen/test/go/mx/smufl_accidental_glyph_name.go | 22 + gen/test/go/mx/smufl_coda_glyph_name.go | 22 + gen/test/go/mx/smufl_glyph_name.go | 22 + gen/test/go/mx/smufl_lyrics_glyph_name.go | 22 + gen/test/go/mx/smufl_pictogram_glyph_name.go | 22 + gen/test/go/mx/smufl_segno_glyph_name.go | 22 + gen/test/go/mx/staff_divide_symbol.go | 48 +++ gen/test/go/mx/staff_line.go | 28 ++ gen/test/go/mx/staff_number.go | 34 ++ gen/test/go/mx/staff_type.go | 55 +++ gen/test/go/mx/start_note.go | 48 +++ gen/test/go/mx/start_stop.go | 49 +++ gen/test/go/mx/start_stop_continue.go | 54 +++ gen/test/go/mx/start_stop_discontinue.go | 52 +++ gen/test/go/mx/start_stop_single.go | 48 +++ gen/test/go/mx/stem_value.go | 50 +++ gen/test/go/mx/step.go | 60 +++ gen/test/go/mx/stick_location.go | 51 +++ gen/test/go/mx/stick_material.go | 53 +++ gen/test/go/mx/stick_type.go | 69 ++++ gen/test/go/mx/string_number.go | 34 ++ gen/test/go/mx/syllabic.go | 52 +++ gen/test/go/mx/symbol_size.go | 51 +++ gen/test/go/mx/tap_hand.go | 45 +++ gen/test/go/mx/tenths.go | 33 ++ gen/test/go/mx/text_direction.go | 55 +++ gen/test/go/mx/tied_type.go | 57 +++ gen/test/go/mx/time_only.go | 23 ++ gen/test/go/mx/time_relation.go | 57 +++ gen/test/go/mx/time_separator.go | 58 +++ gen/test/go/mx/time_symbol.go | 62 +++ gen/test/go/mx/tip_direction.go | 63 +++ gen/test/go/mx/top_bottom.go | 45 +++ gen/test/go/mx/tremolo_marks.go | 37 ++ gen/test/go/mx/tremolo_type.go | 50 +++ gen/test/go/mx/trill_beats.go | 34 ++ gen/test/go/mx/trill_step.go | 48 +++ gen/test/go/mx/two_note_turn.go | 48 +++ gen/test/go/mx/up_down.go | 45 +++ gen/test/go/mx/up_down_stop_continue.go | 51 +++ gen/test/go/mx/upright_inverted.go | 45 +++ gen/test/go/mx/valign.go | 51 +++ gen/test/go/mx/valign_image.go | 48 +++ gen/test/go/mx/wedge_type.go | 53 +++ gen/test/go/mx/winged.go | 56 +++ gen/test/go/mx/wood.go | 102 +++++ gen/test/go/mx/yes_no.go | 45 +++ gen/test/go/mx/yes_no_number.go | 46 +++ gen/test/go/mx/yyyy_mm_dd.go | 21 + 141 files changed, 7370 insertions(+), 6 deletions(-) create mode 100644 gen/emit/c/common.py create mode 100644 gen/emit/go/__init__.py create mode 100644 gen/emit/go/common.py create mode 100644 gen/emit/go/runtime.py create mode 100644 gen/emit/go/values.py create mode 100644 gen/test/go/corert/values_smoke_test.go create mode 100644 gen/test/go/mx/above_below.go create mode 100644 gen/test/go/mx/accidental_value.go create mode 100644 gen/test/go/mx/accordion_middle.go create mode 100644 gen/test/go/mx/arrow_direction.go create mode 100644 gen/test/go/mx/arrow_style.go create mode 100644 gen/test/go/mx/backward_forward.go create mode 100644 gen/test/go/mx/bar_style.go create mode 100644 gen/test/go/mx/beam_level.go create mode 100644 gen/test/go/mx/beam_value.go create mode 100644 gen/test/go/mx/beater_value.go create mode 100644 gen/test/go/mx/breath_mark_value.go create mode 100644 gen/test/go/mx/caesura_value.go create mode 100644 gen/test/go/mx/cancel_location.go create mode 100644 gen/test/go/mx/circular_arrow.go create mode 100644 gen/test/go/mx/clef_sign.go create mode 100644 gen/test/go/mx/color.go create mode 100644 gen/test/go/mx/comma_separated_text.go create mode 100644 gen/test/go/mx/css_font_size.go create mode 100644 gen/test/go/mx/degree_symbol_value.go create mode 100644 gen/test/go/mx/degree_type_value.go create mode 100644 gen/test/go/mx/distance_type.go create mode 100644 gen/test/go/mx/divisions.go create mode 100644 gen/test/go/mx/effect.go create mode 100644 gen/test/go/mx/enclosure_shape.go create mode 100644 gen/test/go/mx/ending_number.go create mode 100644 gen/test/go/mx/fan.go create mode 100644 gen/test/go/mx/fermata_shape.go create mode 100644 gen/test/go/mx/fifths.go create mode 100644 gen/test/go/mx/font_size.go create mode 100644 gen/test/go/mx/font_style.go create mode 100644 gen/test/go/mx/font_weight.go create mode 100644 gen/test/go/mx/glass_value.go create mode 100644 gen/test/go/mx/glyph_type.go create mode 100644 gen/test/go/mx/group_barline_value.go create mode 100644 gen/test/go/mx/group_symbol_value.go create mode 100644 gen/test/go/mx/handbell_value.go create mode 100644 gen/test/go/mx/harmon_closed_location.go create mode 100644 gen/test/go/mx/harmon_closed_value.go create mode 100644 gen/test/go/mx/harmony_type.go create mode 100644 gen/test/go/mx/hole_closed_location.go create mode 100644 gen/test/go/mx/hole_closed_value.go create mode 100644 gen/test/go/mx/kind_value.go create mode 100644 gen/test/go/mx/left_center_right.go create mode 100644 gen/test/go/mx/left_right.go create mode 100644 gen/test/go/mx/line_end.go create mode 100644 gen/test/go/mx/line_length.go create mode 100644 gen/test/go/mx/line_shape.go create mode 100644 gen/test/go/mx/line_type.go create mode 100644 gen/test/go/mx/line_width_type.go create mode 100644 gen/test/go/mx/margin_type.go create mode 100644 gen/test/go/mx/measure_numbering_value.go create mode 100644 gen/test/go/mx/measure_text.go create mode 100644 gen/test/go/mx/membrane.go create mode 100644 gen/test/go/mx/metal.go create mode 100644 gen/test/go/mx/midi_128.go create mode 100644 gen/test/go/mx/midi_16.go create mode 100644 gen/test/go/mx/midi_16384.go create mode 100644 gen/test/go/mx/millimeters.go create mode 100644 gen/test/go/mx/mode.go create mode 100644 gen/test/go/mx/mute.go create mode 100644 gen/test/go/mx/non_negative_decimal.go create mode 100644 gen/test/go/mx/note_size_type.go create mode 100644 gen/test/go/mx/note_type_value.go create mode 100644 gen/test/go/mx/notehead_value.go create mode 100644 gen/test/go/mx/number_level.go create mode 100644 gen/test/go/mx/number_of_lines.go create mode 100644 gen/test/go/mx/number_or_normal.go create mode 100644 gen/test/go/mx/octave.go create mode 100644 gen/test/go/mx/on_off.go create mode 100644 gen/test/go/mx/over_under.go create mode 100644 gen/test/go/mx/pedal_type.go create mode 100644 gen/test/go/mx/percent.go create mode 100644 gen/test/go/mx/pitched_value.go create mode 100644 gen/test/go/mx/positive_divisions.go create mode 100644 gen/test/go/mx/positive_integer_or_empty.go create mode 100644 gen/test/go/mx/principal_voice_symbol.go create mode 100644 gen/test/go/mx/right_left_middle.go create mode 100644 gen/test/go/mx/rotation_degrees.go create mode 100644 gen/test/go/mx/runtime.go create mode 100644 gen/test/go/mx/semi_pitched.go create mode 100644 gen/test/go/mx/semitones.go create mode 100644 gen/test/go/mx/show_frets.go create mode 100644 gen/test/go/mx/show_tuplet.go create mode 100644 gen/test/go/mx/smufl_accidental_glyph_name.go create mode 100644 gen/test/go/mx/smufl_coda_glyph_name.go create mode 100644 gen/test/go/mx/smufl_glyph_name.go create mode 100644 gen/test/go/mx/smufl_lyrics_glyph_name.go create mode 100644 gen/test/go/mx/smufl_pictogram_glyph_name.go create mode 100644 gen/test/go/mx/smufl_segno_glyph_name.go create mode 100644 gen/test/go/mx/staff_divide_symbol.go create mode 100644 gen/test/go/mx/staff_line.go create mode 100644 gen/test/go/mx/staff_number.go create mode 100644 gen/test/go/mx/staff_type.go create mode 100644 gen/test/go/mx/start_note.go create mode 100644 gen/test/go/mx/start_stop.go create mode 100644 gen/test/go/mx/start_stop_continue.go create mode 100644 gen/test/go/mx/start_stop_discontinue.go create mode 100644 gen/test/go/mx/start_stop_single.go create mode 100644 gen/test/go/mx/stem_value.go create mode 100644 gen/test/go/mx/step.go create mode 100644 gen/test/go/mx/stick_location.go create mode 100644 gen/test/go/mx/stick_material.go create mode 100644 gen/test/go/mx/stick_type.go create mode 100644 gen/test/go/mx/string_number.go create mode 100644 gen/test/go/mx/syllabic.go create mode 100644 gen/test/go/mx/symbol_size.go create mode 100644 gen/test/go/mx/tap_hand.go create mode 100644 gen/test/go/mx/tenths.go create mode 100644 gen/test/go/mx/text_direction.go create mode 100644 gen/test/go/mx/tied_type.go create mode 100644 gen/test/go/mx/time_only.go create mode 100644 gen/test/go/mx/time_relation.go create mode 100644 gen/test/go/mx/time_separator.go create mode 100644 gen/test/go/mx/time_symbol.go create mode 100644 gen/test/go/mx/tip_direction.go create mode 100644 gen/test/go/mx/top_bottom.go create mode 100644 gen/test/go/mx/tremolo_marks.go create mode 100644 gen/test/go/mx/tremolo_type.go create mode 100644 gen/test/go/mx/trill_beats.go create mode 100644 gen/test/go/mx/trill_step.go create mode 100644 gen/test/go/mx/two_note_turn.go create mode 100644 gen/test/go/mx/up_down.go create mode 100644 gen/test/go/mx/up_down_stop_continue.go create mode 100644 gen/test/go/mx/upright_inverted.go create mode 100644 gen/test/go/mx/valign.go create mode 100644 gen/test/go/mx/valign_image.go create mode 100644 gen/test/go/mx/wedge_type.go create mode 100644 gen/test/go/mx/winged.go create mode 100644 gen/test/go/mx/wood.go create mode 100644 gen/test/go/mx/yes_no.go create mode 100644 gen/test/go/mx/yes_no_number.go create mode 100644 gen/test/go/mx/yyyy_mm_dd.go diff --git a/AGENTS.md b/AGENTS.md index 4c7d43c18..434fdaebf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -163,9 +163,11 @@ unioned with an open string (element `instrument-sound` retyped from `string` to the only place the IR depends on an input beyond the XSD; it is opt-in per target, so the base IR stays a pure function of the schema. -**Status.** The parse, IR, analysis, and Plates stages exist (`python3 -m gen plates --config C -[--check]` dumps or gates the projection); the emit stage and its templates are not yet -implemented, so `python3 -m gen ` still exits with an error. +**Status.** The parse, IR, analysis, Plates, and emit stages exist (`python3 -m gen plates +--config C [--check]` dumps or gates the projection; `python3 -m gen ` emits). +Backends are landing bottom-up: the Go backend renders the four value shapes (the leaf types) +into `gen/test/go/mx/`; complex types, the document entry points, and the C backend are next. +Generated output is committed. ## Language targets diff --git a/gen/README.md b/gen/README.md index f8a6cd98c..094577605 100644 --- a/gen/README.md +++ b/gen/README.md @@ -22,8 +22,10 @@ XSD file --parse--> XSD model --lower--> IR --project--> Plates --emit--> casings, renames, primitive type mappings, emit strategies, file layout -- is made here, once, producing one **plate** per emitted type. The collection projected for a target is the **Plates**. Designed in [`../docs/ai/design/plates.md`](../docs/ai/design/plates.md). -4. Emit renders each plate through per-language templates ("dumb renderers": walk the plate, print - text, no naming logic). Not yet implemented. +4. Emit (`gen/emit/`) renders each plate through a per-language backend ("dumb renderers": walk + the plate, print text, no naming logic; see `gen/emit/__init__.py` for the backend contract). + The Go backend currently renders the four value shapes; complex types and the C backend are + landing bottom-up. ### Layout diff --git a/gen/emit/__init__.py b/gen/emit/__init__.py index 6b19537b3..ec7db23d4 100644 --- a/gen/emit/__init__.py +++ b/gen/emit/__init__.py @@ -27,7 +27,9 @@ # language -> backend module path, imported lazily so a target can be # projected (`gen plates`) without every backend being importable. -BACKENDS: dict[str, str] = {} +BACKENDS: dict[str, str] = { + "go": "gen.emit.go", +} class EmitError(Exception): diff --git a/gen/emit/c/common.py b/gen/emit/c/common.py new file mode 100644 index 000000000..62755c628 --- /dev/null +++ b/gen/emit/c/common.py @@ -0,0 +1,103 @@ +"""C grammar helpers shared by the C templates. + +Language spelling only: string literal quoting, block doc comments, the +header/impl file frames, include guards, and the composition rules C uses +for scoped names (functions are prefix_snake_verb, enum constants are +PREFIX_TYPE_VARIANT). Identifiers and casings arrive resolved on the plates. +""" + +from __future__ import annotations + +from gen.emit.writer import banner +from gen.plates.model import Name, Plates, Variant + +_ESCAPES = { + "\\": "\\\\", + '"': '\\"', + "\n": "\\n", + "\t": "\\t", + "\r": "\\r", +} + + +def c_string(s: str) -> str: + out = ['"'] + for ch in s: + if ch in _ESCAPES: + out.append(_ESCAPES[ch]) + elif ord(ch) < 0x20: + out.append(f"\\{ord(ch):03o}") + else: + out.append(ch) # generated files are UTF-8; pass non-ASCII through + out.append('"') + return "".join(out) + + +def doc_comment(text: str | None, wrap: int) -> list[str]: + """Wrap schema documentation into a block comment. Comment-closing + sequences in the text are defused.""" + if not text: + return [] + words = text.replace("*/", "*\\/").split() + if not words: + return [] + lines = ["/*"] + current = " *" + for word in words: + if len(current) + 1 + len(word) > wrap and current != " *": + lines.append(current) + current = " *" + current += " " + word + lines.append(current) + lines.append(" */") + return lines + + +def fn_prefix(plates: Plates) -> str: + """The function-name prefix: the symbol prefix lowered (Mx -> mx_).""" + return plates.target.prefix.lower() + "_" + + +def const_prefix(plates: Plates) -> str: + """The constant-name prefix: the symbol prefix screamed (Mx -> MX_).""" + return plates.target.prefix.upper() + "_" + + +def fn_name(plates: Plates, type_name: Name, verb: str) -> str: + return f"{fn_prefix(plates)}{type_name.snake}_{verb}" + + +def variant_const(plates: Plates, type_name: Name, variant: Variant) -> str: + """C enum constants share one global namespace, so they are composed as + PREFIX_TYPE_VARIANT in the variant convention (screaming). Composition + keeps digit-led variants legal (MX_NOTE_TYPE_VALUE_1024TH), so the + standalone sanitized `ident` is not used here.""" + casing = variant.name.cased[plates.target.variant_convention] + return f"{const_prefix(plates)}{type_name.screaming}_{casing}" + + +def guard(stem: str) -> str: + return stem.upper() + "_H" + + +def header_file(plates: Plates, stem: str, body: list[str], includes: list[str]) -> str: + lines = [f"/* {banner(plates.source)} */", ""] + lines += [f"#ifndef {guard(stem)}", f"#define {guard(stem)}", ""] + for inc in includes: + lines.append(f"#include {inc}") + if includes: + lines.append("") + lines += body + lines += ["", f"#endif /* {guard(stem)} */"] + return "\n".join(lines).rstrip("\n") + "\n" + + +def impl_file(plates: Plates, stem: str, body: list[str], includes: list[str]) -> str: + lines = [f"/* {banner(plates.source)} */", ""] + lines += [f'#include "{stem}.h"', ""] + for inc in includes: + lines.append(f"#include {inc}") + if includes: + lines.append("") + lines += body + return "\n".join(lines).rstrip("\n") + "\n" diff --git a/gen/emit/go/__init__.py b/gen/emit/go/__init__.py new file mode 100644 index 000000000..19479336c --- /dev/null +++ b/gen/emit/go/__init__.py @@ -0,0 +1,46 @@ +"""Go backend: render the Plates into the Go test target package. + +Dumb renderers per the design: identifiers, casings, type mappings, file +stems, and structure all arrive resolved on the plates; this package owns +only Go grammar (declarations, parse/serialize bodies, comment syntax) and +the runtime support file. + +Currently rendered: the four value shapes (the leaf node types) plus the +runtime. Complex types and the document entry points are later phases; until +they land, the emitted package is a compilable library of value types and +the corert harness keeps using its stub. +""" + +from __future__ import annotations + +import shutil +import subprocess +import tempfile +from pathlib import Path + +from gen.emit.go.runtime import runtime_file +from gen.emit.go.values import value_file +from gen.plates.model import Plates + + +def render(plates: Plates) -> dict[str, str]: + files: dict[str, str] = {"runtime.go": runtime_file(plates)} + for plate in plates.value_types: + files[plate.file + ".go"] = value_file(plates, plate) + return _gofmt(files) + + +def _gofmt(files: dict[str, str]) -> dict[str, str]: + """Run the rendered files through gofmt, once, in a scratch directory. + The templates emit straightforward go syntax; gofmt owns the alignment + rules (struct fields, map literals) that are its tabwriter's business, + exactly as Go codegen convention expects. Requiring the formatter keeps + the emitted bytes canonical instead of approximating its heuristics.""" + if shutil.which("gofmt") is None: + raise RuntimeError("the Go backend requires gofmt on PATH") + with tempfile.TemporaryDirectory() as scratch: + root = Path(scratch) + for rel, content in files.items(): + (root / rel).write_text(content, encoding="utf-8") + subprocess.run(["gofmt", "-w", scratch], check=True) + return {rel: (root / rel).read_text(encoding="utf-8") for rel in files} diff --git a/gen/emit/go/common.py b/gen/emit/go/common.py new file mode 100644 index 000000000..30f7ca5d3 --- /dev/null +++ b/gen/emit/go/common.py @@ -0,0 +1,63 @@ +"""Go grammar helpers shared by the Go templates. + +Everything here is language spelling: string literal quoting, doc comments, +the file frame, and the composition rules Go uses for scoped names (enum +constants are package-scoped, so variant constants are composed as +TypeIdent + VariantCasing). No naming decisions happen here -- identifiers +and casings arrive resolved on the plates. +""" + +from __future__ import annotations + +import json + +from gen.emit.writer import banner +from gen.plates.model import Name, Plates + + +def go_string(s: str) -> str: + """A Go interpreted string literal. JSON escaping is a strict subset of + Go's escape syntax (\\uXXXX, \\n, \\\"), so this is always valid Go.""" + return json.dumps(s, ensure_ascii=True) + + +def doc_comment(text: str | None, wrap: int, prefix: str = "") -> list[str]: + """Wrap schema documentation into // comment lines. The text arrives raw + on the plate (neutral core); the comment syntax is applied here.""" + if not text: + return [] + words = text.split() + if not words: + return [] + lines: list[str] = [] + current = f"{prefix}//" + for word in words: + if len(current) + 1 + len(word) > wrap and current != f"{prefix}//": + lines.append(current) + current = f"{prefix}//" + current += " " + word + lines.append(current) + return lines + + +def file_frame(plates: Plates, body: list[str], imports: list[str] | None = None) -> str: + """The standard generated-file frame: marker, package clause, imports.""" + lines = [f"// {banner(plates.source)}", "", f"package {plates.target.namespace}"] + if imports: + lines += ["", "import ("] + lines += [f"\t{go_string(imp)}" for imp in sorted(imports)] + lines += [")"] + lines += [""] + lines += body + return "\n".join(lines).rstrip("\n") + "\n" + + +def member_field(plates: Plates, name: Name) -> str: + """A struct field or kind-name component in the target's field casing.""" + return name.cased[plates.target.field_convention] + + +def unexported(name: Name) -> str: + """An unexported helper identifier derived from a type's name (its camel + casing): used for package-private lookup tables.""" + return name.camel diff --git a/gen/emit/go/runtime.py b/gen/emit/go/runtime.py new file mode 100644 index 000000000..dbb52a7b3 --- /dev/null +++ b/gen/emit/go/runtime.py @@ -0,0 +1,65 @@ +"""The static Go runtime support file. + +These helpers are the shared substrate the generated types call into; they +carry the lenient-parse policies (see gen.emit.go.values) in one place. The +file is emitted through the same writer as the generated types so it carries +the marker and participates in pruning. +""" + +from __future__ import annotations + +from gen.emit.go.common import file_frame +from gen.plates.model import Plates + +_BODY = '''\ +// tryParseDecimal parses s strictly as a decimal number. +func tryParseDecimal(s string) (float64, bool) { + v, err := strconv.ParseFloat(strings.TrimSpace(s), 64) + if err != nil || math.IsNaN(v) || math.IsInf(v, 0) { + return 0, false + } + return v, true +} + +// parseDecimal is the lenient form: unparseable input becomes 0, so a +// malformed document still loads deterministically. Range clamping is the +// typed wrappers' job. +func parseDecimal(s string) float64 { + v, _ := tryParseDecimal(s) + return v +} + +// tryParseInt parses s strictly as an integer. +func tryParseInt(s string) (int, bool) { + v, err := strconv.ParseInt(strings.TrimSpace(s), 10, 64) + if err != nil { + return 0, false + } + return int(v), true +} + +// parseInt is the lenient form: a decimal-looking value truncates toward +// zero, anything else becomes 0. +func parseInt(s string) int { + if v, ok := tryParseInt(s); ok { + return v + } + if v, ok := tryParseDecimal(s); ok { + return int(v) + } + return 0 +} + +// formatDecimal prints the shortest decimal that round-trips the value, +// without exponent notation (8.5 -> "8.5", 4 -> "4"). +func formatDecimal(v float64) string { + return strconv.FormatFloat(v, 'f', -1, 64) +} + +func formatInt(v int) string { + return strconv.Itoa(v) +}''' + + +def runtime_file(plates: Plates) -> str: + return file_frame(plates, _BODY.split("\n"), imports=["math", "strconv", "strings"]) diff --git a/gen/emit/go/values.py b/gen/emit/go/values.py new file mode 100644 index 000000000..639cebdea --- /dev/null +++ b/gen/emit/go/values.py @@ -0,0 +1,380 @@ +"""Go templates for the four value shapes (the leaf node types). + +One template per shape, per the design: enum-class, numeric-wrapper, +string-wrapper, tagged-variant. Every type exposes the same surface so the +complex-type templates can call them uniformly: + + TryParse(s string) (T, bool) strict: does s belong to the type + Parse(s string) T lenient: malformed input degrades + deterministically (see policies below) + (T) String() string the wire spelling + +Leniency policies (the corpus fixup conventions in data/README.md encode +these): an unknown enum literal falls back to the first variant; an +unparseable number becomes 0; every number is clamped into its declared +range, where an exclusive decimal bound clamps to bound +/- 1e-6 and an +exclusive integer bound to the next integer. +""" + +from __future__ import annotations + +from gen.emit.go.common import ( + doc_comment, + file_frame, + go_string, + member_field, + unexported, +) +from gen.plates.model import ( + EnumPlate, + NumberPlate, + Plates, + StringPlate, + UnionPlate, + UnionPlateMember, +) + +# IR primitive -> the strict-try helper and serialize spelling in runtime.go. +# A string-family primitive has no strict form: any string belongs to it. +_PRIM_TRY = { + "decimal": "tryParseDecimal", + "integer": "tryParseInt", + "positive_integer": "tryParseInt", + "non_negative_integer": "tryParseInt", +} +_PRIM_PARSE = { + "decimal": "parseDecimal", + "integer": "parseInt", + "positive_integer": "parseInt", + "non_negative_integer": "parseInt", +} +_PRIM_FORMAT = { + "decimal": "formatDecimal({0})", + "integer": "formatInt({0})", + "positive_integer": "formatInt({0})", + "non_negative_integer": "formatInt({0})", +} + +# Primitive-implied lower bounds the schema leaves unstated. +_IMPLIED_MIN = {"positive_integer": 1, "non_negative_integer": 0} + + +def value_file(plates: Plates, plate) -> str: + if isinstance(plate, EnumPlate): + body = _enum_body(plates, plate) + elif isinstance(plate, NumberPlate): + body = _number_body(plates, plate) + elif isinstance(plate, StringPlate): + body = _string_body(plates, plate) + elif isinstance(plate, UnionPlate): + body = _union_body(plates, plate) + else: + raise TypeError(f"not a value plate: {plate!r}") + return file_frame(plates, body) + + +# --------------------------------------------------------------------------- # +# enum-class +# --------------------------------------------------------------------------- # + + +def _enum_body(plates: Plates, plate: EnumPlate) -> list[str]: + ident = plate.ident + wrap = plates.target.doc_style.wrap + table = unexported(plate.name) + consts = [v.ident for v in plate.variants] # final, composed by the plates + + lines = doc_comment(plate.doc, wrap) + lines += [f"type {ident} int", ""] + lines += ["const ("] + lines += [f"\t{consts[0]} {ident} = iota"] + lines += [f"\t{c}" for c in consts[1:]] + lines += [")", ""] + + lines += [f"// {table}Values lists the wire literals by variant ordinal."] + lines += [f"var {table}Values = []string{{"] + lines += [f"\t{go_string(v.wire)}," for v in plate.variants] + lines += ["}", ""] + + lines += [f"var {table}Index = map[string]{ident}{{"] + lines += [f"\t{go_string(v.wire)}: {c}," for v, c in zip(plate.variants, consts)] + lines += ["}", ""] + + lines += [ + f"// TryParse{ident} matches s against the wire literals.", + f"func TryParse{ident}(s string) ({ident}, bool) {{", + f"\tv, ok := {table}Index[s]", + "\treturn v, ok", + "}", + "", + f"// Parse{ident} is lenient: unknown input falls back to the first variant.", + f"func Parse{ident}(s string) {ident} {{", + f"\tif v, ok := {table}Index[s]; ok {{", + "\t\treturn v", + "\t}", + f"\treturn {consts[0]}", + "}", + "", + "// String returns the wire literal.", + f"func (v {ident}) String() string {{", + f"\tif int(v) < 0 || int(v) >= len({table}Values) {{", + f"\t\treturn {table}Values[0]", + "\t}", + f"\treturn {table}Values[v]", + "}", + ] + return lines + + +# --------------------------------------------------------------------------- # +# numeric-wrapper +# --------------------------------------------------------------------------- # + + +def _go_int(literal: str) -> str: + return str(int(float(literal))) + + +def _go_float(literal: str) -> str: + return repr(float(literal)) + + +def _clamp_steps(plate: NumberPlate) -> list[tuple[str, str, str]]: + """(comparison op, bound literal, replacement literal) per active bound. + Exclusive bounds clamp to the nearest representable in-range value.""" + is_int = plate.target_type != "float64" + b = plate.bounds + steps: list[tuple[str, str, str]] = [] + + lows: list[tuple[float, bool, str]] = [] # (value, exclusive, literal) + if b.min_inclusive is not None: + lows.append((float(b.min_inclusive), False, b.min_inclusive)) + if b.min_exclusive is not None: + lows.append((float(b.min_exclusive), True, b.min_exclusive)) + implied = _IMPLIED_MIN.get(plate.base) + if implied is not None: + lows.append((float(implied), False, str(implied))) + if lows: + # The tightest lower bound wins (an exclusive bound at v is tighter + # than an inclusive one at the same v). + value, exclusive, literal = max(lows, key=lambda t: (t[0], t[1])) + if is_int: + bound = _go_int(literal) + steps.append(("<=" if exclusive else "<", + bound, + str(int(float(literal)) + 1) if exclusive else bound)) + else: + bound = _go_float(literal) + steps.append(("<=" if exclusive else "<", + bound, + repr(float(literal) + 1e-6) if exclusive else bound)) + + highs: list[tuple[float, bool, str]] = [] + if b.max_inclusive is not None: + highs.append((float(b.max_inclusive), False, b.max_inclusive)) + if b.max_exclusive is not None: + highs.append((float(b.max_exclusive), True, b.max_exclusive)) + if highs: + value, exclusive, literal = min(highs, key=lambda t: (t[0], -t[1])) + if is_int: + bound = _go_int(literal) + steps.append((">=" if exclusive else ">", + bound, + str(int(float(literal)) - 1) if exclusive else bound)) + else: + bound = _go_float(literal) + steps.append((">=" if exclusive else ">", + bound, + repr(float(literal) - 1e-6) if exclusive else bound)) + return steps + + +def _number_body(plates: Plates, plate: NumberPlate) -> list[str]: + ident = plate.ident + wrap = plates.target.doc_style.wrap + go_type = plate.target_type + try_fn = _PRIM_TRY[plate.base] + parse_fn = _PRIM_PARSE[plate.base] + fmt = _PRIM_FORMAT[plate.base].format(f"{go_type}(v)") + steps = _clamp_steps(plate) + + lines = doc_comment(plate.doc, wrap) + lines += [f"type {ident} {go_type}", ""] + + if steps: + convert = f"clamp{ident}(v)" + else: + convert = f"{ident}(v)" + + lines += [ + f"// TryParse{ident} parses s strictly, then clamps into the declared range.", + f"func TryParse{ident}(s string) ({ident}, bool) {{", + f"\tv, ok := {try_fn}(s)", + "\tif !ok {", + f"\t\treturn {ident}(0), false", + "\t}", + f"\treturn {convert}, true", + "}", + "", + f"// Parse{ident} is lenient: unparseable input becomes 0, then clamps.", + f"func Parse{ident}(s string) {ident} {{", + f"\tv := {parse_fn}(s)", + f"\treturn {convert}", + "}", + "", + ] + if steps: + lines += [f"func clamp{ident}(v {go_type}) {ident} {{"] + for op, bound, repl in steps: + lines += [f"\tif v {op} {bound} {{", f"\t\tv = {repl}", "\t}"] + lines += [f"\treturn {ident}(v)", "}", ""] + lines += [ + "// String returns the wire spelling.", + f"func (v {ident}) String() string {{", + f"\treturn {fmt}", + "}", + ] + return lines + + +# --------------------------------------------------------------------------- # +# string-wrapper +# --------------------------------------------------------------------------- # + + +def _string_body(plates: Plates, plate: StringPlate) -> list[str]: + ident = plate.ident + wrap = plates.target.doc_style.wrap + doc = plate.doc or "" + if plate.patterns: + # Patterns are documented, not enforced: round-trip fidelity wants the + # input back out, valid or not. + note = "Pattern (not enforced): " + " | ".join(plate.patterns) + doc = f"{doc} {note}".strip() + + lines = doc_comment(doc, wrap) + lines += [ + f"type {ident} string", + "", + f"// TryParse{ident} accepts any string: the wire form is the value.", + f"func TryParse{ident}(s string) ({ident}, bool) {{", + f"\treturn {ident}(s), true", + "}", + "", + f"func Parse{ident}(s string) {ident} {{", + f"\treturn {ident}(s)", + "}", + "", + "// String returns the wire spelling.", + f"func (v {ident}) String() string {{", + "\treturn string(v)", + "}", + ] + return lines + + +# --------------------------------------------------------------------------- # +# tagged-variant +# --------------------------------------------------------------------------- # + + +def _member_cases(plates: Plates, plate: UnionPlate): + """Flatten union members into kind cases: one per ref member, one per + literal. Each case: (kind const, payload field or None, payload Go type, + strict-try spelling, lenient-parse spelling, to-string spelling).""" + ident = plate.ident + cases = [] + for m in plate.members: + if m.ref is not None: + field = member_field(plates, m.name) + kind = f"{ident}Kind{field}" + if m.ref.category == "value": + cases.append( + (kind, field, m.ref.ident, + f"TryParse{m.ref.ident}(s)", + f"Parse{m.ref.ident}(s)", + f"v.{field}.String()") + ) + else: # primitive + try_fn = _PRIM_TRY.get(m.ref.wire) + cases.append( + (kind, field, m.ref.ident, + f"{try_fn}(s)" if try_fn else None, # None: any string matches + f"{_PRIM_PARSE[m.ref.wire]}(s)" if m.ref.wire in _PRIM_PARSE else "s", + _PRIM_FORMAT.get(m.ref.wire, "{0}").format(f"v.{field}")) + ) + else: + for variant in m.literals or []: + kind = f"{ident}Kind{variant.name.cased[plates.target.variant_convention]}" + cases.append( + (kind, None, None, f"s == {go_string(variant.wire)}", None, + go_string(variant.wire)) + ) + return cases + + +def _union_body(plates: Plates, plate: UnionPlate) -> list[str]: + ident = plate.ident + wrap = plates.target.doc_style.wrap + cases = _member_cases(plates, plate) + + lines = doc_comment(plate.doc, wrap) + lines += [f"type {ident} struct {{", f"\tKind {ident}Kind"] + for kind, field, go_type, *_ in cases: + if field is not None: + lines += [f"\t{field} {go_type}"] + lines += ["}", ""] + + lines += [f"type {ident}Kind int", "", "const ("] + lines += [f"\t{cases[0][0]} {ident}Kind = iota"] + lines += [f"\t{kind}" for kind, *_ in cases[1:]] + lines += [")", ""] + + lines += [ + f"// TryParse{ident} tries each union member in schema order.", + f"func TryParse{ident}(s string) ({ident}, bool) {{", + ] + for kind, field, _go_type, try_expr, _parse, _str in cases: + if field is None: + lines += [ + f"\tif {try_expr} {{", + f"\t\treturn {ident}{{Kind: {kind}}}, true", + "\t}", + ] + elif try_expr is None: + lines += [f"\treturn {ident}{{Kind: {kind}, {field}: s}}, true"] + else: + lines += [ + f"\tif v, ok := {try_expr}; ok {{", + f"\t\treturn {ident}{{Kind: {kind}, {field}: v}}, true", + "\t}", + ] + open_ended = any(f is not None and t is None for _, f, _g, t, _p, _s in cases) + if not open_ended: + lines += [f"\treturn {ident}{{}}, false"] + lines += ["}", ""] + + first_kind, first_field, _gt, _try, first_parse, _s = cases[0] + lines += [ + f"// Parse{ident} is lenient: when no member matches, the first member", + "// absorbs the input under its own leniency rules.", + f"func Parse{ident}(s string) {ident} {{", + f"\tif v, ok := TryParse{ident}(s); ok {{", + "\t\treturn v", + "\t}", + ] + if first_field is None: + lines += [f"\treturn {ident}{{Kind: {first_kind}}}"] + else: + lines += [f"\treturn {ident}{{Kind: {first_kind}, {first_field}: {first_parse}}}"] + lines += ["}", ""] + + lines += [ + "// String returns the wire spelling of whichever member is held.", + f"func (v {ident}) String() string {{", + "\tswitch v.Kind {", + ] + for kind, _field, _gt, _try, _parse, to_str in cases[1:]: + lines += [f"\tcase {kind}:", f"\t\treturn {to_str}"] + lines += ["\t}", f"\treturn {cases[0][5]}", "}"] + return lines diff --git a/gen/test/go/corert/values_smoke_test.go b/gen/test/go/corert/values_smoke_test.go new file mode 100644 index 000000000..04efdad28 --- /dev/null +++ b/gen/test/go/corert/values_smoke_test.go @@ -0,0 +1,50 @@ +package corert_test + +import ( + "testing" + + "github.com/webern/mx/gen/test/go/mx" +) + +// TestValueSmoke exercises the generated value types (the leaf node types) +// end to end: wire -> typed -> wire, including the leniency policies the +// corpus fixups encode (unknown enum literal -> first variant, unparseable +// number -> 0, clamping into declared ranges, exclusive-bound epsilon). +func TestValueSmoke(t *testing.T) { + cases := []struct { + name string + got string + want string + }{ + {"enum round-trip", mx.ParseAboveBelow("below").String(), "below"}, + {"enum unknown falls back to first variant", mx.ParseAboveBelow("sideways").String(), "above"}, + {"enum digit-led variant", mx.NoteTypeValue1024th.String(), "1024th"}, + {"enum space-separated literal", mx.ParseBeamValue("backward hook").String(), "backward hook"}, + {"enum empty literal", mx.ParseBreathMarkValue("").String(), ""}, + {"number trailing zeros drop on reprint", mx.ParseTenths("8.50").String(), "8.5"}, + {"number unparseable becomes zero", mx.ParseTenths("abc").String(), "0"}, + {"number clamps to inclusive min", mx.ParseMIDI16("0").String(), "1"}, + {"number clamps to inclusive max", mx.ParseMIDI16("99").String(), "16"}, + {"number int leniently truncates decimals", mx.ParseMIDI16("3.7").String(), "3"}, + {"number exclusive min clamps to epsilon", mx.ParsePositiveDivisions("0").String(), "0.000001"}, + {"number implied positive-integer min", mx.ParseStringNumber("").String(), "1"}, + {"string passthrough", mx.ParseColor("#FF0000").String(), "#FF0000"}, + {"union picks enum member", mx.ParseFontSize("small").String(), "small"}, + {"union picks numeric member", mx.ParseFontSize("24").String(), "24"}, + {"union literal member", mx.ParseNumberOrNormal("normal").String(), "normal"}, + {"union empty literal member", mx.ParsePositiveIntegerOrEmpty("").String(), ""}, + {"union integer member", mx.ParsePositiveIntegerOrEmpty("5").String(), "5"}, + } + for _, c := range cases { + if c.got != c.want { + t.Errorf("%s: got %q, want %q", c.name, c.got, c.want) + } + } + + if v, ok := mx.TryParseAboveBelow("nope"); ok { + t.Errorf("TryParse accepted an unknown literal: %v", v) + } + if fs := mx.ParseFontSize("small"); fs.Kind != mx.FontSizeKindCSSFontSize { + t.Errorf("union kind: got %v, want CSSFontSize", fs.Kind) + } +} diff --git a/gen/test/go/mx/above_below.go b/gen/test/go/mx/above_below.go new file mode 100644 index 000000000..5450d400b --- /dev/null +++ b/gen/test/go/mx/above_below.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The above-below type is used to indicate whether one element appears above or below another +// element. +type AboveBelow int + +const ( + AboveBelowAbove AboveBelow = iota + AboveBelowBelow +) + +// aboveBelowValues lists the wire literals by variant ordinal. +var aboveBelowValues = []string{ + "above", + "below", +} + +var aboveBelowIndex = map[string]AboveBelow{ + "above": AboveBelowAbove, + "below": AboveBelowBelow, +} + +// TryParseAboveBelow matches s against the wire literals. +func TryParseAboveBelow(s string) (AboveBelow, bool) { + v, ok := aboveBelowIndex[s] + return v, ok +} + +// ParseAboveBelow is lenient: unknown input falls back to the first variant. +func ParseAboveBelow(s string) AboveBelow { + if v, ok := aboveBelowIndex[s]; ok { + return v + } + return AboveBelowAbove +} + +// String returns the wire literal. +func (v AboveBelow) String() string { + if int(v) < 0 || int(v) >= len(aboveBelowValues) { + return aboveBelowValues[0] + } + return aboveBelowValues[v] +} diff --git a/gen/test/go/mx/accidental_value.go b/gen/test/go/mx/accidental_value.go new file mode 100644 index 000000000..b2f1f48fc --- /dev/null +++ b/gen/test/go/mx/accidental_value.go @@ -0,0 +1,171 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The accidental-value type represents notated accidentals supported by MusicXML. In the MusicXML +// 2.0 DTD this was a string with values that could be included. The XSD strengthens the data typing +// to an enumerated list. The quarter- and three-quarters- accidentals are Tartini-style +// quarter-tone accidentals. The -down and -up accidentals are quarter-tone accidentals that include +// arrows pointing down or up. The slash- accidentals are used in Turkish classical music. The +// numbered sharp and flat accidentals are superscripted versions of the accidental signs, used in +// Turkish folk music. The sori and koron accidentals are microtonal sharp and flat accidentals used +// in Iranian and Persian music. The other accidental covers accidentals other than those listed +// here. It is usually used in combination with the smufl attribute to specify a particular SMuFL +// accidental. The smufl attribute may be used with any accidental value to help specify the +// appearance of symbols that share the same MusicXML semantics. +type AccidentalValue int + +const ( + AccidentalValueSharp AccidentalValue = iota + AccidentalValueNatural + AccidentalValueFlat + AccidentalValueDoubleSharp + AccidentalValueSharpSharp + AccidentalValueFlatFlat + AccidentalValueNaturalSharp + AccidentalValueNaturalFlat + AccidentalValueQuarterFlat + AccidentalValueQuarterSharp + AccidentalValueThreeQuartersFlat + AccidentalValueThreeQuartersSharp + AccidentalValueSharpDown + AccidentalValueSharpUp + AccidentalValueNaturalDown + AccidentalValueNaturalUp + AccidentalValueFlatDown + AccidentalValueFlatUp + AccidentalValueDoubleSharpDown + AccidentalValueDoubleSharpUp + AccidentalValueFlatFlatDown + AccidentalValueFlatFlatUp + AccidentalValueArrowDown + AccidentalValueArrowUp + AccidentalValueTripleSharp + AccidentalValueTripleFlat + AccidentalValueSlashQuarterSharp + AccidentalValueSlashSharp + AccidentalValueSlashFlat + AccidentalValueDoubleSlashFlat + AccidentalValueSharp1 + AccidentalValueSharp2 + AccidentalValueSharp3 + AccidentalValueSharp5 + AccidentalValueFlat1 + AccidentalValueFlat2 + AccidentalValueFlat3 + AccidentalValueFlat4 + AccidentalValueSori + AccidentalValueKoron + AccidentalValueOther +) + +// accidentalValueValues lists the wire literals by variant ordinal. +var accidentalValueValues = []string{ + "sharp", + "natural", + "flat", + "double-sharp", + "sharp-sharp", + "flat-flat", + "natural-sharp", + "natural-flat", + "quarter-flat", + "quarter-sharp", + "three-quarters-flat", + "three-quarters-sharp", + "sharp-down", + "sharp-up", + "natural-down", + "natural-up", + "flat-down", + "flat-up", + "double-sharp-down", + "double-sharp-up", + "flat-flat-down", + "flat-flat-up", + "arrow-down", + "arrow-up", + "triple-sharp", + "triple-flat", + "slash-quarter-sharp", + "slash-sharp", + "slash-flat", + "double-slash-flat", + "sharp-1", + "sharp-2", + "sharp-3", + "sharp-5", + "flat-1", + "flat-2", + "flat-3", + "flat-4", + "sori", + "koron", + "other", +} + +var accidentalValueIndex = map[string]AccidentalValue{ + "sharp": AccidentalValueSharp, + "natural": AccidentalValueNatural, + "flat": AccidentalValueFlat, + "double-sharp": AccidentalValueDoubleSharp, + "sharp-sharp": AccidentalValueSharpSharp, + "flat-flat": AccidentalValueFlatFlat, + "natural-sharp": AccidentalValueNaturalSharp, + "natural-flat": AccidentalValueNaturalFlat, + "quarter-flat": AccidentalValueQuarterFlat, + "quarter-sharp": AccidentalValueQuarterSharp, + "three-quarters-flat": AccidentalValueThreeQuartersFlat, + "three-quarters-sharp": AccidentalValueThreeQuartersSharp, + "sharp-down": AccidentalValueSharpDown, + "sharp-up": AccidentalValueSharpUp, + "natural-down": AccidentalValueNaturalDown, + "natural-up": AccidentalValueNaturalUp, + "flat-down": AccidentalValueFlatDown, + "flat-up": AccidentalValueFlatUp, + "double-sharp-down": AccidentalValueDoubleSharpDown, + "double-sharp-up": AccidentalValueDoubleSharpUp, + "flat-flat-down": AccidentalValueFlatFlatDown, + "flat-flat-up": AccidentalValueFlatFlatUp, + "arrow-down": AccidentalValueArrowDown, + "arrow-up": AccidentalValueArrowUp, + "triple-sharp": AccidentalValueTripleSharp, + "triple-flat": AccidentalValueTripleFlat, + "slash-quarter-sharp": AccidentalValueSlashQuarterSharp, + "slash-sharp": AccidentalValueSlashSharp, + "slash-flat": AccidentalValueSlashFlat, + "double-slash-flat": AccidentalValueDoubleSlashFlat, + "sharp-1": AccidentalValueSharp1, + "sharp-2": AccidentalValueSharp2, + "sharp-3": AccidentalValueSharp3, + "sharp-5": AccidentalValueSharp5, + "flat-1": AccidentalValueFlat1, + "flat-2": AccidentalValueFlat2, + "flat-3": AccidentalValueFlat3, + "flat-4": AccidentalValueFlat4, + "sori": AccidentalValueSori, + "koron": AccidentalValueKoron, + "other": AccidentalValueOther, +} + +// TryParseAccidentalValue matches s against the wire literals. +func TryParseAccidentalValue(s string) (AccidentalValue, bool) { + v, ok := accidentalValueIndex[s] + return v, ok +} + +// ParseAccidentalValue is lenient: unknown input falls back to the first variant. +func ParseAccidentalValue(s string) AccidentalValue { + if v, ok := accidentalValueIndex[s]; ok { + return v + } + return AccidentalValueSharp +} + +// String returns the wire literal. +func (v AccidentalValue) String() string { + if int(v) < 0 || int(v) >= len(accidentalValueValues) { + return accidentalValueValues[0] + } + return accidentalValueValues[v] +} diff --git a/gen/test/go/mx/accordion_middle.go b/gen/test/go/mx/accordion_middle.go new file mode 100644 index 000000000..c2abf28b6 --- /dev/null +++ b/gen/test/go/mx/accordion_middle.go @@ -0,0 +1,38 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The accordion-middle type may have values of 1, 2, or 3, corresponding to having 1 to 3 dots in +// the middle section of the accordion registration symbol. This type is not used if no dots are +// present. +type AccordionMiddle int + +// TryParseAccordionMiddle parses s strictly, then clamps into the declared range. +func TryParseAccordionMiddle(s string) (AccordionMiddle, bool) { + v, ok := tryParseInt(s) + if !ok { + return AccordionMiddle(0), false + } + return clampAccordionMiddle(v), true +} + +// ParseAccordionMiddle is lenient: unparseable input becomes 0, then clamps. +func ParseAccordionMiddle(s string) AccordionMiddle { + v := parseInt(s) + return clampAccordionMiddle(v) +} + +func clampAccordionMiddle(v int) AccordionMiddle { + if v < 1 { + v = 1 + } + if v > 3 { + v = 3 + } + return AccordionMiddle(v) +} + +// String returns the wire spelling. +func (v AccordionMiddle) String() string { + return formatInt(int(v)) +} diff --git a/gen/test/go/mx/arrow_direction.go b/gen/test/go/mx/arrow_direction.go new file mode 100644 index 000000000..a21b08367 --- /dev/null +++ b/gen/test/go/mx/arrow_direction.go @@ -0,0 +1,78 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The arrow-direction type represents the direction in which an arrow points, using Unicode arrow +// terminology. +type ArrowDirection int + +const ( + ArrowDirectionLeft ArrowDirection = iota + ArrowDirectionUp + ArrowDirectionRight + ArrowDirectionDown + ArrowDirectionNorthwest + ArrowDirectionNortheast + ArrowDirectionSoutheast + ArrowDirectionSouthwest + ArrowDirectionLeftRight + ArrowDirectionUpDown + ArrowDirectionNorthwestSoutheast + ArrowDirectionNortheastSouthwest + ArrowDirectionOther +) + +// arrowDirectionValues lists the wire literals by variant ordinal. +var arrowDirectionValues = []string{ + "left", + "up", + "right", + "down", + "northwest", + "northeast", + "southeast", + "southwest", + "left right", + "up down", + "northwest southeast", + "northeast southwest", + "other", +} + +var arrowDirectionIndex = map[string]ArrowDirection{ + "left": ArrowDirectionLeft, + "up": ArrowDirectionUp, + "right": ArrowDirectionRight, + "down": ArrowDirectionDown, + "northwest": ArrowDirectionNorthwest, + "northeast": ArrowDirectionNortheast, + "southeast": ArrowDirectionSoutheast, + "southwest": ArrowDirectionSouthwest, + "left right": ArrowDirectionLeftRight, + "up down": ArrowDirectionUpDown, + "northwest southeast": ArrowDirectionNorthwestSoutheast, + "northeast southwest": ArrowDirectionNortheastSouthwest, + "other": ArrowDirectionOther, +} + +// TryParseArrowDirection matches s against the wire literals. +func TryParseArrowDirection(s string) (ArrowDirection, bool) { + v, ok := arrowDirectionIndex[s] + return v, ok +} + +// ParseArrowDirection is lenient: unknown input falls back to the first variant. +func ParseArrowDirection(s string) ArrowDirection { + if v, ok := arrowDirectionIndex[s]; ok { + return v + } + return ArrowDirectionLeft +} + +// String returns the wire literal. +func (v ArrowDirection) String() string { + if int(v) < 0 || int(v) >= len(arrowDirectionValues) { + return arrowDirectionValues[0] + } + return arrowDirectionValues[v] +} diff --git a/gen/test/go/mx/arrow_style.go b/gen/test/go/mx/arrow_style.go new file mode 100644 index 000000000..1ac48d6f8 --- /dev/null +++ b/gen/test/go/mx/arrow_style.go @@ -0,0 +1,62 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The arrow-style type represents the style of an arrow, using Unicode arrow terminology. Filled +// and hollow arrows indicate polygonal single arrows. Paired arrows are duplicate single arrows in +// the same direction. Combined arrows apply to double direction arrows like left right, indicating +// that an arrow in one direction should be combined with an arrow in the other direction. +type ArrowStyle int + +const ( + ArrowStyleSingle ArrowStyle = iota + ArrowStyleDouble + ArrowStyleFilled + ArrowStyleHollow + ArrowStylePaired + ArrowStyleCombined + ArrowStyleOther +) + +// arrowStyleValues lists the wire literals by variant ordinal. +var arrowStyleValues = []string{ + "single", + "double", + "filled", + "hollow", + "paired", + "combined", + "other", +} + +var arrowStyleIndex = map[string]ArrowStyle{ + "single": ArrowStyleSingle, + "double": ArrowStyleDouble, + "filled": ArrowStyleFilled, + "hollow": ArrowStyleHollow, + "paired": ArrowStylePaired, + "combined": ArrowStyleCombined, + "other": ArrowStyleOther, +} + +// TryParseArrowStyle matches s against the wire literals. +func TryParseArrowStyle(s string) (ArrowStyle, bool) { + v, ok := arrowStyleIndex[s] + return v, ok +} + +// ParseArrowStyle is lenient: unknown input falls back to the first variant. +func ParseArrowStyle(s string) ArrowStyle { + if v, ok := arrowStyleIndex[s]; ok { + return v + } + return ArrowStyleSingle +} + +// String returns the wire literal. +func (v ArrowStyle) String() string { + if int(v) < 0 || int(v) >= len(arrowStyleValues) { + return arrowStyleValues[0] + } + return arrowStyleValues[v] +} diff --git a/gen/test/go/mx/backward_forward.go b/gen/test/go/mx/backward_forward.go new file mode 100644 index 000000000..9b45a91b8 --- /dev/null +++ b/gen/test/go/mx/backward_forward.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The backward-forward type is used to specify repeat directions. The start of the repeat has a +// forward direction while the end of the repeat has a backward direction. +type BackwardForward int + +const ( + BackwardForwardBackward BackwardForward = iota + BackwardForwardForward +) + +// backwardForwardValues lists the wire literals by variant ordinal. +var backwardForwardValues = []string{ + "backward", + "forward", +} + +var backwardForwardIndex = map[string]BackwardForward{ + "backward": BackwardForwardBackward, + "forward": BackwardForwardForward, +} + +// TryParseBackwardForward matches s against the wire literals. +func TryParseBackwardForward(s string) (BackwardForward, bool) { + v, ok := backwardForwardIndex[s] + return v, ok +} + +// ParseBackwardForward is lenient: unknown input falls back to the first variant. +func ParseBackwardForward(s string) BackwardForward { + if v, ok := backwardForwardIndex[s]; ok { + return v + } + return BackwardForwardBackward +} + +// String returns the wire literal. +func (v BackwardForward) String() string { + if int(v) < 0 || int(v) >= len(backwardForwardValues) { + return backwardForwardValues[0] + } + return backwardForwardValues[v] +} diff --git a/gen/test/go/mx/bar_style.go b/gen/test/go/mx/bar_style.go new file mode 100644 index 000000000..8027abec0 --- /dev/null +++ b/gen/test/go/mx/bar_style.go @@ -0,0 +1,73 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The bar-style type represents barline style information. Choices are regular, dotted, dashed, +// heavy, light-light, light-heavy, heavy-light, heavy-heavy, tick (a short stroke through the top +// line), short (a partial barline between the 2nd and 4th lines), and none. +type BarStyle int + +const ( + BarStyleRegular BarStyle = iota + BarStyleDotted + BarStyleDashed + BarStyleHeavy + BarStyleLightLight + BarStyleLightHeavy + BarStyleHeavyLight + BarStyleHeavyHeavy + BarStyleTick + BarStyleShort + BarStyleNone +) + +// barStyleValues lists the wire literals by variant ordinal. +var barStyleValues = []string{ + "regular", + "dotted", + "dashed", + "heavy", + "light-light", + "light-heavy", + "heavy-light", + "heavy-heavy", + "tick", + "short", + "none", +} + +var barStyleIndex = map[string]BarStyle{ + "regular": BarStyleRegular, + "dotted": BarStyleDotted, + "dashed": BarStyleDashed, + "heavy": BarStyleHeavy, + "light-light": BarStyleLightLight, + "light-heavy": BarStyleLightHeavy, + "heavy-light": BarStyleHeavyLight, + "heavy-heavy": BarStyleHeavyHeavy, + "tick": BarStyleTick, + "short": BarStyleShort, + "none": BarStyleNone, +} + +// TryParseBarStyle matches s against the wire literals. +func TryParseBarStyle(s string) (BarStyle, bool) { + v, ok := barStyleIndex[s] + return v, ok +} + +// ParseBarStyle is lenient: unknown input falls back to the first variant. +func ParseBarStyle(s string) BarStyle { + if v, ok := barStyleIndex[s]; ok { + return v + } + return BarStyleRegular +} + +// String returns the wire literal. +func (v BarStyle) String() string { + if int(v) < 0 || int(v) >= len(barStyleValues) { + return barStyleValues[0] + } + return barStyleValues[v] +} diff --git a/gen/test/go/mx/beam_level.go b/gen/test/go/mx/beam_level.go new file mode 100644 index 000000000..d6db8a380 --- /dev/null +++ b/gen/test/go/mx/beam_level.go @@ -0,0 +1,38 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The MusicXML format supports six levels of beaming, up to 1024th notes. Unlike the number-level +// type, the beam-level type identifies concurrent beams in a beam group. It does not distinguish +// overlapping beams such as grace notes within regular notes, or beams used in different voices. +type BeamLevel int + +// TryParseBeamLevel parses s strictly, then clamps into the declared range. +func TryParseBeamLevel(s string) (BeamLevel, bool) { + v, ok := tryParseInt(s) + if !ok { + return BeamLevel(0), false + } + return clampBeamLevel(v), true +} + +// ParseBeamLevel is lenient: unparseable input becomes 0, then clamps. +func ParseBeamLevel(s string) BeamLevel { + v := parseInt(s) + return clampBeamLevel(v) +} + +func clampBeamLevel(v int) BeamLevel { + if v < 1 { + v = 1 + } + if v > 8 { + v = 8 + } + return BeamLevel(v) +} + +// String returns the wire spelling. +func (v BeamLevel) String() string { + return formatInt(int(v)) +} diff --git a/gen/test/go/mx/beam_value.go b/gen/test/go/mx/beam_value.go new file mode 100644 index 000000000..a2e25ce4e --- /dev/null +++ b/gen/test/go/mx/beam_value.go @@ -0,0 +1,54 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The beam-value type represents the type of beam associated with each of 8 beam levels (up to +// 1024th notes) available for each note. +type BeamValue int + +const ( + BeamValueBegin BeamValue = iota + BeamValueContinue + BeamValueEnd + BeamValueForwardHook + BeamValueBackwardHook +) + +// beamValueValues lists the wire literals by variant ordinal. +var beamValueValues = []string{ + "begin", + "continue", + "end", + "forward hook", + "backward hook", +} + +var beamValueIndex = map[string]BeamValue{ + "begin": BeamValueBegin, + "continue": BeamValueContinue, + "end": BeamValueEnd, + "forward hook": BeamValueForwardHook, + "backward hook": BeamValueBackwardHook, +} + +// TryParseBeamValue matches s against the wire literals. +func TryParseBeamValue(s string) (BeamValue, bool) { + v, ok := beamValueIndex[s] + return v, ok +} + +// ParseBeamValue is lenient: unknown input falls back to the first variant. +func ParseBeamValue(s string) BeamValue { + if v, ok := beamValueIndex[s]; ok { + return v + } + return BeamValueBegin +} + +// String returns the wire literal. +func (v BeamValue) String() string { + if int(v) < 0 || int(v) >= len(beamValueValues) { + return beamValueValues[0] + } + return beamValueValues[v] +} diff --git a/gen/test/go/mx/beater_value.go b/gen/test/go/mx/beater_value.go new file mode 100644 index 000000000..d196a5853 --- /dev/null +++ b/gen/test/go/mx/beater_value.go @@ -0,0 +1,100 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The beater-value type represents pictograms for beaters, mallets, and sticks that do not have +// different materials represented in the pictogram. The finger and hammer values are in addition to +// Stone's list. +type BeaterValue int + +const ( + BeaterValueBow BeaterValue = iota + BeaterValueChimeHammer + BeaterValueCoin + BeaterValueDrumStick + BeaterValueFinger + BeaterValueFingernail + BeaterValueFist + BeaterValueGuiroScraper + BeaterValueHammer + BeaterValueHand + BeaterValueJazzStick + BeaterValueKnittingNeedle + BeaterValueMetalHammer + BeaterValueSlideBrushOnGong + BeaterValueSnareStick + BeaterValueSpoonMallet + BeaterValueSuperball + BeaterValueTriangleBeater + BeaterValueTriangleBeaterPlain + BeaterValueWireBrush +) + +// beaterValueValues lists the wire literals by variant ordinal. +var beaterValueValues = []string{ + "bow", + "chime hammer", + "coin", + "drum stick", + "finger", + "fingernail", + "fist", + "guiro scraper", + "hammer", + "hand", + "jazz stick", + "knitting needle", + "metal hammer", + "slide brush on gong", + "snare stick", + "spoon mallet", + "superball", + "triangle beater", + "triangle beater plain", + "wire brush", +} + +var beaterValueIndex = map[string]BeaterValue{ + "bow": BeaterValueBow, + "chime hammer": BeaterValueChimeHammer, + "coin": BeaterValueCoin, + "drum stick": BeaterValueDrumStick, + "finger": BeaterValueFinger, + "fingernail": BeaterValueFingernail, + "fist": BeaterValueFist, + "guiro scraper": BeaterValueGuiroScraper, + "hammer": BeaterValueHammer, + "hand": BeaterValueHand, + "jazz stick": BeaterValueJazzStick, + "knitting needle": BeaterValueKnittingNeedle, + "metal hammer": BeaterValueMetalHammer, + "slide brush on gong": BeaterValueSlideBrushOnGong, + "snare stick": BeaterValueSnareStick, + "spoon mallet": BeaterValueSpoonMallet, + "superball": BeaterValueSuperball, + "triangle beater": BeaterValueTriangleBeater, + "triangle beater plain": BeaterValueTriangleBeaterPlain, + "wire brush": BeaterValueWireBrush, +} + +// TryParseBeaterValue matches s against the wire literals. +func TryParseBeaterValue(s string) (BeaterValue, bool) { + v, ok := beaterValueIndex[s] + return v, ok +} + +// ParseBeaterValue is lenient: unknown input falls back to the first variant. +func ParseBeaterValue(s string) BeaterValue { + if v, ok := beaterValueIndex[s]; ok { + return v + } + return BeaterValueBow +} + +// String returns the wire literal. +func (v BeaterValue) String() string { + if int(v) < 0 || int(v) >= len(beaterValueValues) { + return beaterValueValues[0] + } + return beaterValueValues[v] +} diff --git a/gen/test/go/mx/breath_mark_value.go b/gen/test/go/mx/breath_mark_value.go new file mode 100644 index 000000000..095ec40bc --- /dev/null +++ b/gen/test/go/mx/breath_mark_value.go @@ -0,0 +1,53 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The breath-mark-value type represents the symbol used for a breath mark. +type BreathMarkValue int + +const ( + BreathMarkValueEmpty BreathMarkValue = iota + BreathMarkValueComma + BreathMarkValueTick + BreathMarkValueUpbow + BreathMarkValueSalzedo +) + +// breathMarkValueValues lists the wire literals by variant ordinal. +var breathMarkValueValues = []string{ + "", + "comma", + "tick", + "upbow", + "salzedo", +} + +var breathMarkValueIndex = map[string]BreathMarkValue{ + "": BreathMarkValueEmpty, + "comma": BreathMarkValueComma, + "tick": BreathMarkValueTick, + "upbow": BreathMarkValueUpbow, + "salzedo": BreathMarkValueSalzedo, +} + +// TryParseBreathMarkValue matches s against the wire literals. +func TryParseBreathMarkValue(s string) (BreathMarkValue, bool) { + v, ok := breathMarkValueIndex[s] + return v, ok +} + +// ParseBreathMarkValue is lenient: unknown input falls back to the first variant. +func ParseBreathMarkValue(s string) BreathMarkValue { + if v, ok := breathMarkValueIndex[s]; ok { + return v + } + return BreathMarkValueEmpty +} + +// String returns the wire literal. +func (v BreathMarkValue) String() string { + if int(v) < 0 || int(v) >= len(breathMarkValueValues) { + return breathMarkValueValues[0] + } + return breathMarkValueValues[v] +} diff --git a/gen/test/go/mx/caesura_value.go b/gen/test/go/mx/caesura_value.go new file mode 100644 index 000000000..660224d5b --- /dev/null +++ b/gen/test/go/mx/caesura_value.go @@ -0,0 +1,56 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The caesura-value type represents the shape of the caesura sign. +type CaesuraValue int + +const ( + CaesuraValueNormal CaesuraValue = iota + CaesuraValueThick + CaesuraValueShort + CaesuraValueCurved + CaesuraValueSingle + CaesuraValueEmpty +) + +// caesuraValueValues lists the wire literals by variant ordinal. +var caesuraValueValues = []string{ + "normal", + "thick", + "short", + "curved", + "single", + "", +} + +var caesuraValueIndex = map[string]CaesuraValue{ + "normal": CaesuraValueNormal, + "thick": CaesuraValueThick, + "short": CaesuraValueShort, + "curved": CaesuraValueCurved, + "single": CaesuraValueSingle, + "": CaesuraValueEmpty, +} + +// TryParseCaesuraValue matches s against the wire literals. +func TryParseCaesuraValue(s string) (CaesuraValue, bool) { + v, ok := caesuraValueIndex[s] + return v, ok +} + +// ParseCaesuraValue is lenient: unknown input falls back to the first variant. +func ParseCaesuraValue(s string) CaesuraValue { + if v, ok := caesuraValueIndex[s]; ok { + return v + } + return CaesuraValueNormal +} + +// String returns the wire literal. +func (v CaesuraValue) String() string { + if int(v) < 0 || int(v) >= len(caesuraValueValues) { + return caesuraValueValues[0] + } + return caesuraValueValues[v] +} diff --git a/gen/test/go/mx/cancel_location.go b/gen/test/go/mx/cancel_location.go new file mode 100644 index 000000000..44387632b --- /dev/null +++ b/gen/test/go/mx/cancel_location.go @@ -0,0 +1,50 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The cancel-location type is used to indicate where a key signature cancellation appears relative +// to a new key signature: to the left, to the right, or before the barline and to the left. It is +// left by default. For mid-measure key elements, a cancel-location of before-barline should be +// treated like a cancel-location of left. +type CancelLocation int + +const ( + CancelLocationLeft CancelLocation = iota + CancelLocationRight + CancelLocationBeforeBarline +) + +// cancelLocationValues lists the wire literals by variant ordinal. +var cancelLocationValues = []string{ + "left", + "right", + "before-barline", +} + +var cancelLocationIndex = map[string]CancelLocation{ + "left": CancelLocationLeft, + "right": CancelLocationRight, + "before-barline": CancelLocationBeforeBarline, +} + +// TryParseCancelLocation matches s against the wire literals. +func TryParseCancelLocation(s string) (CancelLocation, bool) { + v, ok := cancelLocationIndex[s] + return v, ok +} + +// ParseCancelLocation is lenient: unknown input falls back to the first variant. +func ParseCancelLocation(s string) CancelLocation { + if v, ok := cancelLocationIndex[s]; ok { + return v + } + return CancelLocationLeft +} + +// String returns the wire literal. +func (v CancelLocation) String() string { + if int(v) < 0 || int(v) >= len(cancelLocationValues) { + return cancelLocationValues[0] + } + return cancelLocationValues[v] +} diff --git a/gen/test/go/mx/circular_arrow.go b/gen/test/go/mx/circular_arrow.go new file mode 100644 index 000000000..b67501b38 --- /dev/null +++ b/gen/test/go/mx/circular_arrow.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The circular-arrow type represents the direction in which a circular arrow points, using Unicode +// arrow terminology. +type CircularArrow int + +const ( + CircularArrowClockwise CircularArrow = iota + CircularArrowAnticlockwise +) + +// circularArrowValues lists the wire literals by variant ordinal. +var circularArrowValues = []string{ + "clockwise", + "anticlockwise", +} + +var circularArrowIndex = map[string]CircularArrow{ + "clockwise": CircularArrowClockwise, + "anticlockwise": CircularArrowAnticlockwise, +} + +// TryParseCircularArrow matches s against the wire literals. +func TryParseCircularArrow(s string) (CircularArrow, bool) { + v, ok := circularArrowIndex[s] + return v, ok +} + +// ParseCircularArrow is lenient: unknown input falls back to the first variant. +func ParseCircularArrow(s string) CircularArrow { + if v, ok := circularArrowIndex[s]; ok { + return v + } + return CircularArrowClockwise +} + +// String returns the wire literal. +func (v CircularArrow) String() string { + if int(v) < 0 || int(v) >= len(circularArrowValues) { + return circularArrowValues[0] + } + return circularArrowValues[v] +} diff --git a/gen/test/go/mx/clef_sign.go b/gen/test/go/mx/clef_sign.go new file mode 100644 index 000000000..ce59cde8a --- /dev/null +++ b/gen/test/go/mx/clef_sign.go @@ -0,0 +1,62 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The clef-sign element represents the different clef symbols. The jianpu sign indicates that the +// music that follows should be in jianpu numbered notation, just as the TAB sign indicates that the +// music that follows should be in tablature notation. Unlike TAB, a jianpu sign does not correspond +// to a visual clef notation. +type ClefSign int + +const ( + ClefSignG ClefSign = iota + ClefSignF + ClefSignC + ClefSignPercussion + ClefSignTab + ClefSignJianpu + ClefSignNone +) + +// clefSignValues lists the wire literals by variant ordinal. +var clefSignValues = []string{ + "G", + "F", + "C", + "percussion", + "TAB", + "jianpu", + "none", +} + +var clefSignIndex = map[string]ClefSign{ + "G": ClefSignG, + "F": ClefSignF, + "C": ClefSignC, + "percussion": ClefSignPercussion, + "TAB": ClefSignTab, + "jianpu": ClefSignJianpu, + "none": ClefSignNone, +} + +// TryParseClefSign matches s against the wire literals. +func TryParseClefSign(s string) (ClefSign, bool) { + v, ok := clefSignIndex[s] + return v, ok +} + +// ParseClefSign is lenient: unknown input falls back to the first variant. +func ParseClefSign(s string) ClefSign { + if v, ok := clefSignIndex[s]; ok { + return v + } + return ClefSignG +} + +// String returns the wire literal. +func (v ClefSign) String() string { + if int(v) < 0 || int(v) >= len(clefSignValues) { + return clefSignValues[0] + } + return clefSignValues[v] +} diff --git a/gen/test/go/mx/color.go b/gen/test/go/mx/color.go new file mode 100644 index 000000000..a8c384940 --- /dev/null +++ b/gen/test/go/mx/color.go @@ -0,0 +1,25 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The color type indicates the color of an element. Color may be represented as hexadecimal RGB +// triples, as in HTML, or as hexadecimal ARGB tuples, with the A indicating alpha of transparency. +// An alpha value of 00 is totally transparent; FF is totally opaque. If RGB is used, the A value is +// assumed to be FF. For instance, the RGB value "#800080" represents purple. An ARGB value of +// "#40800080" would be a transparent purple. As in SVG 1.1, colors are defined in terms of the sRGB +// color space (IEC 61966). Pattern (not enforced): #[\dA-F]{6}([\dA-F][\dA-F])? +type Color string + +// TryParseColor accepts any string: the wire form is the value. +func TryParseColor(s string) (Color, bool) { + return Color(s), true +} + +func ParseColor(s string) Color { + return Color(s) +} + +// String returns the wire spelling. +func (v Color) String() string { + return string(v) +} diff --git a/gen/test/go/mx/comma_separated_text.go b/gen/test/go/mx/comma_separated_text.go new file mode 100644 index 000000000..a57796cdc --- /dev/null +++ b/gen/test/go/mx/comma_separated_text.go @@ -0,0 +1,21 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The comma-separated-text type is used to specify a comma-separated list of text elements, as is +// used by the font-family attribute. Pattern (not enforced): [^,]+(, ?[^,]+)* +type CommaSeparatedText string + +// TryParseCommaSeparatedText accepts any string: the wire form is the value. +func TryParseCommaSeparatedText(s string) (CommaSeparatedText, bool) { + return CommaSeparatedText(s), true +} + +func ParseCommaSeparatedText(s string) CommaSeparatedText { + return CommaSeparatedText(s) +} + +// String returns the wire spelling. +func (v CommaSeparatedText) String() string { + return string(v) +} diff --git a/gen/test/go/mx/css_font_size.go b/gen/test/go/mx/css_font_size.go new file mode 100644 index 000000000..ac51b01be --- /dev/null +++ b/gen/test/go/mx/css_font_size.go @@ -0,0 +1,60 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The css-font-size type includes the CSS font sizes used as an alternative to a numeric point +// size. +type CSSFontSize int + +const ( + CSSFontSizeXxSmall CSSFontSize = iota + CSSFontSizeXSmall + CSSFontSizeSmall + CSSFontSizeMedium + CSSFontSizeLarge + CSSFontSizeXLarge + CSSFontSizeXxLarge +) + +// cssFontSizeValues lists the wire literals by variant ordinal. +var cssFontSizeValues = []string{ + "xx-small", + "x-small", + "small", + "medium", + "large", + "x-large", + "xx-large", +} + +var cssFontSizeIndex = map[string]CSSFontSize{ + "xx-small": CSSFontSizeXxSmall, + "x-small": CSSFontSizeXSmall, + "small": CSSFontSizeSmall, + "medium": CSSFontSizeMedium, + "large": CSSFontSizeLarge, + "x-large": CSSFontSizeXLarge, + "xx-large": CSSFontSizeXxLarge, +} + +// TryParseCSSFontSize matches s against the wire literals. +func TryParseCSSFontSize(s string) (CSSFontSize, bool) { + v, ok := cssFontSizeIndex[s] + return v, ok +} + +// ParseCSSFontSize is lenient: unknown input falls back to the first variant. +func ParseCSSFontSize(s string) CSSFontSize { + if v, ok := cssFontSizeIndex[s]; ok { + return v + } + return CSSFontSizeXxSmall +} + +// String returns the wire literal. +func (v CSSFontSize) String() string { + if int(v) < 0 || int(v) >= len(cssFontSizeValues) { + return cssFontSizeValues[0] + } + return cssFontSizeValues[v] +} diff --git a/gen/test/go/mx/degree_symbol_value.go b/gen/test/go/mx/degree_symbol_value.go new file mode 100644 index 000000000..4eb874426 --- /dev/null +++ b/gen/test/go/mx/degree_symbol_value.go @@ -0,0 +1,54 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The degree-symbol-value type indicates indicates that a symbol should be used in specifying the +// degree. +type DegreeSymbolValue int + +const ( + DegreeSymbolValueMajor DegreeSymbolValue = iota + DegreeSymbolValueMinor + DegreeSymbolValueAugmented + DegreeSymbolValueDiminished + DegreeSymbolValueHalfDiminished +) + +// degreeSymbolValueValues lists the wire literals by variant ordinal. +var degreeSymbolValueValues = []string{ + "major", + "minor", + "augmented", + "diminished", + "half-diminished", +} + +var degreeSymbolValueIndex = map[string]DegreeSymbolValue{ + "major": DegreeSymbolValueMajor, + "minor": DegreeSymbolValueMinor, + "augmented": DegreeSymbolValueAugmented, + "diminished": DegreeSymbolValueDiminished, + "half-diminished": DegreeSymbolValueHalfDiminished, +} + +// TryParseDegreeSymbolValue matches s against the wire literals. +func TryParseDegreeSymbolValue(s string) (DegreeSymbolValue, bool) { + v, ok := degreeSymbolValueIndex[s] + return v, ok +} + +// ParseDegreeSymbolValue is lenient: unknown input falls back to the first variant. +func ParseDegreeSymbolValue(s string) DegreeSymbolValue { + if v, ok := degreeSymbolValueIndex[s]; ok { + return v + } + return DegreeSymbolValueMajor +} + +// String returns the wire literal. +func (v DegreeSymbolValue) String() string { + if int(v) < 0 || int(v) >= len(degreeSymbolValueValues) { + return degreeSymbolValueValues[0] + } + return degreeSymbolValueValues[v] +} diff --git a/gen/test/go/mx/degree_type_value.go b/gen/test/go/mx/degree_type_value.go new file mode 100644 index 000000000..6ab87e0ee --- /dev/null +++ b/gen/test/go/mx/degree_type_value.go @@ -0,0 +1,48 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The degree-type-value type indicates whether the current degree element is an addition, +// alteration, or subtraction to the kind of the current chord in the harmony element. +type DegreeTypeValue int + +const ( + DegreeTypeValueAdd DegreeTypeValue = iota + DegreeTypeValueAlter + DegreeTypeValueSubtract +) + +// degreeTypeValueValues lists the wire literals by variant ordinal. +var degreeTypeValueValues = []string{ + "add", + "alter", + "subtract", +} + +var degreeTypeValueIndex = map[string]DegreeTypeValue{ + "add": DegreeTypeValueAdd, + "alter": DegreeTypeValueAlter, + "subtract": DegreeTypeValueSubtract, +} + +// TryParseDegreeTypeValue matches s against the wire literals. +func TryParseDegreeTypeValue(s string) (DegreeTypeValue, bool) { + v, ok := degreeTypeValueIndex[s] + return v, ok +} + +// ParseDegreeTypeValue is lenient: unknown input falls back to the first variant. +func ParseDegreeTypeValue(s string) DegreeTypeValue { + if v, ok := degreeTypeValueIndex[s]; ok { + return v + } + return DegreeTypeValueAdd +} + +// String returns the wire literal. +func (v DegreeTypeValue) String() string { + if int(v) < 0 || int(v) >= len(degreeTypeValueValues) { + return degreeTypeValueValues[0] + } + return degreeTypeValueValues[v] +} diff --git a/gen/test/go/mx/distance_type.go b/gen/test/go/mx/distance_type.go new file mode 100644 index 000000000..0b1d011b2 --- /dev/null +++ b/gen/test/go/mx/distance_type.go @@ -0,0 +1,22 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The distance-type defines what type of distance is being defined in a distance element. Values +// include beam and hyphen. This is left as a string so that other application-specific types can be +// defined, but it is made a separate type so that it can be redefined more strictly. +type DistanceType string + +// TryParseDistanceType accepts any string: the wire form is the value. +func TryParseDistanceType(s string) (DistanceType, bool) { + return DistanceType(s), true +} + +func ParseDistanceType(s string) DistanceType { + return DistanceType(s) +} + +// String returns the wire spelling. +func (v DistanceType) String() string { + return string(v) +} diff --git a/gen/test/go/mx/divisions.go b/gen/test/go/mx/divisions.go new file mode 100644 index 000000000..9dabf64c7 --- /dev/null +++ b/gen/test/go/mx/divisions.go @@ -0,0 +1,28 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The divisions type is used to express values in terms of the musical divisions defined by the +// divisions element. It is preferred that these be integer values both for MIDI interoperability +// and to avoid roundoff errors. +type Divisions float64 + +// TryParseDivisions parses s strictly, then clamps into the declared range. +func TryParseDivisions(s string) (Divisions, bool) { + v, ok := tryParseDecimal(s) + if !ok { + return Divisions(0), false + } + return Divisions(v), true +} + +// ParseDivisions is lenient: unparseable input becomes 0, then clamps. +func ParseDivisions(s string) Divisions { + v := parseDecimal(s) + return Divisions(v) +} + +// String returns the wire spelling. +func (v Divisions) String() string { + return formatDecimal(float64(v)) +} diff --git a/gen/test/go/mx/effect.go b/gen/test/go/mx/effect.go new file mode 100644 index 000000000..0d3589a54 --- /dev/null +++ b/gen/test/go/mx/effect.go @@ -0,0 +1,87 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The effect type represents pictograms for sound effect percussion instruments. The cannon, lotus +// flute, and megaphone values are in addition to Stone's list. +type Effect int + +const ( + EffectAnvil Effect = iota + EffectAutoHorn + EffectBirdWhistle + EffectCannon + EffectDuckCall + EffectGunShot + EffectKlaxonHorn + EffectLionsRoar + EffectLotusFlute + EffectMegaphone + EffectPoliceWhistle + EffectSiren + EffectSlideWhistle + EffectThunderSheet + EffectWindMachine + EffectWindWhistle +) + +// effectValues lists the wire literals by variant ordinal. +var effectValues = []string{ + "anvil", + "auto horn", + "bird whistle", + "cannon", + "duck call", + "gun shot", + "klaxon horn", + "lions roar", + "lotus flute", + "megaphone", + "police whistle", + "siren", + "slide whistle", + "thunder sheet", + "wind machine", + "wind whistle", +} + +var effectIndex = map[string]Effect{ + "anvil": EffectAnvil, + "auto horn": EffectAutoHorn, + "bird whistle": EffectBirdWhistle, + "cannon": EffectCannon, + "duck call": EffectDuckCall, + "gun shot": EffectGunShot, + "klaxon horn": EffectKlaxonHorn, + "lions roar": EffectLionsRoar, + "lotus flute": EffectLotusFlute, + "megaphone": EffectMegaphone, + "police whistle": EffectPoliceWhistle, + "siren": EffectSiren, + "slide whistle": EffectSlideWhistle, + "thunder sheet": EffectThunderSheet, + "wind machine": EffectWindMachine, + "wind whistle": EffectWindWhistle, +} + +// TryParseEffect matches s against the wire literals. +func TryParseEffect(s string) (Effect, bool) { + v, ok := effectIndex[s] + return v, ok +} + +// ParseEffect is lenient: unknown input falls back to the first variant. +func ParseEffect(s string) Effect { + if v, ok := effectIndex[s]; ok { + return v + } + return EffectAnvil +} + +// String returns the wire literal. +func (v Effect) String() string { + if int(v) < 0 || int(v) >= len(effectValues) { + return effectValues[0] + } + return effectValues[v] +} diff --git a/gen/test/go/mx/enclosure_shape.go b/gen/test/go/mx/enclosure_shape.go new file mode 100644 index 000000000..a19a1fe16 --- /dev/null +++ b/gen/test/go/mx/enclosure_shape.go @@ -0,0 +1,82 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The enclosure-shape type describes the shape and presence / absence of an enclosure around text +// or symbols. A bracket enclosure is similar to a rectangle with the bottom line missing, as is +// common in jazz notation. +type EnclosureShape int + +const ( + EnclosureShapeRectangle EnclosureShape = iota + EnclosureShapeSquare + EnclosureShapeOval + EnclosureShapeCircle + EnclosureShapeBracket + EnclosureShapeTriangle + EnclosureShapeDiamond + EnclosureShapePentagon + EnclosureShapeHexagon + EnclosureShapeHeptagon + EnclosureShapeOctagon + EnclosureShapeNonagon + EnclosureShapeDecagon + EnclosureShapeNone +) + +// enclosureShapeValues lists the wire literals by variant ordinal. +var enclosureShapeValues = []string{ + "rectangle", + "square", + "oval", + "circle", + "bracket", + "triangle", + "diamond", + "pentagon", + "hexagon", + "heptagon", + "octagon", + "nonagon", + "decagon", + "none", +} + +var enclosureShapeIndex = map[string]EnclosureShape{ + "rectangle": EnclosureShapeRectangle, + "square": EnclosureShapeSquare, + "oval": EnclosureShapeOval, + "circle": EnclosureShapeCircle, + "bracket": EnclosureShapeBracket, + "triangle": EnclosureShapeTriangle, + "diamond": EnclosureShapeDiamond, + "pentagon": EnclosureShapePentagon, + "hexagon": EnclosureShapeHexagon, + "heptagon": EnclosureShapeHeptagon, + "octagon": EnclosureShapeOctagon, + "nonagon": EnclosureShapeNonagon, + "decagon": EnclosureShapeDecagon, + "none": EnclosureShapeNone, +} + +// TryParseEnclosureShape matches s against the wire literals. +func TryParseEnclosureShape(s string) (EnclosureShape, bool) { + v, ok := enclosureShapeIndex[s] + return v, ok +} + +// ParseEnclosureShape is lenient: unknown input falls back to the first variant. +func ParseEnclosureShape(s string) EnclosureShape { + if v, ok := enclosureShapeIndex[s]; ok { + return v + } + return EnclosureShapeRectangle +} + +// String returns the wire literal. +func (v EnclosureShape) String() string { + if int(v) < 0 || int(v) >= len(enclosureShapeValues) { + return enclosureShapeValues[0] + } + return enclosureShapeValues[v] +} diff --git a/gen/test/go/mx/ending_number.go b/gen/test/go/mx/ending_number.go new file mode 100644 index 000000000..b13a58e8e --- /dev/null +++ b/gen/test/go/mx/ending_number.go @@ -0,0 +1,24 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The ending-number type is used to specify either a comma-separated list of positive integers +// without leading zeros, or a string of zero or more spaces. It is used for the number attribute of +// the ending element. The zero or more spaces version is used when software knows that an ending is +// present, but cannot determine the type of the ending. Pattern (not enforced): ([ +// ]*)|([1-9][0-9]*(, ?[1-9][0-9]*)*) +type EndingNumber string + +// TryParseEndingNumber accepts any string: the wire form is the value. +func TryParseEndingNumber(s string) (EndingNumber, bool) { + return EndingNumber(s), true +} + +func ParseEndingNumber(s string) EndingNumber { + return EndingNumber(s) +} + +// String returns the wire spelling. +func (v EndingNumber) String() string { + return string(v) +} diff --git a/gen/test/go/mx/fan.go b/gen/test/go/mx/fan.go new file mode 100644 index 000000000..b6176021d --- /dev/null +++ b/gen/test/go/mx/fan.go @@ -0,0 +1,48 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The fan type represents the type of beam fanning present on a note, used to represent +// accelerandos and ritardandos. +type Fan int + +const ( + FanAccel Fan = iota + FanRit + FanNone +) + +// fanValues lists the wire literals by variant ordinal. +var fanValues = []string{ + "accel", + "rit", + "none", +} + +var fanIndex = map[string]Fan{ + "accel": FanAccel, + "rit": FanRit, + "none": FanNone, +} + +// TryParseFan matches s against the wire literals. +func TryParseFan(s string) (Fan, bool) { + v, ok := fanIndex[s] + return v, ok +} + +// ParseFan is lenient: unknown input falls back to the first variant. +func ParseFan(s string) Fan { + if v, ok := fanIndex[s]; ok { + return v + } + return FanAccel +} + +// String returns the wire literal. +func (v Fan) String() string { + if int(v) < 0 || int(v) >= len(fanValues) { + return fanValues[0] + } + return fanValues[v] +} diff --git a/gen/test/go/mx/fermata_shape.go b/gen/test/go/mx/fermata_shape.go new file mode 100644 index 000000000..976a3a474 --- /dev/null +++ b/gen/test/go/mx/fermata_shape.go @@ -0,0 +1,66 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The fermata-shape type represents the shape of the fermata sign. The empty value is equivalent to +// the normal value. +type FermataShape int + +const ( + FermataShapeNormal FermataShape = iota + FermataShapeAngled + FermataShapeSquare + FermataShapeDoubleAngled + FermataShapeDoubleSquare + FermataShapeDoubleDot + FermataShapeHalfCurve + FermataShapeCurlew + FermataShapeEmpty +) + +// fermataShapeValues lists the wire literals by variant ordinal. +var fermataShapeValues = []string{ + "normal", + "angled", + "square", + "double-angled", + "double-square", + "double-dot", + "half-curve", + "curlew", + "", +} + +var fermataShapeIndex = map[string]FermataShape{ + "normal": FermataShapeNormal, + "angled": FermataShapeAngled, + "square": FermataShapeSquare, + "double-angled": FermataShapeDoubleAngled, + "double-square": FermataShapeDoubleSquare, + "double-dot": FermataShapeDoubleDot, + "half-curve": FermataShapeHalfCurve, + "curlew": FermataShapeCurlew, + "": FermataShapeEmpty, +} + +// TryParseFermataShape matches s against the wire literals. +func TryParseFermataShape(s string) (FermataShape, bool) { + v, ok := fermataShapeIndex[s] + return v, ok +} + +// ParseFermataShape is lenient: unknown input falls back to the first variant. +func ParseFermataShape(s string) FermataShape { + if v, ok := fermataShapeIndex[s]; ok { + return v + } + return FermataShapeNormal +} + +// String returns the wire literal. +func (v FermataShape) String() string { + if int(v) < 0 || int(v) >= len(fermataShapeValues) { + return fermataShapeValues[0] + } + return fermataShapeValues[v] +} diff --git a/gen/test/go/mx/fifths.go b/gen/test/go/mx/fifths.go new file mode 100644 index 000000000..84634b496 --- /dev/null +++ b/gen/test/go/mx/fifths.go @@ -0,0 +1,28 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The fifths type represents the number of flats or sharps in a traditional key signature. Negative +// numbers are used for flats and positive numbers for sharps, reflecting the key's placement within +// the circle of fifths (hence the type name). +type Fifths int + +// TryParseFifths parses s strictly, then clamps into the declared range. +func TryParseFifths(s string) (Fifths, bool) { + v, ok := tryParseInt(s) + if !ok { + return Fifths(0), false + } + return Fifths(v), true +} + +// ParseFifths is lenient: unparseable input becomes 0, then clamps. +func ParseFifths(s string) Fifths { + v := parseInt(s) + return Fifths(v) +} + +// String returns the wire spelling. +func (v Fifths) String() string { + return formatInt(int(v)) +} diff --git a/gen/test/go/mx/font_size.go b/gen/test/go/mx/font_size.go new file mode 100644 index 000000000..0a1212488 --- /dev/null +++ b/gen/test/go/mx/font_size.go @@ -0,0 +1,46 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The font-size can be one of the CSS font sizes or a numeric point size. +type FontSize struct { + Kind FontSizeKind + Decimal float64 + CSSFontSize CSSFontSize +} + +type FontSizeKind int + +const ( + FontSizeKindDecimal FontSizeKind = iota + FontSizeKindCSSFontSize +) + +// TryParseFontSize tries each union member in schema order. +func TryParseFontSize(s string) (FontSize, bool) { + if v, ok := tryParseDecimal(s); ok { + return FontSize{Kind: FontSizeKindDecimal, Decimal: v}, true + } + if v, ok := TryParseCSSFontSize(s); ok { + return FontSize{Kind: FontSizeKindCSSFontSize, CSSFontSize: v}, true + } + return FontSize{}, false +} + +// ParseFontSize is lenient: when no member matches, the first member +// absorbs the input under its own leniency rules. +func ParseFontSize(s string) FontSize { + if v, ok := TryParseFontSize(s); ok { + return v + } + return FontSize{Kind: FontSizeKindDecimal, Decimal: parseDecimal(s)} +} + +// String returns the wire spelling of whichever member is held. +func (v FontSize) String() string { + switch v.Kind { + case FontSizeKindCSSFontSize: + return v.CSSFontSize.String() + } + return formatDecimal(v.Decimal) +} diff --git a/gen/test/go/mx/font_style.go b/gen/test/go/mx/font_style.go new file mode 100644 index 000000000..d489c955a --- /dev/null +++ b/gen/test/go/mx/font_style.go @@ -0,0 +1,44 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The font-style type represents a simplified version of the CSS font-style property. +type FontStyle int + +const ( + FontStyleNormal FontStyle = iota + FontStyleItalic +) + +// fontStyleValues lists the wire literals by variant ordinal. +var fontStyleValues = []string{ + "normal", + "italic", +} + +var fontStyleIndex = map[string]FontStyle{ + "normal": FontStyleNormal, + "italic": FontStyleItalic, +} + +// TryParseFontStyle matches s against the wire literals. +func TryParseFontStyle(s string) (FontStyle, bool) { + v, ok := fontStyleIndex[s] + return v, ok +} + +// ParseFontStyle is lenient: unknown input falls back to the first variant. +func ParseFontStyle(s string) FontStyle { + if v, ok := fontStyleIndex[s]; ok { + return v + } + return FontStyleNormal +} + +// String returns the wire literal. +func (v FontStyle) String() string { + if int(v) < 0 || int(v) >= len(fontStyleValues) { + return fontStyleValues[0] + } + return fontStyleValues[v] +} diff --git a/gen/test/go/mx/font_weight.go b/gen/test/go/mx/font_weight.go new file mode 100644 index 000000000..c56c12bc9 --- /dev/null +++ b/gen/test/go/mx/font_weight.go @@ -0,0 +1,44 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The font-weight type represents a simplified version of the CSS font-weight property. +type FontWeight int + +const ( + FontWeightNormal FontWeight = iota + FontWeightBold +) + +// fontWeightValues lists the wire literals by variant ordinal. +var fontWeightValues = []string{ + "normal", + "bold", +} + +var fontWeightIndex = map[string]FontWeight{ + "normal": FontWeightNormal, + "bold": FontWeightBold, +} + +// TryParseFontWeight matches s against the wire literals. +func TryParseFontWeight(s string) (FontWeight, bool) { + v, ok := fontWeightIndex[s] + return v, ok +} + +// ParseFontWeight is lenient: unknown input falls back to the first variant. +func ParseFontWeight(s string) FontWeight { + if v, ok := fontWeightIndex[s]; ok { + return v + } + return FontWeightNormal +} + +// String returns the wire literal. +func (v FontWeight) String() string { + if int(v) < 0 || int(v) >= len(fontWeightValues) { + return fontWeightValues[0] + } + return fontWeightValues[v] +} diff --git a/gen/test/go/mx/glass_value.go b/gen/test/go/mx/glass_value.go new file mode 100644 index 000000000..f33292149 --- /dev/null +++ b/gen/test/go/mx/glass_value.go @@ -0,0 +1,47 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The glass-value type represents pictograms for glass percussion instruments. +type GlassValue int + +const ( + GlassValueGlassHarmonica GlassValue = iota + GlassValueGlassHarp + GlassValueWindChimes +) + +// glassValueValues lists the wire literals by variant ordinal. +var glassValueValues = []string{ + "glass harmonica", + "glass harp", + "wind chimes", +} + +var glassValueIndex = map[string]GlassValue{ + "glass harmonica": GlassValueGlassHarmonica, + "glass harp": GlassValueGlassHarp, + "wind chimes": GlassValueWindChimes, +} + +// TryParseGlassValue matches s against the wire literals. +func TryParseGlassValue(s string) (GlassValue, bool) { + v, ok := glassValueIndex[s] + return v, ok +} + +// ParseGlassValue is lenient: unknown input falls back to the first variant. +func ParseGlassValue(s string) GlassValue { + if v, ok := glassValueIndex[s]; ok { + return v + } + return GlassValueGlassHarmonica +} + +// String returns the wire literal. +func (v GlassValue) String() string { + if int(v) < 0 || int(v) >= len(glassValueValues) { + return glassValueValues[0] + } + return glassValueValues[v] +} diff --git a/gen/test/go/mx/glyph_type.go b/gen/test/go/mx/glyph_type.go new file mode 100644 index 000000000..c08391eae --- /dev/null +++ b/gen/test/go/mx/glyph_type.go @@ -0,0 +1,31 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The glyph-type defines what type of glyph is being defined in a glyph element. Values include +// quarter-rest, g-clef-ottava-bassa, c-clef, f-clef, percussion-clef, octave-shift-up-8, +// octave-shift-down-8, octave-shift-continue-8, octave-shift-down-15, octave-shift-up-15, +// octave-shift-continue-15, octave-shift-down-22, octave-shift-up-22, and octave-shift-continue-22. +// This is left as a string so that other application-specific types can be defined, but it is made +// a separate type so that it can be redefined more strictly. A quarter-rest type specifies the +// glyph to use when a note has a rest element and a type value of quarter. The c-clef, f-clef, and +// percussion-clef types specify the glyph to use when a clef sign element value is C, F, or +// percussion respectively. The g-clef-ottava-bassa type specifies the glyph to use when a clef sign +// element value is G and the clef-octave-change element value is -1. The octave-shift types specify +// the glyph to use when an octave-shift type attribute value is up, down, or continue and the +// octave-shift size attribute value is 8, 15, or 22. +type GlyphType string + +// TryParseGlyphType accepts any string: the wire form is the value. +func TryParseGlyphType(s string) (GlyphType, bool) { + return GlyphType(s), true +} + +func ParseGlyphType(s string) GlyphType { + return GlyphType(s) +} + +// String returns the wire spelling. +func (v GlyphType) String() string { + return string(v) +} diff --git a/gen/test/go/mx/group_barline_value.go b/gen/test/go/mx/group_barline_value.go new file mode 100644 index 000000000..3f2873ad8 --- /dev/null +++ b/gen/test/go/mx/group_barline_value.go @@ -0,0 +1,47 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The group-barline-value type indicates if the group should have common barlines. +type GroupBarlineValue int + +const ( + GroupBarlineValueYes GroupBarlineValue = iota + GroupBarlineValueNo + GroupBarlineValueMensurstrich +) + +// groupBarlineValueValues lists the wire literals by variant ordinal. +var groupBarlineValueValues = []string{ + "yes", + "no", + "Mensurstrich", +} + +var groupBarlineValueIndex = map[string]GroupBarlineValue{ + "yes": GroupBarlineValueYes, + "no": GroupBarlineValueNo, + "Mensurstrich": GroupBarlineValueMensurstrich, +} + +// TryParseGroupBarlineValue matches s against the wire literals. +func TryParseGroupBarlineValue(s string) (GroupBarlineValue, bool) { + v, ok := groupBarlineValueIndex[s] + return v, ok +} + +// ParseGroupBarlineValue is lenient: unknown input falls back to the first variant. +func ParseGroupBarlineValue(s string) GroupBarlineValue { + if v, ok := groupBarlineValueIndex[s]; ok { + return v + } + return GroupBarlineValueYes +} + +// String returns the wire literal. +func (v GroupBarlineValue) String() string { + if int(v) < 0 || int(v) >= len(groupBarlineValueValues) { + return groupBarlineValueValues[0] + } + return groupBarlineValueValues[v] +} diff --git a/gen/test/go/mx/group_symbol_value.go b/gen/test/go/mx/group_symbol_value.go new file mode 100644 index 000000000..15599dd19 --- /dev/null +++ b/gen/test/go/mx/group_symbol_value.go @@ -0,0 +1,54 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The group-symbol-value type indicates how the symbol for a group is indicated in the score. The +// default value is none. +type GroupSymbolValue int + +const ( + GroupSymbolValueNone GroupSymbolValue = iota + GroupSymbolValueBrace + GroupSymbolValueLine + GroupSymbolValueBracket + GroupSymbolValueSquare +) + +// groupSymbolValueValues lists the wire literals by variant ordinal. +var groupSymbolValueValues = []string{ + "none", + "brace", + "line", + "bracket", + "square", +} + +var groupSymbolValueIndex = map[string]GroupSymbolValue{ + "none": GroupSymbolValueNone, + "brace": GroupSymbolValueBrace, + "line": GroupSymbolValueLine, + "bracket": GroupSymbolValueBracket, + "square": GroupSymbolValueSquare, +} + +// TryParseGroupSymbolValue matches s against the wire literals. +func TryParseGroupSymbolValue(s string) (GroupSymbolValue, bool) { + v, ok := groupSymbolValueIndex[s] + return v, ok +} + +// ParseGroupSymbolValue is lenient: unknown input falls back to the first variant. +func ParseGroupSymbolValue(s string) GroupSymbolValue { + if v, ok := groupSymbolValueIndex[s]; ok { + return v + } + return GroupSymbolValueNone +} + +// String returns the wire literal. +func (v GroupSymbolValue) String() string { + if int(v) < 0 || int(v) >= len(groupSymbolValueValues) { + return groupSymbolValueValues[0] + } + return groupSymbolValueValues[v] +} diff --git a/gen/test/go/mx/handbell_value.go b/gen/test/go/mx/handbell_value.go new file mode 100644 index 000000000..bb04ea269 --- /dev/null +++ b/gen/test/go/mx/handbell_value.go @@ -0,0 +1,74 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The handbell-value type represents the type of handbell technique being notated. +type HandbellValue int + +const ( + HandbellValueBelltree HandbellValue = iota + HandbellValueDamp + HandbellValueEcho + HandbellValueGyro + HandbellValueHandMartellato + HandbellValueMalletLift + HandbellValueMalletTable + HandbellValueMartellato + HandbellValueMartellatoLift + HandbellValueMutedMartellato + HandbellValuePluckLift + HandbellValueSwing +) + +// handbellValueValues lists the wire literals by variant ordinal. +var handbellValueValues = []string{ + "belltree", + "damp", + "echo", + "gyro", + "hand martellato", + "mallet lift", + "mallet table", + "martellato", + "martellato lift", + "muted martellato", + "pluck lift", + "swing", +} + +var handbellValueIndex = map[string]HandbellValue{ + "belltree": HandbellValueBelltree, + "damp": HandbellValueDamp, + "echo": HandbellValueEcho, + "gyro": HandbellValueGyro, + "hand martellato": HandbellValueHandMartellato, + "mallet lift": HandbellValueMalletLift, + "mallet table": HandbellValueMalletTable, + "martellato": HandbellValueMartellato, + "martellato lift": HandbellValueMartellatoLift, + "muted martellato": HandbellValueMutedMartellato, + "pluck lift": HandbellValuePluckLift, + "swing": HandbellValueSwing, +} + +// TryParseHandbellValue matches s against the wire literals. +func TryParseHandbellValue(s string) (HandbellValue, bool) { + v, ok := handbellValueIndex[s] + return v, ok +} + +// ParseHandbellValue is lenient: unknown input falls back to the first variant. +func ParseHandbellValue(s string) HandbellValue { + if v, ok := handbellValueIndex[s]; ok { + return v + } + return HandbellValueBelltree +} + +// String returns the wire literal. +func (v HandbellValue) String() string { + if int(v) < 0 || int(v) >= len(handbellValueValues) { + return handbellValueValues[0] + } + return handbellValueValues[v] +} diff --git a/gen/test/go/mx/harmon_closed_location.go b/gen/test/go/mx/harmon_closed_location.go new file mode 100644 index 000000000..edc134301 --- /dev/null +++ b/gen/test/go/mx/harmon_closed_location.go @@ -0,0 +1,51 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The harmon-closed-location type indicates which portion of the symbol is filled in when the +// corresponding harmon-closed-value is half. +type HarmonClosedLocation int + +const ( + HarmonClosedLocationRight HarmonClosedLocation = iota + HarmonClosedLocationBottom + HarmonClosedLocationLeft + HarmonClosedLocationTop +) + +// harmonClosedLocationValues lists the wire literals by variant ordinal. +var harmonClosedLocationValues = []string{ + "right", + "bottom", + "left", + "top", +} + +var harmonClosedLocationIndex = map[string]HarmonClosedLocation{ + "right": HarmonClosedLocationRight, + "bottom": HarmonClosedLocationBottom, + "left": HarmonClosedLocationLeft, + "top": HarmonClosedLocationTop, +} + +// TryParseHarmonClosedLocation matches s against the wire literals. +func TryParseHarmonClosedLocation(s string) (HarmonClosedLocation, bool) { + v, ok := harmonClosedLocationIndex[s] + return v, ok +} + +// ParseHarmonClosedLocation is lenient: unknown input falls back to the first variant. +func ParseHarmonClosedLocation(s string) HarmonClosedLocation { + if v, ok := harmonClosedLocationIndex[s]; ok { + return v + } + return HarmonClosedLocationRight +} + +// String returns the wire literal. +func (v HarmonClosedLocation) String() string { + if int(v) < 0 || int(v) >= len(harmonClosedLocationValues) { + return harmonClosedLocationValues[0] + } + return harmonClosedLocationValues[v] +} diff --git a/gen/test/go/mx/harmon_closed_value.go b/gen/test/go/mx/harmon_closed_value.go new file mode 100644 index 000000000..56f7fac6f --- /dev/null +++ b/gen/test/go/mx/harmon_closed_value.go @@ -0,0 +1,47 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The harmon-closed-value type represents whether the harmon mute is closed, open, or half-open. +type HarmonClosedValue int + +const ( + HarmonClosedValueYes HarmonClosedValue = iota + HarmonClosedValueNo + HarmonClosedValueHalf +) + +// harmonClosedValueValues lists the wire literals by variant ordinal. +var harmonClosedValueValues = []string{ + "yes", + "no", + "half", +} + +var harmonClosedValueIndex = map[string]HarmonClosedValue{ + "yes": HarmonClosedValueYes, + "no": HarmonClosedValueNo, + "half": HarmonClosedValueHalf, +} + +// TryParseHarmonClosedValue matches s against the wire literals. +func TryParseHarmonClosedValue(s string) (HarmonClosedValue, bool) { + v, ok := harmonClosedValueIndex[s] + return v, ok +} + +// ParseHarmonClosedValue is lenient: unknown input falls back to the first variant. +func ParseHarmonClosedValue(s string) HarmonClosedValue { + if v, ok := harmonClosedValueIndex[s]; ok { + return v + } + return HarmonClosedValueYes +} + +// String returns the wire literal. +func (v HarmonClosedValue) String() string { + if int(v) < 0 || int(v) >= len(harmonClosedValueValues) { + return harmonClosedValueValues[0] + } + return harmonClosedValueValues[v] +} diff --git a/gen/test/go/mx/harmony_type.go b/gen/test/go/mx/harmony_type.go new file mode 100644 index 000000000..75f526dd5 --- /dev/null +++ b/gen/test/go/mx/harmony_type.go @@ -0,0 +1,49 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The harmony-type type differentiates different types of harmonies when alternate harmonies are +// possible. Explicit harmonies have all note present in the music; implied have some notes missing +// but implied; alternate represents alternate analyses. +type HarmonyType int + +const ( + HarmonyTypeExplicit HarmonyType = iota + HarmonyTypeImplied + HarmonyTypeAlternate +) + +// harmonyTypeValues lists the wire literals by variant ordinal. +var harmonyTypeValues = []string{ + "explicit", + "implied", + "alternate", +} + +var harmonyTypeIndex = map[string]HarmonyType{ + "explicit": HarmonyTypeExplicit, + "implied": HarmonyTypeImplied, + "alternate": HarmonyTypeAlternate, +} + +// TryParseHarmonyType matches s against the wire literals. +func TryParseHarmonyType(s string) (HarmonyType, bool) { + v, ok := harmonyTypeIndex[s] + return v, ok +} + +// ParseHarmonyType is lenient: unknown input falls back to the first variant. +func ParseHarmonyType(s string) HarmonyType { + if v, ok := harmonyTypeIndex[s]; ok { + return v + } + return HarmonyTypeExplicit +} + +// String returns the wire literal. +func (v HarmonyType) String() string { + if int(v) < 0 || int(v) >= len(harmonyTypeValues) { + return harmonyTypeValues[0] + } + return harmonyTypeValues[v] +} diff --git a/gen/test/go/mx/hole_closed_location.go b/gen/test/go/mx/hole_closed_location.go new file mode 100644 index 000000000..c8485ed7c --- /dev/null +++ b/gen/test/go/mx/hole_closed_location.go @@ -0,0 +1,51 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The hole-closed-location type indicates which portion of the hole is filled in when the +// corresponding hole-closed-value is half. +type HoleClosedLocation int + +const ( + HoleClosedLocationRight HoleClosedLocation = iota + HoleClosedLocationBottom + HoleClosedLocationLeft + HoleClosedLocationTop +) + +// holeClosedLocationValues lists the wire literals by variant ordinal. +var holeClosedLocationValues = []string{ + "right", + "bottom", + "left", + "top", +} + +var holeClosedLocationIndex = map[string]HoleClosedLocation{ + "right": HoleClosedLocationRight, + "bottom": HoleClosedLocationBottom, + "left": HoleClosedLocationLeft, + "top": HoleClosedLocationTop, +} + +// TryParseHoleClosedLocation matches s against the wire literals. +func TryParseHoleClosedLocation(s string) (HoleClosedLocation, bool) { + v, ok := holeClosedLocationIndex[s] + return v, ok +} + +// ParseHoleClosedLocation is lenient: unknown input falls back to the first variant. +func ParseHoleClosedLocation(s string) HoleClosedLocation { + if v, ok := holeClosedLocationIndex[s]; ok { + return v + } + return HoleClosedLocationRight +} + +// String returns the wire literal. +func (v HoleClosedLocation) String() string { + if int(v) < 0 || int(v) >= len(holeClosedLocationValues) { + return holeClosedLocationValues[0] + } + return holeClosedLocationValues[v] +} diff --git a/gen/test/go/mx/hole_closed_value.go b/gen/test/go/mx/hole_closed_value.go new file mode 100644 index 000000000..557ac020d --- /dev/null +++ b/gen/test/go/mx/hole_closed_value.go @@ -0,0 +1,47 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The hole-closed-value type represents whether the hole is closed, open, or half-open. +type HoleClosedValue int + +const ( + HoleClosedValueYes HoleClosedValue = iota + HoleClosedValueNo + HoleClosedValueHalf +) + +// holeClosedValueValues lists the wire literals by variant ordinal. +var holeClosedValueValues = []string{ + "yes", + "no", + "half", +} + +var holeClosedValueIndex = map[string]HoleClosedValue{ + "yes": HoleClosedValueYes, + "no": HoleClosedValueNo, + "half": HoleClosedValueHalf, +} + +// TryParseHoleClosedValue matches s against the wire literals. +func TryParseHoleClosedValue(s string) (HoleClosedValue, bool) { + v, ok := holeClosedValueIndex[s] + return v, ok +} + +// ParseHoleClosedValue is lenient: unknown input falls back to the first variant. +func ParseHoleClosedValue(s string) HoleClosedValue { + if v, ok := holeClosedValueIndex[s]; ok { + return v + } + return HoleClosedValueYes +} + +// String returns the wire literal. +func (v HoleClosedValue) String() string { + if int(v) < 0 || int(v) >= len(holeClosedValueValues) { + return holeClosedValueValues[0] + } + return holeClosedValueValues[v] +} diff --git a/gen/test/go/mx/kind_value.go b/gen/test/go/mx/kind_value.go new file mode 100644 index 000000000..787a8c1c6 --- /dev/null +++ b/gen/test/go/mx/kind_value.go @@ -0,0 +1,153 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// A kind-value indicates the type of chord. Degree elements can then add, subtract, or alter from +// these starting points. Values include: Triads: major (major third, perfect fifth) minor (minor +// third, perfect fifth) augmented (major third, augmented fifth) diminished (minor third, +// diminished fifth) Sevenths: dominant (major triad, minor seventh) major-seventh (major triad, +// major seventh) minor-seventh (minor triad, minor seventh) diminished-seventh (diminished triad, +// diminished seventh) augmented-seventh (augmented triad, minor seventh) half-diminished +// (diminished triad, minor seventh) major-minor (minor triad, major seventh) Sixths: major-sixth +// (major triad, added sixth) minor-sixth (minor triad, added sixth) Ninths: dominant-ninth +// (dominant-seventh, major ninth) major-ninth (major-seventh, major ninth) minor-ninth +// (minor-seventh, major ninth) 11ths (usually as the basis for alteration): dominant-11th +// (dominant-ninth, perfect 11th) major-11th (major-ninth, perfect 11th) minor-11th (minor-ninth, +// perfect 11th) 13ths (usually as the basis for alteration): dominant-13th (dominant-11th, major +// 13th) major-13th (major-11th, major 13th) minor-13th (minor-11th, major 13th) Suspended: +// suspended-second (major second, perfect fifth) suspended-fourth (perfect fourth, perfect fifth) +// Functional sixths: Neapolitan Italian French German Other: pedal (pedal-point bass) power +// (perfect fifth) Tristan The "other" kind is used when the harmony is entirely composed of add +// elements. The "none" kind is used to explicitly encode absence of chords or functional harmony. +type KindValue int + +const ( + KindValueMajor KindValue = iota + KindValueMinor + KindValueAugmented + KindValueDiminished + KindValueDominant + KindValueMajorSeventh + KindValueMinorSeventh + KindValueDiminishedSeventh + KindValueAugmentedSeventh + KindValueHalfDiminished + KindValueMajorMinor + KindValueMajorSixth + KindValueMinorSixth + KindValueDominantNinth + KindValueMajorNinth + KindValueMinorNinth + KindValueDominant11th + KindValueMajor11th + KindValueMinor11th + KindValueDominant13th + KindValueMajor13th + KindValueMinor13th + KindValueSuspendedSecond + KindValueSuspendedFourth + KindValueNeapolitan + KindValueItalian + KindValueFrench + KindValueGerman + KindValuePedal + KindValuePower + KindValueTristan + KindValueOther + KindValueNone +) + +// kindValueValues lists the wire literals by variant ordinal. +var kindValueValues = []string{ + "major", + "minor", + "augmented", + "diminished", + "dominant", + "major-seventh", + "minor-seventh", + "diminished-seventh", + "augmented-seventh", + "half-diminished", + "major-minor", + "major-sixth", + "minor-sixth", + "dominant-ninth", + "major-ninth", + "minor-ninth", + "dominant-11th", + "major-11th", + "minor-11th", + "dominant-13th", + "major-13th", + "minor-13th", + "suspended-second", + "suspended-fourth", + "Neapolitan", + "Italian", + "French", + "German", + "pedal", + "power", + "Tristan", + "other", + "none", +} + +var kindValueIndex = map[string]KindValue{ + "major": KindValueMajor, + "minor": KindValueMinor, + "augmented": KindValueAugmented, + "diminished": KindValueDiminished, + "dominant": KindValueDominant, + "major-seventh": KindValueMajorSeventh, + "minor-seventh": KindValueMinorSeventh, + "diminished-seventh": KindValueDiminishedSeventh, + "augmented-seventh": KindValueAugmentedSeventh, + "half-diminished": KindValueHalfDiminished, + "major-minor": KindValueMajorMinor, + "major-sixth": KindValueMajorSixth, + "minor-sixth": KindValueMinorSixth, + "dominant-ninth": KindValueDominantNinth, + "major-ninth": KindValueMajorNinth, + "minor-ninth": KindValueMinorNinth, + "dominant-11th": KindValueDominant11th, + "major-11th": KindValueMajor11th, + "minor-11th": KindValueMinor11th, + "dominant-13th": KindValueDominant13th, + "major-13th": KindValueMajor13th, + "minor-13th": KindValueMinor13th, + "suspended-second": KindValueSuspendedSecond, + "suspended-fourth": KindValueSuspendedFourth, + "Neapolitan": KindValueNeapolitan, + "Italian": KindValueItalian, + "French": KindValueFrench, + "German": KindValueGerman, + "pedal": KindValuePedal, + "power": KindValuePower, + "Tristan": KindValueTristan, + "other": KindValueOther, + "none": KindValueNone, +} + +// TryParseKindValue matches s against the wire literals. +func TryParseKindValue(s string) (KindValue, bool) { + v, ok := kindValueIndex[s] + return v, ok +} + +// ParseKindValue is lenient: unknown input falls back to the first variant. +func ParseKindValue(s string) KindValue { + if v, ok := kindValueIndex[s]; ok { + return v + } + return KindValueMajor +} + +// String returns the wire literal. +func (v KindValue) String() string { + if int(v) < 0 || int(v) >= len(kindValueValues) { + return kindValueValues[0] + } + return kindValueValues[v] +} diff --git a/gen/test/go/mx/left_center_right.go b/gen/test/go/mx/left_center_right.go new file mode 100644 index 000000000..a6a2d79ec --- /dev/null +++ b/gen/test/go/mx/left_center_right.go @@ -0,0 +1,47 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The left-center-right type is used to define horizontal alignment and text justification. +type LeftCenterRight int + +const ( + LeftCenterRightLeft LeftCenterRight = iota + LeftCenterRightCenter + LeftCenterRightRight +) + +// leftCenterRightValues lists the wire literals by variant ordinal. +var leftCenterRightValues = []string{ + "left", + "center", + "right", +} + +var leftCenterRightIndex = map[string]LeftCenterRight{ + "left": LeftCenterRightLeft, + "center": LeftCenterRightCenter, + "right": LeftCenterRightRight, +} + +// TryParseLeftCenterRight matches s against the wire literals. +func TryParseLeftCenterRight(s string) (LeftCenterRight, bool) { + v, ok := leftCenterRightIndex[s] + return v, ok +} + +// ParseLeftCenterRight is lenient: unknown input falls back to the first variant. +func ParseLeftCenterRight(s string) LeftCenterRight { + if v, ok := leftCenterRightIndex[s]; ok { + return v + } + return LeftCenterRightLeft +} + +// String returns the wire literal. +func (v LeftCenterRight) String() string { + if int(v) < 0 || int(v) >= len(leftCenterRightValues) { + return leftCenterRightValues[0] + } + return leftCenterRightValues[v] +} diff --git a/gen/test/go/mx/left_right.go b/gen/test/go/mx/left_right.go new file mode 100644 index 000000000..3affe0808 --- /dev/null +++ b/gen/test/go/mx/left_right.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The left-right type is used to indicate whether one element appears to the left or the right of +// another element. +type LeftRight int + +const ( + LeftRightLeft LeftRight = iota + LeftRightRight +) + +// leftRightValues lists the wire literals by variant ordinal. +var leftRightValues = []string{ + "left", + "right", +} + +var leftRightIndex = map[string]LeftRight{ + "left": LeftRightLeft, + "right": LeftRightRight, +} + +// TryParseLeftRight matches s against the wire literals. +func TryParseLeftRight(s string) (LeftRight, bool) { + v, ok := leftRightIndex[s] + return v, ok +} + +// ParseLeftRight is lenient: unknown input falls back to the first variant. +func ParseLeftRight(s string) LeftRight { + if v, ok := leftRightIndex[s]; ok { + return v + } + return LeftRightLeft +} + +// String returns the wire literal. +func (v LeftRight) String() string { + if int(v) < 0 || int(v) >= len(leftRightValues) { + return leftRightValues[0] + } + return leftRightValues[v] +} diff --git a/gen/test/go/mx/line_end.go b/gen/test/go/mx/line_end.go new file mode 100644 index 000000000..4092a3e81 --- /dev/null +++ b/gen/test/go/mx/line_end.go @@ -0,0 +1,54 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The line-end type specifies if there is a jog up or down (or both), an arrow, or nothing at the +// start or end of a bracket. +type LineEnd int + +const ( + LineEndUp LineEnd = iota + LineEndDown + LineEndBoth + LineEndArrow + LineEndNone +) + +// lineEndValues lists the wire literals by variant ordinal. +var lineEndValues = []string{ + "up", + "down", + "both", + "arrow", + "none", +} + +var lineEndIndex = map[string]LineEnd{ + "up": LineEndUp, + "down": LineEndDown, + "both": LineEndBoth, + "arrow": LineEndArrow, + "none": LineEndNone, +} + +// TryParseLineEnd matches s against the wire literals. +func TryParseLineEnd(s string) (LineEnd, bool) { + v, ok := lineEndIndex[s] + return v, ok +} + +// ParseLineEnd is lenient: unknown input falls back to the first variant. +func ParseLineEnd(s string) LineEnd { + if v, ok := lineEndIndex[s]; ok { + return v + } + return LineEndUp +} + +// String returns the wire literal. +func (v LineEnd) String() string { + if int(v) < 0 || int(v) >= len(lineEndValues) { + return lineEndValues[0] + } + return lineEndValues[v] +} diff --git a/gen/test/go/mx/line_length.go b/gen/test/go/mx/line_length.go new file mode 100644 index 000000000..9b3cc5dd4 --- /dev/null +++ b/gen/test/go/mx/line_length.go @@ -0,0 +1,48 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The line-length type distinguishes between different line lengths for doit, falloff, plop, and +// scoop articulations. +type LineLength int + +const ( + LineLengthShort LineLength = iota + LineLengthMedium + LineLengthLong +) + +// lineLengthValues lists the wire literals by variant ordinal. +var lineLengthValues = []string{ + "short", + "medium", + "long", +} + +var lineLengthIndex = map[string]LineLength{ + "short": LineLengthShort, + "medium": LineLengthMedium, + "long": LineLengthLong, +} + +// TryParseLineLength matches s against the wire literals. +func TryParseLineLength(s string) (LineLength, bool) { + v, ok := lineLengthIndex[s] + return v, ok +} + +// ParseLineLength is lenient: unknown input falls back to the first variant. +func ParseLineLength(s string) LineLength { + if v, ok := lineLengthIndex[s]; ok { + return v + } + return LineLengthShort +} + +// String returns the wire literal. +func (v LineLength) String() string { + if int(v) < 0 || int(v) >= len(lineLengthValues) { + return lineLengthValues[0] + } + return lineLengthValues[v] +} diff --git a/gen/test/go/mx/line_shape.go b/gen/test/go/mx/line_shape.go new file mode 100644 index 000000000..508178bb2 --- /dev/null +++ b/gen/test/go/mx/line_shape.go @@ -0,0 +1,44 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The line-shape type distinguishes between straight and curved lines. +type LineShape int + +const ( + LineShapeStraight LineShape = iota + LineShapeCurved +) + +// lineShapeValues lists the wire literals by variant ordinal. +var lineShapeValues = []string{ + "straight", + "curved", +} + +var lineShapeIndex = map[string]LineShape{ + "straight": LineShapeStraight, + "curved": LineShapeCurved, +} + +// TryParseLineShape matches s against the wire literals. +func TryParseLineShape(s string) (LineShape, bool) { + v, ok := lineShapeIndex[s] + return v, ok +} + +// ParseLineShape is lenient: unknown input falls back to the first variant. +func ParseLineShape(s string) LineShape { + if v, ok := lineShapeIndex[s]; ok { + return v + } + return LineShapeStraight +} + +// String returns the wire literal. +func (v LineShape) String() string { + if int(v) < 0 || int(v) >= len(lineShapeValues) { + return lineShapeValues[0] + } + return lineShapeValues[v] +} diff --git a/gen/test/go/mx/line_type.go b/gen/test/go/mx/line_type.go new file mode 100644 index 000000000..b14ceab9d --- /dev/null +++ b/gen/test/go/mx/line_type.go @@ -0,0 +1,50 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The line-type type distinguishes between solid, dashed, dotted, and wavy lines. +type LineType int + +const ( + LineTypeSolid LineType = iota + LineTypeDashed + LineTypeDotted + LineTypeWavy +) + +// lineTypeValues lists the wire literals by variant ordinal. +var lineTypeValues = []string{ + "solid", + "dashed", + "dotted", + "wavy", +} + +var lineTypeIndex = map[string]LineType{ + "solid": LineTypeSolid, + "dashed": LineTypeDashed, + "dotted": LineTypeDotted, + "wavy": LineTypeWavy, +} + +// TryParseLineType matches s against the wire literals. +func TryParseLineType(s string) (LineType, bool) { + v, ok := lineTypeIndex[s] + return v, ok +} + +// ParseLineType is lenient: unknown input falls back to the first variant. +func ParseLineType(s string) LineType { + if v, ok := lineTypeIndex[s]; ok { + return v + } + return LineTypeSolid +} + +// String returns the wire literal. +func (v LineType) String() string { + if int(v) < 0 || int(v) >= len(lineTypeValues) { + return lineTypeValues[0] + } + return lineTypeValues[v] +} diff --git a/gen/test/go/mx/line_width_type.go b/gen/test/go/mx/line_width_type.go new file mode 100644 index 000000000..a272877f1 --- /dev/null +++ b/gen/test/go/mx/line_width_type.go @@ -0,0 +1,24 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The line-width-type defines what type of line is being defined in a line-width element. Values +// include beam, bracket, dashes, enclosure, ending, extend, heavy barline, leger, light barline, +// octave shift, pedal, slur middle, slur tip, staff, stem, tie middle, tie tip, tuplet bracket, and +// wedge. This is left as a string so that other application-specific types can be defined, but it +// is made a separate type so that it can be redefined more strictly. +type LineWidthType string + +// TryParseLineWidthType accepts any string: the wire form is the value. +func TryParseLineWidthType(s string) (LineWidthType, bool) { + return LineWidthType(s), true +} + +func ParseLineWidthType(s string) LineWidthType { + return LineWidthType(s) +} + +// String returns the wire spelling. +func (v LineWidthType) String() string { + return string(v) +} diff --git a/gen/test/go/mx/margin_type.go b/gen/test/go/mx/margin_type.go new file mode 100644 index 000000000..5d3b7d1b7 --- /dev/null +++ b/gen/test/go/mx/margin_type.go @@ -0,0 +1,47 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The margin-type type specifies whether margins apply to even page, odd pages, or both. +type MarginType int + +const ( + MarginTypeOdd MarginType = iota + MarginTypeEven + MarginTypeBoth +) + +// marginTypeValues lists the wire literals by variant ordinal. +var marginTypeValues = []string{ + "odd", + "even", + "both", +} + +var marginTypeIndex = map[string]MarginType{ + "odd": MarginTypeOdd, + "even": MarginTypeEven, + "both": MarginTypeBoth, +} + +// TryParseMarginType matches s against the wire literals. +func TryParseMarginType(s string) (MarginType, bool) { + v, ok := marginTypeIndex[s] + return v, ok +} + +// ParseMarginType is lenient: unknown input falls back to the first variant. +func ParseMarginType(s string) MarginType { + if v, ok := marginTypeIndex[s]; ok { + return v + } + return MarginTypeOdd +} + +// String returns the wire literal. +func (v MarginType) String() string { + if int(v) < 0 || int(v) >= len(marginTypeValues) { + return marginTypeValues[0] + } + return marginTypeValues[v] +} diff --git a/gen/test/go/mx/measure_numbering_value.go b/gen/test/go/mx/measure_numbering_value.go new file mode 100644 index 000000000..0a94f8c19 --- /dev/null +++ b/gen/test/go/mx/measure_numbering_value.go @@ -0,0 +1,48 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The measure-numbering-value type describes how measure numbers are displayed on this part: no +// numbers, numbers every measure, or numbers every system. +type MeasureNumberingValue int + +const ( + MeasureNumberingValueNone MeasureNumberingValue = iota + MeasureNumberingValueMeasure + MeasureNumberingValueSystem +) + +// measureNumberingValueValues lists the wire literals by variant ordinal. +var measureNumberingValueValues = []string{ + "none", + "measure", + "system", +} + +var measureNumberingValueIndex = map[string]MeasureNumberingValue{ + "none": MeasureNumberingValueNone, + "measure": MeasureNumberingValueMeasure, + "system": MeasureNumberingValueSystem, +} + +// TryParseMeasureNumberingValue matches s against the wire literals. +func TryParseMeasureNumberingValue(s string) (MeasureNumberingValue, bool) { + v, ok := measureNumberingValueIndex[s] + return v, ok +} + +// ParseMeasureNumberingValue is lenient: unknown input falls back to the first variant. +func ParseMeasureNumberingValue(s string) MeasureNumberingValue { + if v, ok := measureNumberingValueIndex[s]; ok { + return v + } + return MeasureNumberingValueNone +} + +// String returns the wire literal. +func (v MeasureNumberingValue) String() string { + if int(v) < 0 || int(v) >= len(measureNumberingValueValues) { + return measureNumberingValueValues[0] + } + return measureNumberingValueValues[v] +} diff --git a/gen/test/go/mx/measure_text.go b/gen/test/go/mx/measure_text.go new file mode 100644 index 000000000..2819dde18 --- /dev/null +++ b/gen/test/go/mx/measure_text.go @@ -0,0 +1,22 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The measure-text type is used for the text attribute of measure elements. It has at least one +// character. The implicit attribute of the measure element should be set to "yes" rather than +// setting the text attribute to an empty string. +type MeasureText string + +// TryParseMeasureText accepts any string: the wire form is the value. +func TryParseMeasureText(s string) (MeasureText, bool) { + return MeasureText(s), true +} + +func ParseMeasureText(s string) MeasureText { + return MeasureText(s) +} + +// String returns the wire spelling. +func (v MeasureText) String() string { + return string(v) +} diff --git a/gen/test/go/mx/membrane.go b/gen/test/go/mx/membrane.go new file mode 100644 index 000000000..2b95baaa7 --- /dev/null +++ b/gen/test/go/mx/membrane.go @@ -0,0 +1,89 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The membrane type represents pictograms for membrane percussion instruments. +type Membrane int + +const ( + MembraneBassDrum Membrane = iota + MembraneBassDrumOnSide + MembraneBongos + MembraneChineseTomtom + MembraneCongaDrum + MembraneCuica + MembraneGobletDrum + MembraneIndoAmericanTomtom + MembraneJapaneseTomtom + MembraneMilitaryDrum + MembraneSnareDrum + MembraneSnareDrumSnaresOff + MembraneTabla + MembraneTambourine + MembraneTenorDrum + MembraneTimbales + MembraneTomtom +) + +// membraneValues lists the wire literals by variant ordinal. +var membraneValues = []string{ + "bass drum", + "bass drum on side", + "bongos", + "Chinese tomtom", + "conga drum", + "cuica", + "goblet drum", + "Indo-American tomtom", + "Japanese tomtom", + "military drum", + "snare drum", + "snare drum snares off", + "tabla", + "tambourine", + "tenor drum", + "timbales", + "tomtom", +} + +var membraneIndex = map[string]Membrane{ + "bass drum": MembraneBassDrum, + "bass drum on side": MembraneBassDrumOnSide, + "bongos": MembraneBongos, + "Chinese tomtom": MembraneChineseTomtom, + "conga drum": MembraneCongaDrum, + "cuica": MembraneCuica, + "goblet drum": MembraneGobletDrum, + "Indo-American tomtom": MembraneIndoAmericanTomtom, + "Japanese tomtom": MembraneJapaneseTomtom, + "military drum": MembraneMilitaryDrum, + "snare drum": MembraneSnareDrum, + "snare drum snares off": MembraneSnareDrumSnaresOff, + "tabla": MembraneTabla, + "tambourine": MembraneTambourine, + "tenor drum": MembraneTenorDrum, + "timbales": MembraneTimbales, + "tomtom": MembraneTomtom, +} + +// TryParseMembrane matches s against the wire literals. +func TryParseMembrane(s string) (Membrane, bool) { + v, ok := membraneIndex[s] + return v, ok +} + +// ParseMembrane is lenient: unknown input falls back to the first variant. +func ParseMembrane(s string) Membrane { + if v, ok := membraneIndex[s]; ok { + return v + } + return MembraneBassDrum +} + +// String returns the wire literal. +func (v Membrane) String() string { + if int(v) < 0 || int(v) >= len(membraneValues) { + return membraneValues[0] + } + return membraneValues[v] +} diff --git a/gen/test/go/mx/metal.go b/gen/test/go/mx/metal.go new file mode 100644 index 000000000..069fd6318 --- /dev/null +++ b/gen/test/go/mx/metal.go @@ -0,0 +1,135 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The metal type represents pictograms for metal percussion instruments. The hi-hat value refers to +// a pictogram like Stone's high-hat cymbals but without the long vertical line at the bottom. +type Metal int + +const ( + MetalAgogo Metal = iota + MetalAlmglocken + MetalBell + MetalBellPlate + MetalBellTree + MetalBrakeDrum + MetalCencerro + MetalChainRattle + MetalChineseCymbal + MetalCowbell + MetalCrashCymbals + MetalCrotale + MetalCymbalTongs + MetalDomedGong + MetalFingerCymbals + MetalFlexatone + MetalGong + MetalHiHat + MetalHighHatCymbals + MetalHandbell + MetalJawHarp + MetalJingleBells + MetalMusicalSaw + MetalShellBells + MetalSistrum + MetalSizzleCymbal + MetalSleighBells + MetalSuspendedCymbal + MetalTamTam + MetalTamTamWithBeater + MetalTriangle + MetalVietnameseHat +) + +// metalValues lists the wire literals by variant ordinal. +var metalValues = []string{ + "agogo", + "almglocken", + "bell", + "bell plate", + "bell tree", + "brake drum", + "cencerro", + "chain rattle", + "Chinese cymbal", + "cowbell", + "crash cymbals", + "crotale", + "cymbal tongs", + "domed gong", + "finger cymbals", + "flexatone", + "gong", + "hi-hat", + "high-hat cymbals", + "handbell", + "jaw harp", + "jingle bells", + "musical saw", + "shell bells", + "sistrum", + "sizzle cymbal", + "sleigh bells", + "suspended cymbal", + "tam tam", + "tam tam with beater", + "triangle", + "Vietnamese hat", +} + +var metalIndex = map[string]Metal{ + "agogo": MetalAgogo, + "almglocken": MetalAlmglocken, + "bell": MetalBell, + "bell plate": MetalBellPlate, + "bell tree": MetalBellTree, + "brake drum": MetalBrakeDrum, + "cencerro": MetalCencerro, + "chain rattle": MetalChainRattle, + "Chinese cymbal": MetalChineseCymbal, + "cowbell": MetalCowbell, + "crash cymbals": MetalCrashCymbals, + "crotale": MetalCrotale, + "cymbal tongs": MetalCymbalTongs, + "domed gong": MetalDomedGong, + "finger cymbals": MetalFingerCymbals, + "flexatone": MetalFlexatone, + "gong": MetalGong, + "hi-hat": MetalHiHat, + "high-hat cymbals": MetalHighHatCymbals, + "handbell": MetalHandbell, + "jaw harp": MetalJawHarp, + "jingle bells": MetalJingleBells, + "musical saw": MetalMusicalSaw, + "shell bells": MetalShellBells, + "sistrum": MetalSistrum, + "sizzle cymbal": MetalSizzleCymbal, + "sleigh bells": MetalSleighBells, + "suspended cymbal": MetalSuspendedCymbal, + "tam tam": MetalTamTam, + "tam tam with beater": MetalTamTamWithBeater, + "triangle": MetalTriangle, + "Vietnamese hat": MetalVietnameseHat, +} + +// TryParseMetal matches s against the wire literals. +func TryParseMetal(s string) (Metal, bool) { + v, ok := metalIndex[s] + return v, ok +} + +// ParseMetal is lenient: unknown input falls back to the first variant. +func ParseMetal(s string) Metal { + if v, ok := metalIndex[s]; ok { + return v + } + return MetalAgogo +} + +// String returns the wire literal. +func (v Metal) String() string { + if int(v) < 0 || int(v) >= len(metalValues) { + return metalValues[0] + } + return metalValues[v] +} diff --git a/gen/test/go/mx/midi_128.go b/gen/test/go/mx/midi_128.go new file mode 100644 index 000000000..62c31b163 --- /dev/null +++ b/gen/test/go/mx/midi_128.go @@ -0,0 +1,36 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The midi-16 type is used to express MIDI 1.0 values that range from 1 to 128. +type MIDI128 int + +// TryParseMIDI128 parses s strictly, then clamps into the declared range. +func TryParseMIDI128(s string) (MIDI128, bool) { + v, ok := tryParseInt(s) + if !ok { + return MIDI128(0), false + } + return clampMIDI128(v), true +} + +// ParseMIDI128 is lenient: unparseable input becomes 0, then clamps. +func ParseMIDI128(s string) MIDI128 { + v := parseInt(s) + return clampMIDI128(v) +} + +func clampMIDI128(v int) MIDI128 { + if v < 1 { + v = 1 + } + if v > 128 { + v = 128 + } + return MIDI128(v) +} + +// String returns the wire spelling. +func (v MIDI128) String() string { + return formatInt(int(v)) +} diff --git a/gen/test/go/mx/midi_16.go b/gen/test/go/mx/midi_16.go new file mode 100644 index 000000000..cf5ed0d5a --- /dev/null +++ b/gen/test/go/mx/midi_16.go @@ -0,0 +1,36 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The midi-16 type is used to express MIDI 1.0 values that range from 1 to 16. +type MIDI16 int + +// TryParseMIDI16 parses s strictly, then clamps into the declared range. +func TryParseMIDI16(s string) (MIDI16, bool) { + v, ok := tryParseInt(s) + if !ok { + return MIDI16(0), false + } + return clampMIDI16(v), true +} + +// ParseMIDI16 is lenient: unparseable input becomes 0, then clamps. +func ParseMIDI16(s string) MIDI16 { + v := parseInt(s) + return clampMIDI16(v) +} + +func clampMIDI16(v int) MIDI16 { + if v < 1 { + v = 1 + } + if v > 16 { + v = 16 + } + return MIDI16(v) +} + +// String returns the wire spelling. +func (v MIDI16) String() string { + return formatInt(int(v)) +} diff --git a/gen/test/go/mx/midi_16384.go b/gen/test/go/mx/midi_16384.go new file mode 100644 index 000000000..16d704738 --- /dev/null +++ b/gen/test/go/mx/midi_16384.go @@ -0,0 +1,36 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The midi-16 type is used to express MIDI 1.0 values that range from 1 to 16,384. +type MIDI16384 int + +// TryParseMIDI16384 parses s strictly, then clamps into the declared range. +func TryParseMIDI16384(s string) (MIDI16384, bool) { + v, ok := tryParseInt(s) + if !ok { + return MIDI16384(0), false + } + return clampMIDI16384(v), true +} + +// ParseMIDI16384 is lenient: unparseable input becomes 0, then clamps. +func ParseMIDI16384(s string) MIDI16384 { + v := parseInt(s) + return clampMIDI16384(v) +} + +func clampMIDI16384(v int) MIDI16384 { + if v < 1 { + v = 1 + } + if v > 16384 { + v = 16384 + } + return MIDI16384(v) +} + +// String returns the wire spelling. +func (v MIDI16384) String() string { + return formatInt(int(v)) +} diff --git a/gen/test/go/mx/millimeters.go b/gen/test/go/mx/millimeters.go new file mode 100644 index 000000000..d926df646 --- /dev/null +++ b/gen/test/go/mx/millimeters.go @@ -0,0 +1,27 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The millimeters type is a number representing millimeters. This is used in the scaling element to +// provide a default scaling from tenths to physical units. +type Millimeters float64 + +// TryParseMillimeters parses s strictly, then clamps into the declared range. +func TryParseMillimeters(s string) (Millimeters, bool) { + v, ok := tryParseDecimal(s) + if !ok { + return Millimeters(0), false + } + return Millimeters(v), true +} + +// ParseMillimeters is lenient: unparseable input becomes 0, then clamps. +func ParseMillimeters(s string) Millimeters { + v := parseDecimal(s) + return Millimeters(v) +} + +// String returns the wire spelling. +func (v Millimeters) String() string { + return formatDecimal(float64(v)) +} diff --git a/gen/test/go/mx/mode.go b/gen/test/go/mx/mode.go new file mode 100644 index 000000000..be8de00e5 --- /dev/null +++ b/gen/test/go/mx/mode.go @@ -0,0 +1,21 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The mode type is used to specify major/minor and other mode distinctions. Valid mode values +// include major, minor, dorian, phrygian, lydian, mixolydian, aeolian, ionian, locrian, and none. +type Mode string + +// TryParseMode accepts any string: the wire form is the value. +func TryParseMode(s string) (Mode, bool) { + return Mode(s), true +} + +func ParseMode(s string) Mode { + return Mode(s) +} + +// String returns the wire spelling. +func (v Mode) String() string { + return string(v) +} diff --git a/gen/test/go/mx/mute.go b/gen/test/go/mx/mute.go new file mode 100644 index 000000000..561ba9906 --- /dev/null +++ b/gen/test/go/mx/mute.go @@ -0,0 +1,85 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The mute type represents muting for different instruments, including brass, winds, and strings. +// The on and off values are used for undifferentiated mutes. The remaining values represent +// specific mutes. +type Mute int + +const ( + MuteOn Mute = iota + MuteOff + MuteStraight + MuteCup + MuteHarmonNoStem + MuteHarmonStem + MuteBucket + MutePlunger + MuteHat + MuteSolotone + MutePractice + MuteStopMute + MuteStopHand + MuteEcho + MutePalm +) + +// muteValues lists the wire literals by variant ordinal. +var muteValues = []string{ + "on", + "off", + "straight", + "cup", + "harmon-no-stem", + "harmon-stem", + "bucket", + "plunger", + "hat", + "solotone", + "practice", + "stop-mute", + "stop-hand", + "echo", + "palm", +} + +var muteIndex = map[string]Mute{ + "on": MuteOn, + "off": MuteOff, + "straight": MuteStraight, + "cup": MuteCup, + "harmon-no-stem": MuteHarmonNoStem, + "harmon-stem": MuteHarmonStem, + "bucket": MuteBucket, + "plunger": MutePlunger, + "hat": MuteHat, + "solotone": MuteSolotone, + "practice": MutePractice, + "stop-mute": MuteStopMute, + "stop-hand": MuteStopHand, + "echo": MuteEcho, + "palm": MutePalm, +} + +// TryParseMute matches s against the wire literals. +func TryParseMute(s string) (Mute, bool) { + v, ok := muteIndex[s] + return v, ok +} + +// ParseMute is lenient: unknown input falls back to the first variant. +func ParseMute(s string) Mute { + if v, ok := muteIndex[s]; ok { + return v + } + return MuteOn +} + +// String returns the wire literal. +func (v Mute) String() string { + if int(v) < 0 || int(v) >= len(muteValues) { + return muteValues[0] + } + return muteValues[v] +} diff --git a/gen/test/go/mx/non_negative_decimal.go b/gen/test/go/mx/non_negative_decimal.go new file mode 100644 index 000000000..1ebccd45a --- /dev/null +++ b/gen/test/go/mx/non_negative_decimal.go @@ -0,0 +1,33 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The non-negative-decimal type specifies a non-negative decimal value. +type NonNegativeDecimal float64 + +// TryParseNonNegativeDecimal parses s strictly, then clamps into the declared range. +func TryParseNonNegativeDecimal(s string) (NonNegativeDecimal, bool) { + v, ok := tryParseDecimal(s) + if !ok { + return NonNegativeDecimal(0), false + } + return clampNonNegativeDecimal(v), true +} + +// ParseNonNegativeDecimal is lenient: unparseable input becomes 0, then clamps. +func ParseNonNegativeDecimal(s string) NonNegativeDecimal { + v := parseDecimal(s) + return clampNonNegativeDecimal(v) +} + +func clampNonNegativeDecimal(v float64) NonNegativeDecimal { + if v < 0.0 { + v = 0.0 + } + return NonNegativeDecimal(v) +} + +// String returns the wire spelling. +func (v NonNegativeDecimal) String() string { + return formatDecimal(float64(v)) +} diff --git a/gen/test/go/mx/note_size_type.go b/gen/test/go/mx/note_size_type.go new file mode 100644 index 000000000..326e77105 --- /dev/null +++ b/gen/test/go/mx/note_size_type.go @@ -0,0 +1,54 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The note-size-type type indicates the type of note being defined by a note-size element. The +// grace-cue type is used for notes of grace-cue size. The grace type is used for notes of cue size +// that include a grace element. The cue type is used for all other notes with cue size, whether +// defined explicitly or implicitly via a cue element. The large type is used for notes of large +// size. +type NoteSizeType int + +const ( + NoteSizeTypeCue NoteSizeType = iota + NoteSizeTypeGrace + NoteSizeTypeGraceCue + NoteSizeTypeLarge +) + +// noteSizeTypeValues lists the wire literals by variant ordinal. +var noteSizeTypeValues = []string{ + "cue", + "grace", + "grace-cue", + "large", +} + +var noteSizeTypeIndex = map[string]NoteSizeType{ + "cue": NoteSizeTypeCue, + "grace": NoteSizeTypeGrace, + "grace-cue": NoteSizeTypeGraceCue, + "large": NoteSizeTypeLarge, +} + +// TryParseNoteSizeType matches s against the wire literals. +func TryParseNoteSizeType(s string) (NoteSizeType, bool) { + v, ok := noteSizeTypeIndex[s] + return v, ok +} + +// ParseNoteSizeType is lenient: unknown input falls back to the first variant. +func ParseNoteSizeType(s string) NoteSizeType { + if v, ok := noteSizeTypeIndex[s]; ok { + return v + } + return NoteSizeTypeCue +} + +// String returns the wire literal. +func (v NoteSizeType) String() string { + if int(v) < 0 || int(v) >= len(noteSizeTypeValues) { + return noteSizeTypeValues[0] + } + return noteSizeTypeValues[v] +} diff --git a/gen/test/go/mx/note_type_value.go b/gen/test/go/mx/note_type_value.go new file mode 100644 index 000000000..ee4cc1a59 --- /dev/null +++ b/gen/test/go/mx/note_type_value.go @@ -0,0 +1,81 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The note-type type is used for the MusicXML type element and represents the graphic note type, +// from 1024th (shortest) to maxima (longest). +type NoteTypeValue int + +const ( + NoteTypeValue1024th NoteTypeValue = iota + NoteTypeValue512th + NoteTypeValue256th + NoteTypeValue128th + NoteTypeValue64th + NoteTypeValue32nd + NoteTypeValue16th + NoteTypeValueEighth + NoteTypeValueQuarter + NoteTypeValueHalf + NoteTypeValueWhole + NoteTypeValueBreve + NoteTypeValueLong + NoteTypeValueMaxima +) + +// noteTypeValueValues lists the wire literals by variant ordinal. +var noteTypeValueValues = []string{ + "1024th", + "512th", + "256th", + "128th", + "64th", + "32nd", + "16th", + "eighth", + "quarter", + "half", + "whole", + "breve", + "long", + "maxima", +} + +var noteTypeValueIndex = map[string]NoteTypeValue{ + "1024th": NoteTypeValue1024th, + "512th": NoteTypeValue512th, + "256th": NoteTypeValue256th, + "128th": NoteTypeValue128th, + "64th": NoteTypeValue64th, + "32nd": NoteTypeValue32nd, + "16th": NoteTypeValue16th, + "eighth": NoteTypeValueEighth, + "quarter": NoteTypeValueQuarter, + "half": NoteTypeValueHalf, + "whole": NoteTypeValueWhole, + "breve": NoteTypeValueBreve, + "long": NoteTypeValueLong, + "maxima": NoteTypeValueMaxima, +} + +// TryParseNoteTypeValue matches s against the wire literals. +func TryParseNoteTypeValue(s string) (NoteTypeValue, bool) { + v, ok := noteTypeValueIndex[s] + return v, ok +} + +// ParseNoteTypeValue is lenient: unknown input falls back to the first variant. +func ParseNoteTypeValue(s string) NoteTypeValue { + if v, ok := noteTypeValueIndex[s]; ok { + return v + } + return NoteTypeValue1024th +} + +// String returns the wire literal. +func (v NoteTypeValue) String() string { + if int(v) < 0 || int(v) >= len(noteTypeValueValues) { + return noteTypeValueValues[0] + } + return noteTypeValueValues[v] +} diff --git a/gen/test/go/mx/notehead_value.go b/gen/test/go/mx/notehead_value.go new file mode 100644 index 000000000..454e74bb8 --- /dev/null +++ b/gen/test/go/mx/notehead_value.go @@ -0,0 +1,134 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The notehead-value type indicates shapes other than the open and closed ovals associated with +// note durations. The values do, re, mi, fa, fa up, so, la, and ti correspond to Aikin's 7-shape +// system. The fa up shape is typically used with upstems; the fa shape is typically used with +// downstems or no stems. The arrow shapes differ from triangle and inverted triangle by being +// centered on the stem. Slashed and back slashed notes include both the normal notehead and a +// slash. The triangle shape has the tip of the triangle pointing up; the inverted triangle shape +// has the tip of the triangle pointing down. The left triangle shape is a right triangle with the +// hypotenuse facing up and to the left. The other notehead covers noteheads other than those listed +// here. It is usually used in combination with the smufl attribute to specify a particular SMuFL +// notehead. The smufl attribute may be used with any notehead value to help specify the appearance +// of symbols that share the same MusicXML semantics. Noteheads in the SMuFL "Note name noteheads" +// range (U+E150–U+E1AF) should not use the smufl attribute or the "other" value, but instead use +// the notehead-text element. +type NoteheadValue int + +const ( + NoteheadValueSlash NoteheadValue = iota + NoteheadValueTriangle + NoteheadValueDiamond + NoteheadValueSquare + NoteheadValueCross + NoteheadValueX + NoteheadValueCircleX + NoteheadValueInvertedTriangle + NoteheadValueArrowDown + NoteheadValueArrowUp + NoteheadValueCircled + NoteheadValueSlashed + NoteheadValueBackSlashed + NoteheadValueNormal + NoteheadValueCluster + NoteheadValueCircleDot + NoteheadValueLeftTriangle + NoteheadValueRectangle + NoteheadValueNone + NoteheadValueDo + NoteheadValueRe + NoteheadValueMi + NoteheadValueFa + NoteheadValueFaUp + NoteheadValueSo + NoteheadValueLa + NoteheadValueTi + NoteheadValueOther +) + +// noteheadValueValues lists the wire literals by variant ordinal. +var noteheadValueValues = []string{ + "slash", + "triangle", + "diamond", + "square", + "cross", + "x", + "circle-x", + "inverted triangle", + "arrow down", + "arrow up", + "circled", + "slashed", + "back slashed", + "normal", + "cluster", + "circle dot", + "left triangle", + "rectangle", + "none", + "do", + "re", + "mi", + "fa", + "fa up", + "so", + "la", + "ti", + "other", +} + +var noteheadValueIndex = map[string]NoteheadValue{ + "slash": NoteheadValueSlash, + "triangle": NoteheadValueTriangle, + "diamond": NoteheadValueDiamond, + "square": NoteheadValueSquare, + "cross": NoteheadValueCross, + "x": NoteheadValueX, + "circle-x": NoteheadValueCircleX, + "inverted triangle": NoteheadValueInvertedTriangle, + "arrow down": NoteheadValueArrowDown, + "arrow up": NoteheadValueArrowUp, + "circled": NoteheadValueCircled, + "slashed": NoteheadValueSlashed, + "back slashed": NoteheadValueBackSlashed, + "normal": NoteheadValueNormal, + "cluster": NoteheadValueCluster, + "circle dot": NoteheadValueCircleDot, + "left triangle": NoteheadValueLeftTriangle, + "rectangle": NoteheadValueRectangle, + "none": NoteheadValueNone, + "do": NoteheadValueDo, + "re": NoteheadValueRe, + "mi": NoteheadValueMi, + "fa": NoteheadValueFa, + "fa up": NoteheadValueFaUp, + "so": NoteheadValueSo, + "la": NoteheadValueLa, + "ti": NoteheadValueTi, + "other": NoteheadValueOther, +} + +// TryParseNoteheadValue matches s against the wire literals. +func TryParseNoteheadValue(s string) (NoteheadValue, bool) { + v, ok := noteheadValueIndex[s] + return v, ok +} + +// ParseNoteheadValue is lenient: unknown input falls back to the first variant. +func ParseNoteheadValue(s string) NoteheadValue { + if v, ok := noteheadValueIndex[s]; ok { + return v + } + return NoteheadValueSlash +} + +// String returns the wire literal. +func (v NoteheadValue) String() string { + if int(v) < 0 || int(v) >= len(noteheadValueValues) { + return noteheadValueValues[0] + } + return noteheadValueValues[v] +} diff --git a/gen/test/go/mx/number_level.go b/gen/test/go/mx/number_level.go new file mode 100644 index 000000000..06e842187 --- /dev/null +++ b/gen/test/go/mx/number_level.go @@ -0,0 +1,40 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// Slurs, tuplets, and many other features can be concurrent and overlapping within a single musical +// part. The number-level type distinguishes up to six concurrent objects of the same type. A +// reading program should be prepared to handle cases where the number-levels stop in an arbitrary +// order. Different numbers are needed when the features overlap in MusicXML document order. When a +// number-level value is optional, the value is 1 by default. +type NumberLevel int + +// TryParseNumberLevel parses s strictly, then clamps into the declared range. +func TryParseNumberLevel(s string) (NumberLevel, bool) { + v, ok := tryParseInt(s) + if !ok { + return NumberLevel(0), false + } + return clampNumberLevel(v), true +} + +// ParseNumberLevel is lenient: unparseable input becomes 0, then clamps. +func ParseNumberLevel(s string) NumberLevel { + v := parseInt(s) + return clampNumberLevel(v) +} + +func clampNumberLevel(v int) NumberLevel { + if v < 1 { + v = 1 + } + if v > 6 { + v = 6 + } + return NumberLevel(v) +} + +// String returns the wire spelling. +func (v NumberLevel) String() string { + return formatInt(int(v)) +} diff --git a/gen/test/go/mx/number_of_lines.go b/gen/test/go/mx/number_of_lines.go new file mode 100644 index 000000000..4b8875de2 --- /dev/null +++ b/gen/test/go/mx/number_of_lines.go @@ -0,0 +1,36 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The number-of-lines type is used to specify the number of lines in text decoration attributes. +type NumberOfLines int + +// TryParseNumberOfLines parses s strictly, then clamps into the declared range. +func TryParseNumberOfLines(s string) (NumberOfLines, bool) { + v, ok := tryParseInt(s) + if !ok { + return NumberOfLines(0), false + } + return clampNumberOfLines(v), true +} + +// ParseNumberOfLines is lenient: unparseable input becomes 0, then clamps. +func ParseNumberOfLines(s string) NumberOfLines { + v := parseInt(s) + return clampNumberOfLines(v) +} + +func clampNumberOfLines(v int) NumberOfLines { + if v < 0 { + v = 0 + } + if v > 3 { + v = 3 + } + return NumberOfLines(v) +} + +// String returns the wire spelling. +func (v NumberOfLines) String() string { + return formatInt(int(v)) +} diff --git a/gen/test/go/mx/number_or_normal.go b/gen/test/go/mx/number_or_normal.go new file mode 100644 index 000000000..7d99e66ef --- /dev/null +++ b/gen/test/go/mx/number_or_normal.go @@ -0,0 +1,46 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The number-or-normal values can be either a decimal number or the string "normal". This is used +// by the line-height and letter-spacing attributes. +type NumberOrNormal struct { + Kind NumberOrNormalKind + Decimal float64 +} + +type NumberOrNormalKind int + +const ( + NumberOrNormalKindDecimal NumberOrNormalKind = iota + NumberOrNormalKindNormal +) + +// TryParseNumberOrNormal tries each union member in schema order. +func TryParseNumberOrNormal(s string) (NumberOrNormal, bool) { + if v, ok := tryParseDecimal(s); ok { + return NumberOrNormal{Kind: NumberOrNormalKindDecimal, Decimal: v}, true + } + if s == "normal" { + return NumberOrNormal{Kind: NumberOrNormalKindNormal}, true + } + return NumberOrNormal{}, false +} + +// ParseNumberOrNormal is lenient: when no member matches, the first member +// absorbs the input under its own leniency rules. +func ParseNumberOrNormal(s string) NumberOrNormal { + if v, ok := TryParseNumberOrNormal(s); ok { + return v + } + return NumberOrNormal{Kind: NumberOrNormalKindDecimal, Decimal: parseDecimal(s)} +} + +// String returns the wire spelling of whichever member is held. +func (v NumberOrNormal) String() string { + switch v.Kind { + case NumberOrNormalKindNormal: + return "normal" + } + return formatDecimal(v.Decimal) +} diff --git a/gen/test/go/mx/octave.go b/gen/test/go/mx/octave.go new file mode 100644 index 000000000..47010d46f --- /dev/null +++ b/gen/test/go/mx/octave.go @@ -0,0 +1,36 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// Octaves are represented by the numbers 0 to 9, where 4 indicates the octave started by middle C. +type Octave int + +// TryParseOctave parses s strictly, then clamps into the declared range. +func TryParseOctave(s string) (Octave, bool) { + v, ok := tryParseInt(s) + if !ok { + return Octave(0), false + } + return clampOctave(v), true +} + +// ParseOctave is lenient: unparseable input becomes 0, then clamps. +func ParseOctave(s string) Octave { + v := parseInt(s) + return clampOctave(v) +} + +func clampOctave(v int) Octave { + if v < 0 { + v = 0 + } + if v > 9 { + v = 9 + } + return Octave(v) +} + +// String returns the wire spelling. +func (v Octave) String() string { + return formatInt(int(v)) +} diff --git a/gen/test/go/mx/on_off.go b/gen/test/go/mx/on_off.go new file mode 100644 index 000000000..d29503df3 --- /dev/null +++ b/gen/test/go/mx/on_off.go @@ -0,0 +1,44 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The on-off type is used for notation elements such as string mutes. +type OnOff int + +const ( + OnOffOn OnOff = iota + OnOffOff +) + +// onOffValues lists the wire literals by variant ordinal. +var onOffValues = []string{ + "on", + "off", +} + +var onOffIndex = map[string]OnOff{ + "on": OnOffOn, + "off": OnOffOff, +} + +// TryParseOnOff matches s against the wire literals. +func TryParseOnOff(s string) (OnOff, bool) { + v, ok := onOffIndex[s] + return v, ok +} + +// ParseOnOff is lenient: unknown input falls back to the first variant. +func ParseOnOff(s string) OnOff { + if v, ok := onOffIndex[s]; ok { + return v + } + return OnOffOn +} + +// String returns the wire literal. +func (v OnOff) String() string { + if int(v) < 0 || int(v) >= len(onOffValues) { + return onOffValues[0] + } + return onOffValues[v] +} diff --git a/gen/test/go/mx/over_under.go b/gen/test/go/mx/over_under.go new file mode 100644 index 000000000..e799898e8 --- /dev/null +++ b/gen/test/go/mx/over_under.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The over-under type is used to indicate whether the tips of curved lines such as slurs and ties +// are overhand (tips down) or underhand (tips up). +type OverUnder int + +const ( + OverUnderOver OverUnder = iota + OverUnderUnder +) + +// overUnderValues lists the wire literals by variant ordinal. +var overUnderValues = []string{ + "over", + "under", +} + +var overUnderIndex = map[string]OverUnder{ + "over": OverUnderOver, + "under": OverUnderUnder, +} + +// TryParseOverUnder matches s against the wire literals. +func TryParseOverUnder(s string) (OverUnder, bool) { + v, ok := overUnderIndex[s] + return v, ok +} + +// ParseOverUnder is lenient: unknown input falls back to the first variant. +func ParseOverUnder(s string) OverUnder { + if v, ok := overUnderIndex[s]; ok { + return v + } + return OverUnderOver +} + +// String returns the wire literal. +func (v OverUnder) String() string { + if int(v) < 0 || int(v) >= len(overUnderValues) { + return overUnderValues[0] + } + return overUnderValues[v] +} diff --git a/gen/test/go/mx/pedal_type.go b/gen/test/go/mx/pedal_type.go new file mode 100644 index 000000000..774f4b04f --- /dev/null +++ b/gen/test/go/mx/pedal_type.go @@ -0,0 +1,57 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The pedal-type simple type is used to distinguish types of pedal directions. The start value +// indicates the start of a damper pedal, while the sostenuto value indicates the start of a +// sostenuto pedal. The change, continue, and stop values can be used with either the damper or +// sostenuto pedal. The soft pedal is not included here because there is no special symbol or +// graphic used for it beyond what can be specified with words and bracket elements. +type PedalType int + +const ( + PedalTypeStart PedalType = iota + PedalTypeStop + PedalTypeSostenuto + PedalTypeChange + PedalTypeContinue +) + +// pedalTypeValues lists the wire literals by variant ordinal. +var pedalTypeValues = []string{ + "start", + "stop", + "sostenuto", + "change", + "continue", +} + +var pedalTypeIndex = map[string]PedalType{ + "start": PedalTypeStart, + "stop": PedalTypeStop, + "sostenuto": PedalTypeSostenuto, + "change": PedalTypeChange, + "continue": PedalTypeContinue, +} + +// TryParsePedalType matches s against the wire literals. +func TryParsePedalType(s string) (PedalType, bool) { + v, ok := pedalTypeIndex[s] + return v, ok +} + +// ParsePedalType is lenient: unknown input falls back to the first variant. +func ParsePedalType(s string) PedalType { + if v, ok := pedalTypeIndex[s]; ok { + return v + } + return PedalTypeStart +} + +// String returns the wire literal. +func (v PedalType) String() string { + if int(v) < 0 || int(v) >= len(pedalTypeValues) { + return pedalTypeValues[0] + } + return pedalTypeValues[v] +} diff --git a/gen/test/go/mx/percent.go b/gen/test/go/mx/percent.go new file mode 100644 index 000000000..7083ffc7e --- /dev/null +++ b/gen/test/go/mx/percent.go @@ -0,0 +1,36 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The percent type specifies a percentage from 0 to 100. +type Percent float64 + +// TryParsePercent parses s strictly, then clamps into the declared range. +func TryParsePercent(s string) (Percent, bool) { + v, ok := tryParseDecimal(s) + if !ok { + return Percent(0), false + } + return clampPercent(v), true +} + +// ParsePercent is lenient: unparseable input becomes 0, then clamps. +func ParsePercent(s string) Percent { + v := parseDecimal(s) + return clampPercent(v) +} + +func clampPercent(v float64) Percent { + if v < 0.0 { + v = 0.0 + } + if v > 100.0 { + v = 100.0 + } + return Percent(v) +} + +// String returns the wire spelling. +func (v Percent) String() string { + return formatDecimal(float64(v)) +} diff --git a/gen/test/go/mx/pitched_value.go b/gen/test/go/mx/pitched_value.go new file mode 100644 index 000000000..d637f9373 --- /dev/null +++ b/gen/test/go/mx/pitched_value.go @@ -0,0 +1,72 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The pitched-value type represents pictograms for pitched percussion instruments. The chimes and +// tubular chimes values distinguish the single-line and double-line versions of the pictogram. +type PitchedValue int + +const ( + PitchedValueCelesta PitchedValue = iota + PitchedValueChimes + PitchedValueGlockenspiel + PitchedValueLithophone + PitchedValueMallet + PitchedValueMarimba + PitchedValueSteelDrums + PitchedValueTubaphone + PitchedValueTubularChimes + PitchedValueVibraphone + PitchedValueXylophone +) + +// pitchedValueValues lists the wire literals by variant ordinal. +var pitchedValueValues = []string{ + "celesta", + "chimes", + "glockenspiel", + "lithophone", + "mallet", + "marimba", + "steel drums", + "tubaphone", + "tubular chimes", + "vibraphone", + "xylophone", +} + +var pitchedValueIndex = map[string]PitchedValue{ + "celesta": PitchedValueCelesta, + "chimes": PitchedValueChimes, + "glockenspiel": PitchedValueGlockenspiel, + "lithophone": PitchedValueLithophone, + "mallet": PitchedValueMallet, + "marimba": PitchedValueMarimba, + "steel drums": PitchedValueSteelDrums, + "tubaphone": PitchedValueTubaphone, + "tubular chimes": PitchedValueTubularChimes, + "vibraphone": PitchedValueVibraphone, + "xylophone": PitchedValueXylophone, +} + +// TryParsePitchedValue matches s against the wire literals. +func TryParsePitchedValue(s string) (PitchedValue, bool) { + v, ok := pitchedValueIndex[s] + return v, ok +} + +// ParsePitchedValue is lenient: unknown input falls back to the first variant. +func ParsePitchedValue(s string) PitchedValue { + if v, ok := pitchedValueIndex[s]; ok { + return v + } + return PitchedValueCelesta +} + +// String returns the wire literal. +func (v PitchedValue) String() string { + if int(v) < 0 || int(v) >= len(pitchedValueValues) { + return pitchedValueValues[0] + } + return pitchedValueValues[v] +} diff --git a/gen/test/go/mx/positive_divisions.go b/gen/test/go/mx/positive_divisions.go new file mode 100644 index 000000000..f259c0ff8 --- /dev/null +++ b/gen/test/go/mx/positive_divisions.go @@ -0,0 +1,33 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The positive-divisions type restricts divisions values to positive numbers. +type PositiveDivisions float64 + +// TryParsePositiveDivisions parses s strictly, then clamps into the declared range. +func TryParsePositiveDivisions(s string) (PositiveDivisions, bool) { + v, ok := tryParseDecimal(s) + if !ok { + return PositiveDivisions(0), false + } + return clampPositiveDivisions(v), true +} + +// ParsePositiveDivisions is lenient: unparseable input becomes 0, then clamps. +func ParsePositiveDivisions(s string) PositiveDivisions { + v := parseDecimal(s) + return clampPositiveDivisions(v) +} + +func clampPositiveDivisions(v float64) PositiveDivisions { + if v <= 0.0 { + v = 1e-06 + } + return PositiveDivisions(v) +} + +// String returns the wire spelling. +func (v PositiveDivisions) String() string { + return formatDecimal(float64(v)) +} diff --git a/gen/test/go/mx/positive_integer_or_empty.go b/gen/test/go/mx/positive_integer_or_empty.go new file mode 100644 index 000000000..43abd5128 --- /dev/null +++ b/gen/test/go/mx/positive_integer_or_empty.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The positive-integer-or-empty values can be either a positive integer or an empty string. +type PositiveIntegerOrEmpty struct { + Kind PositiveIntegerOrEmptyKind + PositiveInteger int +} + +type PositiveIntegerOrEmptyKind int + +const ( + PositiveIntegerOrEmptyKindPositiveInteger PositiveIntegerOrEmptyKind = iota + PositiveIntegerOrEmptyKindEmpty +) + +// TryParsePositiveIntegerOrEmpty tries each union member in schema order. +func TryParsePositiveIntegerOrEmpty(s string) (PositiveIntegerOrEmpty, bool) { + if v, ok := tryParseInt(s); ok { + return PositiveIntegerOrEmpty{Kind: PositiveIntegerOrEmptyKindPositiveInteger, PositiveInteger: v}, true + } + if s == "" { + return PositiveIntegerOrEmpty{Kind: PositiveIntegerOrEmptyKindEmpty}, true + } + return PositiveIntegerOrEmpty{}, false +} + +// ParsePositiveIntegerOrEmpty is lenient: when no member matches, the first member +// absorbs the input under its own leniency rules. +func ParsePositiveIntegerOrEmpty(s string) PositiveIntegerOrEmpty { + if v, ok := TryParsePositiveIntegerOrEmpty(s); ok { + return v + } + return PositiveIntegerOrEmpty{Kind: PositiveIntegerOrEmptyKindPositiveInteger, PositiveInteger: parseInt(s)} +} + +// String returns the wire spelling of whichever member is held. +func (v PositiveIntegerOrEmpty) String() string { + switch v.Kind { + case PositiveIntegerOrEmptyKindEmpty: + return "" + } + return formatInt(v.PositiveInteger) +} diff --git a/gen/test/go/mx/principal_voice_symbol.go b/gen/test/go/mx/principal_voice_symbol.go new file mode 100644 index 000000000..5982b5e5c --- /dev/null +++ b/gen/test/go/mx/principal_voice_symbol.go @@ -0,0 +1,53 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The principal-voice-symbol type represents the type of symbol used to indicate the start of a +// principal or secondary voice. The "plain" value represents a plain square bracket. The value of +// "none" is used for analysis markup when the principal-voice element does not have a corresponding +// appearance in the score. +type PrincipalVoiceSymbol int + +const ( + PrincipalVoiceSymbolHauptstimme PrincipalVoiceSymbol = iota + PrincipalVoiceSymbolNebenstimme + PrincipalVoiceSymbolPlain + PrincipalVoiceSymbolNone +) + +// principalVoiceSymbolValues lists the wire literals by variant ordinal. +var principalVoiceSymbolValues = []string{ + "Hauptstimme", + "Nebenstimme", + "plain", + "none", +} + +var principalVoiceSymbolIndex = map[string]PrincipalVoiceSymbol{ + "Hauptstimme": PrincipalVoiceSymbolHauptstimme, + "Nebenstimme": PrincipalVoiceSymbolNebenstimme, + "plain": PrincipalVoiceSymbolPlain, + "none": PrincipalVoiceSymbolNone, +} + +// TryParsePrincipalVoiceSymbol matches s against the wire literals. +func TryParsePrincipalVoiceSymbol(s string) (PrincipalVoiceSymbol, bool) { + v, ok := principalVoiceSymbolIndex[s] + return v, ok +} + +// ParsePrincipalVoiceSymbol is lenient: unknown input falls back to the first variant. +func ParsePrincipalVoiceSymbol(s string) PrincipalVoiceSymbol { + if v, ok := principalVoiceSymbolIndex[s]; ok { + return v + } + return PrincipalVoiceSymbolHauptstimme +} + +// String returns the wire literal. +func (v PrincipalVoiceSymbol) String() string { + if int(v) < 0 || int(v) >= len(principalVoiceSymbolValues) { + return principalVoiceSymbolValues[0] + } + return principalVoiceSymbolValues[v] +} diff --git a/gen/test/go/mx/right_left_middle.go b/gen/test/go/mx/right_left_middle.go new file mode 100644 index 000000000..578a3629a --- /dev/null +++ b/gen/test/go/mx/right_left_middle.go @@ -0,0 +1,47 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The right-left-middle type is used to specify barline location. +type RightLeftMiddle int + +const ( + RightLeftMiddleRight RightLeftMiddle = iota + RightLeftMiddleLeft + RightLeftMiddleMiddle +) + +// rightLeftMiddleValues lists the wire literals by variant ordinal. +var rightLeftMiddleValues = []string{ + "right", + "left", + "middle", +} + +var rightLeftMiddleIndex = map[string]RightLeftMiddle{ + "right": RightLeftMiddleRight, + "left": RightLeftMiddleLeft, + "middle": RightLeftMiddleMiddle, +} + +// TryParseRightLeftMiddle matches s against the wire literals. +func TryParseRightLeftMiddle(s string) (RightLeftMiddle, bool) { + v, ok := rightLeftMiddleIndex[s] + return v, ok +} + +// ParseRightLeftMiddle is lenient: unknown input falls back to the first variant. +func ParseRightLeftMiddle(s string) RightLeftMiddle { + if v, ok := rightLeftMiddleIndex[s]; ok { + return v + } + return RightLeftMiddleRight +} + +// String returns the wire literal. +func (v RightLeftMiddle) String() string { + if int(v) < 0 || int(v) >= len(rightLeftMiddleValues) { + return rightLeftMiddleValues[0] + } + return rightLeftMiddleValues[v] +} diff --git a/gen/test/go/mx/rotation_degrees.go b/gen/test/go/mx/rotation_degrees.go new file mode 100644 index 000000000..7d3bbb0b6 --- /dev/null +++ b/gen/test/go/mx/rotation_degrees.go @@ -0,0 +1,37 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The rotation-degrees type specifies rotation, pan, and elevation values in degrees. Values range +// from -180 to 180. +type RotationDegrees float64 + +// TryParseRotationDegrees parses s strictly, then clamps into the declared range. +func TryParseRotationDegrees(s string) (RotationDegrees, bool) { + v, ok := tryParseDecimal(s) + if !ok { + return RotationDegrees(0), false + } + return clampRotationDegrees(v), true +} + +// ParseRotationDegrees is lenient: unparseable input becomes 0, then clamps. +func ParseRotationDegrees(s string) RotationDegrees { + v := parseDecimal(s) + return clampRotationDegrees(v) +} + +func clampRotationDegrees(v float64) RotationDegrees { + if v < -180.0 { + v = -180.0 + } + if v > 180.0 { + v = 180.0 + } + return RotationDegrees(v) +} + +// String returns the wire spelling. +func (v RotationDegrees) String() string { + return formatDecimal(float64(v)) +} diff --git a/gen/test/go/mx/runtime.go b/gen/test/go/mx/runtime.go new file mode 100644 index 000000000..ca866566d --- /dev/null +++ b/gen/test/go/mx/runtime.go @@ -0,0 +1,57 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "math" + "strconv" + "strings" +) + +// tryParseDecimal parses s strictly as a decimal number. +func tryParseDecimal(s string) (float64, bool) { + v, err := strconv.ParseFloat(strings.TrimSpace(s), 64) + if err != nil || math.IsNaN(v) || math.IsInf(v, 0) { + return 0, false + } + return v, true +} + +// parseDecimal is the lenient form: unparseable input becomes 0, so a +// malformed document still loads deterministically. Range clamping is the +// typed wrappers' job. +func parseDecimal(s string) float64 { + v, _ := tryParseDecimal(s) + return v +} + +// tryParseInt parses s strictly as an integer. +func tryParseInt(s string) (int, bool) { + v, err := strconv.ParseInt(strings.TrimSpace(s), 10, 64) + if err != nil { + return 0, false + } + return int(v), true +} + +// parseInt is the lenient form: a decimal-looking value truncates toward +// zero, anything else becomes 0. +func parseInt(s string) int { + if v, ok := tryParseInt(s); ok { + return v + } + if v, ok := tryParseDecimal(s); ok { + return int(v) + } + return 0 +} + +// formatDecimal prints the shortest decimal that round-trips the value, +// without exponent notation (8.5 -> "8.5", 4 -> "4"). +func formatDecimal(v float64) string { + return strconv.FormatFloat(v, 'f', -1, 64) +} + +func formatInt(v int) string { + return strconv.Itoa(v) +} diff --git a/gen/test/go/mx/semi_pitched.go b/gen/test/go/mx/semi_pitched.go new file mode 100644 index 000000000..8c98a8477 --- /dev/null +++ b/gen/test/go/mx/semi_pitched.go @@ -0,0 +1,56 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The semi-pitched type represents categories of indefinite pitch for percussion instruments. +type SemiPitched int + +const ( + SemiPitchedHigh SemiPitched = iota + SemiPitchedMediumHigh + SemiPitchedMedium + SemiPitchedMediumLow + SemiPitchedLow + SemiPitchedVeryLow +) + +// semiPitchedValues lists the wire literals by variant ordinal. +var semiPitchedValues = []string{ + "high", + "medium-high", + "medium", + "medium-low", + "low", + "very-low", +} + +var semiPitchedIndex = map[string]SemiPitched{ + "high": SemiPitchedHigh, + "medium-high": SemiPitchedMediumHigh, + "medium": SemiPitchedMedium, + "medium-low": SemiPitchedMediumLow, + "low": SemiPitchedLow, + "very-low": SemiPitchedVeryLow, +} + +// TryParseSemiPitched matches s against the wire literals. +func TryParseSemiPitched(s string) (SemiPitched, bool) { + v, ok := semiPitchedIndex[s] + return v, ok +} + +// ParseSemiPitched is lenient: unknown input falls back to the first variant. +func ParseSemiPitched(s string) SemiPitched { + if v, ok := semiPitchedIndex[s]; ok { + return v + } + return SemiPitchedHigh +} + +// String returns the wire literal. +func (v SemiPitched) String() string { + if int(v) < 0 || int(v) >= len(semiPitchedValues) { + return semiPitchedValues[0] + } + return semiPitchedValues[v] +} diff --git a/gen/test/go/mx/semitones.go b/gen/test/go/mx/semitones.go new file mode 100644 index 000000000..a1893ea1b --- /dev/null +++ b/gen/test/go/mx/semitones.go @@ -0,0 +1,28 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The semitones type is a number representing semitones, used for chromatic alteration. A value of +// -1 corresponds to a flat and a value of 1 to a sharp. Decimal values like 0.5 (quarter tone +// sharp) are used for microtones. +type Semitones float64 + +// TryParseSemitones parses s strictly, then clamps into the declared range. +func TryParseSemitones(s string) (Semitones, bool) { + v, ok := tryParseDecimal(s) + if !ok { + return Semitones(0), false + } + return Semitones(v), true +} + +// ParseSemitones is lenient: unparseable input becomes 0, then clamps. +func ParseSemitones(s string) Semitones { + v := parseDecimal(s) + return Semitones(v) +} + +// String returns the wire spelling. +func (v Semitones) String() string { + return formatDecimal(float64(v)) +} diff --git a/gen/test/go/mx/show_frets.go b/gen/test/go/mx/show_frets.go new file mode 100644 index 000000000..bb15316b7 --- /dev/null +++ b/gen/test/go/mx/show_frets.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The show-frets type indicates whether to show tablature frets as numbers (0, 1, 2) or letters (a, +// b, c). The default choice is numbers. +type ShowFrets int + +const ( + ShowFretsNumbers ShowFrets = iota + ShowFretsLetters +) + +// showFretsValues lists the wire literals by variant ordinal. +var showFretsValues = []string{ + "numbers", + "letters", +} + +var showFretsIndex = map[string]ShowFrets{ + "numbers": ShowFretsNumbers, + "letters": ShowFretsLetters, +} + +// TryParseShowFrets matches s against the wire literals. +func TryParseShowFrets(s string) (ShowFrets, bool) { + v, ok := showFretsIndex[s] + return v, ok +} + +// ParseShowFrets is lenient: unknown input falls back to the first variant. +func ParseShowFrets(s string) ShowFrets { + if v, ok := showFretsIndex[s]; ok { + return v + } + return ShowFretsNumbers +} + +// String returns the wire literal. +func (v ShowFrets) String() string { + if int(v) < 0 || int(v) >= len(showFretsValues) { + return showFretsValues[0] + } + return showFretsValues[v] +} diff --git a/gen/test/go/mx/show_tuplet.go b/gen/test/go/mx/show_tuplet.go new file mode 100644 index 000000000..ac2b934ff --- /dev/null +++ b/gen/test/go/mx/show_tuplet.go @@ -0,0 +1,48 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The show-tuplet type indicates whether to show a part of a tuplet relating to the tuplet-actual +// element, both the tuplet-actual and tuplet-normal elements, or neither. +type ShowTuplet int + +const ( + ShowTupletActual ShowTuplet = iota + ShowTupletBoth + ShowTupletNone +) + +// showTupletValues lists the wire literals by variant ordinal. +var showTupletValues = []string{ + "actual", + "both", + "none", +} + +var showTupletIndex = map[string]ShowTuplet{ + "actual": ShowTupletActual, + "both": ShowTupletBoth, + "none": ShowTupletNone, +} + +// TryParseShowTuplet matches s against the wire literals. +func TryParseShowTuplet(s string) (ShowTuplet, bool) { + v, ok := showTupletIndex[s] + return v, ok +} + +// ParseShowTuplet is lenient: unknown input falls back to the first variant. +func ParseShowTuplet(s string) ShowTuplet { + if v, ok := showTupletIndex[s]; ok { + return v + } + return ShowTupletActual +} + +// String returns the wire literal. +func (v ShowTuplet) String() string { + if int(v) < 0 || int(v) >= len(showTupletValues) { + return showTupletValues[0] + } + return showTupletValues[v] +} diff --git a/gen/test/go/mx/smufl_accidental_glyph_name.go b/gen/test/go/mx/smufl_accidental_glyph_name.go new file mode 100644 index 000000000..eff776f96 --- /dev/null +++ b/gen/test/go/mx/smufl_accidental_glyph_name.go @@ -0,0 +1,22 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The smufl-accidental-glyph-name type is used to reference a specific Standard Music Font Layout +// (SMuFL) accidental character. The value is a SMuFL canonical glyph name that starts with acc. +// Pattern (not enforced): acc\c+ +type SMUFLAccidentalGlyphName string + +// TryParseSMUFLAccidentalGlyphName accepts any string: the wire form is the value. +func TryParseSMUFLAccidentalGlyphName(s string) (SMUFLAccidentalGlyphName, bool) { + return SMUFLAccidentalGlyphName(s), true +} + +func ParseSMUFLAccidentalGlyphName(s string) SMUFLAccidentalGlyphName { + return SMUFLAccidentalGlyphName(s) +} + +// String returns the wire spelling. +func (v SMUFLAccidentalGlyphName) String() string { + return string(v) +} diff --git a/gen/test/go/mx/smufl_coda_glyph_name.go b/gen/test/go/mx/smufl_coda_glyph_name.go new file mode 100644 index 000000000..81bd32be5 --- /dev/null +++ b/gen/test/go/mx/smufl_coda_glyph_name.go @@ -0,0 +1,22 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The smufl-coda-glyph-name type is used to reference a specific Standard Music Font Layout (SMuFL) +// coda character. The value is a SMuFL canonical glyph name that starts with coda. Pattern (not +// enforced): coda\c* +type SMUFLCodaGlyphName string + +// TryParseSMUFLCodaGlyphName accepts any string: the wire form is the value. +func TryParseSMUFLCodaGlyphName(s string) (SMUFLCodaGlyphName, bool) { + return SMUFLCodaGlyphName(s), true +} + +func ParseSMUFLCodaGlyphName(s string) SMUFLCodaGlyphName { + return SMUFLCodaGlyphName(s) +} + +// String returns the wire spelling. +func (v SMUFLCodaGlyphName) String() string { + return string(v) +} diff --git a/gen/test/go/mx/smufl_glyph_name.go b/gen/test/go/mx/smufl_glyph_name.go new file mode 100644 index 000000000..d32486ee9 --- /dev/null +++ b/gen/test/go/mx/smufl_glyph_name.go @@ -0,0 +1,22 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The smufl-glyph-name type is used for attributes that reference a specific Standard Music Font +// Layout (SMuFL) character. The value is a SMuFL canonical glyph name, not a code point. For +// instance, the value for a standard piano pedal mark would be keyboardPedalPed, not U+E650. +type SMUFLGlyphName string + +// TryParseSMUFLGlyphName accepts any string: the wire form is the value. +func TryParseSMUFLGlyphName(s string) (SMUFLGlyphName, bool) { + return SMUFLGlyphName(s), true +} + +func ParseSMUFLGlyphName(s string) SMUFLGlyphName { + return SMUFLGlyphName(s) +} + +// String returns the wire spelling. +func (v SMUFLGlyphName) String() string { + return string(v) +} diff --git a/gen/test/go/mx/smufl_lyrics_glyph_name.go b/gen/test/go/mx/smufl_lyrics_glyph_name.go new file mode 100644 index 000000000..2f75d5896 --- /dev/null +++ b/gen/test/go/mx/smufl_lyrics_glyph_name.go @@ -0,0 +1,22 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The smufl-lyrics-glyph-name type is used to reference a specific Standard Music Font Layout +// (SMuFL) lyrics elision character. The value is a SMuFL canonical glyph name that starts with +// lyrics. Pattern (not enforced): lyrics\c+ +type SMUFLLyricsGlyphName string + +// TryParseSMUFLLyricsGlyphName accepts any string: the wire form is the value. +func TryParseSMUFLLyricsGlyphName(s string) (SMUFLLyricsGlyphName, bool) { + return SMUFLLyricsGlyphName(s), true +} + +func ParseSMUFLLyricsGlyphName(s string) SMUFLLyricsGlyphName { + return SMUFLLyricsGlyphName(s) +} + +// String returns the wire spelling. +func (v SMUFLLyricsGlyphName) String() string { + return string(v) +} diff --git a/gen/test/go/mx/smufl_pictogram_glyph_name.go b/gen/test/go/mx/smufl_pictogram_glyph_name.go new file mode 100644 index 000000000..d368ea40c --- /dev/null +++ b/gen/test/go/mx/smufl_pictogram_glyph_name.go @@ -0,0 +1,22 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The smufl-pictogram-glyph-name type is used to reference a specific Standard Music Font Layout +// (SMuFL) percussion pictogram character. The value is a SMuFL canonical glyph name that starts +// with pict. Pattern (not enforced): pict\c+ +type SMUFLPictogramGlyphName string + +// TryParseSMUFLPictogramGlyphName accepts any string: the wire form is the value. +func TryParseSMUFLPictogramGlyphName(s string) (SMUFLPictogramGlyphName, bool) { + return SMUFLPictogramGlyphName(s), true +} + +func ParseSMUFLPictogramGlyphName(s string) SMUFLPictogramGlyphName { + return SMUFLPictogramGlyphName(s) +} + +// String returns the wire spelling. +func (v SMUFLPictogramGlyphName) String() string { + return string(v) +} diff --git a/gen/test/go/mx/smufl_segno_glyph_name.go b/gen/test/go/mx/smufl_segno_glyph_name.go new file mode 100644 index 000000000..9c7e65812 --- /dev/null +++ b/gen/test/go/mx/smufl_segno_glyph_name.go @@ -0,0 +1,22 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The smufl-segno-glyph-name type is used to reference a specific Standard Music Font Layout +// (SMuFL) segno character. The value is a SMuFL canonical glyph name that starts with segno. +// Pattern (not enforced): segno\c* +type SMUFLSegnoGlyphName string + +// TryParseSMUFLSegnoGlyphName accepts any string: the wire form is the value. +func TryParseSMUFLSegnoGlyphName(s string) (SMUFLSegnoGlyphName, bool) { + return SMUFLSegnoGlyphName(s), true +} + +func ParseSMUFLSegnoGlyphName(s string) SMUFLSegnoGlyphName { + return SMUFLSegnoGlyphName(s) +} + +// String returns the wire spelling. +func (v SMUFLSegnoGlyphName) String() string { + return string(v) +} diff --git a/gen/test/go/mx/staff_divide_symbol.go b/gen/test/go/mx/staff_divide_symbol.go new file mode 100644 index 000000000..2353eea61 --- /dev/null +++ b/gen/test/go/mx/staff_divide_symbol.go @@ -0,0 +1,48 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The staff-divide-symbol type is used for staff division symbols. The down, up, and up-down values +// correspond to SMuFL code points U+E00B, U+E00C, and U+E00D respectively. +type StaffDivideSymbol int + +const ( + StaffDivideSymbolDown StaffDivideSymbol = iota + StaffDivideSymbolUp + StaffDivideSymbolUpDown +) + +// staffDivideSymbolValues lists the wire literals by variant ordinal. +var staffDivideSymbolValues = []string{ + "down", + "up", + "up-down", +} + +var staffDivideSymbolIndex = map[string]StaffDivideSymbol{ + "down": StaffDivideSymbolDown, + "up": StaffDivideSymbolUp, + "up-down": StaffDivideSymbolUpDown, +} + +// TryParseStaffDivideSymbol matches s against the wire literals. +func TryParseStaffDivideSymbol(s string) (StaffDivideSymbol, bool) { + v, ok := staffDivideSymbolIndex[s] + return v, ok +} + +// ParseStaffDivideSymbol is lenient: unknown input falls back to the first variant. +func ParseStaffDivideSymbol(s string) StaffDivideSymbol { + if v, ok := staffDivideSymbolIndex[s]; ok { + return v + } + return StaffDivideSymbolDown +} + +// String returns the wire literal. +func (v StaffDivideSymbol) String() string { + if int(v) < 0 || int(v) >= len(staffDivideSymbolValues) { + return staffDivideSymbolValues[0] + } + return staffDivideSymbolValues[v] +} diff --git a/gen/test/go/mx/staff_line.go b/gen/test/go/mx/staff_line.go new file mode 100644 index 000000000..72371a92e --- /dev/null +++ b/gen/test/go/mx/staff_line.go @@ -0,0 +1,28 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The staff-line type indicates the line on a given staff. Staff lines are numbered from bottom to +// top, with 1 being the bottom line on a staff. Staff line values can be used to specify positions +// outside the staff, such as a C clef positioned in the middle of a grand staff. +type StaffLine int + +// TryParseStaffLine parses s strictly, then clamps into the declared range. +func TryParseStaffLine(s string) (StaffLine, bool) { + v, ok := tryParseInt(s) + if !ok { + return StaffLine(0), false + } + return StaffLine(v), true +} + +// ParseStaffLine is lenient: unparseable input becomes 0, then clamps. +func ParseStaffLine(s string) StaffLine { + v := parseInt(s) + return StaffLine(v) +} + +// String returns the wire spelling. +func (v StaffLine) String() string { + return formatInt(int(v)) +} diff --git a/gen/test/go/mx/staff_number.go b/gen/test/go/mx/staff_number.go new file mode 100644 index 000000000..cbd5d87f0 --- /dev/null +++ b/gen/test/go/mx/staff_number.go @@ -0,0 +1,34 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The staff-number type indicates staff numbers within a multi-staff part. Staves are numbered from +// top to bottom, with 1 being the top staff on a part. +type StaffNumber int + +// TryParseStaffNumber parses s strictly, then clamps into the declared range. +func TryParseStaffNumber(s string) (StaffNumber, bool) { + v, ok := tryParseInt(s) + if !ok { + return StaffNumber(0), false + } + return clampStaffNumber(v), true +} + +// ParseStaffNumber is lenient: unparseable input becomes 0, then clamps. +func ParseStaffNumber(s string) StaffNumber { + v := parseInt(s) + return clampStaffNumber(v) +} + +func clampStaffNumber(v int) StaffNumber { + if v < 1 { + v = 1 + } + return StaffNumber(v) +} + +// String returns the wire spelling. +func (v StaffNumber) String() string { + return formatInt(int(v)) +} diff --git a/gen/test/go/mx/staff_type.go b/gen/test/go/mx/staff_type.go new file mode 100644 index 000000000..2548f1a64 --- /dev/null +++ b/gen/test/go/mx/staff_type.go @@ -0,0 +1,55 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The staff-type value can be ossia, cue, editorial, regular, or alternate. An alternate staff +// indicates one that shares the same musical data as the prior staff, but displayed differently +// (e.g., treble and bass clef, standard notation and tab). +type StaffType int + +const ( + StaffTypeOssia StaffType = iota + StaffTypeCue + StaffTypeEditorial + StaffTypeRegular + StaffTypeAlternate +) + +// staffTypeValues lists the wire literals by variant ordinal. +var staffTypeValues = []string{ + "ossia", + "cue", + "editorial", + "regular", + "alternate", +} + +var staffTypeIndex = map[string]StaffType{ + "ossia": StaffTypeOssia, + "cue": StaffTypeCue, + "editorial": StaffTypeEditorial, + "regular": StaffTypeRegular, + "alternate": StaffTypeAlternate, +} + +// TryParseStaffType matches s against the wire literals. +func TryParseStaffType(s string) (StaffType, bool) { + v, ok := staffTypeIndex[s] + return v, ok +} + +// ParseStaffType is lenient: unknown input falls back to the first variant. +func ParseStaffType(s string) StaffType { + if v, ok := staffTypeIndex[s]; ok { + return v + } + return StaffTypeOssia +} + +// String returns the wire literal. +func (v StaffType) String() string { + if int(v) < 0 || int(v) >= len(staffTypeValues) { + return staffTypeValues[0] + } + return staffTypeValues[v] +} diff --git a/gen/test/go/mx/start_note.go b/gen/test/go/mx/start_note.go new file mode 100644 index 000000000..b31724d12 --- /dev/null +++ b/gen/test/go/mx/start_note.go @@ -0,0 +1,48 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The start-note type describes the starting note of trills and mordents for playback, relative to +// the current note. +type StartNote int + +const ( + StartNoteUpper StartNote = iota + StartNoteMain + StartNoteBelow +) + +// startNoteValues lists the wire literals by variant ordinal. +var startNoteValues = []string{ + "upper", + "main", + "below", +} + +var startNoteIndex = map[string]StartNote{ + "upper": StartNoteUpper, + "main": StartNoteMain, + "below": StartNoteBelow, +} + +// TryParseStartNote matches s against the wire literals. +func TryParseStartNote(s string) (StartNote, bool) { + v, ok := startNoteIndex[s] + return v, ok +} + +// ParseStartNote is lenient: unknown input falls back to the first variant. +func ParseStartNote(s string) StartNote { + if v, ok := startNoteIndex[s]; ok { + return v + } + return StartNoteUpper +} + +// String returns the wire literal. +func (v StartNote) String() string { + if int(v) < 0 || int(v) >= len(startNoteValues) { + return startNoteValues[0] + } + return startNoteValues[v] +} diff --git a/gen/test/go/mx/start_stop.go b/gen/test/go/mx/start_stop.go new file mode 100644 index 000000000..4fc9a3066 --- /dev/null +++ b/gen/test/go/mx/start_stop.go @@ -0,0 +1,49 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The start-stop type is used for an attribute of musical elements that can either start or stop, +// such as tuplets. The values of start and stop refer to how an element appears in musical score +// order, not in MusicXML document order. An element with a stop attribute may precede the +// corresponding element with a start attribute within a MusicXML document. This is particularly +// common in multi-staff music. For example, the stopping point for a tuplet may appear in staff 1 +// before the starting point for the tuplet appears in staff 2 later in the document. +type StartStop int + +const ( + StartStopStart StartStop = iota + StartStopStop +) + +// startStopValues lists the wire literals by variant ordinal. +var startStopValues = []string{ + "start", + "stop", +} + +var startStopIndex = map[string]StartStop{ + "start": StartStopStart, + "stop": StartStopStop, +} + +// TryParseStartStop matches s against the wire literals. +func TryParseStartStop(s string) (StartStop, bool) { + v, ok := startStopIndex[s] + return v, ok +} + +// ParseStartStop is lenient: unknown input falls back to the first variant. +func ParseStartStop(s string) StartStop { + if v, ok := startStopIndex[s]; ok { + return v + } + return StartStopStart +} + +// String returns the wire literal. +func (v StartStop) String() string { + if int(v) < 0 || int(v) >= len(startStopValues) { + return startStopValues[0] + } + return startStopValues[v] +} diff --git a/gen/test/go/mx/start_stop_continue.go b/gen/test/go/mx/start_stop_continue.go new file mode 100644 index 000000000..67bc2ff6d --- /dev/null +++ b/gen/test/go/mx/start_stop_continue.go @@ -0,0 +1,54 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The start-stop-continue type is used for an attribute of musical elements that can either start +// or stop, but also need to refer to an intermediate point in the symbol, as for complex slurs or +// for formatting of symbols across system breaks. The values of start, stop, and continue refer to +// how an element appears in musical score order, not in MusicXML document order. An element with a +// stop attribute may precede the corresponding element with a start attribute within a MusicXML +// document. This is particularly common in multi-staff music. For example, the stopping point for a +// slur may appear in staff 1 before the starting point for the slur appears in staff 2 later in the +// document. +type StartStopContinue int + +const ( + StartStopContinueStart StartStopContinue = iota + StartStopContinueStop + StartStopContinueContinue +) + +// startStopContinueValues lists the wire literals by variant ordinal. +var startStopContinueValues = []string{ + "start", + "stop", + "continue", +} + +var startStopContinueIndex = map[string]StartStopContinue{ + "start": StartStopContinueStart, + "stop": StartStopContinueStop, + "continue": StartStopContinueContinue, +} + +// TryParseStartStopContinue matches s against the wire literals. +func TryParseStartStopContinue(s string) (StartStopContinue, bool) { + v, ok := startStopContinueIndex[s] + return v, ok +} + +// ParseStartStopContinue is lenient: unknown input falls back to the first variant. +func ParseStartStopContinue(s string) StartStopContinue { + if v, ok := startStopContinueIndex[s]; ok { + return v + } + return StartStopContinueStart +} + +// String returns the wire literal. +func (v StartStopContinue) String() string { + if int(v) < 0 || int(v) >= len(startStopContinueValues) { + return startStopContinueValues[0] + } + return startStopContinueValues[v] +} diff --git a/gen/test/go/mx/start_stop_discontinue.go b/gen/test/go/mx/start_stop_discontinue.go new file mode 100644 index 000000000..73e038aa9 --- /dev/null +++ b/gen/test/go/mx/start_stop_discontinue.go @@ -0,0 +1,52 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The start-stop-discontinue type is used to specify ending types. Typically, the start type is +// associated with the left barline of the first measure in an ending. The stop and discontinue +// types are associated with the right barline of the last measure in an ending. Stop is used when +// the ending mark concludes with a downward jog, as is typical for first endings. Discontinue is +// used when there is no downward jog, as is typical for second endings that do not conclude a +// piece. +type StartStopDiscontinue int + +const ( + StartStopDiscontinueStart StartStopDiscontinue = iota + StartStopDiscontinueStop + StartStopDiscontinueDiscontinue +) + +// startStopDiscontinueValues lists the wire literals by variant ordinal. +var startStopDiscontinueValues = []string{ + "start", + "stop", + "discontinue", +} + +var startStopDiscontinueIndex = map[string]StartStopDiscontinue{ + "start": StartStopDiscontinueStart, + "stop": StartStopDiscontinueStop, + "discontinue": StartStopDiscontinueDiscontinue, +} + +// TryParseStartStopDiscontinue matches s against the wire literals. +func TryParseStartStopDiscontinue(s string) (StartStopDiscontinue, bool) { + v, ok := startStopDiscontinueIndex[s] + return v, ok +} + +// ParseStartStopDiscontinue is lenient: unknown input falls back to the first variant. +func ParseStartStopDiscontinue(s string) StartStopDiscontinue { + if v, ok := startStopDiscontinueIndex[s]; ok { + return v + } + return StartStopDiscontinueStart +} + +// String returns the wire literal. +func (v StartStopDiscontinue) String() string { + if int(v) < 0 || int(v) >= len(startStopDiscontinueValues) { + return startStopDiscontinueValues[0] + } + return startStopDiscontinueValues[v] +} diff --git a/gen/test/go/mx/start_stop_single.go b/gen/test/go/mx/start_stop_single.go new file mode 100644 index 000000000..ea9197eb8 --- /dev/null +++ b/gen/test/go/mx/start_stop_single.go @@ -0,0 +1,48 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The start-stop-single type is used for an attribute of musical elements that can be used for +// either multi-note or single-note musical elements, as for groupings. +type StartStopSingle int + +const ( + StartStopSingleStart StartStopSingle = iota + StartStopSingleStop + StartStopSingleSingle +) + +// startStopSingleValues lists the wire literals by variant ordinal. +var startStopSingleValues = []string{ + "start", + "stop", + "single", +} + +var startStopSingleIndex = map[string]StartStopSingle{ + "start": StartStopSingleStart, + "stop": StartStopSingleStop, + "single": StartStopSingleSingle, +} + +// TryParseStartStopSingle matches s against the wire literals. +func TryParseStartStopSingle(s string) (StartStopSingle, bool) { + v, ok := startStopSingleIndex[s] + return v, ok +} + +// ParseStartStopSingle is lenient: unknown input falls back to the first variant. +func ParseStartStopSingle(s string) StartStopSingle { + if v, ok := startStopSingleIndex[s]; ok { + return v + } + return StartStopSingleStart +} + +// String returns the wire literal. +func (v StartStopSingle) String() string { + if int(v) < 0 || int(v) >= len(startStopSingleValues) { + return startStopSingleValues[0] + } + return startStopSingleValues[v] +} diff --git a/gen/test/go/mx/stem_value.go b/gen/test/go/mx/stem_value.go new file mode 100644 index 000000000..6ee9b2fe0 --- /dev/null +++ b/gen/test/go/mx/stem_value.go @@ -0,0 +1,50 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The stem type represents the notated stem direction. +type StemValue int + +const ( + StemValueDown StemValue = iota + StemValueUp + StemValueDouble + StemValueNone +) + +// stemValueValues lists the wire literals by variant ordinal. +var stemValueValues = []string{ + "down", + "up", + "double", + "none", +} + +var stemValueIndex = map[string]StemValue{ + "down": StemValueDown, + "up": StemValueUp, + "double": StemValueDouble, + "none": StemValueNone, +} + +// TryParseStemValue matches s against the wire literals. +func TryParseStemValue(s string) (StemValue, bool) { + v, ok := stemValueIndex[s] + return v, ok +} + +// ParseStemValue is lenient: unknown input falls back to the first variant. +func ParseStemValue(s string) StemValue { + if v, ok := stemValueIndex[s]; ok { + return v + } + return StemValueDown +} + +// String returns the wire literal. +func (v StemValue) String() string { + if int(v) < 0 || int(v) >= len(stemValueValues) { + return stemValueValues[0] + } + return stemValueValues[v] +} diff --git a/gen/test/go/mx/step.go b/gen/test/go/mx/step.go new file mode 100644 index 000000000..a6e547e3b --- /dev/null +++ b/gen/test/go/mx/step.go @@ -0,0 +1,60 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The step type represents a step of the diatonic scale, represented using the English letters A +// through G. +type Step int + +const ( + StepA Step = iota + StepB + StepC + StepD + StepE + StepF + StepG +) + +// stepValues lists the wire literals by variant ordinal. +var stepValues = []string{ + "A", + "B", + "C", + "D", + "E", + "F", + "G", +} + +var stepIndex = map[string]Step{ + "A": StepA, + "B": StepB, + "C": StepC, + "D": StepD, + "E": StepE, + "F": StepF, + "G": StepG, +} + +// TryParseStep matches s against the wire literals. +func TryParseStep(s string) (Step, bool) { + v, ok := stepIndex[s] + return v, ok +} + +// ParseStep is lenient: unknown input falls back to the first variant. +func ParseStep(s string) Step { + if v, ok := stepIndex[s]; ok { + return v + } + return StepA +} + +// String returns the wire literal. +func (v Step) String() string { + if int(v) < 0 || int(v) >= len(stepValues) { + return stepValues[0] + } + return stepValues[v] +} diff --git a/gen/test/go/mx/stick_location.go b/gen/test/go/mx/stick_location.go new file mode 100644 index 000000000..8a3b1d685 --- /dev/null +++ b/gen/test/go/mx/stick_location.go @@ -0,0 +1,51 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The stick-location type represents pictograms for the location of sticks, beaters, or mallets on +// cymbals, gongs, drums, and other instruments. +type StickLocation int + +const ( + StickLocationCenter StickLocation = iota + StickLocationRim + StickLocationCymbalBell + StickLocationCymbalEdge +) + +// stickLocationValues lists the wire literals by variant ordinal. +var stickLocationValues = []string{ + "center", + "rim", + "cymbal bell", + "cymbal edge", +} + +var stickLocationIndex = map[string]StickLocation{ + "center": StickLocationCenter, + "rim": StickLocationRim, + "cymbal bell": StickLocationCymbalBell, + "cymbal edge": StickLocationCymbalEdge, +} + +// TryParseStickLocation matches s against the wire literals. +func TryParseStickLocation(s string) (StickLocation, bool) { + v, ok := stickLocationIndex[s] + return v, ok +} + +// ParseStickLocation is lenient: unknown input falls back to the first variant. +func ParseStickLocation(s string) StickLocation { + if v, ok := stickLocationIndex[s]; ok { + return v + } + return StickLocationCenter +} + +// String returns the wire literal. +func (v StickLocation) String() string { + if int(v) < 0 || int(v) >= len(stickLocationValues) { + return stickLocationValues[0] + } + return stickLocationValues[v] +} diff --git a/gen/test/go/mx/stick_material.go b/gen/test/go/mx/stick_material.go new file mode 100644 index 000000000..c7a17c7c6 --- /dev/null +++ b/gen/test/go/mx/stick_material.go @@ -0,0 +1,53 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The stick-material type represents the material being displayed in a stick pictogram. +type StickMaterial int + +const ( + StickMaterialSoft StickMaterial = iota + StickMaterialMedium + StickMaterialHard + StickMaterialShaded + StickMaterialX +) + +// stickMaterialValues lists the wire literals by variant ordinal. +var stickMaterialValues = []string{ + "soft", + "medium", + "hard", + "shaded", + "x", +} + +var stickMaterialIndex = map[string]StickMaterial{ + "soft": StickMaterialSoft, + "medium": StickMaterialMedium, + "hard": StickMaterialHard, + "shaded": StickMaterialShaded, + "x": StickMaterialX, +} + +// TryParseStickMaterial matches s against the wire literals. +func TryParseStickMaterial(s string) (StickMaterial, bool) { + v, ok := stickMaterialIndex[s] + return v, ok +} + +// ParseStickMaterial is lenient: unknown input falls back to the first variant. +func ParseStickMaterial(s string) StickMaterial { + if v, ok := stickMaterialIndex[s]; ok { + return v + } + return StickMaterialSoft +} + +// String returns the wire literal. +func (v StickMaterial) String() string { + if int(v) < 0 || int(v) >= len(stickMaterialValues) { + return stickMaterialValues[0] + } + return stickMaterialValues[v] +} diff --git a/gen/test/go/mx/stick_type.go b/gen/test/go/mx/stick_type.go new file mode 100644 index 000000000..eb73bcfd9 --- /dev/null +++ b/gen/test/go/mx/stick_type.go @@ -0,0 +1,69 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The stick-type type represents the shape of pictograms where the material in the stick, mallet, +// or beater is represented in the pictogram. +type StickType int + +const ( + StickTypeBassDrum StickType = iota + StickTypeDoubleBassDrum + StickTypeGlockenspiel + StickTypeGum + StickTypeHammer + StickTypeSuperball + StickTypeTimpani + StickTypeWound + StickTypeXylophone + StickTypeYarn +) + +// stickTypeValues lists the wire literals by variant ordinal. +var stickTypeValues = []string{ + "bass drum", + "double bass drum", + "glockenspiel", + "gum", + "hammer", + "superball", + "timpani", + "wound", + "xylophone", + "yarn", +} + +var stickTypeIndex = map[string]StickType{ + "bass drum": StickTypeBassDrum, + "double bass drum": StickTypeDoubleBassDrum, + "glockenspiel": StickTypeGlockenspiel, + "gum": StickTypeGum, + "hammer": StickTypeHammer, + "superball": StickTypeSuperball, + "timpani": StickTypeTimpani, + "wound": StickTypeWound, + "xylophone": StickTypeXylophone, + "yarn": StickTypeYarn, +} + +// TryParseStickType matches s against the wire literals. +func TryParseStickType(s string) (StickType, bool) { + v, ok := stickTypeIndex[s] + return v, ok +} + +// ParseStickType is lenient: unknown input falls back to the first variant. +func ParseStickType(s string) StickType { + if v, ok := stickTypeIndex[s]; ok { + return v + } + return StickTypeBassDrum +} + +// String returns the wire literal. +func (v StickType) String() string { + if int(v) < 0 || int(v) >= len(stickTypeValues) { + return stickTypeValues[0] + } + return stickTypeValues[v] +} diff --git a/gen/test/go/mx/string_number.go b/gen/test/go/mx/string_number.go new file mode 100644 index 000000000..83fbf9dd9 --- /dev/null +++ b/gen/test/go/mx/string_number.go @@ -0,0 +1,34 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The string-number type indicates a string number. Strings are numbered from high to low, with 1 +// being the highest pitched full-length string. +type StringNumber int + +// TryParseStringNumber parses s strictly, then clamps into the declared range. +func TryParseStringNumber(s string) (StringNumber, bool) { + v, ok := tryParseInt(s) + if !ok { + return StringNumber(0), false + } + return clampStringNumber(v), true +} + +// ParseStringNumber is lenient: unparseable input becomes 0, then clamps. +func ParseStringNumber(s string) StringNumber { + v := parseInt(s) + return clampStringNumber(v) +} + +func clampStringNumber(v int) StringNumber { + if v < 1 { + v = 1 + } + return StringNumber(v) +} + +// String returns the wire spelling. +func (v StringNumber) String() string { + return formatInt(int(v)) +} diff --git a/gen/test/go/mx/syllabic.go b/gen/test/go/mx/syllabic.go new file mode 100644 index 000000000..9d26e9047 --- /dev/null +++ b/gen/test/go/mx/syllabic.go @@ -0,0 +1,52 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// Lyric hyphenation is indicated by the syllabic type. The single, begin, end, and middle values +// represent single-syllable words, word-beginning syllables, word-ending syllables, and mid-word +// syllables, respectively. +type Syllabic int + +const ( + SyllabicSingle Syllabic = iota + SyllabicBegin + SyllabicEnd + SyllabicMiddle +) + +// syllabicValues lists the wire literals by variant ordinal. +var syllabicValues = []string{ + "single", + "begin", + "end", + "middle", +} + +var syllabicIndex = map[string]Syllabic{ + "single": SyllabicSingle, + "begin": SyllabicBegin, + "end": SyllabicEnd, + "middle": SyllabicMiddle, +} + +// TryParseSyllabic matches s against the wire literals. +func TryParseSyllabic(s string) (Syllabic, bool) { + v, ok := syllabicIndex[s] + return v, ok +} + +// ParseSyllabic is lenient: unknown input falls back to the first variant. +func ParseSyllabic(s string) Syllabic { + if v, ok := syllabicIndex[s]; ok { + return v + } + return SyllabicSingle +} + +// String returns the wire literal. +func (v Syllabic) String() string { + if int(v) < 0 || int(v) >= len(syllabicValues) { + return syllabicValues[0] + } + return syllabicValues[v] +} diff --git a/gen/test/go/mx/symbol_size.go b/gen/test/go/mx/symbol_size.go new file mode 100644 index 000000000..382b08c50 --- /dev/null +++ b/gen/test/go/mx/symbol_size.go @@ -0,0 +1,51 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The symbol-size type is used to distinguish between full, cue sized, grace cue sized, and +// oversized symbols. +type SymbolSize int + +const ( + SymbolSizeFull SymbolSize = iota + SymbolSizeCue + SymbolSizeGraceCue + SymbolSizeLarge +) + +// symbolSizeValues lists the wire literals by variant ordinal. +var symbolSizeValues = []string{ + "full", + "cue", + "grace-cue", + "large", +} + +var symbolSizeIndex = map[string]SymbolSize{ + "full": SymbolSizeFull, + "cue": SymbolSizeCue, + "grace-cue": SymbolSizeGraceCue, + "large": SymbolSizeLarge, +} + +// TryParseSymbolSize matches s against the wire literals. +func TryParseSymbolSize(s string) (SymbolSize, bool) { + v, ok := symbolSizeIndex[s] + return v, ok +} + +// ParseSymbolSize is lenient: unknown input falls back to the first variant. +func ParseSymbolSize(s string) SymbolSize { + if v, ok := symbolSizeIndex[s]; ok { + return v + } + return SymbolSizeFull +} + +// String returns the wire literal. +func (v SymbolSize) String() string { + if int(v) < 0 || int(v) >= len(symbolSizeValues) { + return symbolSizeValues[0] + } + return symbolSizeValues[v] +} diff --git a/gen/test/go/mx/tap_hand.go b/gen/test/go/mx/tap_hand.go new file mode 100644 index 000000000..662b4dc4b --- /dev/null +++ b/gen/test/go/mx/tap_hand.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The tap-hand type represents the symbol to use for a tap element. The left and right values refer +// to the SMuFL guitarLeftHandTapping and guitarRightHandTapping glyphs respectively. +type TapHand int + +const ( + TapHandLeft TapHand = iota + TapHandRight +) + +// tapHandValues lists the wire literals by variant ordinal. +var tapHandValues = []string{ + "left", + "right", +} + +var tapHandIndex = map[string]TapHand{ + "left": TapHandLeft, + "right": TapHandRight, +} + +// TryParseTapHand matches s against the wire literals. +func TryParseTapHand(s string) (TapHand, bool) { + v, ok := tapHandIndex[s] + return v, ok +} + +// ParseTapHand is lenient: unknown input falls back to the first variant. +func ParseTapHand(s string) TapHand { + if v, ok := tapHandIndex[s]; ok { + return v + } + return TapHandLeft +} + +// String returns the wire literal. +func (v TapHand) String() string { + if int(v) < 0 || int(v) >= len(tapHandValues) { + return tapHandValues[0] + } + return tapHandValues[v] +} diff --git a/gen/test/go/mx/tenths.go b/gen/test/go/mx/tenths.go new file mode 100644 index 000000000..440cfff2b --- /dev/null +++ b/gen/test/go/mx/tenths.go @@ -0,0 +1,33 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The tenths type is a number representing tenths of interline staff space (positive or negative). +// Both integer and decimal values are allowed, such as 5 for a half space and 2.5 for a quarter +// space. Interline space is measured from the middle of a staff line. Distances in a MusicXML file +// are measured in tenths of staff space. Tenths are then scaled to millimeters within the scaling +// element, used in the defaults element at the start of a score. Individual staves can apply a +// scaling factor to adjust staff size. When a MusicXML element or attribute refers to tenths, it +// means the global tenths defined by the scaling element, not the local tenths as adjusted by the +// staff-size element. +type Tenths float64 + +// TryParseTenths parses s strictly, then clamps into the declared range. +func TryParseTenths(s string) (Tenths, bool) { + v, ok := tryParseDecimal(s) + if !ok { + return Tenths(0), false + } + return Tenths(v), true +} + +// ParseTenths is lenient: unparseable input becomes 0, then clamps. +func ParseTenths(s string) Tenths { + v := parseDecimal(s) + return Tenths(v) +} + +// String returns the wire spelling. +func (v Tenths) String() string { + return formatDecimal(float64(v)) +} diff --git a/gen/test/go/mx/text_direction.go b/gen/test/go/mx/text_direction.go new file mode 100644 index 000000000..ca2bbea8f --- /dev/null +++ b/gen/test/go/mx/text_direction.go @@ -0,0 +1,55 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The text-direction type is used to adjust and override the Unicode bidirectional text algorithm, +// similar to the W3C Internationalization Tag Set recommendation. Values are ltr (left-to-right +// embed), rtl (right-to-left embed), lro (left-to-right bidi-override), and rlo (right-to-left +// bidi-override). The default value is ltr. This type is typically used by applications that store +// text in left-to-right visual order rather than logical order. Such applications can use the lro +// value to better communicate with other applications that more fully support bidirectional text. +type TextDirection int + +const ( + TextDirectionLtr TextDirection = iota + TextDirectionRtl + TextDirectionLro + TextDirectionRlo +) + +// textDirectionValues lists the wire literals by variant ordinal. +var textDirectionValues = []string{ + "ltr", + "rtl", + "lro", + "rlo", +} + +var textDirectionIndex = map[string]TextDirection{ + "ltr": TextDirectionLtr, + "rtl": TextDirectionRtl, + "lro": TextDirectionLro, + "rlo": TextDirectionRlo, +} + +// TryParseTextDirection matches s against the wire literals. +func TryParseTextDirection(s string) (TextDirection, bool) { + v, ok := textDirectionIndex[s] + return v, ok +} + +// ParseTextDirection is lenient: unknown input falls back to the first variant. +func ParseTextDirection(s string) TextDirection { + if v, ok := textDirectionIndex[s]; ok { + return v + } + return TextDirectionLtr +} + +// String returns the wire literal. +func (v TextDirection) String() string { + if int(v) < 0 || int(v) >= len(textDirectionValues) { + return textDirectionValues[0] + } + return textDirectionValues[v] +} diff --git a/gen/test/go/mx/tied_type.go b/gen/test/go/mx/tied_type.go new file mode 100644 index 000000000..9b05ad8c2 --- /dev/null +++ b/gen/test/go/mx/tied_type.go @@ -0,0 +1,57 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The tied-type type is used as an attribute of the tied element to specify where the visual +// representation of a tie begins and ends. A tied element which joins two notes of the same pitch +// can be specified with tied-type start on the first note and tied-type stop on the second note. To +// indicate a note should be undamped, use a single tied element with tied-type let-ring. For other +// ties that are visually attached to a single note, such as a tie leading into or out of a repeated +// section or coda, use two tied elements on the same note, one start and one stop. In start-stop +// cases, ties can add more elements using a continue type. This is typically used to specify the +// formatting of cross-system ties. +type TiedType int + +const ( + TiedTypeStart TiedType = iota + TiedTypeStop + TiedTypeContinue + TiedTypeLetRing +) + +// tiedTypeValues lists the wire literals by variant ordinal. +var tiedTypeValues = []string{ + "start", + "stop", + "continue", + "let-ring", +} + +var tiedTypeIndex = map[string]TiedType{ + "start": TiedTypeStart, + "stop": TiedTypeStop, + "continue": TiedTypeContinue, + "let-ring": TiedTypeLetRing, +} + +// TryParseTiedType matches s against the wire literals. +func TryParseTiedType(s string) (TiedType, bool) { + v, ok := tiedTypeIndex[s] + return v, ok +} + +// ParseTiedType is lenient: unknown input falls back to the first variant. +func ParseTiedType(s string) TiedType { + if v, ok := tiedTypeIndex[s]; ok { + return v + } + return TiedTypeStart +} + +// String returns the wire literal. +func (v TiedType) String() string { + if int(v) < 0 || int(v) >= len(tiedTypeValues) { + return tiedTypeValues[0] + } + return tiedTypeValues[v] +} diff --git a/gen/test/go/mx/time_only.go b/gen/test/go/mx/time_only.go new file mode 100644 index 000000000..d1f11fc9a --- /dev/null +++ b/gen/test/go/mx/time_only.go @@ -0,0 +1,23 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The time-only type is used to indicate that a particular playback-related element only applies +// particular times through a repeated section. The value is a comma-separated list of positive +// integers arranged in ascending order, indicating which times through the repeated section that +// the element applies. Pattern (not enforced): [1-9][0-9]*(, ?[1-9][0-9]*)* +type TimeOnly string + +// TryParseTimeOnly accepts any string: the wire form is the value. +func TryParseTimeOnly(s string) (TimeOnly, bool) { + return TimeOnly(s), true +} + +func ParseTimeOnly(s string) TimeOnly { + return TimeOnly(s) +} + +// String returns the wire spelling. +func (v TimeOnly) String() string { + return string(v) +} diff --git a/gen/test/go/mx/time_relation.go b/gen/test/go/mx/time_relation.go new file mode 100644 index 000000000..567038dc3 --- /dev/null +++ b/gen/test/go/mx/time_relation.go @@ -0,0 +1,57 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The time-relation type indicates the symbol used to represent the interchangeable aspect of dual +// time signatures. +type TimeRelation int + +const ( + TimeRelationParentheses TimeRelation = iota + TimeRelationBracket + TimeRelationEquals + TimeRelationSlash + TimeRelationSpace + TimeRelationHyphen +) + +// timeRelationValues lists the wire literals by variant ordinal. +var timeRelationValues = []string{ + "parentheses", + "bracket", + "equals", + "slash", + "space", + "hyphen", +} + +var timeRelationIndex = map[string]TimeRelation{ + "parentheses": TimeRelationParentheses, + "bracket": TimeRelationBracket, + "equals": TimeRelationEquals, + "slash": TimeRelationSlash, + "space": TimeRelationSpace, + "hyphen": TimeRelationHyphen, +} + +// TryParseTimeRelation matches s against the wire literals. +func TryParseTimeRelation(s string) (TimeRelation, bool) { + v, ok := timeRelationIndex[s] + return v, ok +} + +// ParseTimeRelation is lenient: unknown input falls back to the first variant. +func ParseTimeRelation(s string) TimeRelation { + if v, ok := timeRelationIndex[s]; ok { + return v + } + return TimeRelationParentheses +} + +// String returns the wire literal. +func (v TimeRelation) String() string { + if int(v) < 0 || int(v) >= len(timeRelationValues) { + return timeRelationValues[0] + } + return timeRelationValues[v] +} diff --git a/gen/test/go/mx/time_separator.go b/gen/test/go/mx/time_separator.go new file mode 100644 index 000000000..dc368b6be --- /dev/null +++ b/gen/test/go/mx/time_separator.go @@ -0,0 +1,58 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The time-separator type indicates how to display the arrangement between the beats and beat-type +// values in a time signature. The default value is none. The horizontal, diagonal, and vertical +// values represent horizontal, diagonal lower-left to upper-right, and vertical lines respectively. +// For these values, the beats and beat-type values are arranged on either side of the separator +// line. The none value represents no separator with the beats and beat-type arranged vertically. +// The adjacent value represents no separator with the beats and beat-type arranged horizontally. +type TimeSeparator int + +const ( + TimeSeparatorNone TimeSeparator = iota + TimeSeparatorHorizontal + TimeSeparatorDiagonal + TimeSeparatorVertical + TimeSeparatorAdjacent +) + +// timeSeparatorValues lists the wire literals by variant ordinal. +var timeSeparatorValues = []string{ + "none", + "horizontal", + "diagonal", + "vertical", + "adjacent", +} + +var timeSeparatorIndex = map[string]TimeSeparator{ + "none": TimeSeparatorNone, + "horizontal": TimeSeparatorHorizontal, + "diagonal": TimeSeparatorDiagonal, + "vertical": TimeSeparatorVertical, + "adjacent": TimeSeparatorAdjacent, +} + +// TryParseTimeSeparator matches s against the wire literals. +func TryParseTimeSeparator(s string) (TimeSeparator, bool) { + v, ok := timeSeparatorIndex[s] + return v, ok +} + +// ParseTimeSeparator is lenient: unknown input falls back to the first variant. +func ParseTimeSeparator(s string) TimeSeparator { + if v, ok := timeSeparatorIndex[s]; ok { + return v + } + return TimeSeparatorNone +} + +// String returns the wire literal. +func (v TimeSeparator) String() string { + if int(v) < 0 || int(v) >= len(timeSeparatorValues) { + return timeSeparatorValues[0] + } + return timeSeparatorValues[v] +} diff --git a/gen/test/go/mx/time_symbol.go b/gen/test/go/mx/time_symbol.go new file mode 100644 index 000000000..50f79147f --- /dev/null +++ b/gen/test/go/mx/time_symbol.go @@ -0,0 +1,62 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The time-symbol type indicates how to display a time signature. The normal value is the usual +// fractional display, and is the implied symbol type if none is specified. Other options are the +// common and cut time symbols, as well as a single number with an implied denominator. The note +// symbol indicates that the beat-type should be represented with the corresponding downstem note +// rather than a number. The dotted-note symbol indicates that the beat-type should be represented +// with a dotted downstem note that corresponds to three times the beat-type value, and a numerator +// that is one third the beats value. +type TimeSymbol int + +const ( + TimeSymbolCommon TimeSymbol = iota + TimeSymbolCut + TimeSymbolSingleNumber + TimeSymbolNote + TimeSymbolDottedNote + TimeSymbolNormal +) + +// timeSymbolValues lists the wire literals by variant ordinal. +var timeSymbolValues = []string{ + "common", + "cut", + "single-number", + "note", + "dotted-note", + "normal", +} + +var timeSymbolIndex = map[string]TimeSymbol{ + "common": TimeSymbolCommon, + "cut": TimeSymbolCut, + "single-number": TimeSymbolSingleNumber, + "note": TimeSymbolNote, + "dotted-note": TimeSymbolDottedNote, + "normal": TimeSymbolNormal, +} + +// TryParseTimeSymbol matches s against the wire literals. +func TryParseTimeSymbol(s string) (TimeSymbol, bool) { + v, ok := timeSymbolIndex[s] + return v, ok +} + +// ParseTimeSymbol is lenient: unknown input falls back to the first variant. +func ParseTimeSymbol(s string) TimeSymbol { + if v, ok := timeSymbolIndex[s]; ok { + return v + } + return TimeSymbolCommon +} + +// String returns the wire literal. +func (v TimeSymbol) String() string { + if int(v) < 0 || int(v) >= len(timeSymbolValues) { + return timeSymbolValues[0] + } + return timeSymbolValues[v] +} diff --git a/gen/test/go/mx/tip_direction.go b/gen/test/go/mx/tip_direction.go new file mode 100644 index 000000000..a43a03ffa --- /dev/null +++ b/gen/test/go/mx/tip_direction.go @@ -0,0 +1,63 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The tip-direction type represents the direction in which the tip of a stick or beater points, +// using Unicode arrow terminology. +type TipDirection int + +const ( + TipDirectionUp TipDirection = iota + TipDirectionDown + TipDirectionLeft + TipDirectionRight + TipDirectionNorthwest + TipDirectionNortheast + TipDirectionSoutheast + TipDirectionSouthwest +) + +// tipDirectionValues lists the wire literals by variant ordinal. +var tipDirectionValues = []string{ + "up", + "down", + "left", + "right", + "northwest", + "northeast", + "southeast", + "southwest", +} + +var tipDirectionIndex = map[string]TipDirection{ + "up": TipDirectionUp, + "down": TipDirectionDown, + "left": TipDirectionLeft, + "right": TipDirectionRight, + "northwest": TipDirectionNorthwest, + "northeast": TipDirectionNortheast, + "southeast": TipDirectionSoutheast, + "southwest": TipDirectionSouthwest, +} + +// TryParseTipDirection matches s against the wire literals. +func TryParseTipDirection(s string) (TipDirection, bool) { + v, ok := tipDirectionIndex[s] + return v, ok +} + +// ParseTipDirection is lenient: unknown input falls back to the first variant. +func ParseTipDirection(s string) TipDirection { + if v, ok := tipDirectionIndex[s]; ok { + return v + } + return TipDirectionUp +} + +// String returns the wire literal. +func (v TipDirection) String() string { + if int(v) < 0 || int(v) >= len(tipDirectionValues) { + return tipDirectionValues[0] + } + return tipDirectionValues[v] +} diff --git a/gen/test/go/mx/top_bottom.go b/gen/test/go/mx/top_bottom.go new file mode 100644 index 000000000..0ff5abad6 --- /dev/null +++ b/gen/test/go/mx/top_bottom.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The top-bottom type is used to indicate the top or bottom part of a vertical shape like +// non-arpeggiate. +type TopBottom int + +const ( + TopBottomTop TopBottom = iota + TopBottomBottom +) + +// topBottomValues lists the wire literals by variant ordinal. +var topBottomValues = []string{ + "top", + "bottom", +} + +var topBottomIndex = map[string]TopBottom{ + "top": TopBottomTop, + "bottom": TopBottomBottom, +} + +// TryParseTopBottom matches s against the wire literals. +func TryParseTopBottom(s string) (TopBottom, bool) { + v, ok := topBottomIndex[s] + return v, ok +} + +// ParseTopBottom is lenient: unknown input falls back to the first variant. +func ParseTopBottom(s string) TopBottom { + if v, ok := topBottomIndex[s]; ok { + return v + } + return TopBottomTop +} + +// String returns the wire literal. +func (v TopBottom) String() string { + if int(v) < 0 || int(v) >= len(topBottomValues) { + return topBottomValues[0] + } + return topBottomValues[v] +} diff --git a/gen/test/go/mx/tremolo_marks.go b/gen/test/go/mx/tremolo_marks.go new file mode 100644 index 000000000..dda601f0c --- /dev/null +++ b/gen/test/go/mx/tremolo_marks.go @@ -0,0 +1,37 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The number of tremolo marks is represented by a number from 0 to 8: the same as beam-level with 0 +// added. +type TremoloMarks int + +// TryParseTremoloMarks parses s strictly, then clamps into the declared range. +func TryParseTremoloMarks(s string) (TremoloMarks, bool) { + v, ok := tryParseInt(s) + if !ok { + return TremoloMarks(0), false + } + return clampTremoloMarks(v), true +} + +// ParseTremoloMarks is lenient: unparseable input becomes 0, then clamps. +func ParseTremoloMarks(s string) TremoloMarks { + v := parseInt(s) + return clampTremoloMarks(v) +} + +func clampTremoloMarks(v int) TremoloMarks { + if v < 0 { + v = 0 + } + if v > 8 { + v = 8 + } + return TremoloMarks(v) +} + +// String returns the wire spelling. +func (v TremoloMarks) String() string { + return formatInt(int(v)) +} diff --git a/gen/test/go/mx/tremolo_type.go b/gen/test/go/mx/tremolo_type.go new file mode 100644 index 000000000..9d738ec48 --- /dev/null +++ b/gen/test/go/mx/tremolo_type.go @@ -0,0 +1,50 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The tremolo-type is used to distinguish multi-note, single-note, and unmeasured tremolos. +type TremoloType int + +const ( + TremoloTypeStart TremoloType = iota + TremoloTypeStop + TremoloTypeSingle + TremoloTypeUnmeasured +) + +// tremoloTypeValues lists the wire literals by variant ordinal. +var tremoloTypeValues = []string{ + "start", + "stop", + "single", + "unmeasured", +} + +var tremoloTypeIndex = map[string]TremoloType{ + "start": TremoloTypeStart, + "stop": TremoloTypeStop, + "single": TremoloTypeSingle, + "unmeasured": TremoloTypeUnmeasured, +} + +// TryParseTremoloType matches s against the wire literals. +func TryParseTremoloType(s string) (TremoloType, bool) { + v, ok := tremoloTypeIndex[s] + return v, ok +} + +// ParseTremoloType is lenient: unknown input falls back to the first variant. +func ParseTremoloType(s string) TremoloType { + if v, ok := tremoloTypeIndex[s]; ok { + return v + } + return TremoloTypeStart +} + +// String returns the wire literal. +func (v TremoloType) String() string { + if int(v) < 0 || int(v) >= len(tremoloTypeValues) { + return tremoloTypeValues[0] + } + return tremoloTypeValues[v] +} diff --git a/gen/test/go/mx/trill_beats.go b/gen/test/go/mx/trill_beats.go new file mode 100644 index 000000000..227178b39 --- /dev/null +++ b/gen/test/go/mx/trill_beats.go @@ -0,0 +1,34 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The trill-beats type specifies the beats used in a trill-sound or bend-sound attribute group. It +// is a decimal value with a minimum value of 2. +type TrillBeats float64 + +// TryParseTrillBeats parses s strictly, then clamps into the declared range. +func TryParseTrillBeats(s string) (TrillBeats, bool) { + v, ok := tryParseDecimal(s) + if !ok { + return TrillBeats(0), false + } + return clampTrillBeats(v), true +} + +// ParseTrillBeats is lenient: unparseable input becomes 0, then clamps. +func ParseTrillBeats(s string) TrillBeats { + v := parseDecimal(s) + return clampTrillBeats(v) +} + +func clampTrillBeats(v float64) TrillBeats { + if v < 2.0 { + v = 2.0 + } + return TrillBeats(v) +} + +// String returns the wire spelling. +func (v TrillBeats) String() string { + return formatDecimal(float64(v)) +} diff --git a/gen/test/go/mx/trill_step.go b/gen/test/go/mx/trill_step.go new file mode 100644 index 000000000..bc01fa161 --- /dev/null +++ b/gen/test/go/mx/trill_step.go @@ -0,0 +1,48 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The trill-step type describes the alternating note of trills and mordents for playback, relative +// to the current note. +type TrillStep int + +const ( + TrillStepWhole TrillStep = iota + TrillStepHalf + TrillStepUnison +) + +// trillStepValues lists the wire literals by variant ordinal. +var trillStepValues = []string{ + "whole", + "half", + "unison", +} + +var trillStepIndex = map[string]TrillStep{ + "whole": TrillStepWhole, + "half": TrillStepHalf, + "unison": TrillStepUnison, +} + +// TryParseTrillStep matches s against the wire literals. +func TryParseTrillStep(s string) (TrillStep, bool) { + v, ok := trillStepIndex[s] + return v, ok +} + +// ParseTrillStep is lenient: unknown input falls back to the first variant. +func ParseTrillStep(s string) TrillStep { + if v, ok := trillStepIndex[s]; ok { + return v + } + return TrillStepWhole +} + +// String returns the wire literal. +func (v TrillStep) String() string { + if int(v) < 0 || int(v) >= len(trillStepValues) { + return trillStepValues[0] + } + return trillStepValues[v] +} diff --git a/gen/test/go/mx/two_note_turn.go b/gen/test/go/mx/two_note_turn.go new file mode 100644 index 000000000..e7b260d8d --- /dev/null +++ b/gen/test/go/mx/two_note_turn.go @@ -0,0 +1,48 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The two-note-turn type describes the ending notes of trills and mordents for playback, relative +// to the current note. +type TwoNoteTurn int + +const ( + TwoNoteTurnWhole TwoNoteTurn = iota + TwoNoteTurnHalf + TwoNoteTurnNone +) + +// twoNoteTurnValues lists the wire literals by variant ordinal. +var twoNoteTurnValues = []string{ + "whole", + "half", + "none", +} + +var twoNoteTurnIndex = map[string]TwoNoteTurn{ + "whole": TwoNoteTurnWhole, + "half": TwoNoteTurnHalf, + "none": TwoNoteTurnNone, +} + +// TryParseTwoNoteTurn matches s against the wire literals. +func TryParseTwoNoteTurn(s string) (TwoNoteTurn, bool) { + v, ok := twoNoteTurnIndex[s] + return v, ok +} + +// ParseTwoNoteTurn is lenient: unknown input falls back to the first variant. +func ParseTwoNoteTurn(s string) TwoNoteTurn { + if v, ok := twoNoteTurnIndex[s]; ok { + return v + } + return TwoNoteTurnWhole +} + +// String returns the wire literal. +func (v TwoNoteTurn) String() string { + if int(v) < 0 || int(v) >= len(twoNoteTurnValues) { + return twoNoteTurnValues[0] + } + return twoNoteTurnValues[v] +} diff --git a/gen/test/go/mx/up_down.go b/gen/test/go/mx/up_down.go new file mode 100644 index 000000000..3c9ae4188 --- /dev/null +++ b/gen/test/go/mx/up_down.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The up-down type is used for the direction of arrows and other pointed symbols like vertical +// accents, indicating which way the tip is pointing. +type UpDown int + +const ( + UpDownUp UpDown = iota + UpDownDown +) + +// upDownValues lists the wire literals by variant ordinal. +var upDownValues = []string{ + "up", + "down", +} + +var upDownIndex = map[string]UpDown{ + "up": UpDownUp, + "down": UpDownDown, +} + +// TryParseUpDown matches s against the wire literals. +func TryParseUpDown(s string) (UpDown, bool) { + v, ok := upDownIndex[s] + return v, ok +} + +// ParseUpDown is lenient: unknown input falls back to the first variant. +func ParseUpDown(s string) UpDown { + if v, ok := upDownIndex[s]; ok { + return v + } + return UpDownUp +} + +// String returns the wire literal. +func (v UpDown) String() string { + if int(v) < 0 || int(v) >= len(upDownValues) { + return upDownValues[0] + } + return upDownValues[v] +} diff --git a/gen/test/go/mx/up_down_stop_continue.go b/gen/test/go/mx/up_down_stop_continue.go new file mode 100644 index 000000000..f02c125fc --- /dev/null +++ b/gen/test/go/mx/up_down_stop_continue.go @@ -0,0 +1,51 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The up-down-stop-continue type is used for octave-shift elements, indicating the direction of the +// shift from their true pitched values because of printing difficulty. +type UpDownStopContinue int + +const ( + UpDownStopContinueUp UpDownStopContinue = iota + UpDownStopContinueDown + UpDownStopContinueStop + UpDownStopContinueContinue +) + +// upDownStopContinueValues lists the wire literals by variant ordinal. +var upDownStopContinueValues = []string{ + "up", + "down", + "stop", + "continue", +} + +var upDownStopContinueIndex = map[string]UpDownStopContinue{ + "up": UpDownStopContinueUp, + "down": UpDownStopContinueDown, + "stop": UpDownStopContinueStop, + "continue": UpDownStopContinueContinue, +} + +// TryParseUpDownStopContinue matches s against the wire literals. +func TryParseUpDownStopContinue(s string) (UpDownStopContinue, bool) { + v, ok := upDownStopContinueIndex[s] + return v, ok +} + +// ParseUpDownStopContinue is lenient: unknown input falls back to the first variant. +func ParseUpDownStopContinue(s string) UpDownStopContinue { + if v, ok := upDownStopContinueIndex[s]; ok { + return v + } + return UpDownStopContinueUp +} + +// String returns the wire literal. +func (v UpDownStopContinue) String() string { + if int(v) < 0 || int(v) >= len(upDownStopContinueValues) { + return upDownStopContinueValues[0] + } + return upDownStopContinueValues[v] +} diff --git a/gen/test/go/mx/upright_inverted.go b/gen/test/go/mx/upright_inverted.go new file mode 100644 index 000000000..c650a8c0b --- /dev/null +++ b/gen/test/go/mx/upright_inverted.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The upright-inverted type describes the appearance of a fermata element. The value is upright if +// not specified. +type UprightInverted int + +const ( + UprightInvertedUpright UprightInverted = iota + UprightInvertedInverted +) + +// uprightInvertedValues lists the wire literals by variant ordinal. +var uprightInvertedValues = []string{ + "upright", + "inverted", +} + +var uprightInvertedIndex = map[string]UprightInverted{ + "upright": UprightInvertedUpright, + "inverted": UprightInvertedInverted, +} + +// TryParseUprightInverted matches s against the wire literals. +func TryParseUprightInverted(s string) (UprightInverted, bool) { + v, ok := uprightInvertedIndex[s] + return v, ok +} + +// ParseUprightInverted is lenient: unknown input falls back to the first variant. +func ParseUprightInverted(s string) UprightInverted { + if v, ok := uprightInvertedIndex[s]; ok { + return v + } + return UprightInvertedUpright +} + +// String returns the wire literal. +func (v UprightInverted) String() string { + if int(v) < 0 || int(v) >= len(uprightInvertedValues) { + return uprightInvertedValues[0] + } + return uprightInvertedValues[v] +} diff --git a/gen/test/go/mx/valign.go b/gen/test/go/mx/valign.go new file mode 100644 index 000000000..023d86a9e --- /dev/null +++ b/gen/test/go/mx/valign.go @@ -0,0 +1,51 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The valign type is used to indicate vertical alignment to the top, middle, bottom, or baseline of +// the text. Defaults are implementation-dependent. +type Valign int + +const ( + ValignTop Valign = iota + ValignMiddle + ValignBottom + ValignBaseline +) + +// valignValues lists the wire literals by variant ordinal. +var valignValues = []string{ + "top", + "middle", + "bottom", + "baseline", +} + +var valignIndex = map[string]Valign{ + "top": ValignTop, + "middle": ValignMiddle, + "bottom": ValignBottom, + "baseline": ValignBaseline, +} + +// TryParseValign matches s against the wire literals. +func TryParseValign(s string) (Valign, bool) { + v, ok := valignIndex[s] + return v, ok +} + +// ParseValign is lenient: unknown input falls back to the first variant. +func ParseValign(s string) Valign { + if v, ok := valignIndex[s]; ok { + return v + } + return ValignTop +} + +// String returns the wire literal. +func (v Valign) String() string { + if int(v) < 0 || int(v) >= len(valignValues) { + return valignValues[0] + } + return valignValues[v] +} diff --git a/gen/test/go/mx/valign_image.go b/gen/test/go/mx/valign_image.go new file mode 100644 index 000000000..4b13e0d87 --- /dev/null +++ b/gen/test/go/mx/valign_image.go @@ -0,0 +1,48 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The valign-image type is used to indicate vertical alignment for images and graphics, so it does +// not include a baseline value. Defaults are implementation-dependent. +type ValignImage int + +const ( + ValignImageTop ValignImage = iota + ValignImageMiddle + ValignImageBottom +) + +// valignImageValues lists the wire literals by variant ordinal. +var valignImageValues = []string{ + "top", + "middle", + "bottom", +} + +var valignImageIndex = map[string]ValignImage{ + "top": ValignImageTop, + "middle": ValignImageMiddle, + "bottom": ValignImageBottom, +} + +// TryParseValignImage matches s against the wire literals. +func TryParseValignImage(s string) (ValignImage, bool) { + v, ok := valignImageIndex[s] + return v, ok +} + +// ParseValignImage is lenient: unknown input falls back to the first variant. +func ParseValignImage(s string) ValignImage { + if v, ok := valignImageIndex[s]; ok { + return v + } + return ValignImageTop +} + +// String returns the wire literal. +func (v ValignImage) String() string { + if int(v) < 0 || int(v) >= len(valignImageValues) { + return valignImageValues[0] + } + return valignImageValues[v] +} diff --git a/gen/test/go/mx/wedge_type.go b/gen/test/go/mx/wedge_type.go new file mode 100644 index 000000000..d081107c9 --- /dev/null +++ b/gen/test/go/mx/wedge_type.go @@ -0,0 +1,53 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The wedge type is crescendo for the start of a wedge that is closed at the left side, diminuendo +// for the start of a wedge that is closed on the right side, and stop for the end of a wedge. The +// continue type is used for formatting wedges over a system break, or for other situations where a +// single wedge is divided into multiple segments. +type WedgeType int + +const ( + WedgeTypeCrescendo WedgeType = iota + WedgeTypeDiminuendo + WedgeTypeStop + WedgeTypeContinue +) + +// wedgeTypeValues lists the wire literals by variant ordinal. +var wedgeTypeValues = []string{ + "crescendo", + "diminuendo", + "stop", + "continue", +} + +var wedgeTypeIndex = map[string]WedgeType{ + "crescendo": WedgeTypeCrescendo, + "diminuendo": WedgeTypeDiminuendo, + "stop": WedgeTypeStop, + "continue": WedgeTypeContinue, +} + +// TryParseWedgeType matches s against the wire literals. +func TryParseWedgeType(s string) (WedgeType, bool) { + v, ok := wedgeTypeIndex[s] + return v, ok +} + +// ParseWedgeType is lenient: unknown input falls back to the first variant. +func ParseWedgeType(s string) WedgeType { + if v, ok := wedgeTypeIndex[s]; ok { + return v + } + return WedgeTypeCrescendo +} + +// String returns the wire literal. +func (v WedgeType) String() string { + if int(v) < 0 || int(v) >= len(wedgeTypeValues) { + return wedgeTypeValues[0] + } + return wedgeTypeValues[v] +} diff --git a/gen/test/go/mx/winged.go b/gen/test/go/mx/winged.go new file mode 100644 index 000000000..c76c28c73 --- /dev/null +++ b/gen/test/go/mx/winged.go @@ -0,0 +1,56 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The winged attribute indicates whether the repeat has winged extensions that appear above and +// below the barline. The straight and curved values represent single wings, while the +// double-straight and double-curved values represent double wings. The none value indicates no +// wings and is the default. +type Winged int + +const ( + WingedNone Winged = iota + WingedStraight + WingedCurved + WingedDoubleStraight + WingedDoubleCurved +) + +// wingedValues lists the wire literals by variant ordinal. +var wingedValues = []string{ + "none", + "straight", + "curved", + "double-straight", + "double-curved", +} + +var wingedIndex = map[string]Winged{ + "none": WingedNone, + "straight": WingedStraight, + "curved": WingedCurved, + "double-straight": WingedDoubleStraight, + "double-curved": WingedDoubleCurved, +} + +// TryParseWinged matches s against the wire literals. +func TryParseWinged(s string) (Winged, bool) { + v, ok := wingedIndex[s] + return v, ok +} + +// ParseWinged is lenient: unknown input falls back to the first variant. +func ParseWinged(s string) Winged { + if v, ok := wingedIndex[s]; ok { + return v + } + return WingedNone +} + +// String returns the wire literal. +func (v Winged) String() string { + if int(v) < 0 || int(v) >= len(wingedValues) { + return wingedValues[0] + } + return wingedValues[v] +} diff --git a/gen/test/go/mx/wood.go b/gen/test/go/mx/wood.go new file mode 100644 index 000000000..7e2ccec61 --- /dev/null +++ b/gen/test/go/mx/wood.go @@ -0,0 +1,102 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The wood type represents pictograms for wood percussion instruments. The maraca and maracas +// values distinguish the one- and two-maraca versions of the pictogram. +type Wood int + +const ( + WoodBambooScraper Wood = iota + WoodBoardClapper + WoodCabasa + WoodCastanets + WoodCastanetsWithHandle + WoodClaves + WoodFootballRattle + WoodGuiro + WoodLogDrum + WoodMaraca + WoodMaracas + WoodQuijada + WoodRainstick + WoodRatchet + WoodRecoReco + WoodSandpaperBlocks + WoodSlitDrum + WoodTempleBlock + WoodVibraslap + WoodWhip + WoodWoodBlock +) + +// woodValues lists the wire literals by variant ordinal. +var woodValues = []string{ + "bamboo scraper", + "board clapper", + "cabasa", + "castanets", + "castanets with handle", + "claves", + "football rattle", + "guiro", + "log drum", + "maraca", + "maracas", + "quijada", + "rainstick", + "ratchet", + "reco-reco", + "sandpaper blocks", + "slit drum", + "temple block", + "vibraslap", + "whip", + "wood block", +} + +var woodIndex = map[string]Wood{ + "bamboo scraper": WoodBambooScraper, + "board clapper": WoodBoardClapper, + "cabasa": WoodCabasa, + "castanets": WoodCastanets, + "castanets with handle": WoodCastanetsWithHandle, + "claves": WoodClaves, + "football rattle": WoodFootballRattle, + "guiro": WoodGuiro, + "log drum": WoodLogDrum, + "maraca": WoodMaraca, + "maracas": WoodMaracas, + "quijada": WoodQuijada, + "rainstick": WoodRainstick, + "ratchet": WoodRatchet, + "reco-reco": WoodRecoReco, + "sandpaper blocks": WoodSandpaperBlocks, + "slit drum": WoodSlitDrum, + "temple block": WoodTempleBlock, + "vibraslap": WoodVibraslap, + "whip": WoodWhip, + "wood block": WoodWoodBlock, +} + +// TryParseWood matches s against the wire literals. +func TryParseWood(s string) (Wood, bool) { + v, ok := woodIndex[s] + return v, ok +} + +// ParseWood is lenient: unknown input falls back to the first variant. +func ParseWood(s string) Wood { + if v, ok := woodIndex[s]; ok { + return v + } + return WoodBambooScraper +} + +// String returns the wire literal. +func (v Wood) String() string { + if int(v) < 0 || int(v) >= len(woodValues) { + return woodValues[0] + } + return woodValues[v] +} diff --git a/gen/test/go/mx/yes_no.go b/gen/test/go/mx/yes_no.go new file mode 100644 index 000000000..2fc123b55 --- /dev/null +++ b/gen/test/go/mx/yes_no.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The yes-no type is used for boolean-like attributes. We cannot use W3C XML Schema booleans due to +// their restrictions on expression of boolean values. +type YesNo int + +const ( + YesNoYes YesNo = iota + YesNoNo +) + +// yesNoValues lists the wire literals by variant ordinal. +var yesNoValues = []string{ + "yes", + "no", +} + +var yesNoIndex = map[string]YesNo{ + "yes": YesNoYes, + "no": YesNoNo, +} + +// TryParseYesNo matches s against the wire literals. +func TryParseYesNo(s string) (YesNo, bool) { + v, ok := yesNoIndex[s] + return v, ok +} + +// ParseYesNo is lenient: unknown input falls back to the first variant. +func ParseYesNo(s string) YesNo { + if v, ok := yesNoIndex[s]; ok { + return v + } + return YesNoYes +} + +// String returns the wire literal. +func (v YesNo) String() string { + if int(v) < 0 || int(v) >= len(yesNoValues) { + return yesNoValues[0] + } + return yesNoValues[v] +} diff --git a/gen/test/go/mx/yes_no_number.go b/gen/test/go/mx/yes_no_number.go new file mode 100644 index 000000000..85f2d5b72 --- /dev/null +++ b/gen/test/go/mx/yes_no_number.go @@ -0,0 +1,46 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// The yes-no-number type is used for attributes that can be either boolean or numeric values. +type YesNoNumber struct { + Kind YesNoNumberKind + YesNo YesNo + Decimal float64 +} + +type YesNoNumberKind int + +const ( + YesNoNumberKindYesNo YesNoNumberKind = iota + YesNoNumberKindDecimal +) + +// TryParseYesNoNumber tries each union member in schema order. +func TryParseYesNoNumber(s string) (YesNoNumber, bool) { + if v, ok := TryParseYesNo(s); ok { + return YesNoNumber{Kind: YesNoNumberKindYesNo, YesNo: v}, true + } + if v, ok := tryParseDecimal(s); ok { + return YesNoNumber{Kind: YesNoNumberKindDecimal, Decimal: v}, true + } + return YesNoNumber{}, false +} + +// ParseYesNoNumber is lenient: when no member matches, the first member +// absorbs the input under its own leniency rules. +func ParseYesNoNumber(s string) YesNoNumber { + if v, ok := TryParseYesNoNumber(s); ok { + return v + } + return YesNoNumber{Kind: YesNoNumberKindYesNo, YesNo: ParseYesNo(s)} +} + +// String returns the wire spelling of whichever member is held. +func (v YesNoNumber) String() string { + switch v.Kind { + case YesNoNumberKindDecimal: + return formatDecimal(v.Decimal) + } + return v.YesNo.String() +} diff --git a/gen/test/go/mx/yyyy_mm_dd.go b/gen/test/go/mx/yyyy_mm_dd.go new file mode 100644 index 000000000..f2ea8b988 --- /dev/null +++ b/gen/test/go/mx/yyyy_mm_dd.go @@ -0,0 +1,21 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +// Calendar dates are represented yyyy-mm-dd format, following ISO 8601. This is a W3C XML Schema +// date type, but without the optional timezone data. Pattern (not enforced): [^:Z]* +type YyyyMmDd string + +// TryParseYyyyMmDd accepts any string: the wire form is the value. +func TryParseYyyyMmDd(s string) (YyyyMmDd, bool) { + return YyyyMmDd(s), true +} + +func ParseYyyyMmDd(s string) YyyyMmDd { + return YyyyMmDd(s) +} + +// String returns the wire spelling. +func (v YyyyMmDd) String() string { + return string(v) +} From 86466daf769ba52257b0564fafcbdc0d9fff342d Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 18:42:41 +0000 Subject: [PATCH 20/45] gen/emit: C templates for the leaf node types (value shapes) The C backend (gen/emit/c/) renders the four value shapes as header/impl pairs (the documented one-FileId-to-two-files mapping for C), mirroring the Go backend's surface in C idiom: bool mx__try_parse(const char *s, MxT *out) strict membership MxT mx__parse(const char *s) lenient (fixup policies) to_string: enums return static storage; numbers and unions return malloc'd strings; string types ARE char* values (parse strdups). Leniency policies match Go exactly (one policy, two spellings): unknown enum literal -> first variant; unparseable number -> 0 with decimal-looking integers truncating; clamps from the plates' bounds plus primitive-implied lower bounds; exclusive decimal bounds clamp +/- 1e-6. mx_format_decimal prints the shortest no-exponent spelling (8.5, 0.000001). Unions are tagged structs whose kinds cover ref members and literals; the open instrument- sound union (sound-id enum | open string) falls through to a strdup'd string member, with mx__free owning the string-bearing kinds. Per-type headers include their dependencies' headers via the plates' include graph, which flushed out a real bug: union/member deps were filtered by stem lookup, so the open string PRIMITIVE member of instrument-sound matched the complex TYPE named 'string' and fabricated an include of a not-yet-emitted header. _type_deps now excludes refs by category, with a regression test pinning instrument-sound's includes to exactly mx_sound_id. The runtime pair (mx_runtime.h/c) carries the shared parse/format helpers; its symbol and file prefixes come from the target config (TargetInfo gains file_prefix). The backend also emits sources.cmake, the explicit build manifest; gen/test/c/CMakeLists.txt now builds the generated model as the mx-c static library and links it into corert-c, plus a values-smoke executable mirroring the Go TestValueSmoke (all checks pass; zero compile warnings). gen/test/c/mx/ holds the 269 generated files, committed per project convention. --- gen/emit/__init__.py | 1 + gen/emit/c/__init__.py | 52 + gen/emit/c/runtime.py | 140 +++ gen/emit/c/values.py | 451 +++++++++ gen/plates/build.py | 20 +- gen/plates/model.py | 1 + gen/test/c/CMakeLists.txt | 13 +- gen/test/c/mx/mx_above_below.c | 33 + gen/test/c/mx/mx_above_below.h | 23 + gen/test/c/mx/mx_accidental_value.c | 72 ++ gen/test/c/mx/mx_accidental_value.h | 71 ++ gen/test/c/mx/mx_accordion_middle.c | 32 + gen/test/c/mx/mx_accordion_middle.h | 22 + gen/test/c/mx/mx_arrow_direction.c | 44 + gen/test/c/mx/mx_arrow_direction.h | 34 + gen/test/c/mx/mx_arrow_style.c | 38 + gen/test/c/mx/mx_arrow_style.h | 30 + gen/test/c/mx/mx_backward_forward.c | 33 + gen/test/c/mx/mx_backward_forward.h | 23 + gen/test/c/mx/mx_bar_style.c | 42 + gen/test/c/mx/mx_bar_style.h | 33 + gen/test/c/mx/mx_beam_level.c | 32 + gen/test/c/mx/mx_beam_level.h | 22 + gen/test/c/mx/mx_beam_value.c | 36 + gen/test/c/mx/mx_beam_value.h | 26 + gen/test/c/mx/mx_beater_value.c | 51 + gen/test/c/mx/mx_beater_value.h | 42 + gen/test/c/mx/mx_breath_mark_value.c | 36 + gen/test/c/mx/mx_breath_mark_value.h | 25 + gen/test/c/mx/mx_caesura_value.c | 37 + gen/test/c/mx/mx_caesura_value.h | 26 + gen/test/c/mx/mx_cancel_location.c | 34 + gen/test/c/mx/mx_cancel_location.h | 26 + gen/test/c/mx/mx_circular_arrow.c | 33 + gen/test/c/mx/mx_circular_arrow.h | 23 + gen/test/c/mx/mx_clef_sign.c | 38 + gen/test/c/mx/mx_clef_sign.h | 30 + gen/test/c/mx/mx_color.c | 9 + gen/test/c/mx/mx_color.h | 19 + gen/test/c/mx/mx_comma_separated_text.c | 9 + gen/test/c/mx/mx_comma_separated_text.h | 15 + gen/test/c/mx/mx_css_font_size.c | 38 + gen/test/c/mx/mx_css_font_size.h | 28 + gen/test/c/mx/mx_degree_symbol_value.c | 36 + gen/test/c/mx/mx_degree_symbol_value.h | 26 + gen/test/c/mx/mx_degree_type_value.c | 34 + gen/test/c/mx/mx_degree_type_value.h | 24 + gen/test/c/mx/mx_distance_type.c | 9 + gen/test/c/mx/mx_distance_type.h | 16 + gen/test/c/mx/mx_divisions.c | 24 + gen/test/c/mx/mx_divisions.h | 22 + gen/test/c/mx/mx_effect.c | 47 + gen/test/c/mx/mx_effect.h | 37 + gen/test/c/mx/mx_enclosure_shape.c | 45 + gen/test/c/mx/mx_enclosure_shape.h | 36 + gen/test/c/mx/mx_ending_number.c | 9 + gen/test/c/mx/mx_ending_number.h | 18 + gen/test/c/mx/mx_fan.c | 34 + gen/test/c/mx/mx_fan.h | 24 + gen/test/c/mx/mx_fermata_shape.c | 40 + gen/test/c/mx/mx_fermata_shape.h | 30 + gen/test/c/mx/mx_fifths.c | 24 + gen/test/c/mx/mx_fifths.h | 22 + gen/test/c/mx/mx_font_size.c | 48 + gen/test/c/mx/mx_font_size.h | 31 + gen/test/c/mx/mx_font_style.c | 33 + gen/test/c/mx/mx_font_style.h | 22 + gen/test/c/mx/mx_font_weight.c | 33 + gen/test/c/mx/mx_font_weight.h | 22 + gen/test/c/mx/mx_glass_value.c | 34 + gen/test/c/mx/mx_glass_value.h | 23 + gen/test/c/mx/mx_glyph_type.c | 9 + gen/test/c/mx/mx_glyph_type.h | 25 + gen/test/c/mx/mx_group_barline_value.c | 34 + gen/test/c/mx/mx_group_barline_value.h | 23 + gen/test/c/mx/mx_group_symbol_value.c | 36 + gen/test/c/mx/mx_group_symbol_value.h | 26 + gen/test/c/mx/mx_handbell_value.c | 43 + gen/test/c/mx/mx_handbell_value.h | 32 + gen/test/c/mx/mx_harmon_closed_location.c | 35 + gen/test/c/mx/mx_harmon_closed_location.h | 25 + gen/test/c/mx/mx_harmon_closed_value.c | 34 + gen/test/c/mx/mx_harmon_closed_value.h | 23 + gen/test/c/mx/mx_harmony_type.c | 34 + gen/test/c/mx/mx_harmony_type.h | 25 + gen/test/c/mx/mx_hole_closed_location.c | 35 + gen/test/c/mx/mx_hole_closed_location.h | 25 + gen/test/c/mx/mx_hole_closed_value.c | 34 + gen/test/c/mx/mx_hole_closed_value.h | 23 + gen/test/c/mx/mx_instrument_sound.c | 49 + gen/test/c/mx/mx_instrument_sound.h | 32 + gen/test/c/mx/mx_kind_value.c | 64 ++ gen/test/c/mx/mx_kind_value.h | 69 ++ gen/test/c/mx/mx_left_center_right.c | 34 + gen/test/c/mx/mx_left_center_right.h | 23 + gen/test/c/mx/mx_left_right.c | 33 + gen/test/c/mx/mx_left_right.h | 23 + gen/test/c/mx/mx_line_end.c | 36 + gen/test/c/mx/mx_line_end.h | 26 + gen/test/c/mx/mx_line_length.c | 34 + gen/test/c/mx/mx_line_length.h | 24 + gen/test/c/mx/mx_line_shape.c | 33 + gen/test/c/mx/mx_line_shape.h | 22 + gen/test/c/mx/mx_line_type.c | 35 + gen/test/c/mx/mx_line_type.h | 24 + gen/test/c/mx/mx_line_width_type.c | 9 + gen/test/c/mx/mx_line_width_type.h | 18 + gen/test/c/mx/mx_margin_type.c | 34 + gen/test/c/mx/mx_margin_type.h | 23 + gen/test/c/mx/mx_measure_numbering_value.c | 34 + gen/test/c/mx/mx_measure_numbering_value.h | 24 + gen/test/c/mx/mx_measure_text.c | 9 + gen/test/c/mx/mx_measure_text.h | 16 + gen/test/c/mx/mx_membrane.c | 48 + gen/test/c/mx/mx_membrane.h | 37 + gen/test/c/mx/mx_metal.c | 63 ++ gen/test/c/mx/mx_metal.h | 53 + gen/test/c/mx/mx_midi_128.c | 32 + gen/test/c/mx/mx_midi_128.h | 20 + gen/test/c/mx/mx_midi_16.c | 32 + gen/test/c/mx/mx_midi_16.h | 20 + gen/test/c/mx/mx_midi_16384.c | 32 + gen/test/c/mx/mx_midi_16384.h | 20 + gen/test/c/mx/mx_millimeters.c | 24 + gen/test/c/mx/mx_millimeters.h | 21 + gen/test/c/mx/mx_mode.c | 9 + gen/test/c/mx/mx_mode.h | 15 + gen/test/c/mx/mx_mute.c | 46 + gen/test/c/mx/mx_mute.h | 37 + gen/test/c/mx/mx_non_negative_decimal.c | 30 + gen/test/c/mx/mx_non_negative_decimal.h | 20 + gen/test/c/mx/mx_note_size_type.c | 35 + gen/test/c/mx/mx_note_size_type.h | 28 + gen/test/c/mx/mx_note_type_value.c | 45 + gen/test/c/mx/mx_note_type_value.h | 35 + gen/test/c/mx/mx_notehead_value.c | 59 ++ gen/test/c/mx/mx_notehead_value.h | 60 ++ gen/test/c/mx/mx_number_level.c | 32 + gen/test/c/mx/mx_number_level.h | 24 + gen/test/c/mx/mx_number_of_lines.c | 32 + gen/test/c/mx/mx_number_of_lines.h | 20 + gen/test/c/mx/mx_number_or_normal.c | 48 + gen/test/c/mx/mx_number_or_normal.h | 30 + gen/test/c/mx/mx_octave.c | 32 + gen/test/c/mx/mx_octave.h | 20 + gen/test/c/mx/mx_on_off.c | 33 + gen/test/c/mx/mx_on_off.h | 22 + gen/test/c/mx/mx_over_under.c | 33 + gen/test/c/mx/mx_over_under.h | 23 + gen/test/c/mx/mx_pedal_type.c | 36 + gen/test/c/mx/mx_pedal_type.h | 29 + gen/test/c/mx/mx_percent.c | 32 + gen/test/c/mx/mx_percent.h | 20 + gen/test/c/mx/mx_pitched_value.c | 42 + gen/test/c/mx/mx_pitched_value.h | 32 + gen/test/c/mx/mx_positive_divisions.c | 30 + gen/test/c/mx/mx_positive_divisions.h | 20 + gen/test/c/mx/mx_positive_integer_or_empty.c | 48 + gen/test/c/mx/mx_positive_integer_or_empty.h | 29 + gen/test/c/mx/mx_principal_voice_symbol.c | 35 + gen/test/c/mx/mx_principal_voice_symbol.h | 27 + gen/test/c/mx/mx_right_left_middle.c | 34 + gen/test/c/mx/mx_right_left_middle.h | 23 + gen/test/c/mx/mx_rotation_degrees.c | 32 + gen/test/c/mx/mx_rotation_degrees.h | 21 + gen/test/c/mx/mx_runtime.c | 89 ++ gen/test/c/mx/mx_runtime.h | 26 + gen/test/c/mx/mx_semi_pitched.c | 37 + gen/test/c/mx/mx_semi_pitched.h | 26 + gen/test/c/mx/mx_semitones.c | 24 + gen/test/c/mx/mx_semitones.h | 22 + gen/test/c/mx/mx_show_frets.c | 33 + gen/test/c/mx/mx_show_frets.h | 23 + gen/test/c/mx/mx_show_tuplet.c | 34 + gen/test/c/mx/mx_show_tuplet.h | 24 + .../c/mx/mx_smufl_accidental_glyph_name.c | 9 + .../c/mx/mx_smufl_accidental_glyph_name.h | 16 + gen/test/c/mx/mx_smufl_coda_glyph_name.c | 9 + gen/test/c/mx/mx_smufl_coda_glyph_name.h | 16 + gen/test/c/mx/mx_smufl_glyph_name.c | 9 + gen/test/c/mx/mx_smufl_glyph_name.h | 16 + gen/test/c/mx/mx_smufl_lyrics_glyph_name.c | 9 + gen/test/c/mx/mx_smufl_lyrics_glyph_name.h | 16 + gen/test/c/mx/mx_smufl_pictogram_glyph_name.c | 9 + gen/test/c/mx/mx_smufl_pictogram_glyph_name.h | 16 + gen/test/c/mx/mx_smufl_segno_glyph_name.c | 9 + gen/test/c/mx/mx_smufl_segno_glyph_name.h | 16 + gen/test/c/mx/mx_sound_id.c | 917 ++++++++++++++++++ gen/test/c/mx/mx_sound_id.h | 907 +++++++++++++++++ gen/test/c/mx/mx_staff_divide_symbol.c | 34 + gen/test/c/mx/mx_staff_divide_symbol.h | 24 + gen/test/c/mx/mx_staff_line.c | 24 + gen/test/c/mx/mx_staff_line.h | 22 + gen/test/c/mx/mx_staff_number.c | 30 + gen/test/c/mx/mx_staff_number.h | 21 + gen/test/c/mx/mx_staff_type.c | 36 + gen/test/c/mx/mx_staff_type.h | 27 + gen/test/c/mx/mx_start_note.c | 34 + gen/test/c/mx/mx_start_note.h | 24 + gen/test/c/mx/mx_start_stop.c | 33 + gen/test/c/mx/mx_start_stop.h | 27 + gen/test/c/mx/mx_start_stop_continue.c | 34 + gen/test/c/mx/mx_start_stop_continue.h | 30 + gen/test/c/mx/mx_start_stop_discontinue.c | 34 + gen/test/c/mx/mx_start_stop_discontinue.h | 28 + gen/test/c/mx/mx_start_stop_single.c | 34 + gen/test/c/mx/mx_start_stop_single.h | 24 + gen/test/c/mx/mx_stem_value.c | 35 + gen/test/c/mx/mx_stem_value.h | 24 + gen/test/c/mx/mx_step.c | 38 + gen/test/c/mx/mx_step.h | 28 + gen/test/c/mx/mx_stick_location.c | 35 + gen/test/c/mx/mx_stick_location.h | 25 + gen/test/c/mx/mx_stick_material.c | 36 + gen/test/c/mx/mx_stick_material.h | 25 + gen/test/c/mx/mx_stick_type.c | 41 + gen/test/c/mx/mx_stick_type.h | 31 + gen/test/c/mx/mx_string_number.c | 30 + gen/test/c/mx/mx_string_number.h | 21 + gen/test/c/mx/mx_syllabic.c | 35 + gen/test/c/mx/mx_syllabic.h | 26 + gen/test/c/mx/mx_symbol_size.c | 35 + gen/test/c/mx/mx_symbol_size.h | 25 + gen/test/c/mx/mx_tap_hand.c | 33 + gen/test/c/mx/mx_tap_hand.h | 23 + gen/test/c/mx/mx_tenths.c | 24 + gen/test/c/mx/mx_tenths.h | 27 + gen/test/c/mx/mx_text_direction.c | 35 + gen/test/c/mx/mx_text_direction.h | 29 + gen/test/c/mx/mx_tied_type.c | 35 + gen/test/c/mx/mx_tied_type.h | 31 + gen/test/c/mx/mx_time_only.c | 9 + gen/test/c/mx/mx_time_only.h | 17 + gen/test/c/mx/mx_time_relation.c | 37 + gen/test/c/mx/mx_time_relation.h | 27 + gen/test/c/mx/mx_time_separator.c | 36 + gen/test/c/mx/mx_time_separator.h | 30 + gen/test/c/mx/mx_time_symbol.c | 37 + gen/test/c/mx/mx_time_symbol.h | 32 + gen/test/c/mx/mx_tip_direction.c | 39 + gen/test/c/mx/mx_tip_direction.h | 29 + gen/test/c/mx/mx_top_bottom.c | 33 + gen/test/c/mx/mx_top_bottom.h | 23 + gen/test/c/mx/mx_tremolo_marks.c | 32 + gen/test/c/mx/mx_tremolo_marks.h | 21 + gen/test/c/mx/mx_tremolo_type.c | 35 + gen/test/c/mx/mx_tremolo_type.h | 24 + gen/test/c/mx/mx_trill_beats.c | 30 + gen/test/c/mx/mx_trill_beats.h | 21 + gen/test/c/mx/mx_trill_step.c | 34 + gen/test/c/mx/mx_trill_step.h | 24 + gen/test/c/mx/mx_two_note_turn.c | 34 + gen/test/c/mx/mx_two_note_turn.h | 24 + gen/test/c/mx/mx_up_down.c | 33 + gen/test/c/mx/mx_up_down.h | 23 + gen/test/c/mx/mx_up_down_stop_continue.c | 35 + gen/test/c/mx/mx_up_down_stop_continue.h | 25 + gen/test/c/mx/mx_upright_inverted.c | 33 + gen/test/c/mx/mx_upright_inverted.h | 23 + gen/test/c/mx/mx_valign.c | 35 + gen/test/c/mx/mx_valign.h | 25 + gen/test/c/mx/mx_valign_image.c | 34 + gen/test/c/mx/mx_valign_image.h | 24 + gen/test/c/mx/mx_wedge_type.c | 35 + gen/test/c/mx/mx_wedge_type.h | 27 + gen/test/c/mx/mx_winged.c | 36 + gen/test/c/mx/mx_winged.h | 28 + gen/test/c/mx/mx_wood.c | 52 + gen/test/c/mx/mx_wood.h | 42 + gen/test/c/mx/mx_yes_no.c | 33 + gen/test/c/mx/mx_yes_no.h | 23 + gen/test/c/mx/mx_yes_no_number.c | 48 + gen/test/c/mx/mx_yes_no_number.h | 31 + gen/test/c/mx/mx_yyyy_mm_dd.c | 9 + gen/test/c/mx/mx_yyyy_mm_dd.h | 15 + gen/test/c/mx/sources.cmake | 138 +++ gen/test/c/src/values_smoke.c | 114 +++ gen/tests/test_plates.py | 7 + 278 files changed, 10665 insertions(+), 5 deletions(-) create mode 100644 gen/emit/c/__init__.py create mode 100644 gen/emit/c/runtime.py create mode 100644 gen/emit/c/values.py create mode 100644 gen/test/c/mx/mx_above_below.c create mode 100644 gen/test/c/mx/mx_above_below.h create mode 100644 gen/test/c/mx/mx_accidental_value.c create mode 100644 gen/test/c/mx/mx_accidental_value.h create mode 100644 gen/test/c/mx/mx_accordion_middle.c create mode 100644 gen/test/c/mx/mx_accordion_middle.h create mode 100644 gen/test/c/mx/mx_arrow_direction.c create mode 100644 gen/test/c/mx/mx_arrow_direction.h create mode 100644 gen/test/c/mx/mx_arrow_style.c create mode 100644 gen/test/c/mx/mx_arrow_style.h create mode 100644 gen/test/c/mx/mx_backward_forward.c create mode 100644 gen/test/c/mx/mx_backward_forward.h create mode 100644 gen/test/c/mx/mx_bar_style.c create mode 100644 gen/test/c/mx/mx_bar_style.h create mode 100644 gen/test/c/mx/mx_beam_level.c create mode 100644 gen/test/c/mx/mx_beam_level.h create mode 100644 gen/test/c/mx/mx_beam_value.c create mode 100644 gen/test/c/mx/mx_beam_value.h create mode 100644 gen/test/c/mx/mx_beater_value.c create mode 100644 gen/test/c/mx/mx_beater_value.h create mode 100644 gen/test/c/mx/mx_breath_mark_value.c create mode 100644 gen/test/c/mx/mx_breath_mark_value.h create mode 100644 gen/test/c/mx/mx_caesura_value.c create mode 100644 gen/test/c/mx/mx_caesura_value.h create mode 100644 gen/test/c/mx/mx_cancel_location.c create mode 100644 gen/test/c/mx/mx_cancel_location.h create mode 100644 gen/test/c/mx/mx_circular_arrow.c create mode 100644 gen/test/c/mx/mx_circular_arrow.h create mode 100644 gen/test/c/mx/mx_clef_sign.c create mode 100644 gen/test/c/mx/mx_clef_sign.h create mode 100644 gen/test/c/mx/mx_color.c create mode 100644 gen/test/c/mx/mx_color.h create mode 100644 gen/test/c/mx/mx_comma_separated_text.c create mode 100644 gen/test/c/mx/mx_comma_separated_text.h create mode 100644 gen/test/c/mx/mx_css_font_size.c create mode 100644 gen/test/c/mx/mx_css_font_size.h create mode 100644 gen/test/c/mx/mx_degree_symbol_value.c create mode 100644 gen/test/c/mx/mx_degree_symbol_value.h create mode 100644 gen/test/c/mx/mx_degree_type_value.c create mode 100644 gen/test/c/mx/mx_degree_type_value.h create mode 100644 gen/test/c/mx/mx_distance_type.c create mode 100644 gen/test/c/mx/mx_distance_type.h create mode 100644 gen/test/c/mx/mx_divisions.c create mode 100644 gen/test/c/mx/mx_divisions.h create mode 100644 gen/test/c/mx/mx_effect.c create mode 100644 gen/test/c/mx/mx_effect.h create mode 100644 gen/test/c/mx/mx_enclosure_shape.c create mode 100644 gen/test/c/mx/mx_enclosure_shape.h create mode 100644 gen/test/c/mx/mx_ending_number.c create mode 100644 gen/test/c/mx/mx_ending_number.h create mode 100644 gen/test/c/mx/mx_fan.c create mode 100644 gen/test/c/mx/mx_fan.h create mode 100644 gen/test/c/mx/mx_fermata_shape.c create mode 100644 gen/test/c/mx/mx_fermata_shape.h create mode 100644 gen/test/c/mx/mx_fifths.c create mode 100644 gen/test/c/mx/mx_fifths.h create mode 100644 gen/test/c/mx/mx_font_size.c create mode 100644 gen/test/c/mx/mx_font_size.h create mode 100644 gen/test/c/mx/mx_font_style.c create mode 100644 gen/test/c/mx/mx_font_style.h create mode 100644 gen/test/c/mx/mx_font_weight.c create mode 100644 gen/test/c/mx/mx_font_weight.h create mode 100644 gen/test/c/mx/mx_glass_value.c create mode 100644 gen/test/c/mx/mx_glass_value.h create mode 100644 gen/test/c/mx/mx_glyph_type.c create mode 100644 gen/test/c/mx/mx_glyph_type.h create mode 100644 gen/test/c/mx/mx_group_barline_value.c create mode 100644 gen/test/c/mx/mx_group_barline_value.h create mode 100644 gen/test/c/mx/mx_group_symbol_value.c create mode 100644 gen/test/c/mx/mx_group_symbol_value.h create mode 100644 gen/test/c/mx/mx_handbell_value.c create mode 100644 gen/test/c/mx/mx_handbell_value.h create mode 100644 gen/test/c/mx/mx_harmon_closed_location.c create mode 100644 gen/test/c/mx/mx_harmon_closed_location.h create mode 100644 gen/test/c/mx/mx_harmon_closed_value.c create mode 100644 gen/test/c/mx/mx_harmon_closed_value.h create mode 100644 gen/test/c/mx/mx_harmony_type.c create mode 100644 gen/test/c/mx/mx_harmony_type.h create mode 100644 gen/test/c/mx/mx_hole_closed_location.c create mode 100644 gen/test/c/mx/mx_hole_closed_location.h create mode 100644 gen/test/c/mx/mx_hole_closed_value.c create mode 100644 gen/test/c/mx/mx_hole_closed_value.h create mode 100644 gen/test/c/mx/mx_instrument_sound.c create mode 100644 gen/test/c/mx/mx_instrument_sound.h create mode 100644 gen/test/c/mx/mx_kind_value.c create mode 100644 gen/test/c/mx/mx_kind_value.h create mode 100644 gen/test/c/mx/mx_left_center_right.c create mode 100644 gen/test/c/mx/mx_left_center_right.h create mode 100644 gen/test/c/mx/mx_left_right.c create mode 100644 gen/test/c/mx/mx_left_right.h create mode 100644 gen/test/c/mx/mx_line_end.c create mode 100644 gen/test/c/mx/mx_line_end.h create mode 100644 gen/test/c/mx/mx_line_length.c create mode 100644 gen/test/c/mx/mx_line_length.h create mode 100644 gen/test/c/mx/mx_line_shape.c create mode 100644 gen/test/c/mx/mx_line_shape.h create mode 100644 gen/test/c/mx/mx_line_type.c create mode 100644 gen/test/c/mx/mx_line_type.h create mode 100644 gen/test/c/mx/mx_line_width_type.c create mode 100644 gen/test/c/mx/mx_line_width_type.h create mode 100644 gen/test/c/mx/mx_margin_type.c create mode 100644 gen/test/c/mx/mx_margin_type.h create mode 100644 gen/test/c/mx/mx_measure_numbering_value.c create mode 100644 gen/test/c/mx/mx_measure_numbering_value.h create mode 100644 gen/test/c/mx/mx_measure_text.c create mode 100644 gen/test/c/mx/mx_measure_text.h create mode 100644 gen/test/c/mx/mx_membrane.c create mode 100644 gen/test/c/mx/mx_membrane.h create mode 100644 gen/test/c/mx/mx_metal.c create mode 100644 gen/test/c/mx/mx_metal.h create mode 100644 gen/test/c/mx/mx_midi_128.c create mode 100644 gen/test/c/mx/mx_midi_128.h create mode 100644 gen/test/c/mx/mx_midi_16.c create mode 100644 gen/test/c/mx/mx_midi_16.h create mode 100644 gen/test/c/mx/mx_midi_16384.c create mode 100644 gen/test/c/mx/mx_midi_16384.h create mode 100644 gen/test/c/mx/mx_millimeters.c create mode 100644 gen/test/c/mx/mx_millimeters.h create mode 100644 gen/test/c/mx/mx_mode.c create mode 100644 gen/test/c/mx/mx_mode.h create mode 100644 gen/test/c/mx/mx_mute.c create mode 100644 gen/test/c/mx/mx_mute.h create mode 100644 gen/test/c/mx/mx_non_negative_decimal.c create mode 100644 gen/test/c/mx/mx_non_negative_decimal.h create mode 100644 gen/test/c/mx/mx_note_size_type.c create mode 100644 gen/test/c/mx/mx_note_size_type.h create mode 100644 gen/test/c/mx/mx_note_type_value.c create mode 100644 gen/test/c/mx/mx_note_type_value.h create mode 100644 gen/test/c/mx/mx_notehead_value.c create mode 100644 gen/test/c/mx/mx_notehead_value.h create mode 100644 gen/test/c/mx/mx_number_level.c create mode 100644 gen/test/c/mx/mx_number_level.h create mode 100644 gen/test/c/mx/mx_number_of_lines.c create mode 100644 gen/test/c/mx/mx_number_of_lines.h create mode 100644 gen/test/c/mx/mx_number_or_normal.c create mode 100644 gen/test/c/mx/mx_number_or_normal.h create mode 100644 gen/test/c/mx/mx_octave.c create mode 100644 gen/test/c/mx/mx_octave.h create mode 100644 gen/test/c/mx/mx_on_off.c create mode 100644 gen/test/c/mx/mx_on_off.h create mode 100644 gen/test/c/mx/mx_over_under.c create mode 100644 gen/test/c/mx/mx_over_under.h create mode 100644 gen/test/c/mx/mx_pedal_type.c create mode 100644 gen/test/c/mx/mx_pedal_type.h create mode 100644 gen/test/c/mx/mx_percent.c create mode 100644 gen/test/c/mx/mx_percent.h create mode 100644 gen/test/c/mx/mx_pitched_value.c create mode 100644 gen/test/c/mx/mx_pitched_value.h create mode 100644 gen/test/c/mx/mx_positive_divisions.c create mode 100644 gen/test/c/mx/mx_positive_divisions.h create mode 100644 gen/test/c/mx/mx_positive_integer_or_empty.c create mode 100644 gen/test/c/mx/mx_positive_integer_or_empty.h create mode 100644 gen/test/c/mx/mx_principal_voice_symbol.c create mode 100644 gen/test/c/mx/mx_principal_voice_symbol.h create mode 100644 gen/test/c/mx/mx_right_left_middle.c create mode 100644 gen/test/c/mx/mx_right_left_middle.h create mode 100644 gen/test/c/mx/mx_rotation_degrees.c create mode 100644 gen/test/c/mx/mx_rotation_degrees.h create mode 100644 gen/test/c/mx/mx_runtime.c create mode 100644 gen/test/c/mx/mx_runtime.h create mode 100644 gen/test/c/mx/mx_semi_pitched.c create mode 100644 gen/test/c/mx/mx_semi_pitched.h create mode 100644 gen/test/c/mx/mx_semitones.c create mode 100644 gen/test/c/mx/mx_semitones.h create mode 100644 gen/test/c/mx/mx_show_frets.c create mode 100644 gen/test/c/mx/mx_show_frets.h create mode 100644 gen/test/c/mx/mx_show_tuplet.c create mode 100644 gen/test/c/mx/mx_show_tuplet.h create mode 100644 gen/test/c/mx/mx_smufl_accidental_glyph_name.c create mode 100644 gen/test/c/mx/mx_smufl_accidental_glyph_name.h create mode 100644 gen/test/c/mx/mx_smufl_coda_glyph_name.c create mode 100644 gen/test/c/mx/mx_smufl_coda_glyph_name.h create mode 100644 gen/test/c/mx/mx_smufl_glyph_name.c create mode 100644 gen/test/c/mx/mx_smufl_glyph_name.h create mode 100644 gen/test/c/mx/mx_smufl_lyrics_glyph_name.c create mode 100644 gen/test/c/mx/mx_smufl_lyrics_glyph_name.h create mode 100644 gen/test/c/mx/mx_smufl_pictogram_glyph_name.c create mode 100644 gen/test/c/mx/mx_smufl_pictogram_glyph_name.h create mode 100644 gen/test/c/mx/mx_smufl_segno_glyph_name.c create mode 100644 gen/test/c/mx/mx_smufl_segno_glyph_name.h create mode 100644 gen/test/c/mx/mx_sound_id.c create mode 100644 gen/test/c/mx/mx_sound_id.h create mode 100644 gen/test/c/mx/mx_staff_divide_symbol.c create mode 100644 gen/test/c/mx/mx_staff_divide_symbol.h create mode 100644 gen/test/c/mx/mx_staff_line.c create mode 100644 gen/test/c/mx/mx_staff_line.h create mode 100644 gen/test/c/mx/mx_staff_number.c create mode 100644 gen/test/c/mx/mx_staff_number.h create mode 100644 gen/test/c/mx/mx_staff_type.c create mode 100644 gen/test/c/mx/mx_staff_type.h create mode 100644 gen/test/c/mx/mx_start_note.c create mode 100644 gen/test/c/mx/mx_start_note.h create mode 100644 gen/test/c/mx/mx_start_stop.c create mode 100644 gen/test/c/mx/mx_start_stop.h create mode 100644 gen/test/c/mx/mx_start_stop_continue.c create mode 100644 gen/test/c/mx/mx_start_stop_continue.h create mode 100644 gen/test/c/mx/mx_start_stop_discontinue.c create mode 100644 gen/test/c/mx/mx_start_stop_discontinue.h create mode 100644 gen/test/c/mx/mx_start_stop_single.c create mode 100644 gen/test/c/mx/mx_start_stop_single.h create mode 100644 gen/test/c/mx/mx_stem_value.c create mode 100644 gen/test/c/mx/mx_stem_value.h create mode 100644 gen/test/c/mx/mx_step.c create mode 100644 gen/test/c/mx/mx_step.h create mode 100644 gen/test/c/mx/mx_stick_location.c create mode 100644 gen/test/c/mx/mx_stick_location.h create mode 100644 gen/test/c/mx/mx_stick_material.c create mode 100644 gen/test/c/mx/mx_stick_material.h create mode 100644 gen/test/c/mx/mx_stick_type.c create mode 100644 gen/test/c/mx/mx_stick_type.h create mode 100644 gen/test/c/mx/mx_string_number.c create mode 100644 gen/test/c/mx/mx_string_number.h create mode 100644 gen/test/c/mx/mx_syllabic.c create mode 100644 gen/test/c/mx/mx_syllabic.h create mode 100644 gen/test/c/mx/mx_symbol_size.c create mode 100644 gen/test/c/mx/mx_symbol_size.h create mode 100644 gen/test/c/mx/mx_tap_hand.c create mode 100644 gen/test/c/mx/mx_tap_hand.h create mode 100644 gen/test/c/mx/mx_tenths.c create mode 100644 gen/test/c/mx/mx_tenths.h create mode 100644 gen/test/c/mx/mx_text_direction.c create mode 100644 gen/test/c/mx/mx_text_direction.h create mode 100644 gen/test/c/mx/mx_tied_type.c create mode 100644 gen/test/c/mx/mx_tied_type.h create mode 100644 gen/test/c/mx/mx_time_only.c create mode 100644 gen/test/c/mx/mx_time_only.h create mode 100644 gen/test/c/mx/mx_time_relation.c create mode 100644 gen/test/c/mx/mx_time_relation.h create mode 100644 gen/test/c/mx/mx_time_separator.c create mode 100644 gen/test/c/mx/mx_time_separator.h create mode 100644 gen/test/c/mx/mx_time_symbol.c create mode 100644 gen/test/c/mx/mx_time_symbol.h create mode 100644 gen/test/c/mx/mx_tip_direction.c create mode 100644 gen/test/c/mx/mx_tip_direction.h create mode 100644 gen/test/c/mx/mx_top_bottom.c create mode 100644 gen/test/c/mx/mx_top_bottom.h create mode 100644 gen/test/c/mx/mx_tremolo_marks.c create mode 100644 gen/test/c/mx/mx_tremolo_marks.h create mode 100644 gen/test/c/mx/mx_tremolo_type.c create mode 100644 gen/test/c/mx/mx_tremolo_type.h create mode 100644 gen/test/c/mx/mx_trill_beats.c create mode 100644 gen/test/c/mx/mx_trill_beats.h create mode 100644 gen/test/c/mx/mx_trill_step.c create mode 100644 gen/test/c/mx/mx_trill_step.h create mode 100644 gen/test/c/mx/mx_two_note_turn.c create mode 100644 gen/test/c/mx/mx_two_note_turn.h create mode 100644 gen/test/c/mx/mx_up_down.c create mode 100644 gen/test/c/mx/mx_up_down.h create mode 100644 gen/test/c/mx/mx_up_down_stop_continue.c create mode 100644 gen/test/c/mx/mx_up_down_stop_continue.h create mode 100644 gen/test/c/mx/mx_upright_inverted.c create mode 100644 gen/test/c/mx/mx_upright_inverted.h create mode 100644 gen/test/c/mx/mx_valign.c create mode 100644 gen/test/c/mx/mx_valign.h create mode 100644 gen/test/c/mx/mx_valign_image.c create mode 100644 gen/test/c/mx/mx_valign_image.h create mode 100644 gen/test/c/mx/mx_wedge_type.c create mode 100644 gen/test/c/mx/mx_wedge_type.h create mode 100644 gen/test/c/mx/mx_winged.c create mode 100644 gen/test/c/mx/mx_winged.h create mode 100644 gen/test/c/mx/mx_wood.c create mode 100644 gen/test/c/mx/mx_wood.h create mode 100644 gen/test/c/mx/mx_yes_no.c create mode 100644 gen/test/c/mx/mx_yes_no.h create mode 100644 gen/test/c/mx/mx_yes_no_number.c create mode 100644 gen/test/c/mx/mx_yes_no_number.h create mode 100644 gen/test/c/mx/mx_yyyy_mm_dd.c create mode 100644 gen/test/c/mx/mx_yyyy_mm_dd.h create mode 100644 gen/test/c/mx/sources.cmake create mode 100644 gen/test/c/src/values_smoke.c diff --git a/gen/emit/__init__.py b/gen/emit/__init__.py index ec7db23d4..7b41aa24c 100644 --- a/gen/emit/__init__.py +++ b/gen/emit/__init__.py @@ -29,6 +29,7 @@ # projected (`gen plates`) without every backend being importable. BACKENDS: dict[str, str] = { "go": "gen.emit.go", + "c": "gen.emit.c", } diff --git a/gen/emit/c/__init__.py b/gen/emit/c/__init__.py new file mode 100644 index 000000000..cf3206d10 --- /dev/null +++ b/gen/emit/c/__init__.py @@ -0,0 +1,52 @@ +"""C backend: render the Plates into the C test target sources. + +Dumb renderers per the design: identifiers (including composed enum +constants), casings, type mappings, file stems, and structure arrive +resolved on the plates; this package owns only C grammar and the runtime +support files. Each plate's FileId renders as a header/impl pair -- the +documented one-FileId-to-two-files mapping for C. + +Alongside the sources, the backend emits `sources.cmake`, the generated +manifest the test target's CMakeLists includes, so the build never globs +and never goes stale. + +Currently rendered: the four value shapes (the leaf node types) plus the +runtime. Complex types and the document entry points are later phases. +""" + +from __future__ import annotations + +from gen.emit.c.runtime import runtime_header, runtime_impl, runtime_stem +from gen.emit.c.values import value_files +from gen.emit.writer import banner +from gen.plates.model import Plates + + +def render(plates: Plates) -> dict[str, str]: + rt = runtime_stem(plates) + includes_of = { + spec.file: spec.includes for spec in (plates.files or []) + } + + files: dict[str, str] = { + f"{rt}.h": runtime_header(plates, rt), + f"{rt}.c": runtime_impl(plates, rt), + } + for plate in plates.value_types: + header, impl = value_files(plates, plate, includes_of.get(plate.file, []), rt) + files[plate.file + ".h"] = header + files[plate.file + ".c"] = impl + + files["sources.cmake"] = _sources_cmake(plates, files) + return files + + +def _sources_cmake(plates: Plates, files: dict[str, str]) -> str: + """The build manifest: every emitted .c, listed explicitly so CMake + reconfigures exactly when the generator changes the file set.""" + sources = sorted(name for name in files if name.endswith(".c")) + lines = [f"# {banner(plates.source)}", ""] + lines += ["set(MX_GENERATED_SOURCES"] + lines += [f" ${{CMAKE_CURRENT_LIST_DIR}}/{name}" for name in sources] + lines += [")"] + return "\n".join(lines) + "\n" diff --git a/gen/emit/c/runtime.py b/gen/emit/c/runtime.py new file mode 100644 index 000000000..abe72864e --- /dev/null +++ b/gen/emit/c/runtime.py @@ -0,0 +1,140 @@ +"""The static C runtime support files (mx_runtime.h / mx_runtime.c). + +These helpers are the shared substrate the generated types call into; they +carry the lenient-parse policies in one place, mirroring the Go runtime. +Formatting returns malloc'd strings (C has no other safe idiom for it); +parsing never fails -- malformed input degrades deterministically and range +clamping is the typed wrappers' job. +""" + +from __future__ import annotations + +from gen.emit.c.common import header_file, impl_file +from gen.plates.model import Plates + +_HEADER_BODY = """\ +/* Shared parse/format helpers for the generated MusicXML types. Lenient + parses never fail: unparseable input becomes 0 (range clamping is the + typed wrappers' job). Formatting returns malloc'd strings the caller + frees. */ + +bool mx_try_parse_decimal(const char *s, double *out); +double mx_parse_decimal(const char *s); +bool mx_try_parse_int(const char *s, long *out); +long mx_parse_int(const char *s); + +/* Shortest decimal spelling without exponent notation (8.5 -> "8.5", + 4 -> "4", 1e-6 -> "0.000001"). Malloc'd; caller frees. */ +char *mx_format_decimal(double v); +char *mx_format_int(long v); + +/* strdup that maps NULL to "". Malloc'd; caller frees. */ +char *mx_strdup(const char *s);""" + +_IMPL_BODY = """\ +#include +#include +#include +#include + +bool mx_try_parse_decimal(const char *s, double *out) { + *out = 0; + if (!s || !*s) + return false; + char *end = NULL; + double v = strtod(s, &end); + if (end == s) + return false; + while (*end == ' ' || *end == '\\t' || *end == '\\n' || *end == '\\r') + end++; + if (*end != '\\0') + return false; + if (isnan(v) || isinf(v)) + return false; + *out = v; + return true; +} + +double mx_parse_decimal(const char *s) { + double v = 0; + mx_try_parse_decimal(s, &v); + return v; +} + +bool mx_try_parse_int(const char *s, long *out) { + *out = 0; + if (!s || !*s) + return false; + char *end = NULL; + long v = strtol(s, &end, 10); + if (end == s) + return false; + while (*end == ' ' || *end == '\\t' || *end == '\\n' || *end == '\\r') + end++; + if (*end != '\\0') + return false; + *out = v; + return true; +} + +long mx_parse_int(const char *s) { + long v = 0; + if (mx_try_parse_int(s, &v)) + return v; + double d = 0; + if (mx_try_parse_decimal(s, &d)) + return (long)d; /* decimal-looking input truncates toward zero */ + return 0; +} + +char *mx_format_decimal(double v) { + char buf[64]; + snprintf(buf, sizeof(buf), "%.9f", v); + /* Trim trailing zeros, then a trailing dot; canonicalize "-0" to "0". */ + size_t len = strlen(buf); + while (len > 0 && buf[len - 1] == '0') + buf[--len] = '\\0'; + if (len > 0 && buf[len - 1] == '.') + buf[--len] = '\\0'; + if (strcmp(buf, "-0") == 0) { + buf[0] = '0'; + buf[1] = '\\0'; + } + return mx_strdup(buf); +} + +char *mx_format_int(long v) { + char buf[32]; + snprintf(buf, sizeof(buf), "%ld", v); + return mx_strdup(buf); +} + +char *mx_strdup(const char *s) { + if (!s) + s = ""; + size_t n = strlen(s) + 1; + char *out = malloc(n); + memcpy(out, s, n); + return out; +}""" + + +def _substitute(body: str, plates: Plates) -> list[str]: + # The canonical text spells helpers mx_*; the configured symbol prefix is + # substituted so generated type code (which composes names from the same + # prefix) and the runtime always agree. + from gen.emit.c.common import fn_prefix + + return body.replace("mx_", fn_prefix(plates)).split("\n") + + +def runtime_stem(plates: Plates) -> str: + return plates.target.file_prefix + "runtime" + + +def runtime_header(plates: Plates, stem: str) -> str: + return header_file(plates, stem, _substitute(_HEADER_BODY, plates), [""]) + + +def runtime_impl(plates: Plates, stem: str) -> str: + return impl_file(plates, stem, _substitute(_IMPL_BODY, plates), []) diff --git a/gen/emit/c/values.py b/gen/emit/c/values.py new file mode 100644 index 000000000..bf57060a3 --- /dev/null +++ b/gen/emit/c/values.py @@ -0,0 +1,451 @@ +"""C templates for the four value shapes (the leaf node types). + +One template per shape, mirroring the Go backend's surface in C idiom. Each +type renders as a header/impl pair (the documented one-FileId-to-two-files +mapping for C): + + bool mx__try_parse(const char *s, MxT *out) strict membership + MxT mx__parse(const char *s) lenient (fixup policies) + to_string the wire spelling: + enums return static storage (const char *, do not free); + numbers and unions return malloc'd strings the caller frees; + string types ARE char* values (parse returns a malloc'd copy). + +Leniency policies match the Go backend: unknown enum literal -> first +variant; unparseable number -> 0; every number clamps into its declared +range with primitive-implied lower bounds and exclusive bounds clamping to +the nearest representable value (decimal: +/- 1e-6). +""" + +from __future__ import annotations + +from gen.emit.c.common import c_string, doc_comment, fn_name, fn_prefix, header_file, impl_file +from gen.plates.model import ( + EnumPlate, + NumberPlate, + Plates, + StringPlate, + UnionPlate, +) + +# IR primitive -> (C type, strict-try helper suffix, format helper suffix). +# A string-family primitive has no strict form: any string belongs to it. +_PRIM = { + "decimal": ("double", "try_parse_decimal", "format_decimal"), + "integer": ("long", "try_parse_int", "format_int"), + "positive_integer": ("long", "try_parse_int", "format_int"), + "non_negative_integer": ("long", "try_parse_int", "format_int"), + "string": ("char *", None, None), + "token": ("char *", None, None), + "nmtoken": ("char *", None, None), + "date": ("char *", None, None), +} + +_IMPLIED_MIN = {"positive_integer": 1, "non_negative_integer": 0} + + +def value_files(plates: Plates, plate, includes: list[str], rt: str) -> tuple[str, str]: + """Render one value plate into its (header, impl) pair. `includes` is the + plate's dependency stems (from Plates.files); `rt` the runtime stem.""" + if isinstance(plate, EnumPlate): + header, impl = _enum(plates, plate, rt) + elif isinstance(plate, NumberPlate): + header, impl = _number(plates, plate, rt) + elif isinstance(plate, StringPlate): + header, impl = _string(plates, plate, rt) + elif isinstance(plate, UnionPlate): + header, impl = _union(plates, plate, includes, rt) + else: + raise TypeError(f"not a value plate: {plate!r}") + return header, impl + + +def _fn(plates: Plates, plate, verb: str) -> str: + return fn_name(plates, plate.name, verb) + + +# --------------------------------------------------------------------------- # +# enum-class +# --------------------------------------------------------------------------- # + + +def _enum(plates: Plates, plate: EnumPlate, rt: str): + ident = plate.ident + wrap = plates.target.doc_style.wrap + try_parse = _fn(plates, plate, "try_parse") + parse = _fn(plates, plate, "parse") + to_string = _fn(plates, plate, "to_string") + values = fn_name(plates, plate.name, "values") + + decl = doc_comment(plate.doc, wrap) + decl += ["typedef enum {"] + decl += [f" {v.ident} = {i}," for i, v in enumerate(plate.variants[:-1])] + decl += [f" {plate.variants[-1].ident} = {len(plate.variants) - 1}"] + decl += [f"}} {ident};", ""] + decl += [ + f"bool {try_parse}(const char *s, {ident} *out);", + "/* Lenient: unknown input falls back to the first variant. */", + f"{ident} {parse}(const char *s);", + "/* Returns the wire literal (static storage; do not free). */", + f"const char *{to_string}({ident} v);", + ] + + body = [f"static const char *const {values}[] = {{"] + body += [f" {c_string(v.wire)}," for v in plate.variants] + body += ["};", ""] + body += [ + f"bool {try_parse}(const char *s, {ident} *out) {{", + f" for (size_t i = 0; i < sizeof({values}) / sizeof({values}[0]); i++) {{", + f" if (strcmp(s, {values}[i]) == 0) {{", + f" *out = ({ident})i;", + " return true;", + " }", + " }", + f" *out = ({ident})0;", + " return false;", + "}", + "", + f"{ident} {parse}(const char *s) {{", + f" {ident} v;", + f" {try_parse}(s, &v);", + " return v;", + "}", + "", + f"const char *{to_string}({ident} v) {{", + f" if ((size_t)v >= sizeof({values}) / sizeof({values}[0]))", + f" return {values}[0];", + f" return {values}[v];", + "}", + ] + return ( + header_file(plates, plate.file, decl, [""]), + impl_file(plates, plate.file, body, [""]), + ) + + +# --------------------------------------------------------------------------- # +# numeric-wrapper +# --------------------------------------------------------------------------- # + + +def _c_int(literal: str) -> str: + return str(int(float(literal))) + + +def _c_float(literal: str) -> str: + return repr(float(literal)) + + +def _clamp_steps(plate: NumberPlate) -> list[tuple[str, str, str]]: + """(comparison op, bound literal, replacement literal) per active bound, + mirroring the Go backend's policy exactly.""" + is_int = plate.target_type != "double" + b = plate.bounds + steps: list[tuple[str, str, str]] = [] + + lows: list[tuple[float, bool, str]] = [] + if b.min_inclusive is not None: + lows.append((float(b.min_inclusive), False, b.min_inclusive)) + if b.min_exclusive is not None: + lows.append((float(b.min_exclusive), True, b.min_exclusive)) + implied = _IMPLIED_MIN.get(plate.base) + if implied is not None: + lows.append((float(implied), False, str(implied))) + if lows: + value, exclusive, literal = max(lows, key=lambda t: (t[0], t[1])) + if is_int: + bound = _c_int(literal) + steps.append(("<=" if exclusive else "<", + bound, + str(int(float(literal)) + 1) if exclusive else bound)) + else: + bound = _c_float(literal) + steps.append(("<=" if exclusive else "<", + bound, + repr(float(literal) + 1e-6) if exclusive else bound)) + + highs: list[tuple[float, bool, str]] = [] + if b.max_inclusive is not None: + highs.append((float(b.max_inclusive), False, b.max_inclusive)) + if b.max_exclusive is not None: + highs.append((float(b.max_exclusive), True, b.max_exclusive)) + if highs: + value, exclusive, literal = min(highs, key=lambda t: (t[0], -t[1])) + if is_int: + bound = _c_int(literal) + steps.append((">=" if exclusive else ">", + bound, + str(int(float(literal)) - 1) if exclusive else bound)) + else: + bound = _c_float(literal) + steps.append((">=" if exclusive else ">", + bound, + repr(float(literal) - 1e-6) if exclusive else bound)) + return steps + + +def _number(plates: Plates, plate: NumberPlate, rt: str): + ident = plate.ident + wrap = plates.target.doc_style.wrap + c_type, try_helper, fmt_helper = _PRIM[plate.base] + prefix = fn_prefix(plates) + try_parse = _fn(plates, plate, "try_parse") + parse = _fn(plates, plate, "parse") + to_string = _fn(plates, plate, "to_string") + clamp = _fn(plates, plate, "clamp") + steps = _clamp_steps(plate) + runtime_try = prefix + try_helper + runtime_fmt = prefix + fmt_helper + runtime_parse = prefix + ("parse_decimal" if c_type == "double" else "parse_int") + + decl = doc_comment(plate.doc, wrap) + decl += [f"typedef {c_type} {ident};", ""] + decl += [ + "/* Strict parse, then clamps into the declared range. */", + f"bool {try_parse}(const char *s, {ident} *out);", + "/* Lenient: unparseable input becomes 0, then clamps. */", + f"{ident} {parse}(const char *s);", + "/* Malloc'd; caller frees. */", + f"char *{to_string}({ident} v);", + ] + + body = [] + if steps: + body += [f"static {ident} {clamp}({c_type} v) {{"] + for op, bound, repl in steps: + body += [f" if (v {op} {bound})", f" v = {repl};"] + body += [f" return ({ident})v;", "}", ""] + convert = f"{clamp}(v)" + else: + convert = f"({ident})v" + body += [ + f"bool {try_parse}(const char *s, {ident} *out) {{", + f" {c_type} v;", + f" if (!{runtime_try}(s, &v)) {{", + f" *out = ({ident})0;", + " return false;", + " }", + f" *out = {convert};", + " return true;", + "}", + "", + f"{ident} {parse}(const char *s) {{", + f" {c_type} v = {runtime_parse}(s);", + f" return {convert};", + "}", + "", + f"char *{to_string}({ident} v) {{", + f" return {runtime_fmt}(({c_type})v);", + "}", + ] + return ( + header_file(plates, plate.file, decl, [""]), + impl_file(plates, plate.file, body, [f'"{rt}.h"']), + ) + + +# --------------------------------------------------------------------------- # +# string-wrapper +# --------------------------------------------------------------------------- # + + +def _string(plates: Plates, plate: StringPlate, rt: str): + ident = plate.ident + wrap = plates.target.doc_style.wrap + parse = _fn(plates, plate, "parse") + prefix = fn_prefix(plates) + + doc = plate.doc or "" + if plate.patterns: + doc = (doc + " Pattern (not enforced): " + " | ".join(plate.patterns)).strip() + decl = doc_comment(doc, wrap) + decl += [ + f"typedef char *{ident};", + "", + "/* Malloc'd copy of the wire string; the value IS its spelling. */", + f"{ident} {parse}(const char *s);", + ] + body = [ + f"{ident} {parse}(const char *s) {{", + f" return {prefix}strdup(s);", + "}", + ] + return ( + header_file(plates, plate.file, decl, []), + impl_file(plates, plate.file, body, [f'"{rt}.h"']), + ) + + +# --------------------------------------------------------------------------- # +# tagged-variant +# --------------------------------------------------------------------------- # + + +def _union_cases(plates: Plates, plate: UnionPlate): + """Flatten union members into kind cases, mirroring the Go backend: + (kind const, field name or None, C type, try spelling or None for + any-string, lenient-parse spelling, to-string spelling, owns-memory).""" + fconv = plates.target.field_convention + prefix = plates.target.prefix.upper() + "_" if plates.target.prefix else "" + kind_base = f"{prefix}{plate.name.screaming}_KIND_" + fn_pre = fn_prefix(plates) + cases = [] + for m in plate.members: + if m.ref is not None: + field = m.name.cased[fconv] + kind = kind_base + m.name.screaming + if m.ref.category == "value": + member_fns = lambda verb, n=m.name: fn_name(plates, n, verb) + target_plate = plates.plate(m.ref.wire) + if target_plate.kind == "enum": + to_s = f"{fn_pre}strdup({member_fns('to_string')}(v.{field}))" + elif target_plate.kind == "string": + to_s = f"{fn_pre}strdup(v.{field})" + else: + to_s = f"{member_fns('to_string')}(v.{field})" + cases.append({ + "kind": kind, "field": field, "c_type": m.ref.ident, + "try": f"{member_fns('try_parse')}(s, &v.{field})" + if target_plate.kind != "string" else None, + "parse": f"{member_fns('parse')}(s)", + "to_string": to_s, + "owns": target_plate.kind == "string", + }) + else: # primitive + c_type, try_helper, fmt_helper = _PRIM[m.ref.wire] + if try_helper is None: # open string member: any input matches + cases.append({ + "kind": kind, "field": field, "c_type": "char *", + "try": None, + "parse": f"{fn_pre}strdup(s)", + "to_string": f"{fn_pre}strdup(v.{field})", + "owns": True, + }) + else: + cases.append({ + "kind": kind, "field": field, "c_type": c_type, + "try": f"{fn_pre}{try_helper}(s, &v.{field})", + "parse": f"{fn_pre}{('parse_decimal' if c_type == 'double' else 'parse_int')}(s)", + "to_string": f"{fn_pre}{fmt_helper}(v.{field})", + "owns": False, + }) + else: + for variant in m.literals or []: + cases.append({ + "kind": kind_base + variant.name.screaming, + "field": None, "c_type": None, + "try": f"strcmp(s, {c_string(variant.wire)}) == 0", + "parse": None, + "to_string": f"{fn_pre}strdup({c_string(variant.wire)})", + "owns": False, + }) + return cases + + +def _union(plates: Plates, plate: UnionPlate, includes: list[str], rt: str): + ident = plate.ident + wrap = plates.target.doc_style.wrap + try_parse = _fn(plates, plate, "try_parse") + parse = _fn(plates, plate, "parse") + to_string = _fn(plates, plate, "to_string") + free = _fn(plates, plate, "free") + cases = _union_cases(plates, plate) + + decl = doc_comment(plate.doc, wrap) + decl += ["typedef enum {"] + decl += [f" {c['kind']} = {i}," for i, c in enumerate(cases[:-1])] + decl += [f" {cases[-1]['kind']} = {len(cases) - 1}"] + decl += [f"}} {ident}Kind;", ""] + decl += ["typedef struct {", f" {ident}Kind kind;"] + for c in cases: + if c["field"] is not None: + sep = "" if c["c_type"].endswith("*") else " " + decl += [f" {c['c_type']}{sep}{c['field']};"] + decl += [f"}} {ident};", ""] + decl += [ + "/* Tries each union member in schema order. */", + f"bool {try_parse}(const char *s, {ident} *out);", + "/* Lenient: an unmatched input is absorbed by the first member. */", + f"{ident} {parse}(const char *s);", + "/* The wire spelling of whichever member is held. Malloc'd. */", + f"char *{to_string}({ident} v);", + f"void {free}({ident} *v);", + ] + + body = [ + f"bool {try_parse}(const char *s, {ident} *out) {{", + f" {ident} v;", + " memset(&v, 0, sizeof(v));", + ] + open_ended = False + for c in cases: + if c["try"] is None: # open string member: always matches + body += [ + f" v.kind = {c['kind']};", + f" v.{c['field']} = {c['parse']};", + " *out = v;", + " return true;", + ] + open_ended = True + break + if c["field"] is None: + body += [ + f" if ({c['try']}) {{", + f" v.kind = {c['kind']};", + " *out = v;", + " return true;", + " }", + ] + else: + body += [ + f" if ({c['try']}) {{", + f" v.kind = {c['kind']};", + " *out = v;", + " return true;", + " }", + ] + if not open_ended: + body += [" *out = v;", " return false;"] + body += ["}", ""] + + first = cases[0] + body += [ + f"{ident} {parse}(const char *s) {{", + f" {ident} v;", + f" if ({try_parse}(s, &v))", + " return v;", + " memset(&v, 0, sizeof(v));", + f" v.kind = {first['kind']};", + ] + if first["field"] is not None: + body += [f" v.{first['field']} = {first['parse']};"] + body += [" return v;", "}", ""] + + body += [f"char *{to_string}({ident} v) {{", " switch (v.kind) {"] + for c in cases[1:]: + body += [f" case {c['kind']}:", f" return {c['to_string']};"] + body += [ + " default:", + f" return {first['to_string']};", + " }", + "}", + "", + f"void {free}({ident} *v) {{", + " if (!v)", + " return;", + ] + owning = [c for c in cases if c["owns"]] + for c in owning: + body += [ + f" if (v->kind == {c['kind']}) {{", + f" free(v->{c['field']});", + f" v->{c['field']} = NULL;", + " }", + ] + body += ["}"] + + header_includes = [""] + [f'"{inc}.h"' for inc in includes] + return ( + header_file(plates, plate.file, decl, header_includes), + impl_file(plates, plate.file, body, [f'"{rt}.h"', "", ""]), + ) diff --git a/gen/plates/build.py b/gen/plates/build.py index 8b5d3e5dc..ce92ef775 100644 --- a/gen/plates/build.py +++ b/gen/plates/build.py @@ -126,6 +126,7 @@ def _target_info(self) -> TargetInfo: doc_style=doc_style, reserved=sorted(self.reserved), partition=self.cfg.layout.partition, + file_prefix=self.cfg.layout.file_prefix, ) # ----- names and references ---------------------------------------------- # @@ -386,14 +387,25 @@ def _assign_files(self, plates: Plates) -> list[FileSpec] | None: def _type_deps(self, plate) -> set[str]: """Wire names of the types a plate's emitted code references: member - and value types, union members, and the base edge. Primitives are not - plates and are excluded by the caller's stem lookup.""" + and value types, union members, and the base edge. Primitive refs are + excluded by CATEGORY, not by stem lookup: a primitive's name can + coincide with a type's wire name (the `string` primitive vs the + `string` complex type), and matching by name would fabricate a + dependency edge.""" deps: set[str] = set() if isinstance(plate, UnionPlate): - deps.update(m.ref.wire for m in plate.members if m.ref is not None) + deps.update( + m.ref.wire + for m in plate.members + if m.ref is not None and m.ref.category != "primitive" + ) if isinstance(plate, ComplexPlate): for member_list in (plate.members, plate.all_members or []): - deps.update(m.type_ref.wire for m in member_list) + deps.update( + m.type_ref.wire + for m in member_list + if m.type_ref.category != "primitive" + ) if plate.base is not None: deps.add(plate.base.wire) return deps diff --git a/gen/plates/model.py b/gen/plates/model.py index 866633dd4..e55b008f8 100644 --- a/gen/plates/model.py +++ b/gen/plates/model.py @@ -73,6 +73,7 @@ class TargetInfo: doc_style: DocStyle reserved: list[str] # language defaults + [reserved] words, sorted partition: str # "per-type" | "single" + file_prefix: str = "" # [layout] file-prefix; backends name support files with it # --------------------------------------------------------------------------- # diff --git a/gen/test/c/CMakeLists.txt b/gen/test/c/CMakeLists.txt index db820cf89..b7e399372 100644 --- a/gen/test/c/CMakeLists.txt +++ b/gen/test/c/CMakeLists.txt @@ -11,6 +11,12 @@ if(NOT DEFINED MX_REPO_ROOT) get_filename_component(MX_REPO_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../.." ABSOLUTE) endif() +# The generated MusicXML model (mx/ is the generator's output directory; the +# manifest is emitted alongside the sources so the build never globs). +include(mx/sources.cmake) +add_library(mx-c STATIC ${MX_GENERATED_SOURCES}) +target_include_directories(mx-c PUBLIC mx) + add_executable(corert-c src/main.c src/discover.c @@ -21,5 +27,10 @@ add_executable(corert-c src/stub.c) target_include_directories(corert-c PRIVATE ${LIBXML2_INCLUDE_DIRS} src) -target_link_libraries(corert-c ${LIBXML2_LIBRARIES}) +target_link_libraries(corert-c mx-c ${LIBXML2_LIBRARIES}) target_compile_definitions(corert-c PRIVATE MX_REPO_ROOT="${MX_REPO_ROOT}") + +# Generated-value-type smoke test: wire -> typed -> wire across the leniency +# policies (mirrors the Go target's TestValueSmoke). +add_executable(values-smoke src/values_smoke.c) +target_link_libraries(values-smoke mx-c) diff --git a/gen/test/c/mx/mx_above_below.c b/gen/test/c/mx/mx_above_below.c new file mode 100644 index 000000000..4b6b14e8c --- /dev/null +++ b/gen/test/c/mx/mx_above_below.c @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_above_below.h" + +#include + +static const char *const mx_above_below_values[] = { + "above", + "below", +}; + +bool mx_above_below_try_parse(const char *s, MxAboveBelow *out) { + for (size_t i = 0; i < sizeof(mx_above_below_values) / sizeof(mx_above_below_values[0]); i++) { + if (strcmp(s, mx_above_below_values[i]) == 0) { + *out = (MxAboveBelow)i; + return true; + } + } + *out = (MxAboveBelow)0; + return false; +} + +MxAboveBelow mx_above_below_parse(const char *s) { + MxAboveBelow v; + mx_above_below_try_parse(s, &v); + return v; +} + +const char *mx_above_below_to_string(MxAboveBelow v) { + if ((size_t)v >= sizeof(mx_above_below_values) / sizeof(mx_above_below_values[0])) + return mx_above_below_values[0]; + return mx_above_below_values[v]; +} diff --git a/gen/test/c/mx/mx_above_below.h b/gen/test/c/mx/mx_above_below.h new file mode 100644 index 000000000..105f6d120 --- /dev/null +++ b/gen/test/c/mx/mx_above_below.h @@ -0,0 +1,23 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ABOVE_BELOW_H +#define MX_ABOVE_BELOW_H + +#include + +/* + * The above-below type is used to indicate whether one element appears above or below another + * element. + */ +typedef enum { + MX_ABOVE_BELOW_ABOVE = 0, + MX_ABOVE_BELOW_BELOW = 1 +} MxAboveBelow; + +bool mx_above_below_try_parse(const char *s, MxAboveBelow *out); +/* Lenient: unknown input falls back to the first variant. */ +MxAboveBelow mx_above_below_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_above_below_to_string(MxAboveBelow v); + +#endif /* MX_ABOVE_BELOW_H */ diff --git a/gen/test/c/mx/mx_accidental_value.c b/gen/test/c/mx/mx_accidental_value.c new file mode 100644 index 000000000..514132805 --- /dev/null +++ b/gen/test/c/mx/mx_accidental_value.c @@ -0,0 +1,72 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_accidental_value.h" + +#include + +static const char *const mx_accidental_value_values[] = { + "sharp", + "natural", + "flat", + "double-sharp", + "sharp-sharp", + "flat-flat", + "natural-sharp", + "natural-flat", + "quarter-flat", + "quarter-sharp", + "three-quarters-flat", + "three-quarters-sharp", + "sharp-down", + "sharp-up", + "natural-down", + "natural-up", + "flat-down", + "flat-up", + "double-sharp-down", + "double-sharp-up", + "flat-flat-down", + "flat-flat-up", + "arrow-down", + "arrow-up", + "triple-sharp", + "triple-flat", + "slash-quarter-sharp", + "slash-sharp", + "slash-flat", + "double-slash-flat", + "sharp-1", + "sharp-2", + "sharp-3", + "sharp-5", + "flat-1", + "flat-2", + "flat-3", + "flat-4", + "sori", + "koron", + "other", +}; + +bool mx_accidental_value_try_parse(const char *s, MxAccidentalValue *out) { + for (size_t i = 0; i < sizeof(mx_accidental_value_values) / sizeof(mx_accidental_value_values[0]); i++) { + if (strcmp(s, mx_accidental_value_values[i]) == 0) { + *out = (MxAccidentalValue)i; + return true; + } + } + *out = (MxAccidentalValue)0; + return false; +} + +MxAccidentalValue mx_accidental_value_parse(const char *s) { + MxAccidentalValue v; + mx_accidental_value_try_parse(s, &v); + return v; +} + +const char *mx_accidental_value_to_string(MxAccidentalValue v) { + if ((size_t)v >= sizeof(mx_accidental_value_values) / sizeof(mx_accidental_value_values[0])) + return mx_accidental_value_values[0]; + return mx_accidental_value_values[v]; +} diff --git a/gen/test/c/mx/mx_accidental_value.h b/gen/test/c/mx/mx_accidental_value.h new file mode 100644 index 000000000..403a99bd4 --- /dev/null +++ b/gen/test/c/mx/mx_accidental_value.h @@ -0,0 +1,71 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ACCIDENTAL_VALUE_H +#define MX_ACCIDENTAL_VALUE_H + +#include + +/* + * The accidental-value type represents notated accidentals supported by MusicXML. In the MusicXML + * 2.0 DTD this was a string with values that could be included. The XSD strengthens the data typing + * to an enumerated list. The quarter- and three-quarters- accidentals are Tartini-style + * quarter-tone accidentals. The -down and -up accidentals are quarter-tone accidentals that include + * arrows pointing down or up. The slash- accidentals are used in Turkish classical music. The + * numbered sharp and flat accidentals are superscripted versions of the accidental signs, used in + * Turkish folk music. The sori and koron accidentals are microtonal sharp and flat accidentals used + * in Iranian and Persian music. The other accidental covers accidentals other than those listed + * here. It is usually used in combination with the smufl attribute to specify a particular SMuFL + * accidental. The smufl attribute may be used with any accidental value to help specify the + * appearance of symbols that share the same MusicXML semantics. + */ +typedef enum { + MX_ACCIDENTAL_VALUE_SHARP = 0, + MX_ACCIDENTAL_VALUE_NATURAL = 1, + MX_ACCIDENTAL_VALUE_FLAT = 2, + MX_ACCIDENTAL_VALUE_DOUBLE_SHARP = 3, + MX_ACCIDENTAL_VALUE_SHARP_SHARP = 4, + MX_ACCIDENTAL_VALUE_FLAT_FLAT = 5, + MX_ACCIDENTAL_VALUE_NATURAL_SHARP = 6, + MX_ACCIDENTAL_VALUE_NATURAL_FLAT = 7, + MX_ACCIDENTAL_VALUE_QUARTER_FLAT = 8, + MX_ACCIDENTAL_VALUE_QUARTER_SHARP = 9, + MX_ACCIDENTAL_VALUE_THREE_QUARTERS_FLAT = 10, + MX_ACCIDENTAL_VALUE_THREE_QUARTERS_SHARP = 11, + MX_ACCIDENTAL_VALUE_SHARP_DOWN = 12, + MX_ACCIDENTAL_VALUE_SHARP_UP = 13, + MX_ACCIDENTAL_VALUE_NATURAL_DOWN = 14, + MX_ACCIDENTAL_VALUE_NATURAL_UP = 15, + MX_ACCIDENTAL_VALUE_FLAT_DOWN = 16, + MX_ACCIDENTAL_VALUE_FLAT_UP = 17, + MX_ACCIDENTAL_VALUE_DOUBLE_SHARP_DOWN = 18, + MX_ACCIDENTAL_VALUE_DOUBLE_SHARP_UP = 19, + MX_ACCIDENTAL_VALUE_FLAT_FLAT_DOWN = 20, + MX_ACCIDENTAL_VALUE_FLAT_FLAT_UP = 21, + MX_ACCIDENTAL_VALUE_ARROW_DOWN = 22, + MX_ACCIDENTAL_VALUE_ARROW_UP = 23, + MX_ACCIDENTAL_VALUE_TRIPLE_SHARP = 24, + MX_ACCIDENTAL_VALUE_TRIPLE_FLAT = 25, + MX_ACCIDENTAL_VALUE_SLASH_QUARTER_SHARP = 26, + MX_ACCIDENTAL_VALUE_SLASH_SHARP = 27, + MX_ACCIDENTAL_VALUE_SLASH_FLAT = 28, + MX_ACCIDENTAL_VALUE_DOUBLE_SLASH_FLAT = 29, + MX_ACCIDENTAL_VALUE_SHARP_1 = 30, + MX_ACCIDENTAL_VALUE_SHARP_2 = 31, + MX_ACCIDENTAL_VALUE_SHARP_3 = 32, + MX_ACCIDENTAL_VALUE_SHARP_5 = 33, + MX_ACCIDENTAL_VALUE_FLAT_1 = 34, + MX_ACCIDENTAL_VALUE_FLAT_2 = 35, + MX_ACCIDENTAL_VALUE_FLAT_3 = 36, + MX_ACCIDENTAL_VALUE_FLAT_4 = 37, + MX_ACCIDENTAL_VALUE_SORI = 38, + MX_ACCIDENTAL_VALUE_KORON = 39, + MX_ACCIDENTAL_VALUE_OTHER = 40 +} MxAccidentalValue; + +bool mx_accidental_value_try_parse(const char *s, MxAccidentalValue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxAccidentalValue mx_accidental_value_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_accidental_value_to_string(MxAccidentalValue v); + +#endif /* MX_ACCIDENTAL_VALUE_H */ diff --git a/gen/test/c/mx/mx_accordion_middle.c b/gen/test/c/mx/mx_accordion_middle.c new file mode 100644 index 000000000..7e5628120 --- /dev/null +++ b/gen/test/c/mx/mx_accordion_middle.c @@ -0,0 +1,32 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_accordion_middle.h" + +#include "mx_runtime.h" + +static MxAccordionMiddle mx_accordion_middle_clamp(long v) { + if (v < 1) + v = 1; + if (v > 3) + v = 3; + return (MxAccordionMiddle)v; +} + +bool mx_accordion_middle_try_parse(const char *s, MxAccordionMiddle *out) { + long v; + if (!mx_try_parse_int(s, &v)) { + *out = (MxAccordionMiddle)0; + return false; + } + *out = mx_accordion_middle_clamp(v); + return true; +} + +MxAccordionMiddle mx_accordion_middle_parse(const char *s) { + long v = mx_parse_int(s); + return mx_accordion_middle_clamp(v); +} + +char *mx_accordion_middle_to_string(MxAccordionMiddle v) { + return mx_format_int((long)v); +} diff --git a/gen/test/c/mx/mx_accordion_middle.h b/gen/test/c/mx/mx_accordion_middle.h new file mode 100644 index 000000000..fc0daf753 --- /dev/null +++ b/gen/test/c/mx/mx_accordion_middle.h @@ -0,0 +1,22 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ACCORDION_MIDDLE_H +#define MX_ACCORDION_MIDDLE_H + +#include + +/* + * The accordion-middle type may have values of 1, 2, or 3, corresponding to having 1 to 3 dots in + * the middle section of the accordion registration symbol. This type is not used if no dots are + * present. + */ +typedef long MxAccordionMiddle; + +/* Strict parse, then clamps into the declared range. */ +bool mx_accordion_middle_try_parse(const char *s, MxAccordionMiddle *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxAccordionMiddle mx_accordion_middle_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_accordion_middle_to_string(MxAccordionMiddle v); + +#endif /* MX_ACCORDION_MIDDLE_H */ diff --git a/gen/test/c/mx/mx_arrow_direction.c b/gen/test/c/mx/mx_arrow_direction.c new file mode 100644 index 000000000..ffe718827 --- /dev/null +++ b/gen/test/c/mx/mx_arrow_direction.c @@ -0,0 +1,44 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_arrow_direction.h" + +#include + +static const char *const mx_arrow_direction_values[] = { + "left", + "up", + "right", + "down", + "northwest", + "northeast", + "southeast", + "southwest", + "left right", + "up down", + "northwest southeast", + "northeast southwest", + "other", +}; + +bool mx_arrow_direction_try_parse(const char *s, MxArrowDirection *out) { + for (size_t i = 0; i < sizeof(mx_arrow_direction_values) / sizeof(mx_arrow_direction_values[0]); i++) { + if (strcmp(s, mx_arrow_direction_values[i]) == 0) { + *out = (MxArrowDirection)i; + return true; + } + } + *out = (MxArrowDirection)0; + return false; +} + +MxArrowDirection mx_arrow_direction_parse(const char *s) { + MxArrowDirection v; + mx_arrow_direction_try_parse(s, &v); + return v; +} + +const char *mx_arrow_direction_to_string(MxArrowDirection v) { + if ((size_t)v >= sizeof(mx_arrow_direction_values) / sizeof(mx_arrow_direction_values[0])) + return mx_arrow_direction_values[0]; + return mx_arrow_direction_values[v]; +} diff --git a/gen/test/c/mx/mx_arrow_direction.h b/gen/test/c/mx/mx_arrow_direction.h new file mode 100644 index 000000000..c98215708 --- /dev/null +++ b/gen/test/c/mx/mx_arrow_direction.h @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ARROW_DIRECTION_H +#define MX_ARROW_DIRECTION_H + +#include + +/* + * The arrow-direction type represents the direction in which an arrow points, using Unicode arrow + * terminology. + */ +typedef enum { + MX_ARROW_DIRECTION_LEFT = 0, + MX_ARROW_DIRECTION_UP = 1, + MX_ARROW_DIRECTION_RIGHT = 2, + MX_ARROW_DIRECTION_DOWN = 3, + MX_ARROW_DIRECTION_NORTHWEST = 4, + MX_ARROW_DIRECTION_NORTHEAST = 5, + MX_ARROW_DIRECTION_SOUTHEAST = 6, + MX_ARROW_DIRECTION_SOUTHWEST = 7, + MX_ARROW_DIRECTION_LEFT_RIGHT = 8, + MX_ARROW_DIRECTION_UP_DOWN = 9, + MX_ARROW_DIRECTION_NORTHWEST_SOUTHEAST = 10, + MX_ARROW_DIRECTION_NORTHEAST_SOUTHWEST = 11, + MX_ARROW_DIRECTION_OTHER = 12 +} MxArrowDirection; + +bool mx_arrow_direction_try_parse(const char *s, MxArrowDirection *out); +/* Lenient: unknown input falls back to the first variant. */ +MxArrowDirection mx_arrow_direction_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_arrow_direction_to_string(MxArrowDirection v); + +#endif /* MX_ARROW_DIRECTION_H */ diff --git a/gen/test/c/mx/mx_arrow_style.c b/gen/test/c/mx/mx_arrow_style.c new file mode 100644 index 000000000..e80527dc4 --- /dev/null +++ b/gen/test/c/mx/mx_arrow_style.c @@ -0,0 +1,38 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_arrow_style.h" + +#include + +static const char *const mx_arrow_style_values[] = { + "single", + "double", + "filled", + "hollow", + "paired", + "combined", + "other", +}; + +bool mx_arrow_style_try_parse(const char *s, MxArrowStyle *out) { + for (size_t i = 0; i < sizeof(mx_arrow_style_values) / sizeof(mx_arrow_style_values[0]); i++) { + if (strcmp(s, mx_arrow_style_values[i]) == 0) { + *out = (MxArrowStyle)i; + return true; + } + } + *out = (MxArrowStyle)0; + return false; +} + +MxArrowStyle mx_arrow_style_parse(const char *s) { + MxArrowStyle v; + mx_arrow_style_try_parse(s, &v); + return v; +} + +const char *mx_arrow_style_to_string(MxArrowStyle v) { + if ((size_t)v >= sizeof(mx_arrow_style_values) / sizeof(mx_arrow_style_values[0])) + return mx_arrow_style_values[0]; + return mx_arrow_style_values[v]; +} diff --git a/gen/test/c/mx/mx_arrow_style.h b/gen/test/c/mx/mx_arrow_style.h new file mode 100644 index 000000000..2be3df8fc --- /dev/null +++ b/gen/test/c/mx/mx_arrow_style.h @@ -0,0 +1,30 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ARROW_STYLE_H +#define MX_ARROW_STYLE_H + +#include + +/* + * The arrow-style type represents the style of an arrow, using Unicode arrow terminology. Filled + * and hollow arrows indicate polygonal single arrows. Paired arrows are duplicate single arrows in + * the same direction. Combined arrows apply to double direction arrows like left right, indicating + * that an arrow in one direction should be combined with an arrow in the other direction. + */ +typedef enum { + MX_ARROW_STYLE_SINGLE = 0, + MX_ARROW_STYLE_DOUBLE = 1, + MX_ARROW_STYLE_FILLED = 2, + MX_ARROW_STYLE_HOLLOW = 3, + MX_ARROW_STYLE_PAIRED = 4, + MX_ARROW_STYLE_COMBINED = 5, + MX_ARROW_STYLE_OTHER = 6 +} MxArrowStyle; + +bool mx_arrow_style_try_parse(const char *s, MxArrowStyle *out); +/* Lenient: unknown input falls back to the first variant. */ +MxArrowStyle mx_arrow_style_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_arrow_style_to_string(MxArrowStyle v); + +#endif /* MX_ARROW_STYLE_H */ diff --git a/gen/test/c/mx/mx_backward_forward.c b/gen/test/c/mx/mx_backward_forward.c new file mode 100644 index 000000000..706f370be --- /dev/null +++ b/gen/test/c/mx/mx_backward_forward.c @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_backward_forward.h" + +#include + +static const char *const mx_backward_forward_values[] = { + "backward", + "forward", +}; + +bool mx_backward_forward_try_parse(const char *s, MxBackwardForward *out) { + for (size_t i = 0; i < sizeof(mx_backward_forward_values) / sizeof(mx_backward_forward_values[0]); i++) { + if (strcmp(s, mx_backward_forward_values[i]) == 0) { + *out = (MxBackwardForward)i; + return true; + } + } + *out = (MxBackwardForward)0; + return false; +} + +MxBackwardForward mx_backward_forward_parse(const char *s) { + MxBackwardForward v; + mx_backward_forward_try_parse(s, &v); + return v; +} + +const char *mx_backward_forward_to_string(MxBackwardForward v) { + if ((size_t)v >= sizeof(mx_backward_forward_values) / sizeof(mx_backward_forward_values[0])) + return mx_backward_forward_values[0]; + return mx_backward_forward_values[v]; +} diff --git a/gen/test/c/mx/mx_backward_forward.h b/gen/test/c/mx/mx_backward_forward.h new file mode 100644 index 000000000..857e53e45 --- /dev/null +++ b/gen/test/c/mx/mx_backward_forward.h @@ -0,0 +1,23 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BACKWARD_FORWARD_H +#define MX_BACKWARD_FORWARD_H + +#include + +/* + * The backward-forward type is used to specify repeat directions. The start of the repeat has a + * forward direction while the end of the repeat has a backward direction. + */ +typedef enum { + MX_BACKWARD_FORWARD_BACKWARD = 0, + MX_BACKWARD_FORWARD_FORWARD = 1 +} MxBackwardForward; + +bool mx_backward_forward_try_parse(const char *s, MxBackwardForward *out); +/* Lenient: unknown input falls back to the first variant. */ +MxBackwardForward mx_backward_forward_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_backward_forward_to_string(MxBackwardForward v); + +#endif /* MX_BACKWARD_FORWARD_H */ diff --git a/gen/test/c/mx/mx_bar_style.c b/gen/test/c/mx/mx_bar_style.c new file mode 100644 index 000000000..105c3e795 --- /dev/null +++ b/gen/test/c/mx/mx_bar_style.c @@ -0,0 +1,42 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_bar_style.h" + +#include + +static const char *const mx_bar_style_values[] = { + "regular", + "dotted", + "dashed", + "heavy", + "light-light", + "light-heavy", + "heavy-light", + "heavy-heavy", + "tick", + "short", + "none", +}; + +bool mx_bar_style_try_parse(const char *s, MxBarStyle *out) { + for (size_t i = 0; i < sizeof(mx_bar_style_values) / sizeof(mx_bar_style_values[0]); i++) { + if (strcmp(s, mx_bar_style_values[i]) == 0) { + *out = (MxBarStyle)i; + return true; + } + } + *out = (MxBarStyle)0; + return false; +} + +MxBarStyle mx_bar_style_parse(const char *s) { + MxBarStyle v; + mx_bar_style_try_parse(s, &v); + return v; +} + +const char *mx_bar_style_to_string(MxBarStyle v) { + if ((size_t)v >= sizeof(mx_bar_style_values) / sizeof(mx_bar_style_values[0])) + return mx_bar_style_values[0]; + return mx_bar_style_values[v]; +} diff --git a/gen/test/c/mx/mx_bar_style.h b/gen/test/c/mx/mx_bar_style.h new file mode 100644 index 000000000..e3aa582bc --- /dev/null +++ b/gen/test/c/mx/mx_bar_style.h @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BAR_STYLE_H +#define MX_BAR_STYLE_H + +#include + +/* + * The bar-style type represents barline style information. Choices are regular, dotted, dashed, + * heavy, light-light, light-heavy, heavy-light, heavy-heavy, tick (a short stroke through the top + * line), short (a partial barline between the 2nd and 4th lines), and none. + */ +typedef enum { + MX_BAR_STYLE_REGULAR = 0, + MX_BAR_STYLE_DOTTED = 1, + MX_BAR_STYLE_DASHED = 2, + MX_BAR_STYLE_HEAVY = 3, + MX_BAR_STYLE_LIGHT_LIGHT = 4, + MX_BAR_STYLE_LIGHT_HEAVY = 5, + MX_BAR_STYLE_HEAVY_LIGHT = 6, + MX_BAR_STYLE_HEAVY_HEAVY = 7, + MX_BAR_STYLE_TICK = 8, + MX_BAR_STYLE_SHORT = 9, + MX_BAR_STYLE_NONE = 10 +} MxBarStyle; + +bool mx_bar_style_try_parse(const char *s, MxBarStyle *out); +/* Lenient: unknown input falls back to the first variant. */ +MxBarStyle mx_bar_style_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_bar_style_to_string(MxBarStyle v); + +#endif /* MX_BAR_STYLE_H */ diff --git a/gen/test/c/mx/mx_beam_level.c b/gen/test/c/mx/mx_beam_level.c new file mode 100644 index 000000000..6c7b1dad7 --- /dev/null +++ b/gen/test/c/mx/mx_beam_level.c @@ -0,0 +1,32 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_beam_level.h" + +#include "mx_runtime.h" + +static MxBeamLevel mx_beam_level_clamp(long v) { + if (v < 1) + v = 1; + if (v > 8) + v = 8; + return (MxBeamLevel)v; +} + +bool mx_beam_level_try_parse(const char *s, MxBeamLevel *out) { + long v; + if (!mx_try_parse_int(s, &v)) { + *out = (MxBeamLevel)0; + return false; + } + *out = mx_beam_level_clamp(v); + return true; +} + +MxBeamLevel mx_beam_level_parse(const char *s) { + long v = mx_parse_int(s); + return mx_beam_level_clamp(v); +} + +char *mx_beam_level_to_string(MxBeamLevel v) { + return mx_format_int((long)v); +} diff --git a/gen/test/c/mx/mx_beam_level.h b/gen/test/c/mx/mx_beam_level.h new file mode 100644 index 000000000..0c5efeab1 --- /dev/null +++ b/gen/test/c/mx/mx_beam_level.h @@ -0,0 +1,22 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BEAM_LEVEL_H +#define MX_BEAM_LEVEL_H + +#include + +/* + * The MusicXML format supports six levels of beaming, up to 1024th notes. Unlike the number-level + * type, the beam-level type identifies concurrent beams in a beam group. It does not distinguish + * overlapping beams such as grace notes within regular notes, or beams used in different voices. + */ +typedef long MxBeamLevel; + +/* Strict parse, then clamps into the declared range. */ +bool mx_beam_level_try_parse(const char *s, MxBeamLevel *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxBeamLevel mx_beam_level_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_beam_level_to_string(MxBeamLevel v); + +#endif /* MX_BEAM_LEVEL_H */ diff --git a/gen/test/c/mx/mx_beam_value.c b/gen/test/c/mx/mx_beam_value.c new file mode 100644 index 000000000..f7d8f5c1b --- /dev/null +++ b/gen/test/c/mx/mx_beam_value.c @@ -0,0 +1,36 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_beam_value.h" + +#include + +static const char *const mx_beam_value_values[] = { + "begin", + "continue", + "end", + "forward hook", + "backward hook", +}; + +bool mx_beam_value_try_parse(const char *s, MxBeamValue *out) { + for (size_t i = 0; i < sizeof(mx_beam_value_values) / sizeof(mx_beam_value_values[0]); i++) { + if (strcmp(s, mx_beam_value_values[i]) == 0) { + *out = (MxBeamValue)i; + return true; + } + } + *out = (MxBeamValue)0; + return false; +} + +MxBeamValue mx_beam_value_parse(const char *s) { + MxBeamValue v; + mx_beam_value_try_parse(s, &v); + return v; +} + +const char *mx_beam_value_to_string(MxBeamValue v) { + if ((size_t)v >= sizeof(mx_beam_value_values) / sizeof(mx_beam_value_values[0])) + return mx_beam_value_values[0]; + return mx_beam_value_values[v]; +} diff --git a/gen/test/c/mx/mx_beam_value.h b/gen/test/c/mx/mx_beam_value.h new file mode 100644 index 000000000..a77c14f42 --- /dev/null +++ b/gen/test/c/mx/mx_beam_value.h @@ -0,0 +1,26 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BEAM_VALUE_H +#define MX_BEAM_VALUE_H + +#include + +/* + * The beam-value type represents the type of beam associated with each of 8 beam levels (up to + * 1024th notes) available for each note. + */ +typedef enum { + MX_BEAM_VALUE_BEGIN = 0, + MX_BEAM_VALUE_CONTINUE = 1, + MX_BEAM_VALUE_END = 2, + MX_BEAM_VALUE_FORWARD_HOOK = 3, + MX_BEAM_VALUE_BACKWARD_HOOK = 4 +} MxBeamValue; + +bool mx_beam_value_try_parse(const char *s, MxBeamValue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxBeamValue mx_beam_value_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_beam_value_to_string(MxBeamValue v); + +#endif /* MX_BEAM_VALUE_H */ diff --git a/gen/test/c/mx/mx_beater_value.c b/gen/test/c/mx/mx_beater_value.c new file mode 100644 index 000000000..6d162f922 --- /dev/null +++ b/gen/test/c/mx/mx_beater_value.c @@ -0,0 +1,51 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_beater_value.h" + +#include + +static const char *const mx_beater_value_values[] = { + "bow", + "chime hammer", + "coin", + "drum stick", + "finger", + "fingernail", + "fist", + "guiro scraper", + "hammer", + "hand", + "jazz stick", + "knitting needle", + "metal hammer", + "slide brush on gong", + "snare stick", + "spoon mallet", + "superball", + "triangle beater", + "triangle beater plain", + "wire brush", +}; + +bool mx_beater_value_try_parse(const char *s, MxBeaterValue *out) { + for (size_t i = 0; i < sizeof(mx_beater_value_values) / sizeof(mx_beater_value_values[0]); i++) { + if (strcmp(s, mx_beater_value_values[i]) == 0) { + *out = (MxBeaterValue)i; + return true; + } + } + *out = (MxBeaterValue)0; + return false; +} + +MxBeaterValue mx_beater_value_parse(const char *s) { + MxBeaterValue v; + mx_beater_value_try_parse(s, &v); + return v; +} + +const char *mx_beater_value_to_string(MxBeaterValue v) { + if ((size_t)v >= sizeof(mx_beater_value_values) / sizeof(mx_beater_value_values[0])) + return mx_beater_value_values[0]; + return mx_beater_value_values[v]; +} diff --git a/gen/test/c/mx/mx_beater_value.h b/gen/test/c/mx/mx_beater_value.h new file mode 100644 index 000000000..1400d5c29 --- /dev/null +++ b/gen/test/c/mx/mx_beater_value.h @@ -0,0 +1,42 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BEATER_VALUE_H +#define MX_BEATER_VALUE_H + +#include + +/* + * The beater-value type represents pictograms for beaters, mallets, and sticks that do not have + * different materials represented in the pictogram. The finger and hammer values are in addition to + * Stone's list. + */ +typedef enum { + MX_BEATER_VALUE_BOW = 0, + MX_BEATER_VALUE_CHIME_HAMMER = 1, + MX_BEATER_VALUE_COIN = 2, + MX_BEATER_VALUE_DRUM_STICK = 3, + MX_BEATER_VALUE_FINGER = 4, + MX_BEATER_VALUE_FINGERNAIL = 5, + MX_BEATER_VALUE_FIST = 6, + MX_BEATER_VALUE_GUIRO_SCRAPER = 7, + MX_BEATER_VALUE_HAMMER = 8, + MX_BEATER_VALUE_HAND = 9, + MX_BEATER_VALUE_JAZZ_STICK = 10, + MX_BEATER_VALUE_KNITTING_NEEDLE = 11, + MX_BEATER_VALUE_METAL_HAMMER = 12, + MX_BEATER_VALUE_SLIDE_BRUSH_ON_GONG = 13, + MX_BEATER_VALUE_SNARE_STICK = 14, + MX_BEATER_VALUE_SPOON_MALLET = 15, + MX_BEATER_VALUE_SUPERBALL = 16, + MX_BEATER_VALUE_TRIANGLE_BEATER = 17, + MX_BEATER_VALUE_TRIANGLE_BEATER_PLAIN = 18, + MX_BEATER_VALUE_WIRE_BRUSH = 19 +} MxBeaterValue; + +bool mx_beater_value_try_parse(const char *s, MxBeaterValue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxBeaterValue mx_beater_value_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_beater_value_to_string(MxBeaterValue v); + +#endif /* MX_BEATER_VALUE_H */ diff --git a/gen/test/c/mx/mx_breath_mark_value.c b/gen/test/c/mx/mx_breath_mark_value.c new file mode 100644 index 000000000..0c207cd9d --- /dev/null +++ b/gen/test/c/mx/mx_breath_mark_value.c @@ -0,0 +1,36 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_breath_mark_value.h" + +#include + +static const char *const mx_breath_mark_value_values[] = { + "", + "comma", + "tick", + "upbow", + "salzedo", +}; + +bool mx_breath_mark_value_try_parse(const char *s, MxBreathMarkValue *out) { + for (size_t i = 0; i < sizeof(mx_breath_mark_value_values) / sizeof(mx_breath_mark_value_values[0]); i++) { + if (strcmp(s, mx_breath_mark_value_values[i]) == 0) { + *out = (MxBreathMarkValue)i; + return true; + } + } + *out = (MxBreathMarkValue)0; + return false; +} + +MxBreathMarkValue mx_breath_mark_value_parse(const char *s) { + MxBreathMarkValue v; + mx_breath_mark_value_try_parse(s, &v); + return v; +} + +const char *mx_breath_mark_value_to_string(MxBreathMarkValue v) { + if ((size_t)v >= sizeof(mx_breath_mark_value_values) / sizeof(mx_breath_mark_value_values[0])) + return mx_breath_mark_value_values[0]; + return mx_breath_mark_value_values[v]; +} diff --git a/gen/test/c/mx/mx_breath_mark_value.h b/gen/test/c/mx/mx_breath_mark_value.h new file mode 100644 index 000000000..b56c97048 --- /dev/null +++ b/gen/test/c/mx/mx_breath_mark_value.h @@ -0,0 +1,25 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BREATH_MARK_VALUE_H +#define MX_BREATH_MARK_VALUE_H + +#include + +/* + * The breath-mark-value type represents the symbol used for a breath mark. + */ +typedef enum { + MX_BREATH_MARK_VALUE_EMPTY = 0, + MX_BREATH_MARK_VALUE_COMMA = 1, + MX_BREATH_MARK_VALUE_TICK = 2, + MX_BREATH_MARK_VALUE_UPBOW = 3, + MX_BREATH_MARK_VALUE_SALZEDO = 4 +} MxBreathMarkValue; + +bool mx_breath_mark_value_try_parse(const char *s, MxBreathMarkValue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxBreathMarkValue mx_breath_mark_value_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_breath_mark_value_to_string(MxBreathMarkValue v); + +#endif /* MX_BREATH_MARK_VALUE_H */ diff --git a/gen/test/c/mx/mx_caesura_value.c b/gen/test/c/mx/mx_caesura_value.c new file mode 100644 index 000000000..2492910d8 --- /dev/null +++ b/gen/test/c/mx/mx_caesura_value.c @@ -0,0 +1,37 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_caesura_value.h" + +#include + +static const char *const mx_caesura_value_values[] = { + "normal", + "thick", + "short", + "curved", + "single", + "", +}; + +bool mx_caesura_value_try_parse(const char *s, MxCaesuraValue *out) { + for (size_t i = 0; i < sizeof(mx_caesura_value_values) / sizeof(mx_caesura_value_values[0]); i++) { + if (strcmp(s, mx_caesura_value_values[i]) == 0) { + *out = (MxCaesuraValue)i; + return true; + } + } + *out = (MxCaesuraValue)0; + return false; +} + +MxCaesuraValue mx_caesura_value_parse(const char *s) { + MxCaesuraValue v; + mx_caesura_value_try_parse(s, &v); + return v; +} + +const char *mx_caesura_value_to_string(MxCaesuraValue v) { + if ((size_t)v >= sizeof(mx_caesura_value_values) / sizeof(mx_caesura_value_values[0])) + return mx_caesura_value_values[0]; + return mx_caesura_value_values[v]; +} diff --git a/gen/test/c/mx/mx_caesura_value.h b/gen/test/c/mx/mx_caesura_value.h new file mode 100644 index 000000000..2f1e5e75f --- /dev/null +++ b/gen/test/c/mx/mx_caesura_value.h @@ -0,0 +1,26 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_CAESURA_VALUE_H +#define MX_CAESURA_VALUE_H + +#include + +/* + * The caesura-value type represents the shape of the caesura sign. + */ +typedef enum { + MX_CAESURA_VALUE_NORMAL = 0, + MX_CAESURA_VALUE_THICK = 1, + MX_CAESURA_VALUE_SHORT = 2, + MX_CAESURA_VALUE_CURVED = 3, + MX_CAESURA_VALUE_SINGLE = 4, + MX_CAESURA_VALUE_EMPTY = 5 +} MxCaesuraValue; + +bool mx_caesura_value_try_parse(const char *s, MxCaesuraValue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxCaesuraValue mx_caesura_value_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_caesura_value_to_string(MxCaesuraValue v); + +#endif /* MX_CAESURA_VALUE_H */ diff --git a/gen/test/c/mx/mx_cancel_location.c b/gen/test/c/mx/mx_cancel_location.c new file mode 100644 index 000000000..c561478b9 --- /dev/null +++ b/gen/test/c/mx/mx_cancel_location.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_cancel_location.h" + +#include + +static const char *const mx_cancel_location_values[] = { + "left", + "right", + "before-barline", +}; + +bool mx_cancel_location_try_parse(const char *s, MxCancelLocation *out) { + for (size_t i = 0; i < sizeof(mx_cancel_location_values) / sizeof(mx_cancel_location_values[0]); i++) { + if (strcmp(s, mx_cancel_location_values[i]) == 0) { + *out = (MxCancelLocation)i; + return true; + } + } + *out = (MxCancelLocation)0; + return false; +} + +MxCancelLocation mx_cancel_location_parse(const char *s) { + MxCancelLocation v; + mx_cancel_location_try_parse(s, &v); + return v; +} + +const char *mx_cancel_location_to_string(MxCancelLocation v) { + if ((size_t)v >= sizeof(mx_cancel_location_values) / sizeof(mx_cancel_location_values[0])) + return mx_cancel_location_values[0]; + return mx_cancel_location_values[v]; +} diff --git a/gen/test/c/mx/mx_cancel_location.h b/gen/test/c/mx/mx_cancel_location.h new file mode 100644 index 000000000..a8635f2bd --- /dev/null +++ b/gen/test/c/mx/mx_cancel_location.h @@ -0,0 +1,26 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_CANCEL_LOCATION_H +#define MX_CANCEL_LOCATION_H + +#include + +/* + * The cancel-location type is used to indicate where a key signature cancellation appears relative + * to a new key signature: to the left, to the right, or before the barline and to the left. It is + * left by default. For mid-measure key elements, a cancel-location of before-barline should be + * treated like a cancel-location of left. + */ +typedef enum { + MX_CANCEL_LOCATION_LEFT = 0, + MX_CANCEL_LOCATION_RIGHT = 1, + MX_CANCEL_LOCATION_BEFORE_BARLINE = 2 +} MxCancelLocation; + +bool mx_cancel_location_try_parse(const char *s, MxCancelLocation *out); +/* Lenient: unknown input falls back to the first variant. */ +MxCancelLocation mx_cancel_location_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_cancel_location_to_string(MxCancelLocation v); + +#endif /* MX_CANCEL_LOCATION_H */ diff --git a/gen/test/c/mx/mx_circular_arrow.c b/gen/test/c/mx/mx_circular_arrow.c new file mode 100644 index 000000000..2cd935660 --- /dev/null +++ b/gen/test/c/mx/mx_circular_arrow.c @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_circular_arrow.h" + +#include + +static const char *const mx_circular_arrow_values[] = { + "clockwise", + "anticlockwise", +}; + +bool mx_circular_arrow_try_parse(const char *s, MxCircularArrow *out) { + for (size_t i = 0; i < sizeof(mx_circular_arrow_values) / sizeof(mx_circular_arrow_values[0]); i++) { + if (strcmp(s, mx_circular_arrow_values[i]) == 0) { + *out = (MxCircularArrow)i; + return true; + } + } + *out = (MxCircularArrow)0; + return false; +} + +MxCircularArrow mx_circular_arrow_parse(const char *s) { + MxCircularArrow v; + mx_circular_arrow_try_parse(s, &v); + return v; +} + +const char *mx_circular_arrow_to_string(MxCircularArrow v) { + if ((size_t)v >= sizeof(mx_circular_arrow_values) / sizeof(mx_circular_arrow_values[0])) + return mx_circular_arrow_values[0]; + return mx_circular_arrow_values[v]; +} diff --git a/gen/test/c/mx/mx_circular_arrow.h b/gen/test/c/mx/mx_circular_arrow.h new file mode 100644 index 000000000..eb52319f1 --- /dev/null +++ b/gen/test/c/mx/mx_circular_arrow.h @@ -0,0 +1,23 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_CIRCULAR_ARROW_H +#define MX_CIRCULAR_ARROW_H + +#include + +/* + * The circular-arrow type represents the direction in which a circular arrow points, using Unicode + * arrow terminology. + */ +typedef enum { + MX_CIRCULAR_ARROW_CLOCKWISE = 0, + MX_CIRCULAR_ARROW_ANTICLOCKWISE = 1 +} MxCircularArrow; + +bool mx_circular_arrow_try_parse(const char *s, MxCircularArrow *out); +/* Lenient: unknown input falls back to the first variant. */ +MxCircularArrow mx_circular_arrow_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_circular_arrow_to_string(MxCircularArrow v); + +#endif /* MX_CIRCULAR_ARROW_H */ diff --git a/gen/test/c/mx/mx_clef_sign.c b/gen/test/c/mx/mx_clef_sign.c new file mode 100644 index 000000000..46ce00809 --- /dev/null +++ b/gen/test/c/mx/mx_clef_sign.c @@ -0,0 +1,38 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_clef_sign.h" + +#include + +static const char *const mx_clef_sign_values[] = { + "G", + "F", + "C", + "percussion", + "TAB", + "jianpu", + "none", +}; + +bool mx_clef_sign_try_parse(const char *s, MxClefSign *out) { + for (size_t i = 0; i < sizeof(mx_clef_sign_values) / sizeof(mx_clef_sign_values[0]); i++) { + if (strcmp(s, mx_clef_sign_values[i]) == 0) { + *out = (MxClefSign)i; + return true; + } + } + *out = (MxClefSign)0; + return false; +} + +MxClefSign mx_clef_sign_parse(const char *s) { + MxClefSign v; + mx_clef_sign_try_parse(s, &v); + return v; +} + +const char *mx_clef_sign_to_string(MxClefSign v) { + if ((size_t)v >= sizeof(mx_clef_sign_values) / sizeof(mx_clef_sign_values[0])) + return mx_clef_sign_values[0]; + return mx_clef_sign_values[v]; +} diff --git a/gen/test/c/mx/mx_clef_sign.h b/gen/test/c/mx/mx_clef_sign.h new file mode 100644 index 000000000..9cb7dae10 --- /dev/null +++ b/gen/test/c/mx/mx_clef_sign.h @@ -0,0 +1,30 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_CLEF_SIGN_H +#define MX_CLEF_SIGN_H + +#include + +/* + * The clef-sign element represents the different clef symbols. The jianpu sign indicates that the + * music that follows should be in jianpu numbered notation, just as the TAB sign indicates that the + * music that follows should be in tablature notation. Unlike TAB, a jianpu sign does not correspond + * to a visual clef notation. + */ +typedef enum { + MX_CLEF_SIGN_G = 0, + MX_CLEF_SIGN_F = 1, + MX_CLEF_SIGN_C = 2, + MX_CLEF_SIGN_PERCUSSION = 3, + MX_CLEF_SIGN_TAB = 4, + MX_CLEF_SIGN_JIANPU = 5, + MX_CLEF_SIGN_NONE = 6 +} MxClefSign; + +bool mx_clef_sign_try_parse(const char *s, MxClefSign *out); +/* Lenient: unknown input falls back to the first variant. */ +MxClefSign mx_clef_sign_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_clef_sign_to_string(MxClefSign v); + +#endif /* MX_CLEF_SIGN_H */ diff --git a/gen/test/c/mx/mx_color.c b/gen/test/c/mx/mx_color.c new file mode 100644 index 000000000..a5422597a --- /dev/null +++ b/gen/test/c/mx/mx_color.c @@ -0,0 +1,9 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_color.h" + +#include "mx_runtime.h" + +MxColor mx_color_parse(const char *s) { + return mx_strdup(s); +} diff --git a/gen/test/c/mx/mx_color.h b/gen/test/c/mx/mx_color.h new file mode 100644 index 000000000..8ce5918e9 --- /dev/null +++ b/gen/test/c/mx/mx_color.h @@ -0,0 +1,19 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_COLOR_H +#define MX_COLOR_H + +/* + * The color type indicates the color of an element. Color may be represented as hexadecimal RGB + * triples, as in HTML, or as hexadecimal ARGB tuples, with the A indicating alpha of transparency. + * An alpha value of 00 is totally transparent; FF is totally opaque. If RGB is used, the A value is + * assumed to be FF. For instance, the RGB value "#800080" represents purple. An ARGB value of + * "#40800080" would be a transparent purple. As in SVG 1.1, colors are defined in terms of the sRGB + * color space (IEC 61966). Pattern (not enforced): #[\dA-F]{6}([\dA-F][\dA-F])? + */ +typedef char *MxColor; + +/* Malloc'd copy of the wire string; the value IS its spelling. */ +MxColor mx_color_parse(const char *s); + +#endif /* MX_COLOR_H */ diff --git a/gen/test/c/mx/mx_comma_separated_text.c b/gen/test/c/mx/mx_comma_separated_text.c new file mode 100644 index 000000000..f13c64194 --- /dev/null +++ b/gen/test/c/mx/mx_comma_separated_text.c @@ -0,0 +1,9 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_comma_separated_text.h" + +#include "mx_runtime.h" + +MxCommaSeparatedText mx_comma_separated_text_parse(const char *s) { + return mx_strdup(s); +} diff --git a/gen/test/c/mx/mx_comma_separated_text.h b/gen/test/c/mx/mx_comma_separated_text.h new file mode 100644 index 000000000..09a7f2e9e --- /dev/null +++ b/gen/test/c/mx/mx_comma_separated_text.h @@ -0,0 +1,15 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_COMMA_SEPARATED_TEXT_H +#define MX_COMMA_SEPARATED_TEXT_H + +/* + * The comma-separated-text type is used to specify a comma-separated list of text elements, as is + * used by the font-family attribute. Pattern (not enforced): [^,]+(, ?[^,]+)* + */ +typedef char *MxCommaSeparatedText; + +/* Malloc'd copy of the wire string; the value IS its spelling. */ +MxCommaSeparatedText mx_comma_separated_text_parse(const char *s); + +#endif /* MX_COMMA_SEPARATED_TEXT_H */ diff --git a/gen/test/c/mx/mx_css_font_size.c b/gen/test/c/mx/mx_css_font_size.c new file mode 100644 index 000000000..6c697ea28 --- /dev/null +++ b/gen/test/c/mx/mx_css_font_size.c @@ -0,0 +1,38 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_css_font_size.h" + +#include + +static const char *const mx_css_font_size_values[] = { + "xx-small", + "x-small", + "small", + "medium", + "large", + "x-large", + "xx-large", +}; + +bool mx_css_font_size_try_parse(const char *s, MxCSSFontSize *out) { + for (size_t i = 0; i < sizeof(mx_css_font_size_values) / sizeof(mx_css_font_size_values[0]); i++) { + if (strcmp(s, mx_css_font_size_values[i]) == 0) { + *out = (MxCSSFontSize)i; + return true; + } + } + *out = (MxCSSFontSize)0; + return false; +} + +MxCSSFontSize mx_css_font_size_parse(const char *s) { + MxCSSFontSize v; + mx_css_font_size_try_parse(s, &v); + return v; +} + +const char *mx_css_font_size_to_string(MxCSSFontSize v) { + if ((size_t)v >= sizeof(mx_css_font_size_values) / sizeof(mx_css_font_size_values[0])) + return mx_css_font_size_values[0]; + return mx_css_font_size_values[v]; +} diff --git a/gen/test/c/mx/mx_css_font_size.h b/gen/test/c/mx/mx_css_font_size.h new file mode 100644 index 000000000..a02318e38 --- /dev/null +++ b/gen/test/c/mx/mx_css_font_size.h @@ -0,0 +1,28 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_CSS_FONT_SIZE_H +#define MX_CSS_FONT_SIZE_H + +#include + +/* + * The css-font-size type includes the CSS font sizes used as an alternative to a numeric point + * size. + */ +typedef enum { + MX_CSS_FONT_SIZE_XX_SMALL = 0, + MX_CSS_FONT_SIZE_X_SMALL = 1, + MX_CSS_FONT_SIZE_SMALL = 2, + MX_CSS_FONT_SIZE_MEDIUM = 3, + MX_CSS_FONT_SIZE_LARGE = 4, + MX_CSS_FONT_SIZE_X_LARGE = 5, + MX_CSS_FONT_SIZE_XX_LARGE = 6 +} MxCSSFontSize; + +bool mx_css_font_size_try_parse(const char *s, MxCSSFontSize *out); +/* Lenient: unknown input falls back to the first variant. */ +MxCSSFontSize mx_css_font_size_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_css_font_size_to_string(MxCSSFontSize v); + +#endif /* MX_CSS_FONT_SIZE_H */ diff --git a/gen/test/c/mx/mx_degree_symbol_value.c b/gen/test/c/mx/mx_degree_symbol_value.c new file mode 100644 index 000000000..1b5071904 --- /dev/null +++ b/gen/test/c/mx/mx_degree_symbol_value.c @@ -0,0 +1,36 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_degree_symbol_value.h" + +#include + +static const char *const mx_degree_symbol_value_values[] = { + "major", + "minor", + "augmented", + "diminished", + "half-diminished", +}; + +bool mx_degree_symbol_value_try_parse(const char *s, MxDegreeSymbolValue *out) { + for (size_t i = 0; i < sizeof(mx_degree_symbol_value_values) / sizeof(mx_degree_symbol_value_values[0]); i++) { + if (strcmp(s, mx_degree_symbol_value_values[i]) == 0) { + *out = (MxDegreeSymbolValue)i; + return true; + } + } + *out = (MxDegreeSymbolValue)0; + return false; +} + +MxDegreeSymbolValue mx_degree_symbol_value_parse(const char *s) { + MxDegreeSymbolValue v; + mx_degree_symbol_value_try_parse(s, &v); + return v; +} + +const char *mx_degree_symbol_value_to_string(MxDegreeSymbolValue v) { + if ((size_t)v >= sizeof(mx_degree_symbol_value_values) / sizeof(mx_degree_symbol_value_values[0])) + return mx_degree_symbol_value_values[0]; + return mx_degree_symbol_value_values[v]; +} diff --git a/gen/test/c/mx/mx_degree_symbol_value.h b/gen/test/c/mx/mx_degree_symbol_value.h new file mode 100644 index 000000000..da2341416 --- /dev/null +++ b/gen/test/c/mx/mx_degree_symbol_value.h @@ -0,0 +1,26 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_DEGREE_SYMBOL_VALUE_H +#define MX_DEGREE_SYMBOL_VALUE_H + +#include + +/* + * The degree-symbol-value type indicates indicates that a symbol should be used in specifying the + * degree. + */ +typedef enum { + MX_DEGREE_SYMBOL_VALUE_MAJOR = 0, + MX_DEGREE_SYMBOL_VALUE_MINOR = 1, + MX_DEGREE_SYMBOL_VALUE_AUGMENTED = 2, + MX_DEGREE_SYMBOL_VALUE_DIMINISHED = 3, + MX_DEGREE_SYMBOL_VALUE_HALF_DIMINISHED = 4 +} MxDegreeSymbolValue; + +bool mx_degree_symbol_value_try_parse(const char *s, MxDegreeSymbolValue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxDegreeSymbolValue mx_degree_symbol_value_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_degree_symbol_value_to_string(MxDegreeSymbolValue v); + +#endif /* MX_DEGREE_SYMBOL_VALUE_H */ diff --git a/gen/test/c/mx/mx_degree_type_value.c b/gen/test/c/mx/mx_degree_type_value.c new file mode 100644 index 000000000..efa38488a --- /dev/null +++ b/gen/test/c/mx/mx_degree_type_value.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_degree_type_value.h" + +#include + +static const char *const mx_degree_type_value_values[] = { + "add", + "alter", + "subtract", +}; + +bool mx_degree_type_value_try_parse(const char *s, MxDegreeTypeValue *out) { + for (size_t i = 0; i < sizeof(mx_degree_type_value_values) / sizeof(mx_degree_type_value_values[0]); i++) { + if (strcmp(s, mx_degree_type_value_values[i]) == 0) { + *out = (MxDegreeTypeValue)i; + return true; + } + } + *out = (MxDegreeTypeValue)0; + return false; +} + +MxDegreeTypeValue mx_degree_type_value_parse(const char *s) { + MxDegreeTypeValue v; + mx_degree_type_value_try_parse(s, &v); + return v; +} + +const char *mx_degree_type_value_to_string(MxDegreeTypeValue v) { + if ((size_t)v >= sizeof(mx_degree_type_value_values) / sizeof(mx_degree_type_value_values[0])) + return mx_degree_type_value_values[0]; + return mx_degree_type_value_values[v]; +} diff --git a/gen/test/c/mx/mx_degree_type_value.h b/gen/test/c/mx/mx_degree_type_value.h new file mode 100644 index 000000000..6f5503f65 --- /dev/null +++ b/gen/test/c/mx/mx_degree_type_value.h @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_DEGREE_TYPE_VALUE_H +#define MX_DEGREE_TYPE_VALUE_H + +#include + +/* + * The degree-type-value type indicates whether the current degree element is an addition, + * alteration, or subtraction to the kind of the current chord in the harmony element. + */ +typedef enum { + MX_DEGREE_TYPE_VALUE_ADD = 0, + MX_DEGREE_TYPE_VALUE_ALTER = 1, + MX_DEGREE_TYPE_VALUE_SUBTRACT = 2 +} MxDegreeTypeValue; + +bool mx_degree_type_value_try_parse(const char *s, MxDegreeTypeValue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxDegreeTypeValue mx_degree_type_value_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_degree_type_value_to_string(MxDegreeTypeValue v); + +#endif /* MX_DEGREE_TYPE_VALUE_H */ diff --git a/gen/test/c/mx/mx_distance_type.c b/gen/test/c/mx/mx_distance_type.c new file mode 100644 index 000000000..78650513f --- /dev/null +++ b/gen/test/c/mx/mx_distance_type.c @@ -0,0 +1,9 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_distance_type.h" + +#include "mx_runtime.h" + +MxDistanceType mx_distance_type_parse(const char *s) { + return mx_strdup(s); +} diff --git a/gen/test/c/mx/mx_distance_type.h b/gen/test/c/mx/mx_distance_type.h new file mode 100644 index 000000000..b3fea6499 --- /dev/null +++ b/gen/test/c/mx/mx_distance_type.h @@ -0,0 +1,16 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_DISTANCE_TYPE_H +#define MX_DISTANCE_TYPE_H + +/* + * The distance-type defines what type of distance is being defined in a distance element. Values + * include beam and hyphen. This is left as a string so that other application-specific types can be + * defined, but it is made a separate type so that it can be redefined more strictly. + */ +typedef char *MxDistanceType; + +/* Malloc'd copy of the wire string; the value IS its spelling. */ +MxDistanceType mx_distance_type_parse(const char *s); + +#endif /* MX_DISTANCE_TYPE_H */ diff --git a/gen/test/c/mx/mx_divisions.c b/gen/test/c/mx/mx_divisions.c new file mode 100644 index 000000000..33c6b65c3 --- /dev/null +++ b/gen/test/c/mx/mx_divisions.c @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_divisions.h" + +#include "mx_runtime.h" + +bool mx_divisions_try_parse(const char *s, MxDivisions *out) { + double v; + if (!mx_try_parse_decimal(s, &v)) { + *out = (MxDivisions)0; + return false; + } + *out = (MxDivisions)v; + return true; +} + +MxDivisions mx_divisions_parse(const char *s) { + double v = mx_parse_decimal(s); + return (MxDivisions)v; +} + +char *mx_divisions_to_string(MxDivisions v) { + return mx_format_decimal((double)v); +} diff --git a/gen/test/c/mx/mx_divisions.h b/gen/test/c/mx/mx_divisions.h new file mode 100644 index 000000000..8ec718e38 --- /dev/null +++ b/gen/test/c/mx/mx_divisions.h @@ -0,0 +1,22 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_DIVISIONS_H +#define MX_DIVISIONS_H + +#include + +/* + * The divisions type is used to express values in terms of the musical divisions defined by the + * divisions element. It is preferred that these be integer values both for MIDI interoperability + * and to avoid roundoff errors. + */ +typedef double MxDivisions; + +/* Strict parse, then clamps into the declared range. */ +bool mx_divisions_try_parse(const char *s, MxDivisions *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxDivisions mx_divisions_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_divisions_to_string(MxDivisions v); + +#endif /* MX_DIVISIONS_H */ diff --git a/gen/test/c/mx/mx_effect.c b/gen/test/c/mx/mx_effect.c new file mode 100644 index 000000000..3e1d13381 --- /dev/null +++ b/gen/test/c/mx/mx_effect.c @@ -0,0 +1,47 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_effect.h" + +#include + +static const char *const mx_effect_values[] = { + "anvil", + "auto horn", + "bird whistle", + "cannon", + "duck call", + "gun shot", + "klaxon horn", + "lions roar", + "lotus flute", + "megaphone", + "police whistle", + "siren", + "slide whistle", + "thunder sheet", + "wind machine", + "wind whistle", +}; + +bool mx_effect_try_parse(const char *s, MxEffect *out) { + for (size_t i = 0; i < sizeof(mx_effect_values) / sizeof(mx_effect_values[0]); i++) { + if (strcmp(s, mx_effect_values[i]) == 0) { + *out = (MxEffect)i; + return true; + } + } + *out = (MxEffect)0; + return false; +} + +MxEffect mx_effect_parse(const char *s) { + MxEffect v; + mx_effect_try_parse(s, &v); + return v; +} + +const char *mx_effect_to_string(MxEffect v) { + if ((size_t)v >= sizeof(mx_effect_values) / sizeof(mx_effect_values[0])) + return mx_effect_values[0]; + return mx_effect_values[v]; +} diff --git a/gen/test/c/mx/mx_effect.h b/gen/test/c/mx/mx_effect.h new file mode 100644 index 000000000..a94f378ff --- /dev/null +++ b/gen/test/c/mx/mx_effect.h @@ -0,0 +1,37 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_EFFECT_H +#define MX_EFFECT_H + +#include + +/* + * The effect type represents pictograms for sound effect percussion instruments. The cannon, lotus + * flute, and megaphone values are in addition to Stone's list. + */ +typedef enum { + MX_EFFECT_ANVIL = 0, + MX_EFFECT_AUTO_HORN = 1, + MX_EFFECT_BIRD_WHISTLE = 2, + MX_EFFECT_CANNON = 3, + MX_EFFECT_DUCK_CALL = 4, + MX_EFFECT_GUN_SHOT = 5, + MX_EFFECT_KLAXON_HORN = 6, + MX_EFFECT_LIONS_ROAR = 7, + MX_EFFECT_LOTUS_FLUTE = 8, + MX_EFFECT_MEGAPHONE = 9, + MX_EFFECT_POLICE_WHISTLE = 10, + MX_EFFECT_SIREN = 11, + MX_EFFECT_SLIDE_WHISTLE = 12, + MX_EFFECT_THUNDER_SHEET = 13, + MX_EFFECT_WIND_MACHINE = 14, + MX_EFFECT_WIND_WHISTLE = 15 +} MxEffect; + +bool mx_effect_try_parse(const char *s, MxEffect *out); +/* Lenient: unknown input falls back to the first variant. */ +MxEffect mx_effect_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_effect_to_string(MxEffect v); + +#endif /* MX_EFFECT_H */ diff --git a/gen/test/c/mx/mx_enclosure_shape.c b/gen/test/c/mx/mx_enclosure_shape.c new file mode 100644 index 000000000..3be4e8e43 --- /dev/null +++ b/gen/test/c/mx/mx_enclosure_shape.c @@ -0,0 +1,45 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_enclosure_shape.h" + +#include + +static const char *const mx_enclosure_shape_values[] = { + "rectangle", + "square", + "oval", + "circle", + "bracket", + "triangle", + "diamond", + "pentagon", + "hexagon", + "heptagon", + "octagon", + "nonagon", + "decagon", + "none", +}; + +bool mx_enclosure_shape_try_parse(const char *s, MxEnclosureShape *out) { + for (size_t i = 0; i < sizeof(mx_enclosure_shape_values) / sizeof(mx_enclosure_shape_values[0]); i++) { + if (strcmp(s, mx_enclosure_shape_values[i]) == 0) { + *out = (MxEnclosureShape)i; + return true; + } + } + *out = (MxEnclosureShape)0; + return false; +} + +MxEnclosureShape mx_enclosure_shape_parse(const char *s) { + MxEnclosureShape v; + mx_enclosure_shape_try_parse(s, &v); + return v; +} + +const char *mx_enclosure_shape_to_string(MxEnclosureShape v) { + if ((size_t)v >= sizeof(mx_enclosure_shape_values) / sizeof(mx_enclosure_shape_values[0])) + return mx_enclosure_shape_values[0]; + return mx_enclosure_shape_values[v]; +} diff --git a/gen/test/c/mx/mx_enclosure_shape.h b/gen/test/c/mx/mx_enclosure_shape.h new file mode 100644 index 000000000..650fb8e47 --- /dev/null +++ b/gen/test/c/mx/mx_enclosure_shape.h @@ -0,0 +1,36 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ENCLOSURE_SHAPE_H +#define MX_ENCLOSURE_SHAPE_H + +#include + +/* + * The enclosure-shape type describes the shape and presence / absence of an enclosure around text + * or symbols. A bracket enclosure is similar to a rectangle with the bottom line missing, as is + * common in jazz notation. + */ +typedef enum { + MX_ENCLOSURE_SHAPE_RECTANGLE = 0, + MX_ENCLOSURE_SHAPE_SQUARE = 1, + MX_ENCLOSURE_SHAPE_OVAL = 2, + MX_ENCLOSURE_SHAPE_CIRCLE = 3, + MX_ENCLOSURE_SHAPE_BRACKET = 4, + MX_ENCLOSURE_SHAPE_TRIANGLE = 5, + MX_ENCLOSURE_SHAPE_DIAMOND = 6, + MX_ENCLOSURE_SHAPE_PENTAGON = 7, + MX_ENCLOSURE_SHAPE_HEXAGON = 8, + MX_ENCLOSURE_SHAPE_HEPTAGON = 9, + MX_ENCLOSURE_SHAPE_OCTAGON = 10, + MX_ENCLOSURE_SHAPE_NONAGON = 11, + MX_ENCLOSURE_SHAPE_DECAGON = 12, + MX_ENCLOSURE_SHAPE_NONE = 13 +} MxEnclosureShape; + +bool mx_enclosure_shape_try_parse(const char *s, MxEnclosureShape *out); +/* Lenient: unknown input falls back to the first variant. */ +MxEnclosureShape mx_enclosure_shape_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_enclosure_shape_to_string(MxEnclosureShape v); + +#endif /* MX_ENCLOSURE_SHAPE_H */ diff --git a/gen/test/c/mx/mx_ending_number.c b/gen/test/c/mx/mx_ending_number.c new file mode 100644 index 000000000..8d062d8b2 --- /dev/null +++ b/gen/test/c/mx/mx_ending_number.c @@ -0,0 +1,9 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_ending_number.h" + +#include "mx_runtime.h" + +MxEndingNumber mx_ending_number_parse(const char *s) { + return mx_strdup(s); +} diff --git a/gen/test/c/mx/mx_ending_number.h b/gen/test/c/mx/mx_ending_number.h new file mode 100644 index 000000000..5b0dd1df5 --- /dev/null +++ b/gen/test/c/mx/mx_ending_number.h @@ -0,0 +1,18 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ENDING_NUMBER_H +#define MX_ENDING_NUMBER_H + +/* + * The ending-number type is used to specify either a comma-separated list of positive integers + * without leading zeros, or a string of zero or more spaces. It is used for the number attribute of + * the ending element. The zero or more spaces version is used when software knows that an ending is + * present, but cannot determine the type of the ending. Pattern (not enforced): ([ + * ]*)|([1-9][0-9]*(, ?[1-9][0-9]*)*) + */ +typedef char *MxEndingNumber; + +/* Malloc'd copy of the wire string; the value IS its spelling. */ +MxEndingNumber mx_ending_number_parse(const char *s); + +#endif /* MX_ENDING_NUMBER_H */ diff --git a/gen/test/c/mx/mx_fan.c b/gen/test/c/mx/mx_fan.c new file mode 100644 index 000000000..fc518b502 --- /dev/null +++ b/gen/test/c/mx/mx_fan.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_fan.h" + +#include + +static const char *const mx_fan_values[] = { + "accel", + "rit", + "none", +}; + +bool mx_fan_try_parse(const char *s, MxFan *out) { + for (size_t i = 0; i < sizeof(mx_fan_values) / sizeof(mx_fan_values[0]); i++) { + if (strcmp(s, mx_fan_values[i]) == 0) { + *out = (MxFan)i; + return true; + } + } + *out = (MxFan)0; + return false; +} + +MxFan mx_fan_parse(const char *s) { + MxFan v; + mx_fan_try_parse(s, &v); + return v; +} + +const char *mx_fan_to_string(MxFan v) { + if ((size_t)v >= sizeof(mx_fan_values) / sizeof(mx_fan_values[0])) + return mx_fan_values[0]; + return mx_fan_values[v]; +} diff --git a/gen/test/c/mx/mx_fan.h b/gen/test/c/mx/mx_fan.h new file mode 100644 index 000000000..216e4db8d --- /dev/null +++ b/gen/test/c/mx/mx_fan.h @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_FAN_H +#define MX_FAN_H + +#include + +/* + * The fan type represents the type of beam fanning present on a note, used to represent + * accelerandos and ritardandos. + */ +typedef enum { + MX_FAN_ACCEL = 0, + MX_FAN_RIT = 1, + MX_FAN_NONE = 2 +} MxFan; + +bool mx_fan_try_parse(const char *s, MxFan *out); +/* Lenient: unknown input falls back to the first variant. */ +MxFan mx_fan_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_fan_to_string(MxFan v); + +#endif /* MX_FAN_H */ diff --git a/gen/test/c/mx/mx_fermata_shape.c b/gen/test/c/mx/mx_fermata_shape.c new file mode 100644 index 000000000..3a9881b25 --- /dev/null +++ b/gen/test/c/mx/mx_fermata_shape.c @@ -0,0 +1,40 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_fermata_shape.h" + +#include + +static const char *const mx_fermata_shape_values[] = { + "normal", + "angled", + "square", + "double-angled", + "double-square", + "double-dot", + "half-curve", + "curlew", + "", +}; + +bool mx_fermata_shape_try_parse(const char *s, MxFermataShape *out) { + for (size_t i = 0; i < sizeof(mx_fermata_shape_values) / sizeof(mx_fermata_shape_values[0]); i++) { + if (strcmp(s, mx_fermata_shape_values[i]) == 0) { + *out = (MxFermataShape)i; + return true; + } + } + *out = (MxFermataShape)0; + return false; +} + +MxFermataShape mx_fermata_shape_parse(const char *s) { + MxFermataShape v; + mx_fermata_shape_try_parse(s, &v); + return v; +} + +const char *mx_fermata_shape_to_string(MxFermataShape v) { + if ((size_t)v >= sizeof(mx_fermata_shape_values) / sizeof(mx_fermata_shape_values[0])) + return mx_fermata_shape_values[0]; + return mx_fermata_shape_values[v]; +} diff --git a/gen/test/c/mx/mx_fermata_shape.h b/gen/test/c/mx/mx_fermata_shape.h new file mode 100644 index 000000000..061087cb9 --- /dev/null +++ b/gen/test/c/mx/mx_fermata_shape.h @@ -0,0 +1,30 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_FERMATA_SHAPE_H +#define MX_FERMATA_SHAPE_H + +#include + +/* + * The fermata-shape type represents the shape of the fermata sign. The empty value is equivalent to + * the normal value. + */ +typedef enum { + MX_FERMATA_SHAPE_NORMAL = 0, + MX_FERMATA_SHAPE_ANGLED = 1, + MX_FERMATA_SHAPE_SQUARE = 2, + MX_FERMATA_SHAPE_DOUBLE_ANGLED = 3, + MX_FERMATA_SHAPE_DOUBLE_SQUARE = 4, + MX_FERMATA_SHAPE_DOUBLE_DOT = 5, + MX_FERMATA_SHAPE_HALF_CURVE = 6, + MX_FERMATA_SHAPE_CURLEW = 7, + MX_FERMATA_SHAPE_EMPTY = 8 +} MxFermataShape; + +bool mx_fermata_shape_try_parse(const char *s, MxFermataShape *out); +/* Lenient: unknown input falls back to the first variant. */ +MxFermataShape mx_fermata_shape_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_fermata_shape_to_string(MxFermataShape v); + +#endif /* MX_FERMATA_SHAPE_H */ diff --git a/gen/test/c/mx/mx_fifths.c b/gen/test/c/mx/mx_fifths.c new file mode 100644 index 000000000..21c297084 --- /dev/null +++ b/gen/test/c/mx/mx_fifths.c @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_fifths.h" + +#include "mx_runtime.h" + +bool mx_fifths_try_parse(const char *s, MxFifths *out) { + long v; + if (!mx_try_parse_int(s, &v)) { + *out = (MxFifths)0; + return false; + } + *out = (MxFifths)v; + return true; +} + +MxFifths mx_fifths_parse(const char *s) { + long v = mx_parse_int(s); + return (MxFifths)v; +} + +char *mx_fifths_to_string(MxFifths v) { + return mx_format_int((long)v); +} diff --git a/gen/test/c/mx/mx_fifths.h b/gen/test/c/mx/mx_fifths.h new file mode 100644 index 000000000..bd18e4754 --- /dev/null +++ b/gen/test/c/mx/mx_fifths.h @@ -0,0 +1,22 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_FIFTHS_H +#define MX_FIFTHS_H + +#include + +/* + * The fifths type represents the number of flats or sharps in a traditional key signature. Negative + * numbers are used for flats and positive numbers for sharps, reflecting the key's placement within + * the circle of fifths (hence the type name). + */ +typedef long MxFifths; + +/* Strict parse, then clamps into the declared range. */ +bool mx_fifths_try_parse(const char *s, MxFifths *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxFifths mx_fifths_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_fifths_to_string(MxFifths v); + +#endif /* MX_FIFTHS_H */ diff --git a/gen/test/c/mx/mx_font_size.c b/gen/test/c/mx/mx_font_size.c new file mode 100644 index 000000000..fadbde4c9 --- /dev/null +++ b/gen/test/c/mx/mx_font_size.c @@ -0,0 +1,48 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_font_size.h" + +#include "mx_runtime.h" +#include +#include + +bool mx_font_size_try_parse(const char *s, MxFontSize *out) { + MxFontSize v; + memset(&v, 0, sizeof(v)); + if (mx_try_parse_decimal(s, &v.decimal)) { + v.kind = MX_FONT_SIZE_KIND_DECIMAL; + *out = v; + return true; + } + if (mx_css_font_size_try_parse(s, &v.css_font_size)) { + v.kind = MX_FONT_SIZE_KIND_CSS_FONT_SIZE; + *out = v; + return true; + } + *out = v; + return false; +} + +MxFontSize mx_font_size_parse(const char *s) { + MxFontSize v; + if (mx_font_size_try_parse(s, &v)) + return v; + memset(&v, 0, sizeof(v)); + v.kind = MX_FONT_SIZE_KIND_DECIMAL; + v.decimal = mx_parse_decimal(s); + return v; +} + +char *mx_font_size_to_string(MxFontSize v) { + switch (v.kind) { + case MX_FONT_SIZE_KIND_CSS_FONT_SIZE: + return mx_strdup(mx_css_font_size_to_string(v.css_font_size)); + default: + return mx_format_decimal(v.decimal); + } +} + +void mx_font_size_free(MxFontSize *v) { + if (!v) + return; +} diff --git a/gen/test/c/mx/mx_font_size.h b/gen/test/c/mx/mx_font_size.h new file mode 100644 index 000000000..748ac3e95 --- /dev/null +++ b/gen/test/c/mx/mx_font_size.h @@ -0,0 +1,31 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_FONT_SIZE_H +#define MX_FONT_SIZE_H + +#include +#include "mx_css_font_size.h" + +/* + * The font-size can be one of the CSS font sizes or a numeric point size. + */ +typedef enum { + MX_FONT_SIZE_KIND_DECIMAL = 0, + MX_FONT_SIZE_KIND_CSS_FONT_SIZE = 1 +} MxFontSizeKind; + +typedef struct { + MxFontSizeKind kind; + double decimal; + MxCSSFontSize css_font_size; +} MxFontSize; + +/* Tries each union member in schema order. */ +bool mx_font_size_try_parse(const char *s, MxFontSize *out); +/* Lenient: an unmatched input is absorbed by the first member. */ +MxFontSize mx_font_size_parse(const char *s); +/* The wire spelling of whichever member is held. Malloc'd. */ +char *mx_font_size_to_string(MxFontSize v); +void mx_font_size_free(MxFontSize *v); + +#endif /* MX_FONT_SIZE_H */ diff --git a/gen/test/c/mx/mx_font_style.c b/gen/test/c/mx/mx_font_style.c new file mode 100644 index 000000000..9ef638b22 --- /dev/null +++ b/gen/test/c/mx/mx_font_style.c @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_font_style.h" + +#include + +static const char *const mx_font_style_values[] = { + "normal", + "italic", +}; + +bool mx_font_style_try_parse(const char *s, MxFontStyle *out) { + for (size_t i = 0; i < sizeof(mx_font_style_values) / sizeof(mx_font_style_values[0]); i++) { + if (strcmp(s, mx_font_style_values[i]) == 0) { + *out = (MxFontStyle)i; + return true; + } + } + *out = (MxFontStyle)0; + return false; +} + +MxFontStyle mx_font_style_parse(const char *s) { + MxFontStyle v; + mx_font_style_try_parse(s, &v); + return v; +} + +const char *mx_font_style_to_string(MxFontStyle v) { + if ((size_t)v >= sizeof(mx_font_style_values) / sizeof(mx_font_style_values[0])) + return mx_font_style_values[0]; + return mx_font_style_values[v]; +} diff --git a/gen/test/c/mx/mx_font_style.h b/gen/test/c/mx/mx_font_style.h new file mode 100644 index 000000000..d33b447a2 --- /dev/null +++ b/gen/test/c/mx/mx_font_style.h @@ -0,0 +1,22 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_FONT_STYLE_H +#define MX_FONT_STYLE_H + +#include + +/* + * The font-style type represents a simplified version of the CSS font-style property. + */ +typedef enum { + MX_FONT_STYLE_NORMAL = 0, + MX_FONT_STYLE_ITALIC = 1 +} MxFontStyle; + +bool mx_font_style_try_parse(const char *s, MxFontStyle *out); +/* Lenient: unknown input falls back to the first variant. */ +MxFontStyle mx_font_style_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_font_style_to_string(MxFontStyle v); + +#endif /* MX_FONT_STYLE_H */ diff --git a/gen/test/c/mx/mx_font_weight.c b/gen/test/c/mx/mx_font_weight.c new file mode 100644 index 000000000..565d9a842 --- /dev/null +++ b/gen/test/c/mx/mx_font_weight.c @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_font_weight.h" + +#include + +static const char *const mx_font_weight_values[] = { + "normal", + "bold", +}; + +bool mx_font_weight_try_parse(const char *s, MxFontWeight *out) { + for (size_t i = 0; i < sizeof(mx_font_weight_values) / sizeof(mx_font_weight_values[0]); i++) { + if (strcmp(s, mx_font_weight_values[i]) == 0) { + *out = (MxFontWeight)i; + return true; + } + } + *out = (MxFontWeight)0; + return false; +} + +MxFontWeight mx_font_weight_parse(const char *s) { + MxFontWeight v; + mx_font_weight_try_parse(s, &v); + return v; +} + +const char *mx_font_weight_to_string(MxFontWeight v) { + if ((size_t)v >= sizeof(mx_font_weight_values) / sizeof(mx_font_weight_values[0])) + return mx_font_weight_values[0]; + return mx_font_weight_values[v]; +} diff --git a/gen/test/c/mx/mx_font_weight.h b/gen/test/c/mx/mx_font_weight.h new file mode 100644 index 000000000..96c062c79 --- /dev/null +++ b/gen/test/c/mx/mx_font_weight.h @@ -0,0 +1,22 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_FONT_WEIGHT_H +#define MX_FONT_WEIGHT_H + +#include + +/* + * The font-weight type represents a simplified version of the CSS font-weight property. + */ +typedef enum { + MX_FONT_WEIGHT_NORMAL = 0, + MX_FONT_WEIGHT_BOLD = 1 +} MxFontWeight; + +bool mx_font_weight_try_parse(const char *s, MxFontWeight *out); +/* Lenient: unknown input falls back to the first variant. */ +MxFontWeight mx_font_weight_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_font_weight_to_string(MxFontWeight v); + +#endif /* MX_FONT_WEIGHT_H */ diff --git a/gen/test/c/mx/mx_glass_value.c b/gen/test/c/mx/mx_glass_value.c new file mode 100644 index 000000000..a79290eed --- /dev/null +++ b/gen/test/c/mx/mx_glass_value.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_glass_value.h" + +#include + +static const char *const mx_glass_value_values[] = { + "glass harmonica", + "glass harp", + "wind chimes", +}; + +bool mx_glass_value_try_parse(const char *s, MxGlassValue *out) { + for (size_t i = 0; i < sizeof(mx_glass_value_values) / sizeof(mx_glass_value_values[0]); i++) { + if (strcmp(s, mx_glass_value_values[i]) == 0) { + *out = (MxGlassValue)i; + return true; + } + } + *out = (MxGlassValue)0; + return false; +} + +MxGlassValue mx_glass_value_parse(const char *s) { + MxGlassValue v; + mx_glass_value_try_parse(s, &v); + return v; +} + +const char *mx_glass_value_to_string(MxGlassValue v) { + if ((size_t)v >= sizeof(mx_glass_value_values) / sizeof(mx_glass_value_values[0])) + return mx_glass_value_values[0]; + return mx_glass_value_values[v]; +} diff --git a/gen/test/c/mx/mx_glass_value.h b/gen/test/c/mx/mx_glass_value.h new file mode 100644 index 000000000..ed62d3df0 --- /dev/null +++ b/gen/test/c/mx/mx_glass_value.h @@ -0,0 +1,23 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_GLASS_VALUE_H +#define MX_GLASS_VALUE_H + +#include + +/* + * The glass-value type represents pictograms for glass percussion instruments. + */ +typedef enum { + MX_GLASS_VALUE_GLASS_HARMONICA = 0, + MX_GLASS_VALUE_GLASS_HARP = 1, + MX_GLASS_VALUE_WIND_CHIMES = 2 +} MxGlassValue; + +bool mx_glass_value_try_parse(const char *s, MxGlassValue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxGlassValue mx_glass_value_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_glass_value_to_string(MxGlassValue v); + +#endif /* MX_GLASS_VALUE_H */ diff --git a/gen/test/c/mx/mx_glyph_type.c b/gen/test/c/mx/mx_glyph_type.c new file mode 100644 index 000000000..c07a8a947 --- /dev/null +++ b/gen/test/c/mx/mx_glyph_type.c @@ -0,0 +1,9 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_glyph_type.h" + +#include "mx_runtime.h" + +MxGlyphType mx_glyph_type_parse(const char *s) { + return mx_strdup(s); +} diff --git a/gen/test/c/mx/mx_glyph_type.h b/gen/test/c/mx/mx_glyph_type.h new file mode 100644 index 000000000..69dab3d89 --- /dev/null +++ b/gen/test/c/mx/mx_glyph_type.h @@ -0,0 +1,25 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_GLYPH_TYPE_H +#define MX_GLYPH_TYPE_H + +/* + * The glyph-type defines what type of glyph is being defined in a glyph element. Values include + * quarter-rest, g-clef-ottava-bassa, c-clef, f-clef, percussion-clef, octave-shift-up-8, + * octave-shift-down-8, octave-shift-continue-8, octave-shift-down-15, octave-shift-up-15, + * octave-shift-continue-15, octave-shift-down-22, octave-shift-up-22, and octave-shift-continue-22. + * This is left as a string so that other application-specific types can be defined, but it is made + * a separate type so that it can be redefined more strictly. A quarter-rest type specifies the + * glyph to use when a note has a rest element and a type value of quarter. The c-clef, f-clef, and + * percussion-clef types specify the glyph to use when a clef sign element value is C, F, or + * percussion respectively. The g-clef-ottava-bassa type specifies the glyph to use when a clef sign + * element value is G and the clef-octave-change element value is -1. The octave-shift types specify + * the glyph to use when an octave-shift type attribute value is up, down, or continue and the + * octave-shift size attribute value is 8, 15, or 22. + */ +typedef char *MxGlyphType; + +/* Malloc'd copy of the wire string; the value IS its spelling. */ +MxGlyphType mx_glyph_type_parse(const char *s); + +#endif /* MX_GLYPH_TYPE_H */ diff --git a/gen/test/c/mx/mx_group_barline_value.c b/gen/test/c/mx/mx_group_barline_value.c new file mode 100644 index 000000000..904228ab0 --- /dev/null +++ b/gen/test/c/mx/mx_group_barline_value.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_group_barline_value.h" + +#include + +static const char *const mx_group_barline_value_values[] = { + "yes", + "no", + "Mensurstrich", +}; + +bool mx_group_barline_value_try_parse(const char *s, MxGroupBarlineValue *out) { + for (size_t i = 0; i < sizeof(mx_group_barline_value_values) / sizeof(mx_group_barline_value_values[0]); i++) { + if (strcmp(s, mx_group_barline_value_values[i]) == 0) { + *out = (MxGroupBarlineValue)i; + return true; + } + } + *out = (MxGroupBarlineValue)0; + return false; +} + +MxGroupBarlineValue mx_group_barline_value_parse(const char *s) { + MxGroupBarlineValue v; + mx_group_barline_value_try_parse(s, &v); + return v; +} + +const char *mx_group_barline_value_to_string(MxGroupBarlineValue v) { + if ((size_t)v >= sizeof(mx_group_barline_value_values) / sizeof(mx_group_barline_value_values[0])) + return mx_group_barline_value_values[0]; + return mx_group_barline_value_values[v]; +} diff --git a/gen/test/c/mx/mx_group_barline_value.h b/gen/test/c/mx/mx_group_barline_value.h new file mode 100644 index 000000000..fe2645dc2 --- /dev/null +++ b/gen/test/c/mx/mx_group_barline_value.h @@ -0,0 +1,23 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_GROUP_BARLINE_VALUE_H +#define MX_GROUP_BARLINE_VALUE_H + +#include + +/* + * The group-barline-value type indicates if the group should have common barlines. + */ +typedef enum { + MX_GROUP_BARLINE_VALUE_YES = 0, + MX_GROUP_BARLINE_VALUE_NO = 1, + MX_GROUP_BARLINE_VALUE_MENSURSTRICH = 2 +} MxGroupBarlineValue; + +bool mx_group_barline_value_try_parse(const char *s, MxGroupBarlineValue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxGroupBarlineValue mx_group_barline_value_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_group_barline_value_to_string(MxGroupBarlineValue v); + +#endif /* MX_GROUP_BARLINE_VALUE_H */ diff --git a/gen/test/c/mx/mx_group_symbol_value.c b/gen/test/c/mx/mx_group_symbol_value.c new file mode 100644 index 000000000..a66de51c4 --- /dev/null +++ b/gen/test/c/mx/mx_group_symbol_value.c @@ -0,0 +1,36 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_group_symbol_value.h" + +#include + +static const char *const mx_group_symbol_value_values[] = { + "none", + "brace", + "line", + "bracket", + "square", +}; + +bool mx_group_symbol_value_try_parse(const char *s, MxGroupSymbolValue *out) { + for (size_t i = 0; i < sizeof(mx_group_symbol_value_values) / sizeof(mx_group_symbol_value_values[0]); i++) { + if (strcmp(s, mx_group_symbol_value_values[i]) == 0) { + *out = (MxGroupSymbolValue)i; + return true; + } + } + *out = (MxGroupSymbolValue)0; + return false; +} + +MxGroupSymbolValue mx_group_symbol_value_parse(const char *s) { + MxGroupSymbolValue v; + mx_group_symbol_value_try_parse(s, &v); + return v; +} + +const char *mx_group_symbol_value_to_string(MxGroupSymbolValue v) { + if ((size_t)v >= sizeof(mx_group_symbol_value_values) / sizeof(mx_group_symbol_value_values[0])) + return mx_group_symbol_value_values[0]; + return mx_group_symbol_value_values[v]; +} diff --git a/gen/test/c/mx/mx_group_symbol_value.h b/gen/test/c/mx/mx_group_symbol_value.h new file mode 100644 index 000000000..ada0599d3 --- /dev/null +++ b/gen/test/c/mx/mx_group_symbol_value.h @@ -0,0 +1,26 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_GROUP_SYMBOL_VALUE_H +#define MX_GROUP_SYMBOL_VALUE_H + +#include + +/* + * The group-symbol-value type indicates how the symbol for a group is indicated in the score. The + * default value is none. + */ +typedef enum { + MX_GROUP_SYMBOL_VALUE_NONE = 0, + MX_GROUP_SYMBOL_VALUE_BRACE = 1, + MX_GROUP_SYMBOL_VALUE_LINE = 2, + MX_GROUP_SYMBOL_VALUE_BRACKET = 3, + MX_GROUP_SYMBOL_VALUE_SQUARE = 4 +} MxGroupSymbolValue; + +bool mx_group_symbol_value_try_parse(const char *s, MxGroupSymbolValue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxGroupSymbolValue mx_group_symbol_value_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_group_symbol_value_to_string(MxGroupSymbolValue v); + +#endif /* MX_GROUP_SYMBOL_VALUE_H */ diff --git a/gen/test/c/mx/mx_handbell_value.c b/gen/test/c/mx/mx_handbell_value.c new file mode 100644 index 000000000..6b720b552 --- /dev/null +++ b/gen/test/c/mx/mx_handbell_value.c @@ -0,0 +1,43 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_handbell_value.h" + +#include + +static const char *const mx_handbell_value_values[] = { + "belltree", + "damp", + "echo", + "gyro", + "hand martellato", + "mallet lift", + "mallet table", + "martellato", + "martellato lift", + "muted martellato", + "pluck lift", + "swing", +}; + +bool mx_handbell_value_try_parse(const char *s, MxHandbellValue *out) { + for (size_t i = 0; i < sizeof(mx_handbell_value_values) / sizeof(mx_handbell_value_values[0]); i++) { + if (strcmp(s, mx_handbell_value_values[i]) == 0) { + *out = (MxHandbellValue)i; + return true; + } + } + *out = (MxHandbellValue)0; + return false; +} + +MxHandbellValue mx_handbell_value_parse(const char *s) { + MxHandbellValue v; + mx_handbell_value_try_parse(s, &v); + return v; +} + +const char *mx_handbell_value_to_string(MxHandbellValue v) { + if ((size_t)v >= sizeof(mx_handbell_value_values) / sizeof(mx_handbell_value_values[0])) + return mx_handbell_value_values[0]; + return mx_handbell_value_values[v]; +} diff --git a/gen/test/c/mx/mx_handbell_value.h b/gen/test/c/mx/mx_handbell_value.h new file mode 100644 index 000000000..2f581531c --- /dev/null +++ b/gen/test/c/mx/mx_handbell_value.h @@ -0,0 +1,32 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_HANDBELL_VALUE_H +#define MX_HANDBELL_VALUE_H + +#include + +/* + * The handbell-value type represents the type of handbell technique being notated. + */ +typedef enum { + MX_HANDBELL_VALUE_BELLTREE = 0, + MX_HANDBELL_VALUE_DAMP = 1, + MX_HANDBELL_VALUE_ECHO = 2, + MX_HANDBELL_VALUE_GYRO = 3, + MX_HANDBELL_VALUE_HAND_MARTELLATO = 4, + MX_HANDBELL_VALUE_MALLET_LIFT = 5, + MX_HANDBELL_VALUE_MALLET_TABLE = 6, + MX_HANDBELL_VALUE_MARTELLATO = 7, + MX_HANDBELL_VALUE_MARTELLATO_LIFT = 8, + MX_HANDBELL_VALUE_MUTED_MARTELLATO = 9, + MX_HANDBELL_VALUE_PLUCK_LIFT = 10, + MX_HANDBELL_VALUE_SWING = 11 +} MxHandbellValue; + +bool mx_handbell_value_try_parse(const char *s, MxHandbellValue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxHandbellValue mx_handbell_value_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_handbell_value_to_string(MxHandbellValue v); + +#endif /* MX_HANDBELL_VALUE_H */ diff --git a/gen/test/c/mx/mx_harmon_closed_location.c b/gen/test/c/mx/mx_harmon_closed_location.c new file mode 100644 index 000000000..1b1602af2 --- /dev/null +++ b/gen/test/c/mx/mx_harmon_closed_location.c @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_harmon_closed_location.h" + +#include + +static const char *const mx_harmon_closed_location_values[] = { + "right", + "bottom", + "left", + "top", +}; + +bool mx_harmon_closed_location_try_parse(const char *s, MxHarmonClosedLocation *out) { + for (size_t i = 0; i < sizeof(mx_harmon_closed_location_values) / sizeof(mx_harmon_closed_location_values[0]); i++) { + if (strcmp(s, mx_harmon_closed_location_values[i]) == 0) { + *out = (MxHarmonClosedLocation)i; + return true; + } + } + *out = (MxHarmonClosedLocation)0; + return false; +} + +MxHarmonClosedLocation mx_harmon_closed_location_parse(const char *s) { + MxHarmonClosedLocation v; + mx_harmon_closed_location_try_parse(s, &v); + return v; +} + +const char *mx_harmon_closed_location_to_string(MxHarmonClosedLocation v) { + if ((size_t)v >= sizeof(mx_harmon_closed_location_values) / sizeof(mx_harmon_closed_location_values[0])) + return mx_harmon_closed_location_values[0]; + return mx_harmon_closed_location_values[v]; +} diff --git a/gen/test/c/mx/mx_harmon_closed_location.h b/gen/test/c/mx/mx_harmon_closed_location.h new file mode 100644 index 000000000..63fa35bce --- /dev/null +++ b/gen/test/c/mx/mx_harmon_closed_location.h @@ -0,0 +1,25 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_HARMON_CLOSED_LOCATION_H +#define MX_HARMON_CLOSED_LOCATION_H + +#include + +/* + * The harmon-closed-location type indicates which portion of the symbol is filled in when the + * corresponding harmon-closed-value is half. + */ +typedef enum { + MX_HARMON_CLOSED_LOCATION_RIGHT = 0, + MX_HARMON_CLOSED_LOCATION_BOTTOM = 1, + MX_HARMON_CLOSED_LOCATION_LEFT = 2, + MX_HARMON_CLOSED_LOCATION_TOP = 3 +} MxHarmonClosedLocation; + +bool mx_harmon_closed_location_try_parse(const char *s, MxHarmonClosedLocation *out); +/* Lenient: unknown input falls back to the first variant. */ +MxHarmonClosedLocation mx_harmon_closed_location_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_harmon_closed_location_to_string(MxHarmonClosedLocation v); + +#endif /* MX_HARMON_CLOSED_LOCATION_H */ diff --git a/gen/test/c/mx/mx_harmon_closed_value.c b/gen/test/c/mx/mx_harmon_closed_value.c new file mode 100644 index 000000000..9e98e7d7d --- /dev/null +++ b/gen/test/c/mx/mx_harmon_closed_value.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_harmon_closed_value.h" + +#include + +static const char *const mx_harmon_closed_value_values[] = { + "yes", + "no", + "half", +}; + +bool mx_harmon_closed_value_try_parse(const char *s, MxHarmonClosedValue *out) { + for (size_t i = 0; i < sizeof(mx_harmon_closed_value_values) / sizeof(mx_harmon_closed_value_values[0]); i++) { + if (strcmp(s, mx_harmon_closed_value_values[i]) == 0) { + *out = (MxHarmonClosedValue)i; + return true; + } + } + *out = (MxHarmonClosedValue)0; + return false; +} + +MxHarmonClosedValue mx_harmon_closed_value_parse(const char *s) { + MxHarmonClosedValue v; + mx_harmon_closed_value_try_parse(s, &v); + return v; +} + +const char *mx_harmon_closed_value_to_string(MxHarmonClosedValue v) { + if ((size_t)v >= sizeof(mx_harmon_closed_value_values) / sizeof(mx_harmon_closed_value_values[0])) + return mx_harmon_closed_value_values[0]; + return mx_harmon_closed_value_values[v]; +} diff --git a/gen/test/c/mx/mx_harmon_closed_value.h b/gen/test/c/mx/mx_harmon_closed_value.h new file mode 100644 index 000000000..525f759a3 --- /dev/null +++ b/gen/test/c/mx/mx_harmon_closed_value.h @@ -0,0 +1,23 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_HARMON_CLOSED_VALUE_H +#define MX_HARMON_CLOSED_VALUE_H + +#include + +/* + * The harmon-closed-value type represents whether the harmon mute is closed, open, or half-open. + */ +typedef enum { + MX_HARMON_CLOSED_VALUE_YES = 0, + MX_HARMON_CLOSED_VALUE_NO = 1, + MX_HARMON_CLOSED_VALUE_HALF = 2 +} MxHarmonClosedValue; + +bool mx_harmon_closed_value_try_parse(const char *s, MxHarmonClosedValue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxHarmonClosedValue mx_harmon_closed_value_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_harmon_closed_value_to_string(MxHarmonClosedValue v); + +#endif /* MX_HARMON_CLOSED_VALUE_H */ diff --git a/gen/test/c/mx/mx_harmony_type.c b/gen/test/c/mx/mx_harmony_type.c new file mode 100644 index 000000000..d939161bf --- /dev/null +++ b/gen/test/c/mx/mx_harmony_type.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_harmony_type.h" + +#include + +static const char *const mx_harmony_type_values[] = { + "explicit", + "implied", + "alternate", +}; + +bool mx_harmony_type_try_parse(const char *s, MxHarmonyType *out) { + for (size_t i = 0; i < sizeof(mx_harmony_type_values) / sizeof(mx_harmony_type_values[0]); i++) { + if (strcmp(s, mx_harmony_type_values[i]) == 0) { + *out = (MxHarmonyType)i; + return true; + } + } + *out = (MxHarmonyType)0; + return false; +} + +MxHarmonyType mx_harmony_type_parse(const char *s) { + MxHarmonyType v; + mx_harmony_type_try_parse(s, &v); + return v; +} + +const char *mx_harmony_type_to_string(MxHarmonyType v) { + if ((size_t)v >= sizeof(mx_harmony_type_values) / sizeof(mx_harmony_type_values[0])) + return mx_harmony_type_values[0]; + return mx_harmony_type_values[v]; +} diff --git a/gen/test/c/mx/mx_harmony_type.h b/gen/test/c/mx/mx_harmony_type.h new file mode 100644 index 000000000..45046dab7 --- /dev/null +++ b/gen/test/c/mx/mx_harmony_type.h @@ -0,0 +1,25 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_HARMONY_TYPE_H +#define MX_HARMONY_TYPE_H + +#include + +/* + * The harmony-type type differentiates different types of harmonies when alternate harmonies are + * possible. Explicit harmonies have all note present in the music; implied have some notes missing + * but implied; alternate represents alternate analyses. + */ +typedef enum { + MX_HARMONY_TYPE_EXPLICIT = 0, + MX_HARMONY_TYPE_IMPLIED = 1, + MX_HARMONY_TYPE_ALTERNATE = 2 +} MxHarmonyType; + +bool mx_harmony_type_try_parse(const char *s, MxHarmonyType *out); +/* Lenient: unknown input falls back to the first variant. */ +MxHarmonyType mx_harmony_type_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_harmony_type_to_string(MxHarmonyType v); + +#endif /* MX_HARMONY_TYPE_H */ diff --git a/gen/test/c/mx/mx_hole_closed_location.c b/gen/test/c/mx/mx_hole_closed_location.c new file mode 100644 index 000000000..bc50629ec --- /dev/null +++ b/gen/test/c/mx/mx_hole_closed_location.c @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_hole_closed_location.h" + +#include + +static const char *const mx_hole_closed_location_values[] = { + "right", + "bottom", + "left", + "top", +}; + +bool mx_hole_closed_location_try_parse(const char *s, MxHoleClosedLocation *out) { + for (size_t i = 0; i < sizeof(mx_hole_closed_location_values) / sizeof(mx_hole_closed_location_values[0]); i++) { + if (strcmp(s, mx_hole_closed_location_values[i]) == 0) { + *out = (MxHoleClosedLocation)i; + return true; + } + } + *out = (MxHoleClosedLocation)0; + return false; +} + +MxHoleClosedLocation mx_hole_closed_location_parse(const char *s) { + MxHoleClosedLocation v; + mx_hole_closed_location_try_parse(s, &v); + return v; +} + +const char *mx_hole_closed_location_to_string(MxHoleClosedLocation v) { + if ((size_t)v >= sizeof(mx_hole_closed_location_values) / sizeof(mx_hole_closed_location_values[0])) + return mx_hole_closed_location_values[0]; + return mx_hole_closed_location_values[v]; +} diff --git a/gen/test/c/mx/mx_hole_closed_location.h b/gen/test/c/mx/mx_hole_closed_location.h new file mode 100644 index 000000000..c7ee5fca9 --- /dev/null +++ b/gen/test/c/mx/mx_hole_closed_location.h @@ -0,0 +1,25 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_HOLE_CLOSED_LOCATION_H +#define MX_HOLE_CLOSED_LOCATION_H + +#include + +/* + * The hole-closed-location type indicates which portion of the hole is filled in when the + * corresponding hole-closed-value is half. + */ +typedef enum { + MX_HOLE_CLOSED_LOCATION_RIGHT = 0, + MX_HOLE_CLOSED_LOCATION_BOTTOM = 1, + MX_HOLE_CLOSED_LOCATION_LEFT = 2, + MX_HOLE_CLOSED_LOCATION_TOP = 3 +} MxHoleClosedLocation; + +bool mx_hole_closed_location_try_parse(const char *s, MxHoleClosedLocation *out); +/* Lenient: unknown input falls back to the first variant. */ +MxHoleClosedLocation mx_hole_closed_location_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_hole_closed_location_to_string(MxHoleClosedLocation v); + +#endif /* MX_HOLE_CLOSED_LOCATION_H */ diff --git a/gen/test/c/mx/mx_hole_closed_value.c b/gen/test/c/mx/mx_hole_closed_value.c new file mode 100644 index 000000000..34734401e --- /dev/null +++ b/gen/test/c/mx/mx_hole_closed_value.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_hole_closed_value.h" + +#include + +static const char *const mx_hole_closed_value_values[] = { + "yes", + "no", + "half", +}; + +bool mx_hole_closed_value_try_parse(const char *s, MxHoleClosedValue *out) { + for (size_t i = 0; i < sizeof(mx_hole_closed_value_values) / sizeof(mx_hole_closed_value_values[0]); i++) { + if (strcmp(s, mx_hole_closed_value_values[i]) == 0) { + *out = (MxHoleClosedValue)i; + return true; + } + } + *out = (MxHoleClosedValue)0; + return false; +} + +MxHoleClosedValue mx_hole_closed_value_parse(const char *s) { + MxHoleClosedValue v; + mx_hole_closed_value_try_parse(s, &v); + return v; +} + +const char *mx_hole_closed_value_to_string(MxHoleClosedValue v) { + if ((size_t)v >= sizeof(mx_hole_closed_value_values) / sizeof(mx_hole_closed_value_values[0])) + return mx_hole_closed_value_values[0]; + return mx_hole_closed_value_values[v]; +} diff --git a/gen/test/c/mx/mx_hole_closed_value.h b/gen/test/c/mx/mx_hole_closed_value.h new file mode 100644 index 000000000..e94c3ebac --- /dev/null +++ b/gen/test/c/mx/mx_hole_closed_value.h @@ -0,0 +1,23 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_HOLE_CLOSED_VALUE_H +#define MX_HOLE_CLOSED_VALUE_H + +#include + +/* + * The hole-closed-value type represents whether the hole is closed, open, or half-open. + */ +typedef enum { + MX_HOLE_CLOSED_VALUE_YES = 0, + MX_HOLE_CLOSED_VALUE_NO = 1, + MX_HOLE_CLOSED_VALUE_HALF = 2 +} MxHoleClosedValue; + +bool mx_hole_closed_value_try_parse(const char *s, MxHoleClosedValue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxHoleClosedValue mx_hole_closed_value_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_hole_closed_value_to_string(MxHoleClosedValue v); + +#endif /* MX_HOLE_CLOSED_VALUE_H */ diff --git a/gen/test/c/mx/mx_instrument_sound.c b/gen/test/c/mx/mx_instrument_sound.c new file mode 100644 index 000000000..194581201 --- /dev/null +++ b/gen/test/c/mx/mx_instrument_sound.c @@ -0,0 +1,49 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_instrument_sound.h" + +#include "mx_runtime.h" +#include +#include + +bool mx_instrument_sound_try_parse(const char *s, MxInstrumentSound *out) { + MxInstrumentSound v; + memset(&v, 0, sizeof(v)); + if (mx_sound_id_try_parse(s, &v.sound_id)) { + v.kind = MX_INSTRUMENT_SOUND_KIND_SOUND_ID; + *out = v; + return true; + } + v.kind = MX_INSTRUMENT_SOUND_KIND_STRING; + v.string = mx_strdup(s); + *out = v; + return true; +} + +MxInstrumentSound mx_instrument_sound_parse(const char *s) { + MxInstrumentSound v; + if (mx_instrument_sound_try_parse(s, &v)) + return v; + memset(&v, 0, sizeof(v)); + v.kind = MX_INSTRUMENT_SOUND_KIND_SOUND_ID; + v.sound_id = mx_sound_id_parse(s); + return v; +} + +char *mx_instrument_sound_to_string(MxInstrumentSound v) { + switch (v.kind) { + case MX_INSTRUMENT_SOUND_KIND_STRING: + return mx_strdup(v.string); + default: + return mx_strdup(mx_sound_id_to_string(v.sound_id)); + } +} + +void mx_instrument_sound_free(MxInstrumentSound *v) { + if (!v) + return; + if (v->kind == MX_INSTRUMENT_SOUND_KIND_STRING) { + free(v->string); + v->string = NULL; + } +} diff --git a/gen/test/c/mx/mx_instrument_sound.h b/gen/test/c/mx/mx_instrument_sound.h new file mode 100644 index 000000000..ce2e612a7 --- /dev/null +++ b/gen/test/c/mx/mx_instrument_sound.h @@ -0,0 +1,32 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_INSTRUMENT_SOUND_H +#define MX_INSTRUMENT_SOUND_H + +#include +#include "mx_sound_id.h" + +/* + * The instrument-sound value: one of the standard sound-id identifiers, or any other string. The + * schema leaves the content open (xs:string), so the string member is intrinsic, not a fallback. + */ +typedef enum { + MX_INSTRUMENT_SOUND_KIND_SOUND_ID = 0, + MX_INSTRUMENT_SOUND_KIND_STRING = 1 +} MxInstrumentSoundKind; + +typedef struct { + MxInstrumentSoundKind kind; + MxSoundID sound_id; + char *string; +} MxInstrumentSound; + +/* Tries each union member in schema order. */ +bool mx_instrument_sound_try_parse(const char *s, MxInstrumentSound *out); +/* Lenient: an unmatched input is absorbed by the first member. */ +MxInstrumentSound mx_instrument_sound_parse(const char *s); +/* The wire spelling of whichever member is held. Malloc'd. */ +char *mx_instrument_sound_to_string(MxInstrumentSound v); +void mx_instrument_sound_free(MxInstrumentSound *v); + +#endif /* MX_INSTRUMENT_SOUND_H */ diff --git a/gen/test/c/mx/mx_kind_value.c b/gen/test/c/mx/mx_kind_value.c new file mode 100644 index 000000000..57ff1cfca --- /dev/null +++ b/gen/test/c/mx/mx_kind_value.c @@ -0,0 +1,64 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_kind_value.h" + +#include + +static const char *const mx_kind_value_values[] = { + "major", + "minor", + "augmented", + "diminished", + "dominant", + "major-seventh", + "minor-seventh", + "diminished-seventh", + "augmented-seventh", + "half-diminished", + "major-minor", + "major-sixth", + "minor-sixth", + "dominant-ninth", + "major-ninth", + "minor-ninth", + "dominant-11th", + "major-11th", + "minor-11th", + "dominant-13th", + "major-13th", + "minor-13th", + "suspended-second", + "suspended-fourth", + "Neapolitan", + "Italian", + "French", + "German", + "pedal", + "power", + "Tristan", + "other", + "none", +}; + +bool mx_kind_value_try_parse(const char *s, MxKindValue *out) { + for (size_t i = 0; i < sizeof(mx_kind_value_values) / sizeof(mx_kind_value_values[0]); i++) { + if (strcmp(s, mx_kind_value_values[i]) == 0) { + *out = (MxKindValue)i; + return true; + } + } + *out = (MxKindValue)0; + return false; +} + +MxKindValue mx_kind_value_parse(const char *s) { + MxKindValue v; + mx_kind_value_try_parse(s, &v); + return v; +} + +const char *mx_kind_value_to_string(MxKindValue v) { + if ((size_t)v >= sizeof(mx_kind_value_values) / sizeof(mx_kind_value_values[0])) + return mx_kind_value_values[0]; + return mx_kind_value_values[v]; +} diff --git a/gen/test/c/mx/mx_kind_value.h b/gen/test/c/mx/mx_kind_value.h new file mode 100644 index 000000000..1c824396c --- /dev/null +++ b/gen/test/c/mx/mx_kind_value.h @@ -0,0 +1,69 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_KIND_VALUE_H +#define MX_KIND_VALUE_H + +#include + +/* + * A kind-value indicates the type of chord. Degree elements can then add, subtract, or alter from + * these starting points. Values include: Triads: major (major third, perfect fifth) minor (minor + * third, perfect fifth) augmented (major third, augmented fifth) diminished (minor third, + * diminished fifth) Sevenths: dominant (major triad, minor seventh) major-seventh (major triad, + * major seventh) minor-seventh (minor triad, minor seventh) diminished-seventh (diminished triad, + * diminished seventh) augmented-seventh (augmented triad, minor seventh) half-diminished + * (diminished triad, minor seventh) major-minor (minor triad, major seventh) Sixths: major-sixth + * (major triad, added sixth) minor-sixth (minor triad, added sixth) Ninths: dominant-ninth + * (dominant-seventh, major ninth) major-ninth (major-seventh, major ninth) minor-ninth + * (minor-seventh, major ninth) 11ths (usually as the basis for alteration): dominant-11th + * (dominant-ninth, perfect 11th) major-11th (major-ninth, perfect 11th) minor-11th (minor-ninth, + * perfect 11th) 13ths (usually as the basis for alteration): dominant-13th (dominant-11th, major + * 13th) major-13th (major-11th, major 13th) minor-13th (minor-11th, major 13th) Suspended: + * suspended-second (major second, perfect fifth) suspended-fourth (perfect fourth, perfect fifth) + * Functional sixths: Neapolitan Italian French German Other: pedal (pedal-point bass) power + * (perfect fifth) Tristan The "other" kind is used when the harmony is entirely composed of add + * elements. The "none" kind is used to explicitly encode absence of chords or functional harmony. + */ +typedef enum { + MX_KIND_VALUE_MAJOR = 0, + MX_KIND_VALUE_MINOR = 1, + MX_KIND_VALUE_AUGMENTED = 2, + MX_KIND_VALUE_DIMINISHED = 3, + MX_KIND_VALUE_DOMINANT = 4, + MX_KIND_VALUE_MAJOR_SEVENTH = 5, + MX_KIND_VALUE_MINOR_SEVENTH = 6, + MX_KIND_VALUE_DIMINISHED_SEVENTH = 7, + MX_KIND_VALUE_AUGMENTED_SEVENTH = 8, + MX_KIND_VALUE_HALF_DIMINISHED = 9, + MX_KIND_VALUE_MAJOR_MINOR = 10, + MX_KIND_VALUE_MAJOR_SIXTH = 11, + MX_KIND_VALUE_MINOR_SIXTH = 12, + MX_KIND_VALUE_DOMINANT_NINTH = 13, + MX_KIND_VALUE_MAJOR_NINTH = 14, + MX_KIND_VALUE_MINOR_NINTH = 15, + MX_KIND_VALUE_DOMINANT_11TH = 16, + MX_KIND_VALUE_MAJOR_11TH = 17, + MX_KIND_VALUE_MINOR_11TH = 18, + MX_KIND_VALUE_DOMINANT_13TH = 19, + MX_KIND_VALUE_MAJOR_13TH = 20, + MX_KIND_VALUE_MINOR_13TH = 21, + MX_KIND_VALUE_SUSPENDED_SECOND = 22, + MX_KIND_VALUE_SUSPENDED_FOURTH = 23, + MX_KIND_VALUE_NEAPOLITAN = 24, + MX_KIND_VALUE_ITALIAN = 25, + MX_KIND_VALUE_FRENCH = 26, + MX_KIND_VALUE_GERMAN = 27, + MX_KIND_VALUE_PEDAL = 28, + MX_KIND_VALUE_POWER = 29, + MX_KIND_VALUE_TRISTAN = 30, + MX_KIND_VALUE_OTHER = 31, + MX_KIND_VALUE_NONE = 32 +} MxKindValue; + +bool mx_kind_value_try_parse(const char *s, MxKindValue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxKindValue mx_kind_value_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_kind_value_to_string(MxKindValue v); + +#endif /* MX_KIND_VALUE_H */ diff --git a/gen/test/c/mx/mx_left_center_right.c b/gen/test/c/mx/mx_left_center_right.c new file mode 100644 index 000000000..809a14e52 --- /dev/null +++ b/gen/test/c/mx/mx_left_center_right.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_left_center_right.h" + +#include + +static const char *const mx_left_center_right_values[] = { + "left", + "center", + "right", +}; + +bool mx_left_center_right_try_parse(const char *s, MxLeftCenterRight *out) { + for (size_t i = 0; i < sizeof(mx_left_center_right_values) / sizeof(mx_left_center_right_values[0]); i++) { + if (strcmp(s, mx_left_center_right_values[i]) == 0) { + *out = (MxLeftCenterRight)i; + return true; + } + } + *out = (MxLeftCenterRight)0; + return false; +} + +MxLeftCenterRight mx_left_center_right_parse(const char *s) { + MxLeftCenterRight v; + mx_left_center_right_try_parse(s, &v); + return v; +} + +const char *mx_left_center_right_to_string(MxLeftCenterRight v) { + if ((size_t)v >= sizeof(mx_left_center_right_values) / sizeof(mx_left_center_right_values[0])) + return mx_left_center_right_values[0]; + return mx_left_center_right_values[v]; +} diff --git a/gen/test/c/mx/mx_left_center_right.h b/gen/test/c/mx/mx_left_center_right.h new file mode 100644 index 000000000..deb8ec41c --- /dev/null +++ b/gen/test/c/mx/mx_left_center_right.h @@ -0,0 +1,23 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_LEFT_CENTER_RIGHT_H +#define MX_LEFT_CENTER_RIGHT_H + +#include + +/* + * The left-center-right type is used to define horizontal alignment and text justification. + */ +typedef enum { + MX_LEFT_CENTER_RIGHT_LEFT = 0, + MX_LEFT_CENTER_RIGHT_CENTER = 1, + MX_LEFT_CENTER_RIGHT_RIGHT = 2 +} MxLeftCenterRight; + +bool mx_left_center_right_try_parse(const char *s, MxLeftCenterRight *out); +/* Lenient: unknown input falls back to the first variant. */ +MxLeftCenterRight mx_left_center_right_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_left_center_right_to_string(MxLeftCenterRight v); + +#endif /* MX_LEFT_CENTER_RIGHT_H */ diff --git a/gen/test/c/mx/mx_left_right.c b/gen/test/c/mx/mx_left_right.c new file mode 100644 index 000000000..632636d84 --- /dev/null +++ b/gen/test/c/mx/mx_left_right.c @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_left_right.h" + +#include + +static const char *const mx_left_right_values[] = { + "left", + "right", +}; + +bool mx_left_right_try_parse(const char *s, MxLeftRight *out) { + for (size_t i = 0; i < sizeof(mx_left_right_values) / sizeof(mx_left_right_values[0]); i++) { + if (strcmp(s, mx_left_right_values[i]) == 0) { + *out = (MxLeftRight)i; + return true; + } + } + *out = (MxLeftRight)0; + return false; +} + +MxLeftRight mx_left_right_parse(const char *s) { + MxLeftRight v; + mx_left_right_try_parse(s, &v); + return v; +} + +const char *mx_left_right_to_string(MxLeftRight v) { + if ((size_t)v >= sizeof(mx_left_right_values) / sizeof(mx_left_right_values[0])) + return mx_left_right_values[0]; + return mx_left_right_values[v]; +} diff --git a/gen/test/c/mx/mx_left_right.h b/gen/test/c/mx/mx_left_right.h new file mode 100644 index 000000000..bf5a3339e --- /dev/null +++ b/gen/test/c/mx/mx_left_right.h @@ -0,0 +1,23 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_LEFT_RIGHT_H +#define MX_LEFT_RIGHT_H + +#include + +/* + * The left-right type is used to indicate whether one element appears to the left or the right of + * another element. + */ +typedef enum { + MX_LEFT_RIGHT_LEFT = 0, + MX_LEFT_RIGHT_RIGHT = 1 +} MxLeftRight; + +bool mx_left_right_try_parse(const char *s, MxLeftRight *out); +/* Lenient: unknown input falls back to the first variant. */ +MxLeftRight mx_left_right_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_left_right_to_string(MxLeftRight v); + +#endif /* MX_LEFT_RIGHT_H */ diff --git a/gen/test/c/mx/mx_line_end.c b/gen/test/c/mx/mx_line_end.c new file mode 100644 index 000000000..1530b8a7f --- /dev/null +++ b/gen/test/c/mx/mx_line_end.c @@ -0,0 +1,36 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_line_end.h" + +#include + +static const char *const mx_line_end_values[] = { + "up", + "down", + "both", + "arrow", + "none", +}; + +bool mx_line_end_try_parse(const char *s, MxLineEnd *out) { + for (size_t i = 0; i < sizeof(mx_line_end_values) / sizeof(mx_line_end_values[0]); i++) { + if (strcmp(s, mx_line_end_values[i]) == 0) { + *out = (MxLineEnd)i; + return true; + } + } + *out = (MxLineEnd)0; + return false; +} + +MxLineEnd mx_line_end_parse(const char *s) { + MxLineEnd v; + mx_line_end_try_parse(s, &v); + return v; +} + +const char *mx_line_end_to_string(MxLineEnd v) { + if ((size_t)v >= sizeof(mx_line_end_values) / sizeof(mx_line_end_values[0])) + return mx_line_end_values[0]; + return mx_line_end_values[v]; +} diff --git a/gen/test/c/mx/mx_line_end.h b/gen/test/c/mx/mx_line_end.h new file mode 100644 index 000000000..f2887e216 --- /dev/null +++ b/gen/test/c/mx/mx_line_end.h @@ -0,0 +1,26 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_LINE_END_H +#define MX_LINE_END_H + +#include + +/* + * The line-end type specifies if there is a jog up or down (or both), an arrow, or nothing at the + * start or end of a bracket. + */ +typedef enum { + MX_LINE_END_UP = 0, + MX_LINE_END_DOWN = 1, + MX_LINE_END_BOTH = 2, + MX_LINE_END_ARROW = 3, + MX_LINE_END_NONE = 4 +} MxLineEnd; + +bool mx_line_end_try_parse(const char *s, MxLineEnd *out); +/* Lenient: unknown input falls back to the first variant. */ +MxLineEnd mx_line_end_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_line_end_to_string(MxLineEnd v); + +#endif /* MX_LINE_END_H */ diff --git a/gen/test/c/mx/mx_line_length.c b/gen/test/c/mx/mx_line_length.c new file mode 100644 index 000000000..a321a232d --- /dev/null +++ b/gen/test/c/mx/mx_line_length.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_line_length.h" + +#include + +static const char *const mx_line_length_values[] = { + "short", + "medium", + "long", +}; + +bool mx_line_length_try_parse(const char *s, MxLineLength *out) { + for (size_t i = 0; i < sizeof(mx_line_length_values) / sizeof(mx_line_length_values[0]); i++) { + if (strcmp(s, mx_line_length_values[i]) == 0) { + *out = (MxLineLength)i; + return true; + } + } + *out = (MxLineLength)0; + return false; +} + +MxLineLength mx_line_length_parse(const char *s) { + MxLineLength v; + mx_line_length_try_parse(s, &v); + return v; +} + +const char *mx_line_length_to_string(MxLineLength v) { + if ((size_t)v >= sizeof(mx_line_length_values) / sizeof(mx_line_length_values[0])) + return mx_line_length_values[0]; + return mx_line_length_values[v]; +} diff --git a/gen/test/c/mx/mx_line_length.h b/gen/test/c/mx/mx_line_length.h new file mode 100644 index 000000000..3a3fc81a9 --- /dev/null +++ b/gen/test/c/mx/mx_line_length.h @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_LINE_LENGTH_H +#define MX_LINE_LENGTH_H + +#include + +/* + * The line-length type distinguishes between different line lengths for doit, falloff, plop, and + * scoop articulations. + */ +typedef enum { + MX_LINE_LENGTH_SHORT = 0, + MX_LINE_LENGTH_MEDIUM = 1, + MX_LINE_LENGTH_LONG = 2 +} MxLineLength; + +bool mx_line_length_try_parse(const char *s, MxLineLength *out); +/* Lenient: unknown input falls back to the first variant. */ +MxLineLength mx_line_length_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_line_length_to_string(MxLineLength v); + +#endif /* MX_LINE_LENGTH_H */ diff --git a/gen/test/c/mx/mx_line_shape.c b/gen/test/c/mx/mx_line_shape.c new file mode 100644 index 000000000..bf0c04859 --- /dev/null +++ b/gen/test/c/mx/mx_line_shape.c @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_line_shape.h" + +#include + +static const char *const mx_line_shape_values[] = { + "straight", + "curved", +}; + +bool mx_line_shape_try_parse(const char *s, MxLineShape *out) { + for (size_t i = 0; i < sizeof(mx_line_shape_values) / sizeof(mx_line_shape_values[0]); i++) { + if (strcmp(s, mx_line_shape_values[i]) == 0) { + *out = (MxLineShape)i; + return true; + } + } + *out = (MxLineShape)0; + return false; +} + +MxLineShape mx_line_shape_parse(const char *s) { + MxLineShape v; + mx_line_shape_try_parse(s, &v); + return v; +} + +const char *mx_line_shape_to_string(MxLineShape v) { + if ((size_t)v >= sizeof(mx_line_shape_values) / sizeof(mx_line_shape_values[0])) + return mx_line_shape_values[0]; + return mx_line_shape_values[v]; +} diff --git a/gen/test/c/mx/mx_line_shape.h b/gen/test/c/mx/mx_line_shape.h new file mode 100644 index 000000000..116fc7880 --- /dev/null +++ b/gen/test/c/mx/mx_line_shape.h @@ -0,0 +1,22 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_LINE_SHAPE_H +#define MX_LINE_SHAPE_H + +#include + +/* + * The line-shape type distinguishes between straight and curved lines. + */ +typedef enum { + MX_LINE_SHAPE_STRAIGHT = 0, + MX_LINE_SHAPE_CURVED = 1 +} MxLineShape; + +bool mx_line_shape_try_parse(const char *s, MxLineShape *out); +/* Lenient: unknown input falls back to the first variant. */ +MxLineShape mx_line_shape_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_line_shape_to_string(MxLineShape v); + +#endif /* MX_LINE_SHAPE_H */ diff --git a/gen/test/c/mx/mx_line_type.c b/gen/test/c/mx/mx_line_type.c new file mode 100644 index 000000000..8fe7a07c6 --- /dev/null +++ b/gen/test/c/mx/mx_line_type.c @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_line_type.h" + +#include + +static const char *const mx_line_type_values[] = { + "solid", + "dashed", + "dotted", + "wavy", +}; + +bool mx_line_type_try_parse(const char *s, MxLineType *out) { + for (size_t i = 0; i < sizeof(mx_line_type_values) / sizeof(mx_line_type_values[0]); i++) { + if (strcmp(s, mx_line_type_values[i]) == 0) { + *out = (MxLineType)i; + return true; + } + } + *out = (MxLineType)0; + return false; +} + +MxLineType mx_line_type_parse(const char *s) { + MxLineType v; + mx_line_type_try_parse(s, &v); + return v; +} + +const char *mx_line_type_to_string(MxLineType v) { + if ((size_t)v >= sizeof(mx_line_type_values) / sizeof(mx_line_type_values[0])) + return mx_line_type_values[0]; + return mx_line_type_values[v]; +} diff --git a/gen/test/c/mx/mx_line_type.h b/gen/test/c/mx/mx_line_type.h new file mode 100644 index 000000000..266ed2f28 --- /dev/null +++ b/gen/test/c/mx/mx_line_type.h @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_LINE_TYPE_H +#define MX_LINE_TYPE_H + +#include + +/* + * The line-type type distinguishes between solid, dashed, dotted, and wavy lines. + */ +typedef enum { + MX_LINE_TYPE_SOLID = 0, + MX_LINE_TYPE_DASHED = 1, + MX_LINE_TYPE_DOTTED = 2, + MX_LINE_TYPE_WAVY = 3 +} MxLineType; + +bool mx_line_type_try_parse(const char *s, MxLineType *out); +/* Lenient: unknown input falls back to the first variant. */ +MxLineType mx_line_type_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_line_type_to_string(MxLineType v); + +#endif /* MX_LINE_TYPE_H */ diff --git a/gen/test/c/mx/mx_line_width_type.c b/gen/test/c/mx/mx_line_width_type.c new file mode 100644 index 000000000..8a838e034 --- /dev/null +++ b/gen/test/c/mx/mx_line_width_type.c @@ -0,0 +1,9 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_line_width_type.h" + +#include "mx_runtime.h" + +MxLineWidthType mx_line_width_type_parse(const char *s) { + return mx_strdup(s); +} diff --git a/gen/test/c/mx/mx_line_width_type.h b/gen/test/c/mx/mx_line_width_type.h new file mode 100644 index 000000000..2e052c202 --- /dev/null +++ b/gen/test/c/mx/mx_line_width_type.h @@ -0,0 +1,18 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_LINE_WIDTH_TYPE_H +#define MX_LINE_WIDTH_TYPE_H + +/* + * The line-width-type defines what type of line is being defined in a line-width element. Values + * include beam, bracket, dashes, enclosure, ending, extend, heavy barline, leger, light barline, + * octave shift, pedal, slur middle, slur tip, staff, stem, tie middle, tie tip, tuplet bracket, and + * wedge. This is left as a string so that other application-specific types can be defined, but it + * is made a separate type so that it can be redefined more strictly. + */ +typedef char *MxLineWidthType; + +/* Malloc'd copy of the wire string; the value IS its spelling. */ +MxLineWidthType mx_line_width_type_parse(const char *s); + +#endif /* MX_LINE_WIDTH_TYPE_H */ diff --git a/gen/test/c/mx/mx_margin_type.c b/gen/test/c/mx/mx_margin_type.c new file mode 100644 index 000000000..ae672ccc5 --- /dev/null +++ b/gen/test/c/mx/mx_margin_type.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_margin_type.h" + +#include + +static const char *const mx_margin_type_values[] = { + "odd", + "even", + "both", +}; + +bool mx_margin_type_try_parse(const char *s, MxMarginType *out) { + for (size_t i = 0; i < sizeof(mx_margin_type_values) / sizeof(mx_margin_type_values[0]); i++) { + if (strcmp(s, mx_margin_type_values[i]) == 0) { + *out = (MxMarginType)i; + return true; + } + } + *out = (MxMarginType)0; + return false; +} + +MxMarginType mx_margin_type_parse(const char *s) { + MxMarginType v; + mx_margin_type_try_parse(s, &v); + return v; +} + +const char *mx_margin_type_to_string(MxMarginType v) { + if ((size_t)v >= sizeof(mx_margin_type_values) / sizeof(mx_margin_type_values[0])) + return mx_margin_type_values[0]; + return mx_margin_type_values[v]; +} diff --git a/gen/test/c/mx/mx_margin_type.h b/gen/test/c/mx/mx_margin_type.h new file mode 100644 index 000000000..970b1c117 --- /dev/null +++ b/gen/test/c/mx/mx_margin_type.h @@ -0,0 +1,23 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_MARGIN_TYPE_H +#define MX_MARGIN_TYPE_H + +#include + +/* + * The margin-type type specifies whether margins apply to even page, odd pages, or both. + */ +typedef enum { + MX_MARGIN_TYPE_ODD = 0, + MX_MARGIN_TYPE_EVEN = 1, + MX_MARGIN_TYPE_BOTH = 2 +} MxMarginType; + +bool mx_margin_type_try_parse(const char *s, MxMarginType *out); +/* Lenient: unknown input falls back to the first variant. */ +MxMarginType mx_margin_type_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_margin_type_to_string(MxMarginType v); + +#endif /* MX_MARGIN_TYPE_H */ diff --git a/gen/test/c/mx/mx_measure_numbering_value.c b/gen/test/c/mx/mx_measure_numbering_value.c new file mode 100644 index 000000000..a38af158b --- /dev/null +++ b/gen/test/c/mx/mx_measure_numbering_value.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_measure_numbering_value.h" + +#include + +static const char *const mx_measure_numbering_value_values[] = { + "none", + "measure", + "system", +}; + +bool mx_measure_numbering_value_try_parse(const char *s, MxMeasureNumberingValue *out) { + for (size_t i = 0; i < sizeof(mx_measure_numbering_value_values) / sizeof(mx_measure_numbering_value_values[0]); i++) { + if (strcmp(s, mx_measure_numbering_value_values[i]) == 0) { + *out = (MxMeasureNumberingValue)i; + return true; + } + } + *out = (MxMeasureNumberingValue)0; + return false; +} + +MxMeasureNumberingValue mx_measure_numbering_value_parse(const char *s) { + MxMeasureNumberingValue v; + mx_measure_numbering_value_try_parse(s, &v); + return v; +} + +const char *mx_measure_numbering_value_to_string(MxMeasureNumberingValue v) { + if ((size_t)v >= sizeof(mx_measure_numbering_value_values) / sizeof(mx_measure_numbering_value_values[0])) + return mx_measure_numbering_value_values[0]; + return mx_measure_numbering_value_values[v]; +} diff --git a/gen/test/c/mx/mx_measure_numbering_value.h b/gen/test/c/mx/mx_measure_numbering_value.h new file mode 100644 index 000000000..911fb6dce --- /dev/null +++ b/gen/test/c/mx/mx_measure_numbering_value.h @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_MEASURE_NUMBERING_VALUE_H +#define MX_MEASURE_NUMBERING_VALUE_H + +#include + +/* + * The measure-numbering-value type describes how measure numbers are displayed on this part: no + * numbers, numbers every measure, or numbers every system. + */ +typedef enum { + MX_MEASURE_NUMBERING_VALUE_NONE = 0, + MX_MEASURE_NUMBERING_VALUE_MEASURE = 1, + MX_MEASURE_NUMBERING_VALUE_SYSTEM = 2 +} MxMeasureNumberingValue; + +bool mx_measure_numbering_value_try_parse(const char *s, MxMeasureNumberingValue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxMeasureNumberingValue mx_measure_numbering_value_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_measure_numbering_value_to_string(MxMeasureNumberingValue v); + +#endif /* MX_MEASURE_NUMBERING_VALUE_H */ diff --git a/gen/test/c/mx/mx_measure_text.c b/gen/test/c/mx/mx_measure_text.c new file mode 100644 index 000000000..4e942f8c0 --- /dev/null +++ b/gen/test/c/mx/mx_measure_text.c @@ -0,0 +1,9 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_measure_text.h" + +#include "mx_runtime.h" + +MxMeasureText mx_measure_text_parse(const char *s) { + return mx_strdup(s); +} diff --git a/gen/test/c/mx/mx_measure_text.h b/gen/test/c/mx/mx_measure_text.h new file mode 100644 index 000000000..ff1452ed9 --- /dev/null +++ b/gen/test/c/mx/mx_measure_text.h @@ -0,0 +1,16 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_MEASURE_TEXT_H +#define MX_MEASURE_TEXT_H + +/* + * The measure-text type is used for the text attribute of measure elements. It has at least one + * character. The implicit attribute of the measure element should be set to "yes" rather than + * setting the text attribute to an empty string. + */ +typedef char *MxMeasureText; + +/* Malloc'd copy of the wire string; the value IS its spelling. */ +MxMeasureText mx_measure_text_parse(const char *s); + +#endif /* MX_MEASURE_TEXT_H */ diff --git a/gen/test/c/mx/mx_membrane.c b/gen/test/c/mx/mx_membrane.c new file mode 100644 index 000000000..7c6f78fb6 --- /dev/null +++ b/gen/test/c/mx/mx_membrane.c @@ -0,0 +1,48 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_membrane.h" + +#include + +static const char *const mx_membrane_values[] = { + "bass drum", + "bass drum on side", + "bongos", + "Chinese tomtom", + "conga drum", + "cuica", + "goblet drum", + "Indo-American tomtom", + "Japanese tomtom", + "military drum", + "snare drum", + "snare drum snares off", + "tabla", + "tambourine", + "tenor drum", + "timbales", + "tomtom", +}; + +bool mx_membrane_try_parse(const char *s, MxMembrane *out) { + for (size_t i = 0; i < sizeof(mx_membrane_values) / sizeof(mx_membrane_values[0]); i++) { + if (strcmp(s, mx_membrane_values[i]) == 0) { + *out = (MxMembrane)i; + return true; + } + } + *out = (MxMembrane)0; + return false; +} + +MxMembrane mx_membrane_parse(const char *s) { + MxMembrane v; + mx_membrane_try_parse(s, &v); + return v; +} + +const char *mx_membrane_to_string(MxMembrane v) { + if ((size_t)v >= sizeof(mx_membrane_values) / sizeof(mx_membrane_values[0])) + return mx_membrane_values[0]; + return mx_membrane_values[v]; +} diff --git a/gen/test/c/mx/mx_membrane.h b/gen/test/c/mx/mx_membrane.h new file mode 100644 index 000000000..a9f2c0134 --- /dev/null +++ b/gen/test/c/mx/mx_membrane.h @@ -0,0 +1,37 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_MEMBRANE_H +#define MX_MEMBRANE_H + +#include + +/* + * The membrane type represents pictograms for membrane percussion instruments. + */ +typedef enum { + MX_MEMBRANE_BASS_DRUM = 0, + MX_MEMBRANE_BASS_DRUM_ON_SIDE = 1, + MX_MEMBRANE_BONGOS = 2, + MX_MEMBRANE_CHINESE_TOMTOM = 3, + MX_MEMBRANE_CONGA_DRUM = 4, + MX_MEMBRANE_CUICA = 5, + MX_MEMBRANE_GOBLET_DRUM = 6, + MX_MEMBRANE_INDO_AMERICAN_TOMTOM = 7, + MX_MEMBRANE_JAPANESE_TOMTOM = 8, + MX_MEMBRANE_MILITARY_DRUM = 9, + MX_MEMBRANE_SNARE_DRUM = 10, + MX_MEMBRANE_SNARE_DRUM_SNARES_OFF = 11, + MX_MEMBRANE_TABLA = 12, + MX_MEMBRANE_TAMBOURINE = 13, + MX_MEMBRANE_TENOR_DRUM = 14, + MX_MEMBRANE_TIMBALES = 15, + MX_MEMBRANE_TOMTOM = 16 +} MxMembrane; + +bool mx_membrane_try_parse(const char *s, MxMembrane *out); +/* Lenient: unknown input falls back to the first variant. */ +MxMembrane mx_membrane_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_membrane_to_string(MxMembrane v); + +#endif /* MX_MEMBRANE_H */ diff --git a/gen/test/c/mx/mx_metal.c b/gen/test/c/mx/mx_metal.c new file mode 100644 index 000000000..29ebb5b05 --- /dev/null +++ b/gen/test/c/mx/mx_metal.c @@ -0,0 +1,63 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_metal.h" + +#include + +static const char *const mx_metal_values[] = { + "agogo", + "almglocken", + "bell", + "bell plate", + "bell tree", + "brake drum", + "cencerro", + "chain rattle", + "Chinese cymbal", + "cowbell", + "crash cymbals", + "crotale", + "cymbal tongs", + "domed gong", + "finger cymbals", + "flexatone", + "gong", + "hi-hat", + "high-hat cymbals", + "handbell", + "jaw harp", + "jingle bells", + "musical saw", + "shell bells", + "sistrum", + "sizzle cymbal", + "sleigh bells", + "suspended cymbal", + "tam tam", + "tam tam with beater", + "triangle", + "Vietnamese hat", +}; + +bool mx_metal_try_parse(const char *s, MxMetal *out) { + for (size_t i = 0; i < sizeof(mx_metal_values) / sizeof(mx_metal_values[0]); i++) { + if (strcmp(s, mx_metal_values[i]) == 0) { + *out = (MxMetal)i; + return true; + } + } + *out = (MxMetal)0; + return false; +} + +MxMetal mx_metal_parse(const char *s) { + MxMetal v; + mx_metal_try_parse(s, &v); + return v; +} + +const char *mx_metal_to_string(MxMetal v) { + if ((size_t)v >= sizeof(mx_metal_values) / sizeof(mx_metal_values[0])) + return mx_metal_values[0]; + return mx_metal_values[v]; +} diff --git a/gen/test/c/mx/mx_metal.h b/gen/test/c/mx/mx_metal.h new file mode 100644 index 000000000..f7d07d157 --- /dev/null +++ b/gen/test/c/mx/mx_metal.h @@ -0,0 +1,53 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_METAL_H +#define MX_METAL_H + +#include + +/* + * The metal type represents pictograms for metal percussion instruments. The hi-hat value refers to + * a pictogram like Stone's high-hat cymbals but without the long vertical line at the bottom. + */ +typedef enum { + MX_METAL_AGOGO = 0, + MX_METAL_ALMGLOCKEN = 1, + MX_METAL_BELL = 2, + MX_METAL_BELL_PLATE = 3, + MX_METAL_BELL_TREE = 4, + MX_METAL_BRAKE_DRUM = 5, + MX_METAL_CENCERRO = 6, + MX_METAL_CHAIN_RATTLE = 7, + MX_METAL_CHINESE_CYMBAL = 8, + MX_METAL_COWBELL = 9, + MX_METAL_CRASH_CYMBALS = 10, + MX_METAL_CROTALE = 11, + MX_METAL_CYMBAL_TONGS = 12, + MX_METAL_DOMED_GONG = 13, + MX_METAL_FINGER_CYMBALS = 14, + MX_METAL_FLEXATONE = 15, + MX_METAL_GONG = 16, + MX_METAL_HI_HAT = 17, + MX_METAL_HIGH_HAT_CYMBALS = 18, + MX_METAL_HANDBELL = 19, + MX_METAL_JAW_HARP = 20, + MX_METAL_JINGLE_BELLS = 21, + MX_METAL_MUSICAL_SAW = 22, + MX_METAL_SHELL_BELLS = 23, + MX_METAL_SISTRUM = 24, + MX_METAL_SIZZLE_CYMBAL = 25, + MX_METAL_SLEIGH_BELLS = 26, + MX_METAL_SUSPENDED_CYMBAL = 27, + MX_METAL_TAM_TAM = 28, + MX_METAL_TAM_TAM_WITH_BEATER = 29, + MX_METAL_TRIANGLE = 30, + MX_METAL_VIETNAMESE_HAT = 31 +} MxMetal; + +bool mx_metal_try_parse(const char *s, MxMetal *out); +/* Lenient: unknown input falls back to the first variant. */ +MxMetal mx_metal_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_metal_to_string(MxMetal v); + +#endif /* MX_METAL_H */ diff --git a/gen/test/c/mx/mx_midi_128.c b/gen/test/c/mx/mx_midi_128.c new file mode 100644 index 000000000..8ae7edc1d --- /dev/null +++ b/gen/test/c/mx/mx_midi_128.c @@ -0,0 +1,32 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_midi_128.h" + +#include "mx_runtime.h" + +static MxMIDI128 mx_midi_128_clamp(long v) { + if (v < 1) + v = 1; + if (v > 128) + v = 128; + return (MxMIDI128)v; +} + +bool mx_midi_128_try_parse(const char *s, MxMIDI128 *out) { + long v; + if (!mx_try_parse_int(s, &v)) { + *out = (MxMIDI128)0; + return false; + } + *out = mx_midi_128_clamp(v); + return true; +} + +MxMIDI128 mx_midi_128_parse(const char *s) { + long v = mx_parse_int(s); + return mx_midi_128_clamp(v); +} + +char *mx_midi_128_to_string(MxMIDI128 v) { + return mx_format_int((long)v); +} diff --git a/gen/test/c/mx/mx_midi_128.h b/gen/test/c/mx/mx_midi_128.h new file mode 100644 index 000000000..350041b18 --- /dev/null +++ b/gen/test/c/mx/mx_midi_128.h @@ -0,0 +1,20 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_MIDI_128_H +#define MX_MIDI_128_H + +#include + +/* + * The midi-16 type is used to express MIDI 1.0 values that range from 1 to 128. + */ +typedef long MxMIDI128; + +/* Strict parse, then clamps into the declared range. */ +bool mx_midi_128_try_parse(const char *s, MxMIDI128 *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxMIDI128 mx_midi_128_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_midi_128_to_string(MxMIDI128 v); + +#endif /* MX_MIDI_128_H */ diff --git a/gen/test/c/mx/mx_midi_16.c b/gen/test/c/mx/mx_midi_16.c new file mode 100644 index 000000000..33f0fd25d --- /dev/null +++ b/gen/test/c/mx/mx_midi_16.c @@ -0,0 +1,32 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_midi_16.h" + +#include "mx_runtime.h" + +static MxMIDI16 mx_midi_16_clamp(long v) { + if (v < 1) + v = 1; + if (v > 16) + v = 16; + return (MxMIDI16)v; +} + +bool mx_midi_16_try_parse(const char *s, MxMIDI16 *out) { + long v; + if (!mx_try_parse_int(s, &v)) { + *out = (MxMIDI16)0; + return false; + } + *out = mx_midi_16_clamp(v); + return true; +} + +MxMIDI16 mx_midi_16_parse(const char *s) { + long v = mx_parse_int(s); + return mx_midi_16_clamp(v); +} + +char *mx_midi_16_to_string(MxMIDI16 v) { + return mx_format_int((long)v); +} diff --git a/gen/test/c/mx/mx_midi_16.h b/gen/test/c/mx/mx_midi_16.h new file mode 100644 index 000000000..4303ab564 --- /dev/null +++ b/gen/test/c/mx/mx_midi_16.h @@ -0,0 +1,20 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_MIDI_16_H +#define MX_MIDI_16_H + +#include + +/* + * The midi-16 type is used to express MIDI 1.0 values that range from 1 to 16. + */ +typedef long MxMIDI16; + +/* Strict parse, then clamps into the declared range. */ +bool mx_midi_16_try_parse(const char *s, MxMIDI16 *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxMIDI16 mx_midi_16_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_midi_16_to_string(MxMIDI16 v); + +#endif /* MX_MIDI_16_H */ diff --git a/gen/test/c/mx/mx_midi_16384.c b/gen/test/c/mx/mx_midi_16384.c new file mode 100644 index 000000000..fdd9354f5 --- /dev/null +++ b/gen/test/c/mx/mx_midi_16384.c @@ -0,0 +1,32 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_midi_16384.h" + +#include "mx_runtime.h" + +static MxMIDI16384 mx_midi_16384_clamp(long v) { + if (v < 1) + v = 1; + if (v > 16384) + v = 16384; + return (MxMIDI16384)v; +} + +bool mx_midi_16384_try_parse(const char *s, MxMIDI16384 *out) { + long v; + if (!mx_try_parse_int(s, &v)) { + *out = (MxMIDI16384)0; + return false; + } + *out = mx_midi_16384_clamp(v); + return true; +} + +MxMIDI16384 mx_midi_16384_parse(const char *s) { + long v = mx_parse_int(s); + return mx_midi_16384_clamp(v); +} + +char *mx_midi_16384_to_string(MxMIDI16384 v) { + return mx_format_int((long)v); +} diff --git a/gen/test/c/mx/mx_midi_16384.h b/gen/test/c/mx/mx_midi_16384.h new file mode 100644 index 000000000..e10f8eb9c --- /dev/null +++ b/gen/test/c/mx/mx_midi_16384.h @@ -0,0 +1,20 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_MIDI_16384_H +#define MX_MIDI_16384_H + +#include + +/* + * The midi-16 type is used to express MIDI 1.0 values that range from 1 to 16,384. + */ +typedef long MxMIDI16384; + +/* Strict parse, then clamps into the declared range. */ +bool mx_midi_16384_try_parse(const char *s, MxMIDI16384 *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxMIDI16384 mx_midi_16384_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_midi_16384_to_string(MxMIDI16384 v); + +#endif /* MX_MIDI_16384_H */ diff --git a/gen/test/c/mx/mx_millimeters.c b/gen/test/c/mx/mx_millimeters.c new file mode 100644 index 000000000..e23b4cb97 --- /dev/null +++ b/gen/test/c/mx/mx_millimeters.c @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_millimeters.h" + +#include "mx_runtime.h" + +bool mx_millimeters_try_parse(const char *s, MxMillimeters *out) { + double v; + if (!mx_try_parse_decimal(s, &v)) { + *out = (MxMillimeters)0; + return false; + } + *out = (MxMillimeters)v; + return true; +} + +MxMillimeters mx_millimeters_parse(const char *s) { + double v = mx_parse_decimal(s); + return (MxMillimeters)v; +} + +char *mx_millimeters_to_string(MxMillimeters v) { + return mx_format_decimal((double)v); +} diff --git a/gen/test/c/mx/mx_millimeters.h b/gen/test/c/mx/mx_millimeters.h new file mode 100644 index 000000000..900b4f3dc --- /dev/null +++ b/gen/test/c/mx/mx_millimeters.h @@ -0,0 +1,21 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_MILLIMETERS_H +#define MX_MILLIMETERS_H + +#include + +/* + * The millimeters type is a number representing millimeters. This is used in the scaling element to + * provide a default scaling from tenths to physical units. + */ +typedef double MxMillimeters; + +/* Strict parse, then clamps into the declared range. */ +bool mx_millimeters_try_parse(const char *s, MxMillimeters *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxMillimeters mx_millimeters_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_millimeters_to_string(MxMillimeters v); + +#endif /* MX_MILLIMETERS_H */ diff --git a/gen/test/c/mx/mx_mode.c b/gen/test/c/mx/mx_mode.c new file mode 100644 index 000000000..69b0ccc93 --- /dev/null +++ b/gen/test/c/mx/mx_mode.c @@ -0,0 +1,9 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_mode.h" + +#include "mx_runtime.h" + +MxMode mx_mode_parse(const char *s) { + return mx_strdup(s); +} diff --git a/gen/test/c/mx/mx_mode.h b/gen/test/c/mx/mx_mode.h new file mode 100644 index 000000000..4219892e0 --- /dev/null +++ b/gen/test/c/mx/mx_mode.h @@ -0,0 +1,15 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_MODE_H +#define MX_MODE_H + +/* + * The mode type is used to specify major/minor and other mode distinctions. Valid mode values + * include major, minor, dorian, phrygian, lydian, mixolydian, aeolian, ionian, locrian, and none. + */ +typedef char *MxMode; + +/* Malloc'd copy of the wire string; the value IS its spelling. */ +MxMode mx_mode_parse(const char *s); + +#endif /* MX_MODE_H */ diff --git a/gen/test/c/mx/mx_mute.c b/gen/test/c/mx/mx_mute.c new file mode 100644 index 000000000..64e653030 --- /dev/null +++ b/gen/test/c/mx/mx_mute.c @@ -0,0 +1,46 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_mute.h" + +#include + +static const char *const mx_mute_values[] = { + "on", + "off", + "straight", + "cup", + "harmon-no-stem", + "harmon-stem", + "bucket", + "plunger", + "hat", + "solotone", + "practice", + "stop-mute", + "stop-hand", + "echo", + "palm", +}; + +bool mx_mute_try_parse(const char *s, MxMute *out) { + for (size_t i = 0; i < sizeof(mx_mute_values) / sizeof(mx_mute_values[0]); i++) { + if (strcmp(s, mx_mute_values[i]) == 0) { + *out = (MxMute)i; + return true; + } + } + *out = (MxMute)0; + return false; +} + +MxMute mx_mute_parse(const char *s) { + MxMute v; + mx_mute_try_parse(s, &v); + return v; +} + +const char *mx_mute_to_string(MxMute v) { + if ((size_t)v >= sizeof(mx_mute_values) / sizeof(mx_mute_values[0])) + return mx_mute_values[0]; + return mx_mute_values[v]; +} diff --git a/gen/test/c/mx/mx_mute.h b/gen/test/c/mx/mx_mute.h new file mode 100644 index 000000000..f29a40e27 --- /dev/null +++ b/gen/test/c/mx/mx_mute.h @@ -0,0 +1,37 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_MUTE_H +#define MX_MUTE_H + +#include + +/* + * The mute type represents muting for different instruments, including brass, winds, and strings. + * The on and off values are used for undifferentiated mutes. The remaining values represent + * specific mutes. + */ +typedef enum { + MX_MUTE_ON = 0, + MX_MUTE_OFF = 1, + MX_MUTE_STRAIGHT = 2, + MX_MUTE_CUP = 3, + MX_MUTE_HARMON_NO_STEM = 4, + MX_MUTE_HARMON_STEM = 5, + MX_MUTE_BUCKET = 6, + MX_MUTE_PLUNGER = 7, + MX_MUTE_HAT = 8, + MX_MUTE_SOLOTONE = 9, + MX_MUTE_PRACTICE = 10, + MX_MUTE_STOP_MUTE = 11, + MX_MUTE_STOP_HAND = 12, + MX_MUTE_ECHO = 13, + MX_MUTE_PALM = 14 +} MxMute; + +bool mx_mute_try_parse(const char *s, MxMute *out); +/* Lenient: unknown input falls back to the first variant. */ +MxMute mx_mute_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_mute_to_string(MxMute v); + +#endif /* MX_MUTE_H */ diff --git a/gen/test/c/mx/mx_non_negative_decimal.c b/gen/test/c/mx/mx_non_negative_decimal.c new file mode 100644 index 000000000..84de359c5 --- /dev/null +++ b/gen/test/c/mx/mx_non_negative_decimal.c @@ -0,0 +1,30 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_non_negative_decimal.h" + +#include "mx_runtime.h" + +static MxNonNegativeDecimal mx_non_negative_decimal_clamp(double v) { + if (v < 0.0) + v = 0.0; + return (MxNonNegativeDecimal)v; +} + +bool mx_non_negative_decimal_try_parse(const char *s, MxNonNegativeDecimal *out) { + double v; + if (!mx_try_parse_decimal(s, &v)) { + *out = (MxNonNegativeDecimal)0; + return false; + } + *out = mx_non_negative_decimal_clamp(v); + return true; +} + +MxNonNegativeDecimal mx_non_negative_decimal_parse(const char *s) { + double v = mx_parse_decimal(s); + return mx_non_negative_decimal_clamp(v); +} + +char *mx_non_negative_decimal_to_string(MxNonNegativeDecimal v) { + return mx_format_decimal((double)v); +} diff --git a/gen/test/c/mx/mx_non_negative_decimal.h b/gen/test/c/mx/mx_non_negative_decimal.h new file mode 100644 index 000000000..6b3b2fcd1 --- /dev/null +++ b/gen/test/c/mx/mx_non_negative_decimal.h @@ -0,0 +1,20 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_NON_NEGATIVE_DECIMAL_H +#define MX_NON_NEGATIVE_DECIMAL_H + +#include + +/* + * The non-negative-decimal type specifies a non-negative decimal value. + */ +typedef double MxNonNegativeDecimal; + +/* Strict parse, then clamps into the declared range. */ +bool mx_non_negative_decimal_try_parse(const char *s, MxNonNegativeDecimal *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxNonNegativeDecimal mx_non_negative_decimal_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_non_negative_decimal_to_string(MxNonNegativeDecimal v); + +#endif /* MX_NON_NEGATIVE_DECIMAL_H */ diff --git a/gen/test/c/mx/mx_note_size_type.c b/gen/test/c/mx/mx_note_size_type.c new file mode 100644 index 000000000..0965a6d8c --- /dev/null +++ b/gen/test/c/mx/mx_note_size_type.c @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_note_size_type.h" + +#include + +static const char *const mx_note_size_type_values[] = { + "cue", + "grace", + "grace-cue", + "large", +}; + +bool mx_note_size_type_try_parse(const char *s, MxNoteSizeType *out) { + for (size_t i = 0; i < sizeof(mx_note_size_type_values) / sizeof(mx_note_size_type_values[0]); i++) { + if (strcmp(s, mx_note_size_type_values[i]) == 0) { + *out = (MxNoteSizeType)i; + return true; + } + } + *out = (MxNoteSizeType)0; + return false; +} + +MxNoteSizeType mx_note_size_type_parse(const char *s) { + MxNoteSizeType v; + mx_note_size_type_try_parse(s, &v); + return v; +} + +const char *mx_note_size_type_to_string(MxNoteSizeType v) { + if ((size_t)v >= sizeof(mx_note_size_type_values) / sizeof(mx_note_size_type_values[0])) + return mx_note_size_type_values[0]; + return mx_note_size_type_values[v]; +} diff --git a/gen/test/c/mx/mx_note_size_type.h b/gen/test/c/mx/mx_note_size_type.h new file mode 100644 index 000000000..3101c9bef --- /dev/null +++ b/gen/test/c/mx/mx_note_size_type.h @@ -0,0 +1,28 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_NOTE_SIZE_TYPE_H +#define MX_NOTE_SIZE_TYPE_H + +#include + +/* + * The note-size-type type indicates the type of note being defined by a note-size element. The + * grace-cue type is used for notes of grace-cue size. The grace type is used for notes of cue size + * that include a grace element. The cue type is used for all other notes with cue size, whether + * defined explicitly or implicitly via a cue element. The large type is used for notes of large + * size. + */ +typedef enum { + MX_NOTE_SIZE_TYPE_CUE = 0, + MX_NOTE_SIZE_TYPE_GRACE = 1, + MX_NOTE_SIZE_TYPE_GRACE_CUE = 2, + MX_NOTE_SIZE_TYPE_LARGE = 3 +} MxNoteSizeType; + +bool mx_note_size_type_try_parse(const char *s, MxNoteSizeType *out); +/* Lenient: unknown input falls back to the first variant. */ +MxNoteSizeType mx_note_size_type_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_note_size_type_to_string(MxNoteSizeType v); + +#endif /* MX_NOTE_SIZE_TYPE_H */ diff --git a/gen/test/c/mx/mx_note_type_value.c b/gen/test/c/mx/mx_note_type_value.c new file mode 100644 index 000000000..8f849a19b --- /dev/null +++ b/gen/test/c/mx/mx_note_type_value.c @@ -0,0 +1,45 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_note_type_value.h" + +#include + +static const char *const mx_note_type_value_values[] = { + "1024th", + "512th", + "256th", + "128th", + "64th", + "32nd", + "16th", + "eighth", + "quarter", + "half", + "whole", + "breve", + "long", + "maxima", +}; + +bool mx_note_type_value_try_parse(const char *s, MxNoteTypeValue *out) { + for (size_t i = 0; i < sizeof(mx_note_type_value_values) / sizeof(mx_note_type_value_values[0]); i++) { + if (strcmp(s, mx_note_type_value_values[i]) == 0) { + *out = (MxNoteTypeValue)i; + return true; + } + } + *out = (MxNoteTypeValue)0; + return false; +} + +MxNoteTypeValue mx_note_type_value_parse(const char *s) { + MxNoteTypeValue v; + mx_note_type_value_try_parse(s, &v); + return v; +} + +const char *mx_note_type_value_to_string(MxNoteTypeValue v) { + if ((size_t)v >= sizeof(mx_note_type_value_values) / sizeof(mx_note_type_value_values[0])) + return mx_note_type_value_values[0]; + return mx_note_type_value_values[v]; +} diff --git a/gen/test/c/mx/mx_note_type_value.h b/gen/test/c/mx/mx_note_type_value.h new file mode 100644 index 000000000..48209fc1e --- /dev/null +++ b/gen/test/c/mx/mx_note_type_value.h @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_NOTE_TYPE_VALUE_H +#define MX_NOTE_TYPE_VALUE_H + +#include + +/* + * The note-type type is used for the MusicXML type element and represents the graphic note type, + * from 1024th (shortest) to maxima (longest). + */ +typedef enum { + MX_NOTE_TYPE_VALUE_1024TH = 0, + MX_NOTE_TYPE_VALUE_512TH = 1, + MX_NOTE_TYPE_VALUE_256TH = 2, + MX_NOTE_TYPE_VALUE_128TH = 3, + MX_NOTE_TYPE_VALUE_64TH = 4, + MX_NOTE_TYPE_VALUE_32ND = 5, + MX_NOTE_TYPE_VALUE_16TH = 6, + MX_NOTE_TYPE_VALUE_EIGHTH = 7, + MX_NOTE_TYPE_VALUE_QUARTER = 8, + MX_NOTE_TYPE_VALUE_HALF = 9, + MX_NOTE_TYPE_VALUE_WHOLE = 10, + MX_NOTE_TYPE_VALUE_BREVE = 11, + MX_NOTE_TYPE_VALUE_LONG = 12, + MX_NOTE_TYPE_VALUE_MAXIMA = 13 +} MxNoteTypeValue; + +bool mx_note_type_value_try_parse(const char *s, MxNoteTypeValue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxNoteTypeValue mx_note_type_value_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_note_type_value_to_string(MxNoteTypeValue v); + +#endif /* MX_NOTE_TYPE_VALUE_H */ diff --git a/gen/test/c/mx/mx_notehead_value.c b/gen/test/c/mx/mx_notehead_value.c new file mode 100644 index 000000000..96c811a6d --- /dev/null +++ b/gen/test/c/mx/mx_notehead_value.c @@ -0,0 +1,59 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_notehead_value.h" + +#include + +static const char *const mx_notehead_value_values[] = { + "slash", + "triangle", + "diamond", + "square", + "cross", + "x", + "circle-x", + "inverted triangle", + "arrow down", + "arrow up", + "circled", + "slashed", + "back slashed", + "normal", + "cluster", + "circle dot", + "left triangle", + "rectangle", + "none", + "do", + "re", + "mi", + "fa", + "fa up", + "so", + "la", + "ti", + "other", +}; + +bool mx_notehead_value_try_parse(const char *s, MxNoteheadValue *out) { + for (size_t i = 0; i < sizeof(mx_notehead_value_values) / sizeof(mx_notehead_value_values[0]); i++) { + if (strcmp(s, mx_notehead_value_values[i]) == 0) { + *out = (MxNoteheadValue)i; + return true; + } + } + *out = (MxNoteheadValue)0; + return false; +} + +MxNoteheadValue mx_notehead_value_parse(const char *s) { + MxNoteheadValue v; + mx_notehead_value_try_parse(s, &v); + return v; +} + +const char *mx_notehead_value_to_string(MxNoteheadValue v) { + if ((size_t)v >= sizeof(mx_notehead_value_values) / sizeof(mx_notehead_value_values[0])) + return mx_notehead_value_values[0]; + return mx_notehead_value_values[v]; +} diff --git a/gen/test/c/mx/mx_notehead_value.h b/gen/test/c/mx/mx_notehead_value.h new file mode 100644 index 000000000..86b7ff28c --- /dev/null +++ b/gen/test/c/mx/mx_notehead_value.h @@ -0,0 +1,60 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_NOTEHEAD_VALUE_H +#define MX_NOTEHEAD_VALUE_H + +#include + +/* + * The notehead-value type indicates shapes other than the open and closed ovals associated with + * note durations. The values do, re, mi, fa, fa up, so, la, and ti correspond to Aikin's 7-shape + * system. The fa up shape is typically used with upstems; the fa shape is typically used with + * downstems or no stems. The arrow shapes differ from triangle and inverted triangle by being + * centered on the stem. Slashed and back slashed notes include both the normal notehead and a + * slash. The triangle shape has the tip of the triangle pointing up; the inverted triangle shape + * has the tip of the triangle pointing down. The left triangle shape is a right triangle with the + * hypotenuse facing up and to the left. The other notehead covers noteheads other than those listed + * here. It is usually used in combination with the smufl attribute to specify a particular SMuFL + * notehead. The smufl attribute may be used with any notehead value to help specify the appearance + * of symbols that share the same MusicXML semantics. Noteheads in the SMuFL "Note name noteheads" + * range (U+E150–U+E1AF) should not use the smufl attribute or the "other" value, but instead use + * the notehead-text element. + */ +typedef enum { + MX_NOTEHEAD_VALUE_SLASH = 0, + MX_NOTEHEAD_VALUE_TRIANGLE = 1, + MX_NOTEHEAD_VALUE_DIAMOND = 2, + MX_NOTEHEAD_VALUE_SQUARE = 3, + MX_NOTEHEAD_VALUE_CROSS = 4, + MX_NOTEHEAD_VALUE_X = 5, + MX_NOTEHEAD_VALUE_CIRCLE_X = 6, + MX_NOTEHEAD_VALUE_INVERTED_TRIANGLE = 7, + MX_NOTEHEAD_VALUE_ARROW_DOWN = 8, + MX_NOTEHEAD_VALUE_ARROW_UP = 9, + MX_NOTEHEAD_VALUE_CIRCLED = 10, + MX_NOTEHEAD_VALUE_SLASHED = 11, + MX_NOTEHEAD_VALUE_BACK_SLASHED = 12, + MX_NOTEHEAD_VALUE_NORMAL = 13, + MX_NOTEHEAD_VALUE_CLUSTER = 14, + MX_NOTEHEAD_VALUE_CIRCLE_DOT = 15, + MX_NOTEHEAD_VALUE_LEFT_TRIANGLE = 16, + MX_NOTEHEAD_VALUE_RECTANGLE = 17, + MX_NOTEHEAD_VALUE_NONE = 18, + MX_NOTEHEAD_VALUE_DO = 19, + MX_NOTEHEAD_VALUE_RE = 20, + MX_NOTEHEAD_VALUE_MI = 21, + MX_NOTEHEAD_VALUE_FA = 22, + MX_NOTEHEAD_VALUE_FA_UP = 23, + MX_NOTEHEAD_VALUE_SO = 24, + MX_NOTEHEAD_VALUE_LA = 25, + MX_NOTEHEAD_VALUE_TI = 26, + MX_NOTEHEAD_VALUE_OTHER = 27 +} MxNoteheadValue; + +bool mx_notehead_value_try_parse(const char *s, MxNoteheadValue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxNoteheadValue mx_notehead_value_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_notehead_value_to_string(MxNoteheadValue v); + +#endif /* MX_NOTEHEAD_VALUE_H */ diff --git a/gen/test/c/mx/mx_number_level.c b/gen/test/c/mx/mx_number_level.c new file mode 100644 index 000000000..90b2bc241 --- /dev/null +++ b/gen/test/c/mx/mx_number_level.c @@ -0,0 +1,32 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_number_level.h" + +#include "mx_runtime.h" + +static MxNumberLevel mx_number_level_clamp(long v) { + if (v < 1) + v = 1; + if (v > 6) + v = 6; + return (MxNumberLevel)v; +} + +bool mx_number_level_try_parse(const char *s, MxNumberLevel *out) { + long v; + if (!mx_try_parse_int(s, &v)) { + *out = (MxNumberLevel)0; + return false; + } + *out = mx_number_level_clamp(v); + return true; +} + +MxNumberLevel mx_number_level_parse(const char *s) { + long v = mx_parse_int(s); + return mx_number_level_clamp(v); +} + +char *mx_number_level_to_string(MxNumberLevel v) { + return mx_format_int((long)v); +} diff --git a/gen/test/c/mx/mx_number_level.h b/gen/test/c/mx/mx_number_level.h new file mode 100644 index 000000000..92fd5b1e5 --- /dev/null +++ b/gen/test/c/mx/mx_number_level.h @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_NUMBER_LEVEL_H +#define MX_NUMBER_LEVEL_H + +#include + +/* + * Slurs, tuplets, and many other features can be concurrent and overlapping within a single musical + * part. The number-level type distinguishes up to six concurrent objects of the same type. A + * reading program should be prepared to handle cases where the number-levels stop in an arbitrary + * order. Different numbers are needed when the features overlap in MusicXML document order. When a + * number-level value is optional, the value is 1 by default. + */ +typedef long MxNumberLevel; + +/* Strict parse, then clamps into the declared range. */ +bool mx_number_level_try_parse(const char *s, MxNumberLevel *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxNumberLevel mx_number_level_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_number_level_to_string(MxNumberLevel v); + +#endif /* MX_NUMBER_LEVEL_H */ diff --git a/gen/test/c/mx/mx_number_of_lines.c b/gen/test/c/mx/mx_number_of_lines.c new file mode 100644 index 000000000..81854d40c --- /dev/null +++ b/gen/test/c/mx/mx_number_of_lines.c @@ -0,0 +1,32 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_number_of_lines.h" + +#include "mx_runtime.h" + +static MxNumberOfLines mx_number_of_lines_clamp(long v) { + if (v < 0) + v = 0; + if (v > 3) + v = 3; + return (MxNumberOfLines)v; +} + +bool mx_number_of_lines_try_parse(const char *s, MxNumberOfLines *out) { + long v; + if (!mx_try_parse_int(s, &v)) { + *out = (MxNumberOfLines)0; + return false; + } + *out = mx_number_of_lines_clamp(v); + return true; +} + +MxNumberOfLines mx_number_of_lines_parse(const char *s) { + long v = mx_parse_int(s); + return mx_number_of_lines_clamp(v); +} + +char *mx_number_of_lines_to_string(MxNumberOfLines v) { + return mx_format_int((long)v); +} diff --git a/gen/test/c/mx/mx_number_of_lines.h b/gen/test/c/mx/mx_number_of_lines.h new file mode 100644 index 000000000..dca604a46 --- /dev/null +++ b/gen/test/c/mx/mx_number_of_lines.h @@ -0,0 +1,20 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_NUMBER_OF_LINES_H +#define MX_NUMBER_OF_LINES_H + +#include + +/* + * The number-of-lines type is used to specify the number of lines in text decoration attributes. + */ +typedef long MxNumberOfLines; + +/* Strict parse, then clamps into the declared range. */ +bool mx_number_of_lines_try_parse(const char *s, MxNumberOfLines *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxNumberOfLines mx_number_of_lines_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_number_of_lines_to_string(MxNumberOfLines v); + +#endif /* MX_NUMBER_OF_LINES_H */ diff --git a/gen/test/c/mx/mx_number_or_normal.c b/gen/test/c/mx/mx_number_or_normal.c new file mode 100644 index 000000000..8e246e275 --- /dev/null +++ b/gen/test/c/mx/mx_number_or_normal.c @@ -0,0 +1,48 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_number_or_normal.h" + +#include "mx_runtime.h" +#include +#include + +bool mx_number_or_normal_try_parse(const char *s, MxNumberOrNormal *out) { + MxNumberOrNormal v; + memset(&v, 0, sizeof(v)); + if (mx_try_parse_decimal(s, &v.decimal)) { + v.kind = MX_NUMBER_OR_NORMAL_KIND_DECIMAL; + *out = v; + return true; + } + if (strcmp(s, "normal") == 0) { + v.kind = MX_NUMBER_OR_NORMAL_KIND_NORMAL; + *out = v; + return true; + } + *out = v; + return false; +} + +MxNumberOrNormal mx_number_or_normal_parse(const char *s) { + MxNumberOrNormal v; + if (mx_number_or_normal_try_parse(s, &v)) + return v; + memset(&v, 0, sizeof(v)); + v.kind = MX_NUMBER_OR_NORMAL_KIND_DECIMAL; + v.decimal = mx_parse_decimal(s); + return v; +} + +char *mx_number_or_normal_to_string(MxNumberOrNormal v) { + switch (v.kind) { + case MX_NUMBER_OR_NORMAL_KIND_NORMAL: + return mx_strdup("normal"); + default: + return mx_format_decimal(v.decimal); + } +} + +void mx_number_or_normal_free(MxNumberOrNormal *v) { + if (!v) + return; +} diff --git a/gen/test/c/mx/mx_number_or_normal.h b/gen/test/c/mx/mx_number_or_normal.h new file mode 100644 index 000000000..b9fc6232d --- /dev/null +++ b/gen/test/c/mx/mx_number_or_normal.h @@ -0,0 +1,30 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_NUMBER_OR_NORMAL_H +#define MX_NUMBER_OR_NORMAL_H + +#include + +/* + * The number-or-normal values can be either a decimal number or the string "normal". This is used + * by the line-height and letter-spacing attributes. + */ +typedef enum { + MX_NUMBER_OR_NORMAL_KIND_DECIMAL = 0, + MX_NUMBER_OR_NORMAL_KIND_NORMAL = 1 +} MxNumberOrNormalKind; + +typedef struct { + MxNumberOrNormalKind kind; + double decimal; +} MxNumberOrNormal; + +/* Tries each union member in schema order. */ +bool mx_number_or_normal_try_parse(const char *s, MxNumberOrNormal *out); +/* Lenient: an unmatched input is absorbed by the first member. */ +MxNumberOrNormal mx_number_or_normal_parse(const char *s); +/* The wire spelling of whichever member is held. Malloc'd. */ +char *mx_number_or_normal_to_string(MxNumberOrNormal v); +void mx_number_or_normal_free(MxNumberOrNormal *v); + +#endif /* MX_NUMBER_OR_NORMAL_H */ diff --git a/gen/test/c/mx/mx_octave.c b/gen/test/c/mx/mx_octave.c new file mode 100644 index 000000000..08c45e72b --- /dev/null +++ b/gen/test/c/mx/mx_octave.c @@ -0,0 +1,32 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_octave.h" + +#include "mx_runtime.h" + +static MxOctave mx_octave_clamp(long v) { + if (v < 0) + v = 0; + if (v > 9) + v = 9; + return (MxOctave)v; +} + +bool mx_octave_try_parse(const char *s, MxOctave *out) { + long v; + if (!mx_try_parse_int(s, &v)) { + *out = (MxOctave)0; + return false; + } + *out = mx_octave_clamp(v); + return true; +} + +MxOctave mx_octave_parse(const char *s) { + long v = mx_parse_int(s); + return mx_octave_clamp(v); +} + +char *mx_octave_to_string(MxOctave v) { + return mx_format_int((long)v); +} diff --git a/gen/test/c/mx/mx_octave.h b/gen/test/c/mx/mx_octave.h new file mode 100644 index 000000000..4e4f74d92 --- /dev/null +++ b/gen/test/c/mx/mx_octave.h @@ -0,0 +1,20 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_OCTAVE_H +#define MX_OCTAVE_H + +#include + +/* + * Octaves are represented by the numbers 0 to 9, where 4 indicates the octave started by middle C. + */ +typedef long MxOctave; + +/* Strict parse, then clamps into the declared range. */ +bool mx_octave_try_parse(const char *s, MxOctave *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxOctave mx_octave_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_octave_to_string(MxOctave v); + +#endif /* MX_OCTAVE_H */ diff --git a/gen/test/c/mx/mx_on_off.c b/gen/test/c/mx/mx_on_off.c new file mode 100644 index 000000000..52acc54c1 --- /dev/null +++ b/gen/test/c/mx/mx_on_off.c @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_on_off.h" + +#include + +static const char *const mx_on_off_values[] = { + "on", + "off", +}; + +bool mx_on_off_try_parse(const char *s, MxOnOff *out) { + for (size_t i = 0; i < sizeof(mx_on_off_values) / sizeof(mx_on_off_values[0]); i++) { + if (strcmp(s, mx_on_off_values[i]) == 0) { + *out = (MxOnOff)i; + return true; + } + } + *out = (MxOnOff)0; + return false; +} + +MxOnOff mx_on_off_parse(const char *s) { + MxOnOff v; + mx_on_off_try_parse(s, &v); + return v; +} + +const char *mx_on_off_to_string(MxOnOff v) { + if ((size_t)v >= sizeof(mx_on_off_values) / sizeof(mx_on_off_values[0])) + return mx_on_off_values[0]; + return mx_on_off_values[v]; +} diff --git a/gen/test/c/mx/mx_on_off.h b/gen/test/c/mx/mx_on_off.h new file mode 100644 index 000000000..472d475e5 --- /dev/null +++ b/gen/test/c/mx/mx_on_off.h @@ -0,0 +1,22 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ON_OFF_H +#define MX_ON_OFF_H + +#include + +/* + * The on-off type is used for notation elements such as string mutes. + */ +typedef enum { + MX_ON_OFF_ON = 0, + MX_ON_OFF_OFF = 1 +} MxOnOff; + +bool mx_on_off_try_parse(const char *s, MxOnOff *out); +/* Lenient: unknown input falls back to the first variant. */ +MxOnOff mx_on_off_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_on_off_to_string(MxOnOff v); + +#endif /* MX_ON_OFF_H */ diff --git a/gen/test/c/mx/mx_over_under.c b/gen/test/c/mx/mx_over_under.c new file mode 100644 index 000000000..a7dab752f --- /dev/null +++ b/gen/test/c/mx/mx_over_under.c @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_over_under.h" + +#include + +static const char *const mx_over_under_values[] = { + "over", + "under", +}; + +bool mx_over_under_try_parse(const char *s, MxOverUnder *out) { + for (size_t i = 0; i < sizeof(mx_over_under_values) / sizeof(mx_over_under_values[0]); i++) { + if (strcmp(s, mx_over_under_values[i]) == 0) { + *out = (MxOverUnder)i; + return true; + } + } + *out = (MxOverUnder)0; + return false; +} + +MxOverUnder mx_over_under_parse(const char *s) { + MxOverUnder v; + mx_over_under_try_parse(s, &v); + return v; +} + +const char *mx_over_under_to_string(MxOverUnder v) { + if ((size_t)v >= sizeof(mx_over_under_values) / sizeof(mx_over_under_values[0])) + return mx_over_under_values[0]; + return mx_over_under_values[v]; +} diff --git a/gen/test/c/mx/mx_over_under.h b/gen/test/c/mx/mx_over_under.h new file mode 100644 index 000000000..8840aafff --- /dev/null +++ b/gen/test/c/mx/mx_over_under.h @@ -0,0 +1,23 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_OVER_UNDER_H +#define MX_OVER_UNDER_H + +#include + +/* + * The over-under type is used to indicate whether the tips of curved lines such as slurs and ties + * are overhand (tips down) or underhand (tips up). + */ +typedef enum { + MX_OVER_UNDER_OVER = 0, + MX_OVER_UNDER_UNDER = 1 +} MxOverUnder; + +bool mx_over_under_try_parse(const char *s, MxOverUnder *out); +/* Lenient: unknown input falls back to the first variant. */ +MxOverUnder mx_over_under_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_over_under_to_string(MxOverUnder v); + +#endif /* MX_OVER_UNDER_H */ diff --git a/gen/test/c/mx/mx_pedal_type.c b/gen/test/c/mx/mx_pedal_type.c new file mode 100644 index 000000000..640f3e660 --- /dev/null +++ b/gen/test/c/mx/mx_pedal_type.c @@ -0,0 +1,36 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_pedal_type.h" + +#include + +static const char *const mx_pedal_type_values[] = { + "start", + "stop", + "sostenuto", + "change", + "continue", +}; + +bool mx_pedal_type_try_parse(const char *s, MxPedalType *out) { + for (size_t i = 0; i < sizeof(mx_pedal_type_values) / sizeof(mx_pedal_type_values[0]); i++) { + if (strcmp(s, mx_pedal_type_values[i]) == 0) { + *out = (MxPedalType)i; + return true; + } + } + *out = (MxPedalType)0; + return false; +} + +MxPedalType mx_pedal_type_parse(const char *s) { + MxPedalType v; + mx_pedal_type_try_parse(s, &v); + return v; +} + +const char *mx_pedal_type_to_string(MxPedalType v) { + if ((size_t)v >= sizeof(mx_pedal_type_values) / sizeof(mx_pedal_type_values[0])) + return mx_pedal_type_values[0]; + return mx_pedal_type_values[v]; +} diff --git a/gen/test/c/mx/mx_pedal_type.h b/gen/test/c/mx/mx_pedal_type.h new file mode 100644 index 000000000..7c0cfc3d5 --- /dev/null +++ b/gen/test/c/mx/mx_pedal_type.h @@ -0,0 +1,29 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PEDAL_TYPE_H +#define MX_PEDAL_TYPE_H + +#include + +/* + * The pedal-type simple type is used to distinguish types of pedal directions. The start value + * indicates the start of a damper pedal, while the sostenuto value indicates the start of a + * sostenuto pedal. The change, continue, and stop values can be used with either the damper or + * sostenuto pedal. The soft pedal is not included here because there is no special symbol or + * graphic used for it beyond what can be specified with words and bracket elements. + */ +typedef enum { + MX_PEDAL_TYPE_START = 0, + MX_PEDAL_TYPE_STOP = 1, + MX_PEDAL_TYPE_SOSTENUTO = 2, + MX_PEDAL_TYPE_CHANGE = 3, + MX_PEDAL_TYPE_CONTINUE = 4 +} MxPedalType; + +bool mx_pedal_type_try_parse(const char *s, MxPedalType *out); +/* Lenient: unknown input falls back to the first variant. */ +MxPedalType mx_pedal_type_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_pedal_type_to_string(MxPedalType v); + +#endif /* MX_PEDAL_TYPE_H */ diff --git a/gen/test/c/mx/mx_percent.c b/gen/test/c/mx/mx_percent.c new file mode 100644 index 000000000..a1acb8740 --- /dev/null +++ b/gen/test/c/mx/mx_percent.c @@ -0,0 +1,32 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_percent.h" + +#include "mx_runtime.h" + +static MxPercent mx_percent_clamp(double v) { + if (v < 0.0) + v = 0.0; + if (v > 100.0) + v = 100.0; + return (MxPercent)v; +} + +bool mx_percent_try_parse(const char *s, MxPercent *out) { + double v; + if (!mx_try_parse_decimal(s, &v)) { + *out = (MxPercent)0; + return false; + } + *out = mx_percent_clamp(v); + return true; +} + +MxPercent mx_percent_parse(const char *s) { + double v = mx_parse_decimal(s); + return mx_percent_clamp(v); +} + +char *mx_percent_to_string(MxPercent v) { + return mx_format_decimal((double)v); +} diff --git a/gen/test/c/mx/mx_percent.h b/gen/test/c/mx/mx_percent.h new file mode 100644 index 000000000..b2a681924 --- /dev/null +++ b/gen/test/c/mx/mx_percent.h @@ -0,0 +1,20 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PERCENT_H +#define MX_PERCENT_H + +#include + +/* + * The percent type specifies a percentage from 0 to 100. + */ +typedef double MxPercent; + +/* Strict parse, then clamps into the declared range. */ +bool mx_percent_try_parse(const char *s, MxPercent *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxPercent mx_percent_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_percent_to_string(MxPercent v); + +#endif /* MX_PERCENT_H */ diff --git a/gen/test/c/mx/mx_pitched_value.c b/gen/test/c/mx/mx_pitched_value.c new file mode 100644 index 000000000..1d707b1fb --- /dev/null +++ b/gen/test/c/mx/mx_pitched_value.c @@ -0,0 +1,42 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_pitched_value.h" + +#include + +static const char *const mx_pitched_value_values[] = { + "celesta", + "chimes", + "glockenspiel", + "lithophone", + "mallet", + "marimba", + "steel drums", + "tubaphone", + "tubular chimes", + "vibraphone", + "xylophone", +}; + +bool mx_pitched_value_try_parse(const char *s, MxPitchedValue *out) { + for (size_t i = 0; i < sizeof(mx_pitched_value_values) / sizeof(mx_pitched_value_values[0]); i++) { + if (strcmp(s, mx_pitched_value_values[i]) == 0) { + *out = (MxPitchedValue)i; + return true; + } + } + *out = (MxPitchedValue)0; + return false; +} + +MxPitchedValue mx_pitched_value_parse(const char *s) { + MxPitchedValue v; + mx_pitched_value_try_parse(s, &v); + return v; +} + +const char *mx_pitched_value_to_string(MxPitchedValue v) { + if ((size_t)v >= sizeof(mx_pitched_value_values) / sizeof(mx_pitched_value_values[0])) + return mx_pitched_value_values[0]; + return mx_pitched_value_values[v]; +} diff --git a/gen/test/c/mx/mx_pitched_value.h b/gen/test/c/mx/mx_pitched_value.h new file mode 100644 index 000000000..1c973be4c --- /dev/null +++ b/gen/test/c/mx/mx_pitched_value.h @@ -0,0 +1,32 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PITCHED_VALUE_H +#define MX_PITCHED_VALUE_H + +#include + +/* + * The pitched-value type represents pictograms for pitched percussion instruments. The chimes and + * tubular chimes values distinguish the single-line and double-line versions of the pictogram. + */ +typedef enum { + MX_PITCHED_VALUE_CELESTA = 0, + MX_PITCHED_VALUE_CHIMES = 1, + MX_PITCHED_VALUE_GLOCKENSPIEL = 2, + MX_PITCHED_VALUE_LITHOPHONE = 3, + MX_PITCHED_VALUE_MALLET = 4, + MX_PITCHED_VALUE_MARIMBA = 5, + MX_PITCHED_VALUE_STEEL_DRUMS = 6, + MX_PITCHED_VALUE_TUBAPHONE = 7, + MX_PITCHED_VALUE_TUBULAR_CHIMES = 8, + MX_PITCHED_VALUE_VIBRAPHONE = 9, + MX_PITCHED_VALUE_XYLOPHONE = 10 +} MxPitchedValue; + +bool mx_pitched_value_try_parse(const char *s, MxPitchedValue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxPitchedValue mx_pitched_value_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_pitched_value_to_string(MxPitchedValue v); + +#endif /* MX_PITCHED_VALUE_H */ diff --git a/gen/test/c/mx/mx_positive_divisions.c b/gen/test/c/mx/mx_positive_divisions.c new file mode 100644 index 000000000..7acf5468b --- /dev/null +++ b/gen/test/c/mx/mx_positive_divisions.c @@ -0,0 +1,30 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_positive_divisions.h" + +#include "mx_runtime.h" + +static MxPositiveDivisions mx_positive_divisions_clamp(double v) { + if (v <= 0.0) + v = 1e-06; + return (MxPositiveDivisions)v; +} + +bool mx_positive_divisions_try_parse(const char *s, MxPositiveDivisions *out) { + double v; + if (!mx_try_parse_decimal(s, &v)) { + *out = (MxPositiveDivisions)0; + return false; + } + *out = mx_positive_divisions_clamp(v); + return true; +} + +MxPositiveDivisions mx_positive_divisions_parse(const char *s) { + double v = mx_parse_decimal(s); + return mx_positive_divisions_clamp(v); +} + +char *mx_positive_divisions_to_string(MxPositiveDivisions v) { + return mx_format_decimal((double)v); +} diff --git a/gen/test/c/mx/mx_positive_divisions.h b/gen/test/c/mx/mx_positive_divisions.h new file mode 100644 index 000000000..193f5ae9b --- /dev/null +++ b/gen/test/c/mx/mx_positive_divisions.h @@ -0,0 +1,20 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_POSITIVE_DIVISIONS_H +#define MX_POSITIVE_DIVISIONS_H + +#include + +/* + * The positive-divisions type restricts divisions values to positive numbers. + */ +typedef double MxPositiveDivisions; + +/* Strict parse, then clamps into the declared range. */ +bool mx_positive_divisions_try_parse(const char *s, MxPositiveDivisions *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxPositiveDivisions mx_positive_divisions_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_positive_divisions_to_string(MxPositiveDivisions v); + +#endif /* MX_POSITIVE_DIVISIONS_H */ diff --git a/gen/test/c/mx/mx_positive_integer_or_empty.c b/gen/test/c/mx/mx_positive_integer_or_empty.c new file mode 100644 index 000000000..b4ad3a5d9 --- /dev/null +++ b/gen/test/c/mx/mx_positive_integer_or_empty.c @@ -0,0 +1,48 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_positive_integer_or_empty.h" + +#include "mx_runtime.h" +#include +#include + +bool mx_positive_integer_or_empty_try_parse(const char *s, MxPositiveIntegerOrEmpty *out) { + MxPositiveIntegerOrEmpty v; + memset(&v, 0, sizeof(v)); + if (mx_try_parse_int(s, &v.positive_integer)) { + v.kind = MX_POSITIVE_INTEGER_OR_EMPTY_KIND_POSITIVE_INTEGER; + *out = v; + return true; + } + if (strcmp(s, "") == 0) { + v.kind = MX_POSITIVE_INTEGER_OR_EMPTY_KIND_EMPTY; + *out = v; + return true; + } + *out = v; + return false; +} + +MxPositiveIntegerOrEmpty mx_positive_integer_or_empty_parse(const char *s) { + MxPositiveIntegerOrEmpty v; + if (mx_positive_integer_or_empty_try_parse(s, &v)) + return v; + memset(&v, 0, sizeof(v)); + v.kind = MX_POSITIVE_INTEGER_OR_EMPTY_KIND_POSITIVE_INTEGER; + v.positive_integer = mx_parse_int(s); + return v; +} + +char *mx_positive_integer_or_empty_to_string(MxPositiveIntegerOrEmpty v) { + switch (v.kind) { + case MX_POSITIVE_INTEGER_OR_EMPTY_KIND_EMPTY: + return mx_strdup(""); + default: + return mx_format_int(v.positive_integer); + } +} + +void mx_positive_integer_or_empty_free(MxPositiveIntegerOrEmpty *v) { + if (!v) + return; +} diff --git a/gen/test/c/mx/mx_positive_integer_or_empty.h b/gen/test/c/mx/mx_positive_integer_or_empty.h new file mode 100644 index 000000000..5aa1d08a7 --- /dev/null +++ b/gen/test/c/mx/mx_positive_integer_or_empty.h @@ -0,0 +1,29 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_POSITIVE_INTEGER_OR_EMPTY_H +#define MX_POSITIVE_INTEGER_OR_EMPTY_H + +#include + +/* + * The positive-integer-or-empty values can be either a positive integer or an empty string. + */ +typedef enum { + MX_POSITIVE_INTEGER_OR_EMPTY_KIND_POSITIVE_INTEGER = 0, + MX_POSITIVE_INTEGER_OR_EMPTY_KIND_EMPTY = 1 +} MxPositiveIntegerOrEmptyKind; + +typedef struct { + MxPositiveIntegerOrEmptyKind kind; + long positive_integer; +} MxPositiveIntegerOrEmpty; + +/* Tries each union member in schema order. */ +bool mx_positive_integer_or_empty_try_parse(const char *s, MxPositiveIntegerOrEmpty *out); +/* Lenient: an unmatched input is absorbed by the first member. */ +MxPositiveIntegerOrEmpty mx_positive_integer_or_empty_parse(const char *s); +/* The wire spelling of whichever member is held. Malloc'd. */ +char *mx_positive_integer_or_empty_to_string(MxPositiveIntegerOrEmpty v); +void mx_positive_integer_or_empty_free(MxPositiveIntegerOrEmpty *v); + +#endif /* MX_POSITIVE_INTEGER_OR_EMPTY_H */ diff --git a/gen/test/c/mx/mx_principal_voice_symbol.c b/gen/test/c/mx/mx_principal_voice_symbol.c new file mode 100644 index 000000000..0a4d3780a --- /dev/null +++ b/gen/test/c/mx/mx_principal_voice_symbol.c @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_principal_voice_symbol.h" + +#include + +static const char *const mx_principal_voice_symbol_values[] = { + "Hauptstimme", + "Nebenstimme", + "plain", + "none", +}; + +bool mx_principal_voice_symbol_try_parse(const char *s, MxPrincipalVoiceSymbol *out) { + for (size_t i = 0; i < sizeof(mx_principal_voice_symbol_values) / sizeof(mx_principal_voice_symbol_values[0]); i++) { + if (strcmp(s, mx_principal_voice_symbol_values[i]) == 0) { + *out = (MxPrincipalVoiceSymbol)i; + return true; + } + } + *out = (MxPrincipalVoiceSymbol)0; + return false; +} + +MxPrincipalVoiceSymbol mx_principal_voice_symbol_parse(const char *s) { + MxPrincipalVoiceSymbol v; + mx_principal_voice_symbol_try_parse(s, &v); + return v; +} + +const char *mx_principal_voice_symbol_to_string(MxPrincipalVoiceSymbol v) { + if ((size_t)v >= sizeof(mx_principal_voice_symbol_values) / sizeof(mx_principal_voice_symbol_values[0])) + return mx_principal_voice_symbol_values[0]; + return mx_principal_voice_symbol_values[v]; +} diff --git a/gen/test/c/mx/mx_principal_voice_symbol.h b/gen/test/c/mx/mx_principal_voice_symbol.h new file mode 100644 index 000000000..859ab977c --- /dev/null +++ b/gen/test/c/mx/mx_principal_voice_symbol.h @@ -0,0 +1,27 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PRINCIPAL_VOICE_SYMBOL_H +#define MX_PRINCIPAL_VOICE_SYMBOL_H + +#include + +/* + * The principal-voice-symbol type represents the type of symbol used to indicate the start of a + * principal or secondary voice. The "plain" value represents a plain square bracket. The value of + * "none" is used for analysis markup when the principal-voice element does not have a corresponding + * appearance in the score. + */ +typedef enum { + MX_PRINCIPAL_VOICE_SYMBOL_HAUPTSTIMME = 0, + MX_PRINCIPAL_VOICE_SYMBOL_NEBENSTIMME = 1, + MX_PRINCIPAL_VOICE_SYMBOL_PLAIN = 2, + MX_PRINCIPAL_VOICE_SYMBOL_NONE = 3 +} MxPrincipalVoiceSymbol; + +bool mx_principal_voice_symbol_try_parse(const char *s, MxPrincipalVoiceSymbol *out); +/* Lenient: unknown input falls back to the first variant. */ +MxPrincipalVoiceSymbol mx_principal_voice_symbol_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_principal_voice_symbol_to_string(MxPrincipalVoiceSymbol v); + +#endif /* MX_PRINCIPAL_VOICE_SYMBOL_H */ diff --git a/gen/test/c/mx/mx_right_left_middle.c b/gen/test/c/mx/mx_right_left_middle.c new file mode 100644 index 000000000..56794bfc7 --- /dev/null +++ b/gen/test/c/mx/mx_right_left_middle.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_right_left_middle.h" + +#include + +static const char *const mx_right_left_middle_values[] = { + "right", + "left", + "middle", +}; + +bool mx_right_left_middle_try_parse(const char *s, MxRightLeftMiddle *out) { + for (size_t i = 0; i < sizeof(mx_right_left_middle_values) / sizeof(mx_right_left_middle_values[0]); i++) { + if (strcmp(s, mx_right_left_middle_values[i]) == 0) { + *out = (MxRightLeftMiddle)i; + return true; + } + } + *out = (MxRightLeftMiddle)0; + return false; +} + +MxRightLeftMiddle mx_right_left_middle_parse(const char *s) { + MxRightLeftMiddle v; + mx_right_left_middle_try_parse(s, &v); + return v; +} + +const char *mx_right_left_middle_to_string(MxRightLeftMiddle v) { + if ((size_t)v >= sizeof(mx_right_left_middle_values) / sizeof(mx_right_left_middle_values[0])) + return mx_right_left_middle_values[0]; + return mx_right_left_middle_values[v]; +} diff --git a/gen/test/c/mx/mx_right_left_middle.h b/gen/test/c/mx/mx_right_left_middle.h new file mode 100644 index 000000000..ef7ec6b3f --- /dev/null +++ b/gen/test/c/mx/mx_right_left_middle.h @@ -0,0 +1,23 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_RIGHT_LEFT_MIDDLE_H +#define MX_RIGHT_LEFT_MIDDLE_H + +#include + +/* + * The right-left-middle type is used to specify barline location. + */ +typedef enum { + MX_RIGHT_LEFT_MIDDLE_RIGHT = 0, + MX_RIGHT_LEFT_MIDDLE_LEFT = 1, + MX_RIGHT_LEFT_MIDDLE_MIDDLE = 2 +} MxRightLeftMiddle; + +bool mx_right_left_middle_try_parse(const char *s, MxRightLeftMiddle *out); +/* Lenient: unknown input falls back to the first variant. */ +MxRightLeftMiddle mx_right_left_middle_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_right_left_middle_to_string(MxRightLeftMiddle v); + +#endif /* MX_RIGHT_LEFT_MIDDLE_H */ diff --git a/gen/test/c/mx/mx_rotation_degrees.c b/gen/test/c/mx/mx_rotation_degrees.c new file mode 100644 index 000000000..45f9d512d --- /dev/null +++ b/gen/test/c/mx/mx_rotation_degrees.c @@ -0,0 +1,32 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_rotation_degrees.h" + +#include "mx_runtime.h" + +static MxRotationDegrees mx_rotation_degrees_clamp(double v) { + if (v < -180.0) + v = -180.0; + if (v > 180.0) + v = 180.0; + return (MxRotationDegrees)v; +} + +bool mx_rotation_degrees_try_parse(const char *s, MxRotationDegrees *out) { + double v; + if (!mx_try_parse_decimal(s, &v)) { + *out = (MxRotationDegrees)0; + return false; + } + *out = mx_rotation_degrees_clamp(v); + return true; +} + +MxRotationDegrees mx_rotation_degrees_parse(const char *s) { + double v = mx_parse_decimal(s); + return mx_rotation_degrees_clamp(v); +} + +char *mx_rotation_degrees_to_string(MxRotationDegrees v) { + return mx_format_decimal((double)v); +} diff --git a/gen/test/c/mx/mx_rotation_degrees.h b/gen/test/c/mx/mx_rotation_degrees.h new file mode 100644 index 000000000..79c0ee3d8 --- /dev/null +++ b/gen/test/c/mx/mx_rotation_degrees.h @@ -0,0 +1,21 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ROTATION_DEGREES_H +#define MX_ROTATION_DEGREES_H + +#include + +/* + * The rotation-degrees type specifies rotation, pan, and elevation values in degrees. Values range + * from -180 to 180. + */ +typedef double MxRotationDegrees; + +/* Strict parse, then clamps into the declared range. */ +bool mx_rotation_degrees_try_parse(const char *s, MxRotationDegrees *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxRotationDegrees mx_rotation_degrees_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_rotation_degrees_to_string(MxRotationDegrees v); + +#endif /* MX_ROTATION_DEGREES_H */ diff --git a/gen/test/c/mx/mx_runtime.c b/gen/test/c/mx/mx_runtime.c new file mode 100644 index 000000000..ea3619b72 --- /dev/null +++ b/gen/test/c/mx/mx_runtime.c @@ -0,0 +1,89 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_runtime.h" + +#include +#include +#include +#include + +bool mx_try_parse_decimal(const char *s, double *out) { + *out = 0; + if (!s || !*s) + return false; + char *end = NULL; + double v = strtod(s, &end); + if (end == s) + return false; + while (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r') + end++; + if (*end != '\0') + return false; + if (isnan(v) || isinf(v)) + return false; + *out = v; + return true; +} + +double mx_parse_decimal(const char *s) { + double v = 0; + mx_try_parse_decimal(s, &v); + return v; +} + +bool mx_try_parse_int(const char *s, long *out) { + *out = 0; + if (!s || !*s) + return false; + char *end = NULL; + long v = strtol(s, &end, 10); + if (end == s) + return false; + while (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r') + end++; + if (*end != '\0') + return false; + *out = v; + return true; +} + +long mx_parse_int(const char *s) { + long v = 0; + if (mx_try_parse_int(s, &v)) + return v; + double d = 0; + if (mx_try_parse_decimal(s, &d)) + return (long)d; /* decimal-looking input truncates toward zero */ + return 0; +} + +char *mx_format_decimal(double v) { + char buf[64]; + snprintf(buf, sizeof(buf), "%.9f", v); + /* Trim trailing zeros, then a trailing dot; canonicalize "-0" to "0". */ + size_t len = strlen(buf); + while (len > 0 && buf[len - 1] == '0') + buf[--len] = '\0'; + if (len > 0 && buf[len - 1] == '.') + buf[--len] = '\0'; + if (strcmp(buf, "-0") == 0) { + buf[0] = '0'; + buf[1] = '\0'; + } + return mx_strdup(buf); +} + +char *mx_format_int(long v) { + char buf[32]; + snprintf(buf, sizeof(buf), "%ld", v); + return mx_strdup(buf); +} + +char *mx_strdup(const char *s) { + if (!s) + s = ""; + size_t n = strlen(s) + 1; + char *out = malloc(n); + memcpy(out, s, n); + return out; +} diff --git a/gen/test/c/mx/mx_runtime.h b/gen/test/c/mx/mx_runtime.h new file mode 100644 index 000000000..efcd0107e --- /dev/null +++ b/gen/test/c/mx/mx_runtime.h @@ -0,0 +1,26 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_RUNTIME_H +#define MX_RUNTIME_H + +#include + +/* Shared parse/format helpers for the generated MusicXML types. Lenient + parses never fail: unparseable input becomes 0 (range clamping is the + typed wrappers' job). Formatting returns malloc'd strings the caller + frees. */ + +bool mx_try_parse_decimal(const char *s, double *out); +double mx_parse_decimal(const char *s); +bool mx_try_parse_int(const char *s, long *out); +long mx_parse_int(const char *s); + +/* Shortest decimal spelling without exponent notation (8.5 -> "8.5", + 4 -> "4", 1e-6 -> "0.000001"). Malloc'd; caller frees. */ +char *mx_format_decimal(double v); +char *mx_format_int(long v); + +/* strdup that maps NULL to "". Malloc'd; caller frees. */ +char *mx_strdup(const char *s); + +#endif /* MX_RUNTIME_H */ diff --git a/gen/test/c/mx/mx_semi_pitched.c b/gen/test/c/mx/mx_semi_pitched.c new file mode 100644 index 000000000..91f61720e --- /dev/null +++ b/gen/test/c/mx/mx_semi_pitched.c @@ -0,0 +1,37 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_semi_pitched.h" + +#include + +static const char *const mx_semi_pitched_values[] = { + "high", + "medium-high", + "medium", + "medium-low", + "low", + "very-low", +}; + +bool mx_semi_pitched_try_parse(const char *s, MxSemiPitched *out) { + for (size_t i = 0; i < sizeof(mx_semi_pitched_values) / sizeof(mx_semi_pitched_values[0]); i++) { + if (strcmp(s, mx_semi_pitched_values[i]) == 0) { + *out = (MxSemiPitched)i; + return true; + } + } + *out = (MxSemiPitched)0; + return false; +} + +MxSemiPitched mx_semi_pitched_parse(const char *s) { + MxSemiPitched v; + mx_semi_pitched_try_parse(s, &v); + return v; +} + +const char *mx_semi_pitched_to_string(MxSemiPitched v) { + if ((size_t)v >= sizeof(mx_semi_pitched_values) / sizeof(mx_semi_pitched_values[0])) + return mx_semi_pitched_values[0]; + return mx_semi_pitched_values[v]; +} diff --git a/gen/test/c/mx/mx_semi_pitched.h b/gen/test/c/mx/mx_semi_pitched.h new file mode 100644 index 000000000..ce49b711e --- /dev/null +++ b/gen/test/c/mx/mx_semi_pitched.h @@ -0,0 +1,26 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SEMI_PITCHED_H +#define MX_SEMI_PITCHED_H + +#include + +/* + * The semi-pitched type represents categories of indefinite pitch for percussion instruments. + */ +typedef enum { + MX_SEMI_PITCHED_HIGH = 0, + MX_SEMI_PITCHED_MEDIUM_HIGH = 1, + MX_SEMI_PITCHED_MEDIUM = 2, + MX_SEMI_PITCHED_MEDIUM_LOW = 3, + MX_SEMI_PITCHED_LOW = 4, + MX_SEMI_PITCHED_VERY_LOW = 5 +} MxSemiPitched; + +bool mx_semi_pitched_try_parse(const char *s, MxSemiPitched *out); +/* Lenient: unknown input falls back to the first variant. */ +MxSemiPitched mx_semi_pitched_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_semi_pitched_to_string(MxSemiPitched v); + +#endif /* MX_SEMI_PITCHED_H */ diff --git a/gen/test/c/mx/mx_semitones.c b/gen/test/c/mx/mx_semitones.c new file mode 100644 index 000000000..e63030ebc --- /dev/null +++ b/gen/test/c/mx/mx_semitones.c @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_semitones.h" + +#include "mx_runtime.h" + +bool mx_semitones_try_parse(const char *s, MxSemitones *out) { + double v; + if (!mx_try_parse_decimal(s, &v)) { + *out = (MxSemitones)0; + return false; + } + *out = (MxSemitones)v; + return true; +} + +MxSemitones mx_semitones_parse(const char *s) { + double v = mx_parse_decimal(s); + return (MxSemitones)v; +} + +char *mx_semitones_to_string(MxSemitones v) { + return mx_format_decimal((double)v); +} diff --git a/gen/test/c/mx/mx_semitones.h b/gen/test/c/mx/mx_semitones.h new file mode 100644 index 000000000..4e184fd1a --- /dev/null +++ b/gen/test/c/mx/mx_semitones.h @@ -0,0 +1,22 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SEMITONES_H +#define MX_SEMITONES_H + +#include + +/* + * The semitones type is a number representing semitones, used for chromatic alteration. A value of + * -1 corresponds to a flat and a value of 1 to a sharp. Decimal values like 0.5 (quarter tone + * sharp) are used for microtones. + */ +typedef double MxSemitones; + +/* Strict parse, then clamps into the declared range. */ +bool mx_semitones_try_parse(const char *s, MxSemitones *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxSemitones mx_semitones_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_semitones_to_string(MxSemitones v); + +#endif /* MX_SEMITONES_H */ diff --git a/gen/test/c/mx/mx_show_frets.c b/gen/test/c/mx/mx_show_frets.c new file mode 100644 index 000000000..706d9b0f6 --- /dev/null +++ b/gen/test/c/mx/mx_show_frets.c @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_show_frets.h" + +#include + +static const char *const mx_show_frets_values[] = { + "numbers", + "letters", +}; + +bool mx_show_frets_try_parse(const char *s, MxShowFrets *out) { + for (size_t i = 0; i < sizeof(mx_show_frets_values) / sizeof(mx_show_frets_values[0]); i++) { + if (strcmp(s, mx_show_frets_values[i]) == 0) { + *out = (MxShowFrets)i; + return true; + } + } + *out = (MxShowFrets)0; + return false; +} + +MxShowFrets mx_show_frets_parse(const char *s) { + MxShowFrets v; + mx_show_frets_try_parse(s, &v); + return v; +} + +const char *mx_show_frets_to_string(MxShowFrets v) { + if ((size_t)v >= sizeof(mx_show_frets_values) / sizeof(mx_show_frets_values[0])) + return mx_show_frets_values[0]; + return mx_show_frets_values[v]; +} diff --git a/gen/test/c/mx/mx_show_frets.h b/gen/test/c/mx/mx_show_frets.h new file mode 100644 index 000000000..ddd278be2 --- /dev/null +++ b/gen/test/c/mx/mx_show_frets.h @@ -0,0 +1,23 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SHOW_FRETS_H +#define MX_SHOW_FRETS_H + +#include + +/* + * The show-frets type indicates whether to show tablature frets as numbers (0, 1, 2) or letters (a, + * b, c). The default choice is numbers. + */ +typedef enum { + MX_SHOW_FRETS_NUMBERS = 0, + MX_SHOW_FRETS_LETTERS = 1 +} MxShowFrets; + +bool mx_show_frets_try_parse(const char *s, MxShowFrets *out); +/* Lenient: unknown input falls back to the first variant. */ +MxShowFrets mx_show_frets_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_show_frets_to_string(MxShowFrets v); + +#endif /* MX_SHOW_FRETS_H */ diff --git a/gen/test/c/mx/mx_show_tuplet.c b/gen/test/c/mx/mx_show_tuplet.c new file mode 100644 index 000000000..0d6fc307f --- /dev/null +++ b/gen/test/c/mx/mx_show_tuplet.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_show_tuplet.h" + +#include + +static const char *const mx_show_tuplet_values[] = { + "actual", + "both", + "none", +}; + +bool mx_show_tuplet_try_parse(const char *s, MxShowTuplet *out) { + for (size_t i = 0; i < sizeof(mx_show_tuplet_values) / sizeof(mx_show_tuplet_values[0]); i++) { + if (strcmp(s, mx_show_tuplet_values[i]) == 0) { + *out = (MxShowTuplet)i; + return true; + } + } + *out = (MxShowTuplet)0; + return false; +} + +MxShowTuplet mx_show_tuplet_parse(const char *s) { + MxShowTuplet v; + mx_show_tuplet_try_parse(s, &v); + return v; +} + +const char *mx_show_tuplet_to_string(MxShowTuplet v) { + if ((size_t)v >= sizeof(mx_show_tuplet_values) / sizeof(mx_show_tuplet_values[0])) + return mx_show_tuplet_values[0]; + return mx_show_tuplet_values[v]; +} diff --git a/gen/test/c/mx/mx_show_tuplet.h b/gen/test/c/mx/mx_show_tuplet.h new file mode 100644 index 000000000..2a6b5fe6d --- /dev/null +++ b/gen/test/c/mx/mx_show_tuplet.h @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SHOW_TUPLET_H +#define MX_SHOW_TUPLET_H + +#include + +/* + * The show-tuplet type indicates whether to show a part of a tuplet relating to the tuplet-actual + * element, both the tuplet-actual and tuplet-normal elements, or neither. + */ +typedef enum { + MX_SHOW_TUPLET_ACTUAL = 0, + MX_SHOW_TUPLET_BOTH = 1, + MX_SHOW_TUPLET_NONE = 2 +} MxShowTuplet; + +bool mx_show_tuplet_try_parse(const char *s, MxShowTuplet *out); +/* Lenient: unknown input falls back to the first variant. */ +MxShowTuplet mx_show_tuplet_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_show_tuplet_to_string(MxShowTuplet v); + +#endif /* MX_SHOW_TUPLET_H */ diff --git a/gen/test/c/mx/mx_smufl_accidental_glyph_name.c b/gen/test/c/mx/mx_smufl_accidental_glyph_name.c new file mode 100644 index 000000000..360efb20c --- /dev/null +++ b/gen/test/c/mx/mx_smufl_accidental_glyph_name.c @@ -0,0 +1,9 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_smufl_accidental_glyph_name.h" + +#include "mx_runtime.h" + +MxSMUFLAccidentalGlyphName mx_smufl_accidental_glyph_name_parse(const char *s) { + return mx_strdup(s); +} diff --git a/gen/test/c/mx/mx_smufl_accidental_glyph_name.h b/gen/test/c/mx/mx_smufl_accidental_glyph_name.h new file mode 100644 index 000000000..ca685e911 --- /dev/null +++ b/gen/test/c/mx/mx_smufl_accidental_glyph_name.h @@ -0,0 +1,16 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SMUFL_ACCIDENTAL_GLYPH_NAME_H +#define MX_SMUFL_ACCIDENTAL_GLYPH_NAME_H + +/* + * The smufl-accidental-glyph-name type is used to reference a specific Standard Music Font Layout + * (SMuFL) accidental character. The value is a SMuFL canonical glyph name that starts with acc. + * Pattern (not enforced): acc\c+ + */ +typedef char *MxSMUFLAccidentalGlyphName; + +/* Malloc'd copy of the wire string; the value IS its spelling. */ +MxSMUFLAccidentalGlyphName mx_smufl_accidental_glyph_name_parse(const char *s); + +#endif /* MX_SMUFL_ACCIDENTAL_GLYPH_NAME_H */ diff --git a/gen/test/c/mx/mx_smufl_coda_glyph_name.c b/gen/test/c/mx/mx_smufl_coda_glyph_name.c new file mode 100644 index 000000000..edeb86fa2 --- /dev/null +++ b/gen/test/c/mx/mx_smufl_coda_glyph_name.c @@ -0,0 +1,9 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_smufl_coda_glyph_name.h" + +#include "mx_runtime.h" + +MxSMUFLCodaGlyphName mx_smufl_coda_glyph_name_parse(const char *s) { + return mx_strdup(s); +} diff --git a/gen/test/c/mx/mx_smufl_coda_glyph_name.h b/gen/test/c/mx/mx_smufl_coda_glyph_name.h new file mode 100644 index 000000000..5c5317b57 --- /dev/null +++ b/gen/test/c/mx/mx_smufl_coda_glyph_name.h @@ -0,0 +1,16 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SMUFL_CODA_GLYPH_NAME_H +#define MX_SMUFL_CODA_GLYPH_NAME_H + +/* + * The smufl-coda-glyph-name type is used to reference a specific Standard Music Font Layout (SMuFL) + * coda character. The value is a SMuFL canonical glyph name that starts with coda. Pattern (not + * enforced): coda\c* + */ +typedef char *MxSMUFLCodaGlyphName; + +/* Malloc'd copy of the wire string; the value IS its spelling. */ +MxSMUFLCodaGlyphName mx_smufl_coda_glyph_name_parse(const char *s); + +#endif /* MX_SMUFL_CODA_GLYPH_NAME_H */ diff --git a/gen/test/c/mx/mx_smufl_glyph_name.c b/gen/test/c/mx/mx_smufl_glyph_name.c new file mode 100644 index 000000000..6bf758990 --- /dev/null +++ b/gen/test/c/mx/mx_smufl_glyph_name.c @@ -0,0 +1,9 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_smufl_glyph_name.h" + +#include "mx_runtime.h" + +MxSMUFLGlyphName mx_smufl_glyph_name_parse(const char *s) { + return mx_strdup(s); +} diff --git a/gen/test/c/mx/mx_smufl_glyph_name.h b/gen/test/c/mx/mx_smufl_glyph_name.h new file mode 100644 index 000000000..5f35258bf --- /dev/null +++ b/gen/test/c/mx/mx_smufl_glyph_name.h @@ -0,0 +1,16 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SMUFL_GLYPH_NAME_H +#define MX_SMUFL_GLYPH_NAME_H + +/* + * The smufl-glyph-name type is used for attributes that reference a specific Standard Music Font + * Layout (SMuFL) character. The value is a SMuFL canonical glyph name, not a code point. For + * instance, the value for a standard piano pedal mark would be keyboardPedalPed, not U+E650. + */ +typedef char *MxSMUFLGlyphName; + +/* Malloc'd copy of the wire string; the value IS its spelling. */ +MxSMUFLGlyphName mx_smufl_glyph_name_parse(const char *s); + +#endif /* MX_SMUFL_GLYPH_NAME_H */ diff --git a/gen/test/c/mx/mx_smufl_lyrics_glyph_name.c b/gen/test/c/mx/mx_smufl_lyrics_glyph_name.c new file mode 100644 index 000000000..0ac59338a --- /dev/null +++ b/gen/test/c/mx/mx_smufl_lyrics_glyph_name.c @@ -0,0 +1,9 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_smufl_lyrics_glyph_name.h" + +#include "mx_runtime.h" + +MxSMUFLLyricsGlyphName mx_smufl_lyrics_glyph_name_parse(const char *s) { + return mx_strdup(s); +} diff --git a/gen/test/c/mx/mx_smufl_lyrics_glyph_name.h b/gen/test/c/mx/mx_smufl_lyrics_glyph_name.h new file mode 100644 index 000000000..19af70f30 --- /dev/null +++ b/gen/test/c/mx/mx_smufl_lyrics_glyph_name.h @@ -0,0 +1,16 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SMUFL_LYRICS_GLYPH_NAME_H +#define MX_SMUFL_LYRICS_GLYPH_NAME_H + +/* + * The smufl-lyrics-glyph-name type is used to reference a specific Standard Music Font Layout + * (SMuFL) lyrics elision character. The value is a SMuFL canonical glyph name that starts with + * lyrics. Pattern (not enforced): lyrics\c+ + */ +typedef char *MxSMUFLLyricsGlyphName; + +/* Malloc'd copy of the wire string; the value IS its spelling. */ +MxSMUFLLyricsGlyphName mx_smufl_lyrics_glyph_name_parse(const char *s); + +#endif /* MX_SMUFL_LYRICS_GLYPH_NAME_H */ diff --git a/gen/test/c/mx/mx_smufl_pictogram_glyph_name.c b/gen/test/c/mx/mx_smufl_pictogram_glyph_name.c new file mode 100644 index 000000000..002dd4360 --- /dev/null +++ b/gen/test/c/mx/mx_smufl_pictogram_glyph_name.c @@ -0,0 +1,9 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_smufl_pictogram_glyph_name.h" + +#include "mx_runtime.h" + +MxSMUFLPictogramGlyphName mx_smufl_pictogram_glyph_name_parse(const char *s) { + return mx_strdup(s); +} diff --git a/gen/test/c/mx/mx_smufl_pictogram_glyph_name.h b/gen/test/c/mx/mx_smufl_pictogram_glyph_name.h new file mode 100644 index 000000000..431b07708 --- /dev/null +++ b/gen/test/c/mx/mx_smufl_pictogram_glyph_name.h @@ -0,0 +1,16 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SMUFL_PICTOGRAM_GLYPH_NAME_H +#define MX_SMUFL_PICTOGRAM_GLYPH_NAME_H + +/* + * The smufl-pictogram-glyph-name type is used to reference a specific Standard Music Font Layout + * (SMuFL) percussion pictogram character. The value is a SMuFL canonical glyph name that starts + * with pict. Pattern (not enforced): pict\c+ + */ +typedef char *MxSMUFLPictogramGlyphName; + +/* Malloc'd copy of the wire string; the value IS its spelling. */ +MxSMUFLPictogramGlyphName mx_smufl_pictogram_glyph_name_parse(const char *s); + +#endif /* MX_SMUFL_PICTOGRAM_GLYPH_NAME_H */ diff --git a/gen/test/c/mx/mx_smufl_segno_glyph_name.c b/gen/test/c/mx/mx_smufl_segno_glyph_name.c new file mode 100644 index 000000000..54217836c --- /dev/null +++ b/gen/test/c/mx/mx_smufl_segno_glyph_name.c @@ -0,0 +1,9 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_smufl_segno_glyph_name.h" + +#include "mx_runtime.h" + +MxSMUFLSegnoGlyphName mx_smufl_segno_glyph_name_parse(const char *s) { + return mx_strdup(s); +} diff --git a/gen/test/c/mx/mx_smufl_segno_glyph_name.h b/gen/test/c/mx/mx_smufl_segno_glyph_name.h new file mode 100644 index 000000000..bfaf2cb18 --- /dev/null +++ b/gen/test/c/mx/mx_smufl_segno_glyph_name.h @@ -0,0 +1,16 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SMUFL_SEGNO_GLYPH_NAME_H +#define MX_SMUFL_SEGNO_GLYPH_NAME_H + +/* + * The smufl-segno-glyph-name type is used to reference a specific Standard Music Font Layout + * (SMuFL) segno character. The value is a SMuFL canonical glyph name that starts with segno. + * Pattern (not enforced): segno\c* + */ +typedef char *MxSMUFLSegnoGlyphName; + +/* Malloc'd copy of the wire string; the value IS its spelling. */ +MxSMUFLSegnoGlyphName mx_smufl_segno_glyph_name_parse(const char *s); + +#endif /* MX_SMUFL_SEGNO_GLYPH_NAME_H */ diff --git a/gen/test/c/mx/mx_sound_id.c b/gen/test/c/mx/mx_sound_id.c new file mode 100644 index 000000000..066daea4a --- /dev/null +++ b/gen/test/c/mx/mx_sound_id.c @@ -0,0 +1,917 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_sound_id.h" + +#include + +static const char *const mx_sound_id_values[] = { + "brass.alphorn", + "brass.alto-horn", + "brass.baritone-horn", + "brass.bugle", + "brass.bugle.alto", + "brass.bugle.baritone", + "brass.bugle.contrabass", + "brass.bugle.euphonium-bugle", + "brass.bugle.mellophone-bugle", + "brass.bugle.soprano", + "brass.cimbasso", + "brass.conch-shell", + "brass.cornet", + "brass.cornet.soprano", + "brass.cornett", + "brass.cornett.tenor", + "brass.cornettino", + "brass.didgeridoo", + "brass.euphonium", + "brass.fiscorn", + "brass.flugelhorn", + "brass.french-horn", + "brass.group", + "brass.group.synth", + "brass.helicon", + "brass.horagai", + "brass.kuhlohorn", + "brass.mellophone", + "brass.natural-horn", + "brass.ophicleide", + "brass.posthorn", + "brass.rag-dung", + "brass.sackbutt", + "brass.sackbutt.alto", + "brass.sackbutt.bass", + "brass.sackbutt.tenor", + "brass.saxhorn", + "brass.serpent", + "brass.shofar", + "brass.sousaphone", + "brass.trombone", + "brass.trombone.alto", + "brass.trombone.bass", + "brass.trombone.contrabass", + "brass.trombone.tenor", + "brass.trumpet", + "brass.trumpet.baroque", + "brass.trumpet.bass", + "brass.trumpet.bflat", + "brass.trumpet.c", + "brass.trumpet.d", + "brass.trumpet.piccolo", + "brass.trumpet.pocket", + "brass.trumpet.slide", + "brass.trumpet.tenor", + "brass.tuba", + "brass.tuba.bass", + "brass.tuba.subcontrabass", + "brass.vienna-horn", + "brass.vuvuzela", + "brass.wagner-tuba", + "drum.apentemma", + "drum.ashiko", + "drum.atabaque", + "drum.atoke", + "drum.atsimevu", + "drum.axatse", + "drum.bass-drum", + "drum.bata", + "drum.bata.itotele", + "drum.bata.iya", + "drum.bata.okonkolo", + "drum.bendir", + "drum.bodhran", + "drum.bombo", + "drum.bongo", + "drum.bougarabou", + "drum.buffalo-drum", + "drum.cajon", + "drum.chenda", + "drum.chu-daiko", + "drum.conga", + "drum.cuica", + "drum.dabakan", + "drum.daff", + "drum.dafli", + "drum.daibyosi", + "drum.damroo", + "drum.darabuka", + "drum.def", + "drum.dhol", + "drum.dholak", + "drum.djembe", + "drum.doira", + "drum.dondo", + "drum.doun-doun-ba", + "drum.duff", + "drum.dumbek", + "drum.fontomfrom", + "drum.frame-drum", + "drum.frame-drum.arabian", + "drum.geduk", + "drum.ghatam", + "drum.gome", + "drum.group", + "drum.group.chinese", + "drum.group.ewe", + "drum.group.indian", + "drum.group.set", + "drum.hand-drum", + "drum.hira-daiko", + "drum.ibo", + "drum.igihumurizo", + "drum.inyahura", + "drum.ishakwe", + "drum.jang-gu", + "drum.kagan", + "drum.kakko", + "drum.kanjira", + "drum.kendhang", + "drum.kendhang.ageng", + "drum.kendhang.ciblon", + "drum.kenkeni", + "drum.khol", + "drum.kick-drum", + "drum.kidi", + "drum.ko-daiko", + "drum.kpanlogo", + "drum.kudum", + "drum.lambeg", + "drum.lion-drum", + "drum.log-drum", + "drum.log-drum.african", + "drum.log-drum.native", + "drum.log-drum.nigerian", + "drum.madal", + "drum.maddale", + "drum.mridangam", + "drum.naal", + "drum.nagado-daiko", + "drum.nagara", + "drum.naqara", + "drum.o-daiko", + "drum.okawa", + "drum.okedo-daiko", + "drum.pahu-hula", + "drum.pakhawaj", + "drum.pandeiro", + "drum.pandero", + "drum.powwow", + "drum.pueblo", + "drum.repinique", + "drum.riq", + "drum.rototom", + "drum.sabar", + "drum.sakara", + "drum.sampho", + "drum.sangban", + "drum.shime-daiko", + "drum.slit-drum", + "drum.slit-drum.krin", + "drum.snare-drum", + "drum.snare-drum.electric", + "drum.sogo", + "drum.surdo", + "drum.tabla", + "drum.tabla.bayan", + "drum.tabla.dayan", + "drum.taiko", + "drum.talking", + "drum.tama", + "drum.tamborita", + "drum.tambourine", + "drum.tamte", + "drum.tangku", + "drum.tan-tan", + "drum.taphon", + "drum.tar", + "drum.tasha", + "drum.tenor-drum", + "drum.teponaxtli", + "drum.thavil", + "drum.the-box", + "drum.timbale", + "drum.timpani", + "drum.tinaja", + "drum.toere", + "drum.tombak", + "drum.tom-tom", + "drum.tom-tom.synth", + "drum.tsuzumi", + "drum.tumbak", + "drum.uchiwa-daiko", + "drum.udaku", + "drum.udu", + "drum.zarb", + "effect.aeolian-harp", + "effect.air-horn", + "effect.applause", + "effect.bass-string-slap", + "effect.bird", + "effect.bird.nightingale", + "effect.bird.tweet", + "effect.breath", + "effect.bubble", + "effect.bullroarer", + "effect.burst", + "effect.car", + "effect.car.crash", + "effect.car.engine", + "effect.car.pass", + "effect.car.stop", + "effect.crickets", + "effect.dog", + "effect.door.creak", + "effect.door.slam", + "effect.explosion", + "effect.flute-key-click", + "effect.footsteps", + "effect.frogs", + "effect.guitar-cutting", + "effect.guitar-fret", + "effect.gunshot", + "effect.hand-clap", + "effect.heartbeat", + "effect.helicopter", + "effect.high-q", + "effect.horse-gallop", + "effect.jet-plane", + "effect.laser-gun", + "effect.laugh", + "effect.lions-roar", + "effect.machine-gun", + "effect.marching-machine", + "effect.metronome-bell", + "effect.metronome-click", + "effect.pat", + "effect.punch", + "effect.rain", + "effect.scratch", + "effect.scream", + "effect.seashore", + "effect.siren", + "effect.slap", + "effect.snap", + "effect.stamp", + "effect.starship", + "effect.stream", + "effect.telephone-ring", + "effect.thunder", + "effect.train", + "effect.trash-can", + "effect.whip", + "effect.whistle", + "effect.whistle.mouth-siren", + "effect.whistle.police", + "effect.whistle.slide", + "effect.whistle.train", + "effect.wind", + "keyboard.accordion", + "keyboard.bandoneon", + "keyboard.celesta", + "keyboard.clavichord", + "keyboard.clavichord.synth", + "keyboard.concertina", + "keyboard.fortepiano", + "keyboard.harmonium", + "keyboard.harpsichord", + "keyboard.ondes-martenot", + "keyboard.organ", + "keyboard.organ.drawbar", + "keyboard.organ.percussive", + "keyboard.organ.pipe", + "keyboard.organ.reed", + "keyboard.organ.rotary", + "keyboard.piano", + "keyboard.piano.electric", + "keyboard.piano.grand", + "keyboard.piano.honky-tonk", + "keyboard.piano.prepared", + "keyboard.piano.toy", + "keyboard.piano.upright", + "keyboard.virginal", + "metal.adodo", + "metal.anvil", + "metal.babendil", + "metal.bells.agogo", + "metal.bells.almglocken", + "metal.bells.bell-plate", + "metal.bells.bell-tree", + "metal.bells.carillon", + "metal.bells.chimes", + "metal.bells.chimta", + "metal.bells.chippli", + "metal.bells.church", + "metal.bells.cowbell", + "metal.bells.dawuro", + "metal.bells.gankokwe", + "metal.bells.ghungroo", + "metal.bells.hatheli", + "metal.bells.jingle-bell", + "metal.bells.khartal", + "metal.bells.mark-tree", + "metal.bells.sistrum", + "metal.bells.sleigh-bells", + "metal.bells.temple", + "metal.bells.tibetan", + "metal.bells.tinklebell", + "metal.bells.trychel", + "metal.bells.wind-chimes", + "metal.bells.zills", + "metal.berimbau", + "metal.brake-drums", + "metal.crotales", + "metal.cymbal.bo", + "metal.cymbal.ceng-ceng", + "metal.cymbal.chabara", + "metal.cymbal.chinese", + "metal.cymbal.ching", + "metal.cymbal.clash", + "metal.cymbal.crash", + "metal.cymbal.finger", + "metal.cymbal.hand", + "metal.cymbal.kesi", + "metal.cymbal.manjeera", + "metal.cymbal.reverse", + "metal.cymbal.ride", + "metal.cymbal.sizzle", + "metal.cymbal.splash", + "metal.cymbal.suspended", + "metal.cymbal.tebyoshi", + "metal.cymbal.tibetan", + "metal.cymbal.tingsha", + "metal.flexatone", + "metal.gong", + "metal.gong.ageng", + "metal.gong.agung", + "metal.gong.chanchiki", + "metal.gong.chinese", + "metal.gong.gandingan", + "metal.gong.kempul", + "metal.gong.kempyang", + "metal.gong.ketuk", + "metal.gong.kkwenggwari", + "metal.gong.luo", + "metal.gong.singing", + "metal.gong.thai", + "metal.guira", + "metal.hang", + "metal.hi-hat", + "metal.jaw-harp", + "metal.kengong", + "metal.murchang", + "metal.musical-saw", + "metal.singing-bowl", + "metal.spoons", + "metal.steel-drums", + "metal.tamtam", + "metal.thundersheet", + "metal.triangle", + "metal.washboard", + "pitched-percussion.angklung", + "pitched-percussion.balafon", + "pitched-percussion.bell-lyre", + "pitched-percussion.bells", + "pitched-percussion.bianqing", + "pitched-percussion.bianzhong", + "pitched-percussion.bonang", + "pitched-percussion.cimbalom", + "pitched-percussion.crystal-glasses", + "pitched-percussion.dan-tam-thap-luc", + "pitched-percussion.fangxiang", + "pitched-percussion.gandingan-a-kayo", + "pitched-percussion.gangsa", + "pitched-percussion.gender", + "pitched-percussion.giying", + "pitched-percussion.glass-harmonica", + "pitched-percussion.glockenspiel", + "pitched-percussion.glockenspiel.alto", + "pitched-percussion.glockenspiel.soprano", + "pitched-percussion.gyil", + "pitched-percussion.hammer-dulcimer", + "pitched-percussion.handbells", + "pitched-percussion.kalimba", + "pitched-percussion.kantil", + "pitched-percussion.khim", + "pitched-percussion.kulintang", + "pitched-percussion.kulintang-a-kayo", + "pitched-percussion.kulintang-a-tiniok", + "pitched-percussion.likembe", + "pitched-percussion.luntang", + "pitched-percussion.marimba", + "pitched-percussion.marimba.bass", + "pitched-percussion.mbira", + "pitched-percussion.mbira.array", + "pitched-percussion.metallophone", + "pitched-percussion.metallophone.alto", + "pitched-percussion.metallophone.bass", + "pitched-percussion.metallophone.soprano", + "pitched-percussion.music-box", + "pitched-percussion.pelog-panerus", + "pitched-percussion.pemade", + "pitched-percussion.penyacah", + "pitched-percussion.ranat.ek", + "pitched-percussion.ranat.ek-lek", + "pitched-percussion.ranat.thum", + "pitched-percussion.ranat.thum-lek", + "pitched-percussion.reyong", + "pitched-percussion.sanza", + "pitched-percussion.saron-barung", + "pitched-percussion.saron-demong", + "pitched-percussion.saron-panerus", + "pitched-percussion.slendro-panerus", + "pitched-percussion.slentem", + "pitched-percussion.tsymbaly", + "pitched-percussion.tubes", + "pitched-percussion.tubular-bells", + "pitched-percussion.vibraphone", + "pitched-percussion.xylophone", + "pitched-percussion.xylophone.alto", + "pitched-percussion.xylophone.bass", + "pitched-percussion.xylophone.soprano", + "pitched-percussion.xylorimba", + "pitched-percussion.yangqin", + "pluck.archlute", + "pluck.autoharp", + "pluck.baglama", + "pluck.bajo", + "pluck.balalaika", + "pluck.balalaika.alto", + "pluck.balalaika.bass", + "pluck.balalaika.contrabass", + "pluck.balalaika.piccolo", + "pluck.balalaika.prima", + "pluck.balalaika.secunda", + "pluck.bandola", + "pluck.bandura", + "pluck.bandurria", + "pluck.banjo", + "pluck.banjo.tenor", + "pluck.banjolele", + "pluck.barbat", + "pluck.bass", + "pluck.bass.acoustic", + "pluck.bass.bolon", + "pluck.bass.electric", + "pluck.bass.fretless", + "pluck.bass.guitarron", + "pluck.bass.synth", + "pluck.bass.synth.lead", + "pluck.bass.washtub", + "pluck.bass.whamola", + "pluck.begena", + "pluck.biwa", + "pluck.bordonua", + "pluck.bouzouki", + "pluck.bouzouki.irish", + "pluck.celtic-harp", + "pluck.charango", + "pluck.chitarra-battente", + "pluck.cithara", + "pluck.cittern", + "pluck.cuatro", + "pluck.dan-bau", + "pluck.dan-nguyet", + "pluck.dan-tranh", + "pluck.dan-ty-ba", + "pluck.diddley-bow", + "pluck.domra", + "pluck.domu", + "pluck.dulcimer", + "pluck.dutar", + "pluck.duxianqin", + "pluck.ektara", + "pluck.geomungo", + "pluck.gottuvadhyam", + "pluck.guitar", + "pluck.guitar.acoustic", + "pluck.guitar.electric", + "pluck.guitar.nylon-string", + "pluck.guitar.pedal-steel", + "pluck.guitar.portuguese", + "pluck.guitar.requinto", + "pluck.guitar.resonator", + "pluck.guitar.steel-string", + "pluck.guitjo", + "pluck.guitjo.double-neck", + "pluck.guqin", + "pluck.guzheng", + "pluck.guzheng.choazhou", + "pluck.harp", + "pluck.harp-guitar", + "pluck.huapanguera", + "pluck.jarana-huasteca", + "pluck.jarana-jarocha", + "pluck.jarana-jarocha.mosquito", + "pluck.jarana-jarocha.primera", + "pluck.jarana-jarocha.segunda", + "pluck.jarana-jarocha.tercera", + "pluck.kabosy", + "pluck.kantele", + "pluck.kanun", + "pluck.kayagum", + "pluck.kobza", + "pluck.komuz", + "pluck.kora", + "pluck.koto", + "pluck.kutiyapi", + "pluck.langeleik", + "pluck.laud", + "pluck.lute", + "pluck.lyre", + "pluck.mandobass", + "pluck.mandocello", + "pluck.mandola", + "pluck.mandolin", + "pluck.mandolin.octave", + "pluck.mandora", + "pluck.mandore", + "pluck.marovany", + "pluck.musical-bow", + "pluck.ngoni", + "pluck.oud", + "pluck.pipa", + "pluck.psaltery", + "pluck.ruan", + "pluck.sallaneh", + "pluck.sanshin", + "pluck.santoor", + "pluck.sanxian", + "pluck.sarod", + "pluck.saung", + "pluck.saz", + "pluck.se", + "pluck.setar", + "pluck.shamisen", + "pluck.sitar", + "pluck.synth", + "pluck.synth.charang", + "pluck.synth.chiff", + "pluck.synth.stick", + "pluck.tambura", + "pluck.tambura.bulgarian", + "pluck.tambura.female", + "pluck.tambura.male", + "pluck.tar", + "pluck.theorbo", + "pluck.timple", + "pluck.tiple", + "pluck.tres", + "pluck.ukulele", + "pluck.ukulele.tenor", + "pluck.valiha", + "pluck.veena", + "pluck.veena.mohan", + "pluck.veena.rudra", + "pluck.veena.vichitra", + "pluck.vihuela", + "pluck.vihuela.mexican", + "pluck.xalam", + "pluck.yueqin", + "pluck.zither", + "pluck.zither.overtone", + "rattle.afoxe", + "rattle.birds", + "rattle.cabasa", + "rattle.caxixi", + "rattle.cog", + "rattle.ganza", + "rattle.hosho", + "rattle.jawbone", + "rattle.kayamba", + "rattle.kpoko-kpoko", + "rattle.lava-stones", + "rattle.maraca", + "rattle.rain-stick", + "rattle.ratchet", + "rattle.rattle", + "rattle.shaker", + "rattle.shaker.egg", + "rattle.shekere", + "rattle.sistre", + "rattle.televi", + "rattle.vibraslap", + "rattle.wasembe", + "strings.ajaeng", + "strings.arpeggione", + "strings.baryton", + "strings.cello", + "strings.cello.piccolo", + "strings.contrabass", + "strings.crwth", + "strings.dan-gao", + "strings.dihu", + "strings.erhu", + "strings.erxian", + "strings.esraj", + "strings.fiddle", + "strings.fiddle.hardanger", + "strings.gadulka", + "strings.gaohu", + "strings.gehu", + "strings.group", + "strings.group.synth", + "strings.haegeum", + "strings.hurdy-gurdy", + "strings.igil", + "strings.kamancha", + "strings.kokyu", + "strings.laruan", + "strings.leiqin", + "strings.lirone", + "strings.lyra.byzantine", + "strings.lyra.cretan", + "strings.morin-khuur", + "strings.nyckelharpa", + "strings.octobass", + "strings.rebab", + "strings.rebec", + "strings.sarangi", + "strings.stroh-violin", + "strings.tromba-marina", + "strings.vielle", + "strings.viol", + "strings.viol.alto", + "strings.viol.bass", + "strings.viol.tenor", + "strings.viol.treble", + "strings.viol.violone", + "strings.viola", + "strings.viola-damore", + "strings.violin", + "strings.violono.piccolo", + "strings.violotta", + "strings.yayli-tanbur", + "strings.yazheng", + "strings.zhonghu", + "synth.effects", + "synth.effects.atmosphere", + "synth.effects.brightness", + "synth.effects.crystal", + "synth.effects.echoes", + "synth.effects.goblins", + "synth.effects.rain", + "synth.effects.sci-fi", + "synth.effects.soundtrack", + "synth.group", + "synth.group.fifths", + "synth.group.orchestra", + "synth.pad", + "synth.pad.bowed", + "synth.pad.choir", + "synth.pad.halo", + "synth.pad.metallic", + "synth.pad.polysynth", + "synth.pad.sweep", + "synth.pad.warm", + "synth.theremin", + "synth.tone.sawtooth", + "synth.tone.sine", + "synth.tone.square", + "voice.aa", + "voice.alto", + "voice.aw", + "voice.baritone", + "voice.bass", + "voice.child", + "voice.countertenor", + "voice.doo", + "voice.ee", + "voice.female", + "voice.kazoo", + "voice.male", + "voice.mezzo-soprano", + "voice.mm", + "voice.oo", + "voice.percussion", + "voice.percussion.beatbox", + "voice.soprano", + "voice.synth", + "voice.talk-box", + "voice.tenor", + "voice.vocals", + "wind.flutes.bansuri", + "wind.flutes.blown-bottle", + "wind.flutes.calliope", + "wind.flutes.danso", + "wind.flutes.di-zi", + "wind.flutes.dvojnice", + "wind.flutes.fife", + "wind.flutes.flageolet", + "wind.flutes.flute", + "wind.flutes.flute.alto", + "wind.flutes.flute.bass", + "wind.flutes.flute.contra-alto", + "wind.flutes.flute.contrabass", + "wind.flutes.flute.double-contrabass", + "wind.flutes.flute.irish", + "wind.flutes.flute.piccolo", + "wind.flutes.flute.subcontrabass", + "wind.flutes.fujara", + "wind.flutes.gemshorn", + "wind.flutes.hocchiku", + "wind.flutes.hun", + "wind.flutes.kaval", + "wind.flutes.kawala", + "wind.flutes.khlui", + "wind.flutes.knotweed", + "wind.flutes.koncovka.alto", + "wind.flutes.koudi", + "wind.flutes.ney", + "wind.flutes.nohkan", + "wind.flutes.nose", + "wind.flutes.ocarina", + "wind.flutes.overtone.tenor", + "wind.flutes.palendag", + "wind.flutes.panpipes", + "wind.flutes.quena", + "wind.flutes.recorder", + "wind.flutes.recorder.alto", + "wind.flutes.recorder.bass", + "wind.flutes.recorder.contrabass", + "wind.flutes.recorder.descant", + "wind.flutes.recorder.garklein", + "wind.flutes.recorder.great-bass", + "wind.flutes.recorder.sopranino", + "wind.flutes.recorder.soprano", + "wind.flutes.recorder.tenor", + "wind.flutes.ryuteki", + "wind.flutes.shakuhachi", + "wind.flutes.shepherds-pipe", + "wind.flutes.shinobue", + "wind.flutes.shvi", + "wind.flutes.suling", + "wind.flutes.tarka", + "wind.flutes.tumpong", + "wind.flutes.venu", + "wind.flutes.whistle", + "wind.flutes.whistle.alto", + "wind.flutes.whistle.low-irish", + "wind.flutes.whistle.shiva", + "wind.flutes.whistle.slide", + "wind.flutes.whistle.tin", + "wind.flutes.whistle.tin.bflat", + "wind.flutes.whistle.tin.d", + "wind.flutes.xiao", + "wind.flutes.xun", + "wind.group", + "wind.jug", + "wind.pipes.bagpipes", + "wind.pipes.gaida", + "wind.pipes.highland", + "wind.pipes.uilleann", + "wind.pungi", + "wind.reed.albogue", + "wind.reed.alboka", + "wind.reed.algaita", + "wind.reed.arghul", + "wind.reed.basset-horn", + "wind.reed.bassoon", + "wind.reed.bawu", + "wind.reed.bifora", + "wind.reed.bombarde", + "wind.reed.chalumeau", + "wind.reed.clarinet", + "wind.reed.clarinet.a", + "wind.reed.clarinet.alto", + "wind.reed.clarinet.bass", + "wind.reed.clarinet.basset", + "wind.reed.clarinet.bflat", + "wind.reed.clarinet.contra-alto", + "wind.reed.clarinet.contrabass", + "wind.reed.clarinet.eflat", + "wind.reed.clarinet.piccolo.aflat", + "wind.reed.clarinette-damour", + "wind.reed.contrabass", + "wind.reed.contrabassoon", + "wind.reed.cornamuse", + "wind.reed.cromorne", + "wind.reed.crumhorn", + "wind.reed.crumhorn.alto", + "wind.reed.crumhorn.bass", + "wind.reed.crumhorn.great-bass", + "wind.reed.crumhorn.soprano", + "wind.reed.crumhorn.tenor", + "wind.reed.diple", + "wind.reed.diplica", + "wind.reed.duduk", + "wind.reed.dulcian", + "wind.reed.dulzaina", + "wind.reed.english-horn", + "wind.reed.guanzi", + "wind.reed.harmonica", + "wind.reed.harmonica.bass", + "wind.reed.heckel-clarina", + "wind.reed.heckelphone", + "wind.reed.heckelphone.piccolo", + "wind.reed.heckelphone-clarinet", + "wind.reed.hichiriki", + "wind.reed.hirtenschalmei", + "wind.reed.hne", + "wind.reed.hornpipe", + "wind.reed.houguan", + "wind.reed.hulusi", + "wind.reed.jogi-baja", + "wind.reed.ken-bau", + "wind.reed.khaen-mouth-organ", + "wind.reed.launeddas", + "wind.reed.maqrunah", + "wind.reed.melodica", + "wind.reed.mijwiz", + "wind.reed.mizmar", + "wind.reed.nadaswaram", + "wind.reed.oboe", + "wind.reed.oboe.bass", + "wind.reed.oboe.piccolo", + "wind.reed.oboe-da-caccia", + "wind.reed.oboe-damore", + "wind.reed.octavin", + "wind.reed.pi", + "wind.reed.pibgorn", + "wind.reed.piri", + "wind.reed.rackett", + "wind.reed.rauschpfeife", + "wind.reed.rhaita", + "wind.reed.rothphone", + "wind.reed.sarrusaphone", + "wind.reed.saxonette", + "wind.reed.saxophone", + "wind.reed.saxophone.alto", + "wind.reed.saxophone.aulochrome", + "wind.reed.saxophone.baritone", + "wind.reed.saxophone.bass", + "wind.reed.saxophone.contrabass", + "wind.reed.saxophone.melody", + "wind.reed.saxophone.mezzo-soprano", + "wind.reed.saxophone.sopranino", + "wind.reed.saxophone.sopranissimo", + "wind.reed.saxophone.soprano", + "wind.reed.saxophone.subcontrabass", + "wind.reed.saxophone.tenor", + "wind.reed.shawm", + "wind.reed.shenai", + "wind.reed.sheng", + "wind.reed.sipsi", + "wind.reed.sopila", + "wind.reed.sorna", + "wind.reed.sralai", + "wind.reed.suona", + "wind.reed.surnai", + "wind.reed.taepyeongso", + "wind.reed.tarogato", + "wind.reed.tarogato.ancient", + "wind.reed.trompeta-china", + "wind.reed.tubax", + "wind.reed.xaphoon", + "wind.reed.zhaleika", + "wind.reed.zurla", + "wind.reed.zurna", + "wood.agogo-block", + "wood.agung-a-tamlang", + "wood.ahoko", + "wood.bones", + "wood.castanets", + "wood.claves", + "wood.drum-sticks", + "wood.gourd", + "wood.granite-block", + "wood.guban", + "wood.guiro", + "wood.hyoushigi", + "wood.ipu", + "wood.jam-block", + "wood.kaekeeke", + "wood.kagul", + "wood.kalaau", + "wood.kashiklar", + "wood.kubing", + "wood.pan-clappers", + "wood.sand-block", + "wood.slapstick", + "wood.stir-drum", + "wood.temple-block", + "wood.tic-toc-block", + "wood.tonetang", + "wood.wood-block", +}; + +bool mx_sound_id_try_parse(const char *s, MxSoundID *out) { + for (size_t i = 0; i < sizeof(mx_sound_id_values) / sizeof(mx_sound_id_values[0]); i++) { + if (strcmp(s, mx_sound_id_values[i]) == 0) { + *out = (MxSoundID)i; + return true; + } + } + *out = (MxSoundID)0; + return false; +} + +MxSoundID mx_sound_id_parse(const char *s) { + MxSoundID v; + mx_sound_id_try_parse(s, &v); + return v; +} + +const char *mx_sound_id_to_string(MxSoundID v) { + if ((size_t)v >= sizeof(mx_sound_id_values) / sizeof(mx_sound_id_values[0])) + return mx_sound_id_values[0]; + return mx_sound_id_values[v]; +} diff --git a/gen/test/c/mx/mx_sound_id.h b/gen/test/c/mx/mx_sound_id.h new file mode 100644 index 000000000..913f84fa8 --- /dev/null +++ b/gen/test/c/mx/mx_sound_id.h @@ -0,0 +1,907 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SOUND_ID_H +#define MX_SOUND_ID_H + +#include + +/* + * Standard MusicXML instrument sound identifiers. The XSD types instrument-sound as xs:string and + * lists these values only in the sounds.xml companion file; the generator injects them here. + */ +typedef enum { + MX_SOUND_ID_BRASS_ALPHORN = 0, + MX_SOUND_ID_BRASS_ALTO_HORN = 1, + MX_SOUND_ID_BRASS_BARITONE_HORN = 2, + MX_SOUND_ID_BRASS_BUGLE = 3, + MX_SOUND_ID_BRASS_BUGLE_ALTO = 4, + MX_SOUND_ID_BRASS_BUGLE_BARITONE = 5, + MX_SOUND_ID_BRASS_BUGLE_CONTRABASS = 6, + MX_SOUND_ID_BRASS_BUGLE_EUPHONIUM_BUGLE = 7, + MX_SOUND_ID_BRASS_BUGLE_MELLOPHONE_BUGLE = 8, + MX_SOUND_ID_BRASS_BUGLE_SOPRANO = 9, + MX_SOUND_ID_BRASS_CIMBASSO = 10, + MX_SOUND_ID_BRASS_CONCH_SHELL = 11, + MX_SOUND_ID_BRASS_CORNET = 12, + MX_SOUND_ID_BRASS_CORNET_SOPRANO = 13, + MX_SOUND_ID_BRASS_CORNETT = 14, + MX_SOUND_ID_BRASS_CORNETT_TENOR = 15, + MX_SOUND_ID_BRASS_CORNETTINO = 16, + MX_SOUND_ID_BRASS_DIDGERIDOO = 17, + MX_SOUND_ID_BRASS_EUPHONIUM = 18, + MX_SOUND_ID_BRASS_FISCORN = 19, + MX_SOUND_ID_BRASS_FLUGELHORN = 20, + MX_SOUND_ID_BRASS_FRENCH_HORN = 21, + MX_SOUND_ID_BRASS_GROUP = 22, + MX_SOUND_ID_BRASS_GROUP_SYNTH = 23, + MX_SOUND_ID_BRASS_HELICON = 24, + MX_SOUND_ID_BRASS_HORAGAI = 25, + MX_SOUND_ID_BRASS_KUHLOHORN = 26, + MX_SOUND_ID_BRASS_MELLOPHONE = 27, + MX_SOUND_ID_BRASS_NATURAL_HORN = 28, + MX_SOUND_ID_BRASS_OPHICLEIDE = 29, + MX_SOUND_ID_BRASS_POSTHORN = 30, + MX_SOUND_ID_BRASS_RAG_DUNG = 31, + MX_SOUND_ID_BRASS_SACKBUTT = 32, + MX_SOUND_ID_BRASS_SACKBUTT_ALTO = 33, + MX_SOUND_ID_BRASS_SACKBUTT_BASS = 34, + MX_SOUND_ID_BRASS_SACKBUTT_TENOR = 35, + MX_SOUND_ID_BRASS_SAXHORN = 36, + MX_SOUND_ID_BRASS_SERPENT = 37, + MX_SOUND_ID_BRASS_SHOFAR = 38, + MX_SOUND_ID_BRASS_SOUSAPHONE = 39, + MX_SOUND_ID_BRASS_TROMBONE = 40, + MX_SOUND_ID_BRASS_TROMBONE_ALTO = 41, + MX_SOUND_ID_BRASS_TROMBONE_BASS = 42, + MX_SOUND_ID_BRASS_TROMBONE_CONTRABASS = 43, + MX_SOUND_ID_BRASS_TROMBONE_TENOR = 44, + MX_SOUND_ID_BRASS_TRUMPET = 45, + MX_SOUND_ID_BRASS_TRUMPET_BAROQUE = 46, + MX_SOUND_ID_BRASS_TRUMPET_BASS = 47, + MX_SOUND_ID_BRASS_TRUMPET_BFLAT = 48, + MX_SOUND_ID_BRASS_TRUMPET_C = 49, + MX_SOUND_ID_BRASS_TRUMPET_D = 50, + MX_SOUND_ID_BRASS_TRUMPET_PICCOLO = 51, + MX_SOUND_ID_BRASS_TRUMPET_POCKET = 52, + MX_SOUND_ID_BRASS_TRUMPET_SLIDE = 53, + MX_SOUND_ID_BRASS_TRUMPET_TENOR = 54, + MX_SOUND_ID_BRASS_TUBA = 55, + MX_SOUND_ID_BRASS_TUBA_BASS = 56, + MX_SOUND_ID_BRASS_TUBA_SUBCONTRABASS = 57, + MX_SOUND_ID_BRASS_VIENNA_HORN = 58, + MX_SOUND_ID_BRASS_VUVUZELA = 59, + MX_SOUND_ID_BRASS_WAGNER_TUBA = 60, + MX_SOUND_ID_DRUM_APENTEMMA = 61, + MX_SOUND_ID_DRUM_ASHIKO = 62, + MX_SOUND_ID_DRUM_ATABAQUE = 63, + MX_SOUND_ID_DRUM_ATOKE = 64, + MX_SOUND_ID_DRUM_ATSIMEVU = 65, + MX_SOUND_ID_DRUM_AXATSE = 66, + MX_SOUND_ID_DRUM_BASS_DRUM = 67, + MX_SOUND_ID_DRUM_BATA = 68, + MX_SOUND_ID_DRUM_BATA_ITOTELE = 69, + MX_SOUND_ID_DRUM_BATA_IYA = 70, + MX_SOUND_ID_DRUM_BATA_OKONKOLO = 71, + MX_SOUND_ID_DRUM_BENDIR = 72, + MX_SOUND_ID_DRUM_BODHRAN = 73, + MX_SOUND_ID_DRUM_BOMBO = 74, + MX_SOUND_ID_DRUM_BONGO = 75, + MX_SOUND_ID_DRUM_BOUGARABOU = 76, + MX_SOUND_ID_DRUM_BUFFALO_DRUM = 77, + MX_SOUND_ID_DRUM_CAJON = 78, + MX_SOUND_ID_DRUM_CHENDA = 79, + MX_SOUND_ID_DRUM_CHU_DAIKO = 80, + MX_SOUND_ID_DRUM_CONGA = 81, + MX_SOUND_ID_DRUM_CUICA = 82, + MX_SOUND_ID_DRUM_DABAKAN = 83, + MX_SOUND_ID_DRUM_DAFF = 84, + MX_SOUND_ID_DRUM_DAFLI = 85, + MX_SOUND_ID_DRUM_DAIBYOSI = 86, + MX_SOUND_ID_DRUM_DAMROO = 87, + MX_SOUND_ID_DRUM_DARABUKA = 88, + MX_SOUND_ID_DRUM_DEF = 89, + MX_SOUND_ID_DRUM_DHOL = 90, + MX_SOUND_ID_DRUM_DHOLAK = 91, + MX_SOUND_ID_DRUM_DJEMBE = 92, + MX_SOUND_ID_DRUM_DOIRA = 93, + MX_SOUND_ID_DRUM_DONDO = 94, + MX_SOUND_ID_DRUM_DOUN_DOUN_BA = 95, + MX_SOUND_ID_DRUM_DUFF = 96, + MX_SOUND_ID_DRUM_DUMBEK = 97, + MX_SOUND_ID_DRUM_FONTOMFROM = 98, + MX_SOUND_ID_DRUM_FRAME_DRUM = 99, + MX_SOUND_ID_DRUM_FRAME_DRUM_ARABIAN = 100, + MX_SOUND_ID_DRUM_GEDUK = 101, + MX_SOUND_ID_DRUM_GHATAM = 102, + MX_SOUND_ID_DRUM_GOME = 103, + MX_SOUND_ID_DRUM_GROUP = 104, + MX_SOUND_ID_DRUM_GROUP_CHINESE = 105, + MX_SOUND_ID_DRUM_GROUP_EWE = 106, + MX_SOUND_ID_DRUM_GROUP_INDIAN = 107, + MX_SOUND_ID_DRUM_GROUP_SET = 108, + MX_SOUND_ID_DRUM_HAND_DRUM = 109, + MX_SOUND_ID_DRUM_HIRA_DAIKO = 110, + MX_SOUND_ID_DRUM_IBO = 111, + MX_SOUND_ID_DRUM_IGIHUMURIZO = 112, + MX_SOUND_ID_DRUM_INYAHURA = 113, + MX_SOUND_ID_DRUM_ISHAKWE = 114, + MX_SOUND_ID_DRUM_JANG_GU = 115, + MX_SOUND_ID_DRUM_KAGAN = 116, + MX_SOUND_ID_DRUM_KAKKO = 117, + MX_SOUND_ID_DRUM_KANJIRA = 118, + MX_SOUND_ID_DRUM_KENDHANG = 119, + MX_SOUND_ID_DRUM_KENDHANG_AGENG = 120, + MX_SOUND_ID_DRUM_KENDHANG_CIBLON = 121, + MX_SOUND_ID_DRUM_KENKENI = 122, + MX_SOUND_ID_DRUM_KHOL = 123, + MX_SOUND_ID_DRUM_KICK_DRUM = 124, + MX_SOUND_ID_DRUM_KIDI = 125, + MX_SOUND_ID_DRUM_KO_DAIKO = 126, + MX_SOUND_ID_DRUM_KPANLOGO = 127, + MX_SOUND_ID_DRUM_KUDUM = 128, + MX_SOUND_ID_DRUM_LAMBEG = 129, + MX_SOUND_ID_DRUM_LION_DRUM = 130, + MX_SOUND_ID_DRUM_LOG_DRUM = 131, + MX_SOUND_ID_DRUM_LOG_DRUM_AFRICAN = 132, + MX_SOUND_ID_DRUM_LOG_DRUM_NATIVE = 133, + MX_SOUND_ID_DRUM_LOG_DRUM_NIGERIAN = 134, + MX_SOUND_ID_DRUM_MADAL = 135, + MX_SOUND_ID_DRUM_MADDALE = 136, + MX_SOUND_ID_DRUM_MRIDANGAM = 137, + MX_SOUND_ID_DRUM_NAAL = 138, + MX_SOUND_ID_DRUM_NAGADO_DAIKO = 139, + MX_SOUND_ID_DRUM_NAGARA = 140, + MX_SOUND_ID_DRUM_NAQARA = 141, + MX_SOUND_ID_DRUM_O_DAIKO = 142, + MX_SOUND_ID_DRUM_OKAWA = 143, + MX_SOUND_ID_DRUM_OKEDO_DAIKO = 144, + MX_SOUND_ID_DRUM_PAHU_HULA = 145, + MX_SOUND_ID_DRUM_PAKHAWAJ = 146, + MX_SOUND_ID_DRUM_PANDEIRO = 147, + MX_SOUND_ID_DRUM_PANDERO = 148, + MX_SOUND_ID_DRUM_POWWOW = 149, + MX_SOUND_ID_DRUM_PUEBLO = 150, + MX_SOUND_ID_DRUM_REPINIQUE = 151, + MX_SOUND_ID_DRUM_RIQ = 152, + MX_SOUND_ID_DRUM_ROTOTOM = 153, + MX_SOUND_ID_DRUM_SABAR = 154, + MX_SOUND_ID_DRUM_SAKARA = 155, + MX_SOUND_ID_DRUM_SAMPHO = 156, + MX_SOUND_ID_DRUM_SANGBAN = 157, + MX_SOUND_ID_DRUM_SHIME_DAIKO = 158, + MX_SOUND_ID_DRUM_SLIT_DRUM = 159, + MX_SOUND_ID_DRUM_SLIT_DRUM_KRIN = 160, + MX_SOUND_ID_DRUM_SNARE_DRUM = 161, + MX_SOUND_ID_DRUM_SNARE_DRUM_ELECTRIC = 162, + MX_SOUND_ID_DRUM_SOGO = 163, + MX_SOUND_ID_DRUM_SURDO = 164, + MX_SOUND_ID_DRUM_TABLA = 165, + MX_SOUND_ID_DRUM_TABLA_BAYAN = 166, + MX_SOUND_ID_DRUM_TABLA_DAYAN = 167, + MX_SOUND_ID_DRUM_TAIKO = 168, + MX_SOUND_ID_DRUM_TALKING = 169, + MX_SOUND_ID_DRUM_TAMA = 170, + MX_SOUND_ID_DRUM_TAMBORITA = 171, + MX_SOUND_ID_DRUM_TAMBOURINE = 172, + MX_SOUND_ID_DRUM_TAMTE = 173, + MX_SOUND_ID_DRUM_TANGKU = 174, + MX_SOUND_ID_DRUM_TAN_TAN = 175, + MX_SOUND_ID_DRUM_TAPHON = 176, + MX_SOUND_ID_DRUM_TAR = 177, + MX_SOUND_ID_DRUM_TASHA = 178, + MX_SOUND_ID_DRUM_TENOR_DRUM = 179, + MX_SOUND_ID_DRUM_TEPONAXTLI = 180, + MX_SOUND_ID_DRUM_THAVIL = 181, + MX_SOUND_ID_DRUM_THE_BOX = 182, + MX_SOUND_ID_DRUM_TIMBALE = 183, + MX_SOUND_ID_DRUM_TIMPANI = 184, + MX_SOUND_ID_DRUM_TINAJA = 185, + MX_SOUND_ID_DRUM_TOERE = 186, + MX_SOUND_ID_DRUM_TOMBAK = 187, + MX_SOUND_ID_DRUM_TOM_TOM = 188, + MX_SOUND_ID_DRUM_TOM_TOM_SYNTH = 189, + MX_SOUND_ID_DRUM_TSUZUMI = 190, + MX_SOUND_ID_DRUM_TUMBAK = 191, + MX_SOUND_ID_DRUM_UCHIWA_DAIKO = 192, + MX_SOUND_ID_DRUM_UDAKU = 193, + MX_SOUND_ID_DRUM_UDU = 194, + MX_SOUND_ID_DRUM_ZARB = 195, + MX_SOUND_ID_EFFECT_AEOLIAN_HARP = 196, + MX_SOUND_ID_EFFECT_AIR_HORN = 197, + MX_SOUND_ID_EFFECT_APPLAUSE = 198, + MX_SOUND_ID_EFFECT_BASS_STRING_SLAP = 199, + MX_SOUND_ID_EFFECT_BIRD = 200, + MX_SOUND_ID_EFFECT_BIRD_NIGHTINGALE = 201, + MX_SOUND_ID_EFFECT_BIRD_TWEET = 202, + MX_SOUND_ID_EFFECT_BREATH = 203, + MX_SOUND_ID_EFFECT_BUBBLE = 204, + MX_SOUND_ID_EFFECT_BULLROARER = 205, + MX_SOUND_ID_EFFECT_BURST = 206, + MX_SOUND_ID_EFFECT_CAR = 207, + MX_SOUND_ID_EFFECT_CAR_CRASH = 208, + MX_SOUND_ID_EFFECT_CAR_ENGINE = 209, + MX_SOUND_ID_EFFECT_CAR_PASS = 210, + MX_SOUND_ID_EFFECT_CAR_STOP = 211, + MX_SOUND_ID_EFFECT_CRICKETS = 212, + MX_SOUND_ID_EFFECT_DOG = 213, + MX_SOUND_ID_EFFECT_DOOR_CREAK = 214, + MX_SOUND_ID_EFFECT_DOOR_SLAM = 215, + MX_SOUND_ID_EFFECT_EXPLOSION = 216, + MX_SOUND_ID_EFFECT_FLUTE_KEY_CLICK = 217, + MX_SOUND_ID_EFFECT_FOOTSTEPS = 218, + MX_SOUND_ID_EFFECT_FROGS = 219, + MX_SOUND_ID_EFFECT_GUITAR_CUTTING = 220, + MX_SOUND_ID_EFFECT_GUITAR_FRET = 221, + MX_SOUND_ID_EFFECT_GUNSHOT = 222, + MX_SOUND_ID_EFFECT_HAND_CLAP = 223, + MX_SOUND_ID_EFFECT_HEARTBEAT = 224, + MX_SOUND_ID_EFFECT_HELICOPTER = 225, + MX_SOUND_ID_EFFECT_HIGH_Q = 226, + MX_SOUND_ID_EFFECT_HORSE_GALLOP = 227, + MX_SOUND_ID_EFFECT_JET_PLANE = 228, + MX_SOUND_ID_EFFECT_LASER_GUN = 229, + MX_SOUND_ID_EFFECT_LAUGH = 230, + MX_SOUND_ID_EFFECT_LIONS_ROAR = 231, + MX_SOUND_ID_EFFECT_MACHINE_GUN = 232, + MX_SOUND_ID_EFFECT_MARCHING_MACHINE = 233, + MX_SOUND_ID_EFFECT_METRONOME_BELL = 234, + MX_SOUND_ID_EFFECT_METRONOME_CLICK = 235, + MX_SOUND_ID_EFFECT_PAT = 236, + MX_SOUND_ID_EFFECT_PUNCH = 237, + MX_SOUND_ID_EFFECT_RAIN = 238, + MX_SOUND_ID_EFFECT_SCRATCH = 239, + MX_SOUND_ID_EFFECT_SCREAM = 240, + MX_SOUND_ID_EFFECT_SEASHORE = 241, + MX_SOUND_ID_EFFECT_SIREN = 242, + MX_SOUND_ID_EFFECT_SLAP = 243, + MX_SOUND_ID_EFFECT_SNAP = 244, + MX_SOUND_ID_EFFECT_STAMP = 245, + MX_SOUND_ID_EFFECT_STARSHIP = 246, + MX_SOUND_ID_EFFECT_STREAM = 247, + MX_SOUND_ID_EFFECT_TELEPHONE_RING = 248, + MX_SOUND_ID_EFFECT_THUNDER = 249, + MX_SOUND_ID_EFFECT_TRAIN = 250, + MX_SOUND_ID_EFFECT_TRASH_CAN = 251, + MX_SOUND_ID_EFFECT_WHIP = 252, + MX_SOUND_ID_EFFECT_WHISTLE = 253, + MX_SOUND_ID_EFFECT_WHISTLE_MOUTH_SIREN = 254, + MX_SOUND_ID_EFFECT_WHISTLE_POLICE = 255, + MX_SOUND_ID_EFFECT_WHISTLE_SLIDE = 256, + MX_SOUND_ID_EFFECT_WHISTLE_TRAIN = 257, + MX_SOUND_ID_EFFECT_WIND = 258, + MX_SOUND_ID_KEYBOARD_ACCORDION = 259, + MX_SOUND_ID_KEYBOARD_BANDONEON = 260, + MX_SOUND_ID_KEYBOARD_CELESTA = 261, + MX_SOUND_ID_KEYBOARD_CLAVICHORD = 262, + MX_SOUND_ID_KEYBOARD_CLAVICHORD_SYNTH = 263, + MX_SOUND_ID_KEYBOARD_CONCERTINA = 264, + MX_SOUND_ID_KEYBOARD_FORTEPIANO = 265, + MX_SOUND_ID_KEYBOARD_HARMONIUM = 266, + MX_SOUND_ID_KEYBOARD_HARPSICHORD = 267, + MX_SOUND_ID_KEYBOARD_ONDES_MARTENOT = 268, + MX_SOUND_ID_KEYBOARD_ORGAN = 269, + MX_SOUND_ID_KEYBOARD_ORGAN_DRAWBAR = 270, + MX_SOUND_ID_KEYBOARD_ORGAN_PERCUSSIVE = 271, + MX_SOUND_ID_KEYBOARD_ORGAN_PIPE = 272, + MX_SOUND_ID_KEYBOARD_ORGAN_REED = 273, + MX_SOUND_ID_KEYBOARD_ORGAN_ROTARY = 274, + MX_SOUND_ID_KEYBOARD_PIANO = 275, + MX_SOUND_ID_KEYBOARD_PIANO_ELECTRIC = 276, + MX_SOUND_ID_KEYBOARD_PIANO_GRAND = 277, + MX_SOUND_ID_KEYBOARD_PIANO_HONKY_TONK = 278, + MX_SOUND_ID_KEYBOARD_PIANO_PREPARED = 279, + MX_SOUND_ID_KEYBOARD_PIANO_TOY = 280, + MX_SOUND_ID_KEYBOARD_PIANO_UPRIGHT = 281, + MX_SOUND_ID_KEYBOARD_VIRGINAL = 282, + MX_SOUND_ID_METAL_ADODO = 283, + MX_SOUND_ID_METAL_ANVIL = 284, + MX_SOUND_ID_METAL_BABENDIL = 285, + MX_SOUND_ID_METAL_BELLS_AGOGO = 286, + MX_SOUND_ID_METAL_BELLS_ALMGLOCKEN = 287, + MX_SOUND_ID_METAL_BELLS_BELL_PLATE = 288, + MX_SOUND_ID_METAL_BELLS_BELL_TREE = 289, + MX_SOUND_ID_METAL_BELLS_CARILLON = 290, + MX_SOUND_ID_METAL_BELLS_CHIMES = 291, + MX_SOUND_ID_METAL_BELLS_CHIMTA = 292, + MX_SOUND_ID_METAL_BELLS_CHIPPLI = 293, + MX_SOUND_ID_METAL_BELLS_CHURCH = 294, + MX_SOUND_ID_METAL_BELLS_COWBELL = 295, + MX_SOUND_ID_METAL_BELLS_DAWURO = 296, + MX_SOUND_ID_METAL_BELLS_GANKOKWE = 297, + MX_SOUND_ID_METAL_BELLS_GHUNGROO = 298, + MX_SOUND_ID_METAL_BELLS_HATHELI = 299, + MX_SOUND_ID_METAL_BELLS_JINGLE_BELL = 300, + MX_SOUND_ID_METAL_BELLS_KHARTAL = 301, + MX_SOUND_ID_METAL_BELLS_MARK_TREE = 302, + MX_SOUND_ID_METAL_BELLS_SISTRUM = 303, + MX_SOUND_ID_METAL_BELLS_SLEIGH_BELLS = 304, + MX_SOUND_ID_METAL_BELLS_TEMPLE = 305, + MX_SOUND_ID_METAL_BELLS_TIBETAN = 306, + MX_SOUND_ID_METAL_BELLS_TINKLEBELL = 307, + MX_SOUND_ID_METAL_BELLS_TRYCHEL = 308, + MX_SOUND_ID_METAL_BELLS_WIND_CHIMES = 309, + MX_SOUND_ID_METAL_BELLS_ZILLS = 310, + MX_SOUND_ID_METAL_BERIMBAU = 311, + MX_SOUND_ID_METAL_BRAKE_DRUMS = 312, + MX_SOUND_ID_METAL_CROTALES = 313, + MX_SOUND_ID_METAL_CYMBAL_BO = 314, + MX_SOUND_ID_METAL_CYMBAL_CENG_CENG = 315, + MX_SOUND_ID_METAL_CYMBAL_CHABARA = 316, + MX_SOUND_ID_METAL_CYMBAL_CHINESE = 317, + MX_SOUND_ID_METAL_CYMBAL_CHING = 318, + MX_SOUND_ID_METAL_CYMBAL_CLASH = 319, + MX_SOUND_ID_METAL_CYMBAL_CRASH = 320, + MX_SOUND_ID_METAL_CYMBAL_FINGER = 321, + MX_SOUND_ID_METAL_CYMBAL_HAND = 322, + MX_SOUND_ID_METAL_CYMBAL_KESI = 323, + MX_SOUND_ID_METAL_CYMBAL_MANJEERA = 324, + MX_SOUND_ID_METAL_CYMBAL_REVERSE = 325, + MX_SOUND_ID_METAL_CYMBAL_RIDE = 326, + MX_SOUND_ID_METAL_CYMBAL_SIZZLE = 327, + MX_SOUND_ID_METAL_CYMBAL_SPLASH = 328, + MX_SOUND_ID_METAL_CYMBAL_SUSPENDED = 329, + MX_SOUND_ID_METAL_CYMBAL_TEBYOSHI = 330, + MX_SOUND_ID_METAL_CYMBAL_TIBETAN = 331, + MX_SOUND_ID_METAL_CYMBAL_TINGSHA = 332, + MX_SOUND_ID_METAL_FLEXATONE = 333, + MX_SOUND_ID_METAL_GONG = 334, + MX_SOUND_ID_METAL_GONG_AGENG = 335, + MX_SOUND_ID_METAL_GONG_AGUNG = 336, + MX_SOUND_ID_METAL_GONG_CHANCHIKI = 337, + MX_SOUND_ID_METAL_GONG_CHINESE = 338, + MX_SOUND_ID_METAL_GONG_GANDINGAN = 339, + MX_SOUND_ID_METAL_GONG_KEMPUL = 340, + MX_SOUND_ID_METAL_GONG_KEMPYANG = 341, + MX_SOUND_ID_METAL_GONG_KETUK = 342, + MX_SOUND_ID_METAL_GONG_KKWENGGWARI = 343, + MX_SOUND_ID_METAL_GONG_LUO = 344, + MX_SOUND_ID_METAL_GONG_SINGING = 345, + MX_SOUND_ID_METAL_GONG_THAI = 346, + MX_SOUND_ID_METAL_GUIRA = 347, + MX_SOUND_ID_METAL_HANG = 348, + MX_SOUND_ID_METAL_HI_HAT = 349, + MX_SOUND_ID_METAL_JAW_HARP = 350, + MX_SOUND_ID_METAL_KENGONG = 351, + MX_SOUND_ID_METAL_MURCHANG = 352, + MX_SOUND_ID_METAL_MUSICAL_SAW = 353, + MX_SOUND_ID_METAL_SINGING_BOWL = 354, + MX_SOUND_ID_METAL_SPOONS = 355, + MX_SOUND_ID_METAL_STEEL_DRUMS = 356, + MX_SOUND_ID_METAL_TAMTAM = 357, + MX_SOUND_ID_METAL_THUNDERSHEET = 358, + MX_SOUND_ID_METAL_TRIANGLE = 359, + MX_SOUND_ID_METAL_WASHBOARD = 360, + MX_SOUND_ID_PITCHED_PERCUSSION_ANGKLUNG = 361, + MX_SOUND_ID_PITCHED_PERCUSSION_BALAFON = 362, + MX_SOUND_ID_PITCHED_PERCUSSION_BELL_LYRE = 363, + MX_SOUND_ID_PITCHED_PERCUSSION_BELLS = 364, + MX_SOUND_ID_PITCHED_PERCUSSION_BIANQING = 365, + MX_SOUND_ID_PITCHED_PERCUSSION_BIANZHONG = 366, + MX_SOUND_ID_PITCHED_PERCUSSION_BONANG = 367, + MX_SOUND_ID_PITCHED_PERCUSSION_CIMBALOM = 368, + MX_SOUND_ID_PITCHED_PERCUSSION_CRYSTAL_GLASSES = 369, + MX_SOUND_ID_PITCHED_PERCUSSION_DAN_TAM_THAP_LUC = 370, + MX_SOUND_ID_PITCHED_PERCUSSION_FANGXIANG = 371, + MX_SOUND_ID_PITCHED_PERCUSSION_GANDINGAN_A_KAYO = 372, + MX_SOUND_ID_PITCHED_PERCUSSION_GANGSA = 373, + MX_SOUND_ID_PITCHED_PERCUSSION_GENDER = 374, + MX_SOUND_ID_PITCHED_PERCUSSION_GIYING = 375, + MX_SOUND_ID_PITCHED_PERCUSSION_GLASS_HARMONICA = 376, + MX_SOUND_ID_PITCHED_PERCUSSION_GLOCKENSPIEL = 377, + MX_SOUND_ID_PITCHED_PERCUSSION_GLOCKENSPIEL_ALTO = 378, + MX_SOUND_ID_PITCHED_PERCUSSION_GLOCKENSPIEL_SOPRANO = 379, + MX_SOUND_ID_PITCHED_PERCUSSION_GYIL = 380, + MX_SOUND_ID_PITCHED_PERCUSSION_HAMMER_DULCIMER = 381, + MX_SOUND_ID_PITCHED_PERCUSSION_HANDBELLS = 382, + MX_SOUND_ID_PITCHED_PERCUSSION_KALIMBA = 383, + MX_SOUND_ID_PITCHED_PERCUSSION_KANTIL = 384, + MX_SOUND_ID_PITCHED_PERCUSSION_KHIM = 385, + MX_SOUND_ID_PITCHED_PERCUSSION_KULINTANG = 386, + MX_SOUND_ID_PITCHED_PERCUSSION_KULINTANG_A_KAYO = 387, + MX_SOUND_ID_PITCHED_PERCUSSION_KULINTANG_A_TINIOK = 388, + MX_SOUND_ID_PITCHED_PERCUSSION_LIKEMBE = 389, + MX_SOUND_ID_PITCHED_PERCUSSION_LUNTANG = 390, + MX_SOUND_ID_PITCHED_PERCUSSION_MARIMBA = 391, + MX_SOUND_ID_PITCHED_PERCUSSION_MARIMBA_BASS = 392, + MX_SOUND_ID_PITCHED_PERCUSSION_MBIRA = 393, + MX_SOUND_ID_PITCHED_PERCUSSION_MBIRA_ARRAY = 394, + MX_SOUND_ID_PITCHED_PERCUSSION_METALLOPHONE = 395, + MX_SOUND_ID_PITCHED_PERCUSSION_METALLOPHONE_ALTO = 396, + MX_SOUND_ID_PITCHED_PERCUSSION_METALLOPHONE_BASS = 397, + MX_SOUND_ID_PITCHED_PERCUSSION_METALLOPHONE_SOPRANO = 398, + MX_SOUND_ID_PITCHED_PERCUSSION_MUSIC_BOX = 399, + MX_SOUND_ID_PITCHED_PERCUSSION_PELOG_PANERUS = 400, + MX_SOUND_ID_PITCHED_PERCUSSION_PEMADE = 401, + MX_SOUND_ID_PITCHED_PERCUSSION_PENYACAH = 402, + MX_SOUND_ID_PITCHED_PERCUSSION_RANAT_EK = 403, + MX_SOUND_ID_PITCHED_PERCUSSION_RANAT_EK_LEK = 404, + MX_SOUND_ID_PITCHED_PERCUSSION_RANAT_THUM = 405, + MX_SOUND_ID_PITCHED_PERCUSSION_RANAT_THUM_LEK = 406, + MX_SOUND_ID_PITCHED_PERCUSSION_REYONG = 407, + MX_SOUND_ID_PITCHED_PERCUSSION_SANZA = 408, + MX_SOUND_ID_PITCHED_PERCUSSION_SARON_BARUNG = 409, + MX_SOUND_ID_PITCHED_PERCUSSION_SARON_DEMONG = 410, + MX_SOUND_ID_PITCHED_PERCUSSION_SARON_PANERUS = 411, + MX_SOUND_ID_PITCHED_PERCUSSION_SLENDRO_PANERUS = 412, + MX_SOUND_ID_PITCHED_PERCUSSION_SLENTEM = 413, + MX_SOUND_ID_PITCHED_PERCUSSION_TSYMBALY = 414, + MX_SOUND_ID_PITCHED_PERCUSSION_TUBES = 415, + MX_SOUND_ID_PITCHED_PERCUSSION_TUBULAR_BELLS = 416, + MX_SOUND_ID_PITCHED_PERCUSSION_VIBRAPHONE = 417, + MX_SOUND_ID_PITCHED_PERCUSSION_XYLOPHONE = 418, + MX_SOUND_ID_PITCHED_PERCUSSION_XYLOPHONE_ALTO = 419, + MX_SOUND_ID_PITCHED_PERCUSSION_XYLOPHONE_BASS = 420, + MX_SOUND_ID_PITCHED_PERCUSSION_XYLOPHONE_SOPRANO = 421, + MX_SOUND_ID_PITCHED_PERCUSSION_XYLORIMBA = 422, + MX_SOUND_ID_PITCHED_PERCUSSION_YANGQIN = 423, + MX_SOUND_ID_PLUCK_ARCHLUTE = 424, + MX_SOUND_ID_PLUCK_AUTOHARP = 425, + MX_SOUND_ID_PLUCK_BAGLAMA = 426, + MX_SOUND_ID_PLUCK_BAJO = 427, + MX_SOUND_ID_PLUCK_BALALAIKA = 428, + MX_SOUND_ID_PLUCK_BALALAIKA_ALTO = 429, + MX_SOUND_ID_PLUCK_BALALAIKA_BASS = 430, + MX_SOUND_ID_PLUCK_BALALAIKA_CONTRABASS = 431, + MX_SOUND_ID_PLUCK_BALALAIKA_PICCOLO = 432, + MX_SOUND_ID_PLUCK_BALALAIKA_PRIMA = 433, + MX_SOUND_ID_PLUCK_BALALAIKA_SECUNDA = 434, + MX_SOUND_ID_PLUCK_BANDOLA = 435, + MX_SOUND_ID_PLUCK_BANDURA = 436, + MX_SOUND_ID_PLUCK_BANDURRIA = 437, + MX_SOUND_ID_PLUCK_BANJO = 438, + MX_SOUND_ID_PLUCK_BANJO_TENOR = 439, + MX_SOUND_ID_PLUCK_BANJOLELE = 440, + MX_SOUND_ID_PLUCK_BARBAT = 441, + MX_SOUND_ID_PLUCK_BASS = 442, + MX_SOUND_ID_PLUCK_BASS_ACOUSTIC = 443, + MX_SOUND_ID_PLUCK_BASS_BOLON = 444, + MX_SOUND_ID_PLUCK_BASS_ELECTRIC = 445, + MX_SOUND_ID_PLUCK_BASS_FRETLESS = 446, + MX_SOUND_ID_PLUCK_BASS_GUITARRON = 447, + MX_SOUND_ID_PLUCK_BASS_SYNTH = 448, + MX_SOUND_ID_PLUCK_BASS_SYNTH_LEAD = 449, + MX_SOUND_ID_PLUCK_BASS_WASHTUB = 450, + MX_SOUND_ID_PLUCK_BASS_WHAMOLA = 451, + MX_SOUND_ID_PLUCK_BEGENA = 452, + MX_SOUND_ID_PLUCK_BIWA = 453, + MX_SOUND_ID_PLUCK_BORDONUA = 454, + MX_SOUND_ID_PLUCK_BOUZOUKI = 455, + MX_SOUND_ID_PLUCK_BOUZOUKI_IRISH = 456, + MX_SOUND_ID_PLUCK_CELTIC_HARP = 457, + MX_SOUND_ID_PLUCK_CHARANGO = 458, + MX_SOUND_ID_PLUCK_CHITARRA_BATTENTE = 459, + MX_SOUND_ID_PLUCK_CITHARA = 460, + MX_SOUND_ID_PLUCK_CITTERN = 461, + MX_SOUND_ID_PLUCK_CUATRO = 462, + MX_SOUND_ID_PLUCK_DAN_BAU = 463, + MX_SOUND_ID_PLUCK_DAN_NGUYET = 464, + MX_SOUND_ID_PLUCK_DAN_TRANH = 465, + MX_SOUND_ID_PLUCK_DAN_TY_BA = 466, + MX_SOUND_ID_PLUCK_DIDDLEY_BOW = 467, + MX_SOUND_ID_PLUCK_DOMRA = 468, + MX_SOUND_ID_PLUCK_DOMU = 469, + MX_SOUND_ID_PLUCK_DULCIMER = 470, + MX_SOUND_ID_PLUCK_DUTAR = 471, + MX_SOUND_ID_PLUCK_DUXIANQIN = 472, + MX_SOUND_ID_PLUCK_EKTARA = 473, + MX_SOUND_ID_PLUCK_GEOMUNGO = 474, + MX_SOUND_ID_PLUCK_GOTTUVADHYAM = 475, + MX_SOUND_ID_PLUCK_GUITAR = 476, + MX_SOUND_ID_PLUCK_GUITAR_ACOUSTIC = 477, + MX_SOUND_ID_PLUCK_GUITAR_ELECTRIC = 478, + MX_SOUND_ID_PLUCK_GUITAR_NYLON_STRING = 479, + MX_SOUND_ID_PLUCK_GUITAR_PEDAL_STEEL = 480, + MX_SOUND_ID_PLUCK_GUITAR_PORTUGUESE = 481, + MX_SOUND_ID_PLUCK_GUITAR_REQUINTO = 482, + MX_SOUND_ID_PLUCK_GUITAR_RESONATOR = 483, + MX_SOUND_ID_PLUCK_GUITAR_STEEL_STRING = 484, + MX_SOUND_ID_PLUCK_GUITJO = 485, + MX_SOUND_ID_PLUCK_GUITJO_DOUBLE_NECK = 486, + MX_SOUND_ID_PLUCK_GUQIN = 487, + MX_SOUND_ID_PLUCK_GUZHENG = 488, + MX_SOUND_ID_PLUCK_GUZHENG_CHOAZHOU = 489, + MX_SOUND_ID_PLUCK_HARP = 490, + MX_SOUND_ID_PLUCK_HARP_GUITAR = 491, + MX_SOUND_ID_PLUCK_HUAPANGUERA = 492, + MX_SOUND_ID_PLUCK_JARANA_HUASTECA = 493, + MX_SOUND_ID_PLUCK_JARANA_JAROCHA = 494, + MX_SOUND_ID_PLUCK_JARANA_JAROCHA_MOSQUITO = 495, + MX_SOUND_ID_PLUCK_JARANA_JAROCHA_PRIMERA = 496, + MX_SOUND_ID_PLUCK_JARANA_JAROCHA_SEGUNDA = 497, + MX_SOUND_ID_PLUCK_JARANA_JAROCHA_TERCERA = 498, + MX_SOUND_ID_PLUCK_KABOSY = 499, + MX_SOUND_ID_PLUCK_KANTELE = 500, + MX_SOUND_ID_PLUCK_KANUN = 501, + MX_SOUND_ID_PLUCK_KAYAGUM = 502, + MX_SOUND_ID_PLUCK_KOBZA = 503, + MX_SOUND_ID_PLUCK_KOMUZ = 504, + MX_SOUND_ID_PLUCK_KORA = 505, + MX_SOUND_ID_PLUCK_KOTO = 506, + MX_SOUND_ID_PLUCK_KUTIYAPI = 507, + MX_SOUND_ID_PLUCK_LANGELEIK = 508, + MX_SOUND_ID_PLUCK_LAUD = 509, + MX_SOUND_ID_PLUCK_LUTE = 510, + MX_SOUND_ID_PLUCK_LYRE = 511, + MX_SOUND_ID_PLUCK_MANDOBASS = 512, + MX_SOUND_ID_PLUCK_MANDOCELLO = 513, + MX_SOUND_ID_PLUCK_MANDOLA = 514, + MX_SOUND_ID_PLUCK_MANDOLIN = 515, + MX_SOUND_ID_PLUCK_MANDOLIN_OCTAVE = 516, + MX_SOUND_ID_PLUCK_MANDORA = 517, + MX_SOUND_ID_PLUCK_MANDORE = 518, + MX_SOUND_ID_PLUCK_MAROVANY = 519, + MX_SOUND_ID_PLUCK_MUSICAL_BOW = 520, + MX_SOUND_ID_PLUCK_NGONI = 521, + MX_SOUND_ID_PLUCK_OUD = 522, + MX_SOUND_ID_PLUCK_PIPA = 523, + MX_SOUND_ID_PLUCK_PSALTERY = 524, + MX_SOUND_ID_PLUCK_RUAN = 525, + MX_SOUND_ID_PLUCK_SALLANEH = 526, + MX_SOUND_ID_PLUCK_SANSHIN = 527, + MX_SOUND_ID_PLUCK_SANTOOR = 528, + MX_SOUND_ID_PLUCK_SANXIAN = 529, + MX_SOUND_ID_PLUCK_SAROD = 530, + MX_SOUND_ID_PLUCK_SAUNG = 531, + MX_SOUND_ID_PLUCK_SAZ = 532, + MX_SOUND_ID_PLUCK_SE = 533, + MX_SOUND_ID_PLUCK_SETAR = 534, + MX_SOUND_ID_PLUCK_SHAMISEN = 535, + MX_SOUND_ID_PLUCK_SITAR = 536, + MX_SOUND_ID_PLUCK_SYNTH = 537, + MX_SOUND_ID_PLUCK_SYNTH_CHARANG = 538, + MX_SOUND_ID_PLUCK_SYNTH_CHIFF = 539, + MX_SOUND_ID_PLUCK_SYNTH_STICK = 540, + MX_SOUND_ID_PLUCK_TAMBURA = 541, + MX_SOUND_ID_PLUCK_TAMBURA_BULGARIAN = 542, + MX_SOUND_ID_PLUCK_TAMBURA_FEMALE = 543, + MX_SOUND_ID_PLUCK_TAMBURA_MALE = 544, + MX_SOUND_ID_PLUCK_TAR = 545, + MX_SOUND_ID_PLUCK_THEORBO = 546, + MX_SOUND_ID_PLUCK_TIMPLE = 547, + MX_SOUND_ID_PLUCK_TIPLE = 548, + MX_SOUND_ID_PLUCK_TRES = 549, + MX_SOUND_ID_PLUCK_UKULELE = 550, + MX_SOUND_ID_PLUCK_UKULELE_TENOR = 551, + MX_SOUND_ID_PLUCK_VALIHA = 552, + MX_SOUND_ID_PLUCK_VEENA = 553, + MX_SOUND_ID_PLUCK_VEENA_MOHAN = 554, + MX_SOUND_ID_PLUCK_VEENA_RUDRA = 555, + MX_SOUND_ID_PLUCK_VEENA_VICHITRA = 556, + MX_SOUND_ID_PLUCK_VIHUELA = 557, + MX_SOUND_ID_PLUCK_VIHUELA_MEXICAN = 558, + MX_SOUND_ID_PLUCK_XALAM = 559, + MX_SOUND_ID_PLUCK_YUEQIN = 560, + MX_SOUND_ID_PLUCK_ZITHER = 561, + MX_SOUND_ID_PLUCK_ZITHER_OVERTONE = 562, + MX_SOUND_ID_RATTLE_AFOXE = 563, + MX_SOUND_ID_RATTLE_BIRDS = 564, + MX_SOUND_ID_RATTLE_CABASA = 565, + MX_SOUND_ID_RATTLE_CAXIXI = 566, + MX_SOUND_ID_RATTLE_COG = 567, + MX_SOUND_ID_RATTLE_GANZA = 568, + MX_SOUND_ID_RATTLE_HOSHO = 569, + MX_SOUND_ID_RATTLE_JAWBONE = 570, + MX_SOUND_ID_RATTLE_KAYAMBA = 571, + MX_SOUND_ID_RATTLE_KPOKO_KPOKO = 572, + MX_SOUND_ID_RATTLE_LAVA_STONES = 573, + MX_SOUND_ID_RATTLE_MARACA = 574, + MX_SOUND_ID_RATTLE_RAIN_STICK = 575, + MX_SOUND_ID_RATTLE_RATCHET = 576, + MX_SOUND_ID_RATTLE_RATTLE = 577, + MX_SOUND_ID_RATTLE_SHAKER = 578, + MX_SOUND_ID_RATTLE_SHAKER_EGG = 579, + MX_SOUND_ID_RATTLE_SHEKERE = 580, + MX_SOUND_ID_RATTLE_SISTRE = 581, + MX_SOUND_ID_RATTLE_TELEVI = 582, + MX_SOUND_ID_RATTLE_VIBRASLAP = 583, + MX_SOUND_ID_RATTLE_WASEMBE = 584, + MX_SOUND_ID_STRINGS_AJAENG = 585, + MX_SOUND_ID_STRINGS_ARPEGGIONE = 586, + MX_SOUND_ID_STRINGS_BARYTON = 587, + MX_SOUND_ID_STRINGS_CELLO = 588, + MX_SOUND_ID_STRINGS_CELLO_PICCOLO = 589, + MX_SOUND_ID_STRINGS_CONTRABASS = 590, + MX_SOUND_ID_STRINGS_CRWTH = 591, + MX_SOUND_ID_STRINGS_DAN_GAO = 592, + MX_SOUND_ID_STRINGS_DIHU = 593, + MX_SOUND_ID_STRINGS_ERHU = 594, + MX_SOUND_ID_STRINGS_ERXIAN = 595, + MX_SOUND_ID_STRINGS_ESRAJ = 596, + MX_SOUND_ID_STRINGS_FIDDLE = 597, + MX_SOUND_ID_STRINGS_FIDDLE_HARDANGER = 598, + MX_SOUND_ID_STRINGS_GADULKA = 599, + MX_SOUND_ID_STRINGS_GAOHU = 600, + MX_SOUND_ID_STRINGS_GEHU = 601, + MX_SOUND_ID_STRINGS_GROUP = 602, + MX_SOUND_ID_STRINGS_GROUP_SYNTH = 603, + MX_SOUND_ID_STRINGS_HAEGEUM = 604, + MX_SOUND_ID_STRINGS_HURDY_GURDY = 605, + MX_SOUND_ID_STRINGS_IGIL = 606, + MX_SOUND_ID_STRINGS_KAMANCHA = 607, + MX_SOUND_ID_STRINGS_KOKYU = 608, + MX_SOUND_ID_STRINGS_LARUAN = 609, + MX_SOUND_ID_STRINGS_LEIQIN = 610, + MX_SOUND_ID_STRINGS_LIRONE = 611, + MX_SOUND_ID_STRINGS_LYRA_BYZANTINE = 612, + MX_SOUND_ID_STRINGS_LYRA_CRETAN = 613, + MX_SOUND_ID_STRINGS_MORIN_KHUUR = 614, + MX_SOUND_ID_STRINGS_NYCKELHARPA = 615, + MX_SOUND_ID_STRINGS_OCTOBASS = 616, + MX_SOUND_ID_STRINGS_REBAB = 617, + MX_SOUND_ID_STRINGS_REBEC = 618, + MX_SOUND_ID_STRINGS_SARANGI = 619, + MX_SOUND_ID_STRINGS_STROH_VIOLIN = 620, + MX_SOUND_ID_STRINGS_TROMBA_MARINA = 621, + MX_SOUND_ID_STRINGS_VIELLE = 622, + MX_SOUND_ID_STRINGS_VIOL = 623, + MX_SOUND_ID_STRINGS_VIOL_ALTO = 624, + MX_SOUND_ID_STRINGS_VIOL_BASS = 625, + MX_SOUND_ID_STRINGS_VIOL_TENOR = 626, + MX_SOUND_ID_STRINGS_VIOL_TREBLE = 627, + MX_SOUND_ID_STRINGS_VIOL_VIOLONE = 628, + MX_SOUND_ID_STRINGS_VIOLA = 629, + MX_SOUND_ID_STRINGS_VIOLA_DAMORE = 630, + MX_SOUND_ID_STRINGS_VIOLIN = 631, + MX_SOUND_ID_STRINGS_VIOLONO_PICCOLO = 632, + MX_SOUND_ID_STRINGS_VIOLOTTA = 633, + MX_SOUND_ID_STRINGS_YAYLI_TANBUR = 634, + MX_SOUND_ID_STRINGS_YAZHENG = 635, + MX_SOUND_ID_STRINGS_ZHONGHU = 636, + MX_SOUND_ID_SYNTH_EFFECTS = 637, + MX_SOUND_ID_SYNTH_EFFECTS_ATMOSPHERE = 638, + MX_SOUND_ID_SYNTH_EFFECTS_BRIGHTNESS = 639, + MX_SOUND_ID_SYNTH_EFFECTS_CRYSTAL = 640, + MX_SOUND_ID_SYNTH_EFFECTS_ECHOES = 641, + MX_SOUND_ID_SYNTH_EFFECTS_GOBLINS = 642, + MX_SOUND_ID_SYNTH_EFFECTS_RAIN = 643, + MX_SOUND_ID_SYNTH_EFFECTS_SCI_FI = 644, + MX_SOUND_ID_SYNTH_EFFECTS_SOUNDTRACK = 645, + MX_SOUND_ID_SYNTH_GROUP = 646, + MX_SOUND_ID_SYNTH_GROUP_FIFTHS = 647, + MX_SOUND_ID_SYNTH_GROUP_ORCHESTRA = 648, + MX_SOUND_ID_SYNTH_PAD = 649, + MX_SOUND_ID_SYNTH_PAD_BOWED = 650, + MX_SOUND_ID_SYNTH_PAD_CHOIR = 651, + MX_SOUND_ID_SYNTH_PAD_HALO = 652, + MX_SOUND_ID_SYNTH_PAD_METALLIC = 653, + MX_SOUND_ID_SYNTH_PAD_POLYSYNTH = 654, + MX_SOUND_ID_SYNTH_PAD_SWEEP = 655, + MX_SOUND_ID_SYNTH_PAD_WARM = 656, + MX_SOUND_ID_SYNTH_THEREMIN = 657, + MX_SOUND_ID_SYNTH_TONE_SAWTOOTH = 658, + MX_SOUND_ID_SYNTH_TONE_SINE = 659, + MX_SOUND_ID_SYNTH_TONE_SQUARE = 660, + MX_SOUND_ID_VOICE_AA = 661, + MX_SOUND_ID_VOICE_ALTO = 662, + MX_SOUND_ID_VOICE_AW = 663, + MX_SOUND_ID_VOICE_BARITONE = 664, + MX_SOUND_ID_VOICE_BASS = 665, + MX_SOUND_ID_VOICE_CHILD = 666, + MX_SOUND_ID_VOICE_COUNTERTENOR = 667, + MX_SOUND_ID_VOICE_DOO = 668, + MX_SOUND_ID_VOICE_EE = 669, + MX_SOUND_ID_VOICE_FEMALE = 670, + MX_SOUND_ID_VOICE_KAZOO = 671, + MX_SOUND_ID_VOICE_MALE = 672, + MX_SOUND_ID_VOICE_MEZZO_SOPRANO = 673, + MX_SOUND_ID_VOICE_MM = 674, + MX_SOUND_ID_VOICE_OO = 675, + MX_SOUND_ID_VOICE_PERCUSSION = 676, + MX_SOUND_ID_VOICE_PERCUSSION_BEATBOX = 677, + MX_SOUND_ID_VOICE_SOPRANO = 678, + MX_SOUND_ID_VOICE_SYNTH = 679, + MX_SOUND_ID_VOICE_TALK_BOX = 680, + MX_SOUND_ID_VOICE_TENOR = 681, + MX_SOUND_ID_VOICE_VOCALS = 682, + MX_SOUND_ID_WIND_FLUTES_BANSURI = 683, + MX_SOUND_ID_WIND_FLUTES_BLOWN_BOTTLE = 684, + MX_SOUND_ID_WIND_FLUTES_CALLIOPE = 685, + MX_SOUND_ID_WIND_FLUTES_DANSO = 686, + MX_SOUND_ID_WIND_FLUTES_DI_ZI = 687, + MX_SOUND_ID_WIND_FLUTES_DVOJNICE = 688, + MX_SOUND_ID_WIND_FLUTES_FIFE = 689, + MX_SOUND_ID_WIND_FLUTES_FLAGEOLET = 690, + MX_SOUND_ID_WIND_FLUTES_FLUTE = 691, + MX_SOUND_ID_WIND_FLUTES_FLUTE_ALTO = 692, + MX_SOUND_ID_WIND_FLUTES_FLUTE_BASS = 693, + MX_SOUND_ID_WIND_FLUTES_FLUTE_CONTRA_ALTO = 694, + MX_SOUND_ID_WIND_FLUTES_FLUTE_CONTRABASS = 695, + MX_SOUND_ID_WIND_FLUTES_FLUTE_DOUBLE_CONTRABASS = 696, + MX_SOUND_ID_WIND_FLUTES_FLUTE_IRISH = 697, + MX_SOUND_ID_WIND_FLUTES_FLUTE_PICCOLO = 698, + MX_SOUND_ID_WIND_FLUTES_FLUTE_SUBCONTRABASS = 699, + MX_SOUND_ID_WIND_FLUTES_FUJARA = 700, + MX_SOUND_ID_WIND_FLUTES_GEMSHORN = 701, + MX_SOUND_ID_WIND_FLUTES_HOCCHIKU = 702, + MX_SOUND_ID_WIND_FLUTES_HUN = 703, + MX_SOUND_ID_WIND_FLUTES_KAVAL = 704, + MX_SOUND_ID_WIND_FLUTES_KAWALA = 705, + MX_SOUND_ID_WIND_FLUTES_KHLUI = 706, + MX_SOUND_ID_WIND_FLUTES_KNOTWEED = 707, + MX_SOUND_ID_WIND_FLUTES_KONCOVKA_ALTO = 708, + MX_SOUND_ID_WIND_FLUTES_KOUDI = 709, + MX_SOUND_ID_WIND_FLUTES_NEY = 710, + MX_SOUND_ID_WIND_FLUTES_NOHKAN = 711, + MX_SOUND_ID_WIND_FLUTES_NOSE = 712, + MX_SOUND_ID_WIND_FLUTES_OCARINA = 713, + MX_SOUND_ID_WIND_FLUTES_OVERTONE_TENOR = 714, + MX_SOUND_ID_WIND_FLUTES_PALENDAG = 715, + MX_SOUND_ID_WIND_FLUTES_PANPIPES = 716, + MX_SOUND_ID_WIND_FLUTES_QUENA = 717, + MX_SOUND_ID_WIND_FLUTES_RECORDER = 718, + MX_SOUND_ID_WIND_FLUTES_RECORDER_ALTO = 719, + MX_SOUND_ID_WIND_FLUTES_RECORDER_BASS = 720, + MX_SOUND_ID_WIND_FLUTES_RECORDER_CONTRABASS = 721, + MX_SOUND_ID_WIND_FLUTES_RECORDER_DESCANT = 722, + MX_SOUND_ID_WIND_FLUTES_RECORDER_GARKLEIN = 723, + MX_SOUND_ID_WIND_FLUTES_RECORDER_GREAT_BASS = 724, + MX_SOUND_ID_WIND_FLUTES_RECORDER_SOPRANINO = 725, + MX_SOUND_ID_WIND_FLUTES_RECORDER_SOPRANO = 726, + MX_SOUND_ID_WIND_FLUTES_RECORDER_TENOR = 727, + MX_SOUND_ID_WIND_FLUTES_RYUTEKI = 728, + MX_SOUND_ID_WIND_FLUTES_SHAKUHACHI = 729, + MX_SOUND_ID_WIND_FLUTES_SHEPHERDS_PIPE = 730, + MX_SOUND_ID_WIND_FLUTES_SHINOBUE = 731, + MX_SOUND_ID_WIND_FLUTES_SHVI = 732, + MX_SOUND_ID_WIND_FLUTES_SULING = 733, + MX_SOUND_ID_WIND_FLUTES_TARKA = 734, + MX_SOUND_ID_WIND_FLUTES_TUMPONG = 735, + MX_SOUND_ID_WIND_FLUTES_VENU = 736, + MX_SOUND_ID_WIND_FLUTES_WHISTLE = 737, + MX_SOUND_ID_WIND_FLUTES_WHISTLE_ALTO = 738, + MX_SOUND_ID_WIND_FLUTES_WHISTLE_LOW_IRISH = 739, + MX_SOUND_ID_WIND_FLUTES_WHISTLE_SHIVA = 740, + MX_SOUND_ID_WIND_FLUTES_WHISTLE_SLIDE = 741, + MX_SOUND_ID_WIND_FLUTES_WHISTLE_TIN = 742, + MX_SOUND_ID_WIND_FLUTES_WHISTLE_TIN_BFLAT = 743, + MX_SOUND_ID_WIND_FLUTES_WHISTLE_TIN_D = 744, + MX_SOUND_ID_WIND_FLUTES_XIAO = 745, + MX_SOUND_ID_WIND_FLUTES_XUN = 746, + MX_SOUND_ID_WIND_GROUP = 747, + MX_SOUND_ID_WIND_JUG = 748, + MX_SOUND_ID_WIND_PIPES_BAGPIPES = 749, + MX_SOUND_ID_WIND_PIPES_GAIDA = 750, + MX_SOUND_ID_WIND_PIPES_HIGHLAND = 751, + MX_SOUND_ID_WIND_PIPES_UILLEANN = 752, + MX_SOUND_ID_WIND_PUNGI = 753, + MX_SOUND_ID_WIND_REED_ALBOGUE = 754, + MX_SOUND_ID_WIND_REED_ALBOKA = 755, + MX_SOUND_ID_WIND_REED_ALGAITA = 756, + MX_SOUND_ID_WIND_REED_ARGHUL = 757, + MX_SOUND_ID_WIND_REED_BASSET_HORN = 758, + MX_SOUND_ID_WIND_REED_BASSOON = 759, + MX_SOUND_ID_WIND_REED_BAWU = 760, + MX_SOUND_ID_WIND_REED_BIFORA = 761, + MX_SOUND_ID_WIND_REED_BOMBARDE = 762, + MX_SOUND_ID_WIND_REED_CHALUMEAU = 763, + MX_SOUND_ID_WIND_REED_CLARINET = 764, + MX_SOUND_ID_WIND_REED_CLARINET_A = 765, + MX_SOUND_ID_WIND_REED_CLARINET_ALTO = 766, + MX_SOUND_ID_WIND_REED_CLARINET_BASS = 767, + MX_SOUND_ID_WIND_REED_CLARINET_BASSET = 768, + MX_SOUND_ID_WIND_REED_CLARINET_BFLAT = 769, + MX_SOUND_ID_WIND_REED_CLARINET_CONTRA_ALTO = 770, + MX_SOUND_ID_WIND_REED_CLARINET_CONTRABASS = 771, + MX_SOUND_ID_WIND_REED_CLARINET_EFLAT = 772, + MX_SOUND_ID_WIND_REED_CLARINET_PICCOLO_AFLAT = 773, + MX_SOUND_ID_WIND_REED_CLARINETTE_DAMOUR = 774, + MX_SOUND_ID_WIND_REED_CONTRABASS = 775, + MX_SOUND_ID_WIND_REED_CONTRABASSOON = 776, + MX_SOUND_ID_WIND_REED_CORNAMUSE = 777, + MX_SOUND_ID_WIND_REED_CROMORNE = 778, + MX_SOUND_ID_WIND_REED_CRUMHORN = 779, + MX_SOUND_ID_WIND_REED_CRUMHORN_ALTO = 780, + MX_SOUND_ID_WIND_REED_CRUMHORN_BASS = 781, + MX_SOUND_ID_WIND_REED_CRUMHORN_GREAT_BASS = 782, + MX_SOUND_ID_WIND_REED_CRUMHORN_SOPRANO = 783, + MX_SOUND_ID_WIND_REED_CRUMHORN_TENOR = 784, + MX_SOUND_ID_WIND_REED_DIPLE = 785, + MX_SOUND_ID_WIND_REED_DIPLICA = 786, + MX_SOUND_ID_WIND_REED_DUDUK = 787, + MX_SOUND_ID_WIND_REED_DULCIAN = 788, + MX_SOUND_ID_WIND_REED_DULZAINA = 789, + MX_SOUND_ID_WIND_REED_ENGLISH_HORN = 790, + MX_SOUND_ID_WIND_REED_GUANZI = 791, + MX_SOUND_ID_WIND_REED_HARMONICA = 792, + MX_SOUND_ID_WIND_REED_HARMONICA_BASS = 793, + MX_SOUND_ID_WIND_REED_HECKEL_CLARINA = 794, + MX_SOUND_ID_WIND_REED_HECKELPHONE = 795, + MX_SOUND_ID_WIND_REED_HECKELPHONE_PICCOLO = 796, + MX_SOUND_ID_WIND_REED_HECKELPHONE_CLARINET = 797, + MX_SOUND_ID_WIND_REED_HICHIRIKI = 798, + MX_SOUND_ID_WIND_REED_HIRTENSCHALMEI = 799, + MX_SOUND_ID_WIND_REED_HNE = 800, + MX_SOUND_ID_WIND_REED_HORNPIPE = 801, + MX_SOUND_ID_WIND_REED_HOUGUAN = 802, + MX_SOUND_ID_WIND_REED_HULUSI = 803, + MX_SOUND_ID_WIND_REED_JOGI_BAJA = 804, + MX_SOUND_ID_WIND_REED_KEN_BAU = 805, + MX_SOUND_ID_WIND_REED_KHAEN_MOUTH_ORGAN = 806, + MX_SOUND_ID_WIND_REED_LAUNEDDAS = 807, + MX_SOUND_ID_WIND_REED_MAQRUNAH = 808, + MX_SOUND_ID_WIND_REED_MELODICA = 809, + MX_SOUND_ID_WIND_REED_MIJWIZ = 810, + MX_SOUND_ID_WIND_REED_MIZMAR = 811, + MX_SOUND_ID_WIND_REED_NADASWARAM = 812, + MX_SOUND_ID_WIND_REED_OBOE = 813, + MX_SOUND_ID_WIND_REED_OBOE_BASS = 814, + MX_SOUND_ID_WIND_REED_OBOE_PICCOLO = 815, + MX_SOUND_ID_WIND_REED_OBOE_DA_CACCIA = 816, + MX_SOUND_ID_WIND_REED_OBOE_DAMORE = 817, + MX_SOUND_ID_WIND_REED_OCTAVIN = 818, + MX_SOUND_ID_WIND_REED_PI = 819, + MX_SOUND_ID_WIND_REED_PIBGORN = 820, + MX_SOUND_ID_WIND_REED_PIRI = 821, + MX_SOUND_ID_WIND_REED_RACKETT = 822, + MX_SOUND_ID_WIND_REED_RAUSCHPFEIFE = 823, + MX_SOUND_ID_WIND_REED_RHAITA = 824, + MX_SOUND_ID_WIND_REED_ROTHPHONE = 825, + MX_SOUND_ID_WIND_REED_SARRUSAPHONE = 826, + MX_SOUND_ID_WIND_REED_SAXONETTE = 827, + MX_SOUND_ID_WIND_REED_SAXOPHONE = 828, + MX_SOUND_ID_WIND_REED_SAXOPHONE_ALTO = 829, + MX_SOUND_ID_WIND_REED_SAXOPHONE_AULOCHROME = 830, + MX_SOUND_ID_WIND_REED_SAXOPHONE_BARITONE = 831, + MX_SOUND_ID_WIND_REED_SAXOPHONE_BASS = 832, + MX_SOUND_ID_WIND_REED_SAXOPHONE_CONTRABASS = 833, + MX_SOUND_ID_WIND_REED_SAXOPHONE_MELODY = 834, + MX_SOUND_ID_WIND_REED_SAXOPHONE_MEZZO_SOPRANO = 835, + MX_SOUND_ID_WIND_REED_SAXOPHONE_SOPRANINO = 836, + MX_SOUND_ID_WIND_REED_SAXOPHONE_SOPRANISSIMO = 837, + MX_SOUND_ID_WIND_REED_SAXOPHONE_SOPRANO = 838, + MX_SOUND_ID_WIND_REED_SAXOPHONE_SUBCONTRABASS = 839, + MX_SOUND_ID_WIND_REED_SAXOPHONE_TENOR = 840, + MX_SOUND_ID_WIND_REED_SHAWM = 841, + MX_SOUND_ID_WIND_REED_SHENAI = 842, + MX_SOUND_ID_WIND_REED_SHENG = 843, + MX_SOUND_ID_WIND_REED_SIPSI = 844, + MX_SOUND_ID_WIND_REED_SOPILA = 845, + MX_SOUND_ID_WIND_REED_SORNA = 846, + MX_SOUND_ID_WIND_REED_SRALAI = 847, + MX_SOUND_ID_WIND_REED_SUONA = 848, + MX_SOUND_ID_WIND_REED_SURNAI = 849, + MX_SOUND_ID_WIND_REED_TAEPYEONGSO = 850, + MX_SOUND_ID_WIND_REED_TAROGATO = 851, + MX_SOUND_ID_WIND_REED_TAROGATO_ANCIENT = 852, + MX_SOUND_ID_WIND_REED_TROMPETA_CHINA = 853, + MX_SOUND_ID_WIND_REED_TUBAX = 854, + MX_SOUND_ID_WIND_REED_XAPHOON = 855, + MX_SOUND_ID_WIND_REED_ZHALEIKA = 856, + MX_SOUND_ID_WIND_REED_ZURLA = 857, + MX_SOUND_ID_WIND_REED_ZURNA = 858, + MX_SOUND_ID_WOOD_AGOGO_BLOCK = 859, + MX_SOUND_ID_WOOD_AGUNG_A_TAMLANG = 860, + MX_SOUND_ID_WOOD_AHOKO = 861, + MX_SOUND_ID_WOOD_BONES = 862, + MX_SOUND_ID_WOOD_CASTANETS = 863, + MX_SOUND_ID_WOOD_CLAVES = 864, + MX_SOUND_ID_WOOD_DRUM_STICKS = 865, + MX_SOUND_ID_WOOD_GOURD = 866, + MX_SOUND_ID_WOOD_GRANITE_BLOCK = 867, + MX_SOUND_ID_WOOD_GUBAN = 868, + MX_SOUND_ID_WOOD_GUIRO = 869, + MX_SOUND_ID_WOOD_HYOUSHIGI = 870, + MX_SOUND_ID_WOOD_IPU = 871, + MX_SOUND_ID_WOOD_JAM_BLOCK = 872, + MX_SOUND_ID_WOOD_KAEKEEKE = 873, + MX_SOUND_ID_WOOD_KAGUL = 874, + MX_SOUND_ID_WOOD_KALAAU = 875, + MX_SOUND_ID_WOOD_KASHIKLAR = 876, + MX_SOUND_ID_WOOD_KUBING = 877, + MX_SOUND_ID_WOOD_PAN_CLAPPERS = 878, + MX_SOUND_ID_WOOD_SAND_BLOCK = 879, + MX_SOUND_ID_WOOD_SLAPSTICK = 880, + MX_SOUND_ID_WOOD_STIR_DRUM = 881, + MX_SOUND_ID_WOOD_TEMPLE_BLOCK = 882, + MX_SOUND_ID_WOOD_TIC_TOC_BLOCK = 883, + MX_SOUND_ID_WOOD_TONETANG = 884, + MX_SOUND_ID_WOOD_WOOD_BLOCK = 885 +} MxSoundID; + +bool mx_sound_id_try_parse(const char *s, MxSoundID *out); +/* Lenient: unknown input falls back to the first variant. */ +MxSoundID mx_sound_id_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_sound_id_to_string(MxSoundID v); + +#endif /* MX_SOUND_ID_H */ diff --git a/gen/test/c/mx/mx_staff_divide_symbol.c b/gen/test/c/mx/mx_staff_divide_symbol.c new file mode 100644 index 000000000..8659458af --- /dev/null +++ b/gen/test/c/mx/mx_staff_divide_symbol.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_staff_divide_symbol.h" + +#include + +static const char *const mx_staff_divide_symbol_values[] = { + "down", + "up", + "up-down", +}; + +bool mx_staff_divide_symbol_try_parse(const char *s, MxStaffDivideSymbol *out) { + for (size_t i = 0; i < sizeof(mx_staff_divide_symbol_values) / sizeof(mx_staff_divide_symbol_values[0]); i++) { + if (strcmp(s, mx_staff_divide_symbol_values[i]) == 0) { + *out = (MxStaffDivideSymbol)i; + return true; + } + } + *out = (MxStaffDivideSymbol)0; + return false; +} + +MxStaffDivideSymbol mx_staff_divide_symbol_parse(const char *s) { + MxStaffDivideSymbol v; + mx_staff_divide_symbol_try_parse(s, &v); + return v; +} + +const char *mx_staff_divide_symbol_to_string(MxStaffDivideSymbol v) { + if ((size_t)v >= sizeof(mx_staff_divide_symbol_values) / sizeof(mx_staff_divide_symbol_values[0])) + return mx_staff_divide_symbol_values[0]; + return mx_staff_divide_symbol_values[v]; +} diff --git a/gen/test/c/mx/mx_staff_divide_symbol.h b/gen/test/c/mx/mx_staff_divide_symbol.h new file mode 100644 index 000000000..f564b6ef8 --- /dev/null +++ b/gen/test/c/mx/mx_staff_divide_symbol.h @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_STAFF_DIVIDE_SYMBOL_H +#define MX_STAFF_DIVIDE_SYMBOL_H + +#include + +/* + * The staff-divide-symbol type is used for staff division symbols. The down, up, and up-down values + * correspond to SMuFL code points U+E00B, U+E00C, and U+E00D respectively. + */ +typedef enum { + MX_STAFF_DIVIDE_SYMBOL_DOWN = 0, + MX_STAFF_DIVIDE_SYMBOL_UP = 1, + MX_STAFF_DIVIDE_SYMBOL_UP_DOWN = 2 +} MxStaffDivideSymbol; + +bool mx_staff_divide_symbol_try_parse(const char *s, MxStaffDivideSymbol *out); +/* Lenient: unknown input falls back to the first variant. */ +MxStaffDivideSymbol mx_staff_divide_symbol_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_staff_divide_symbol_to_string(MxStaffDivideSymbol v); + +#endif /* MX_STAFF_DIVIDE_SYMBOL_H */ diff --git a/gen/test/c/mx/mx_staff_line.c b/gen/test/c/mx/mx_staff_line.c new file mode 100644 index 000000000..b9b206773 --- /dev/null +++ b/gen/test/c/mx/mx_staff_line.c @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_staff_line.h" + +#include "mx_runtime.h" + +bool mx_staff_line_try_parse(const char *s, MxStaffLine *out) { + long v; + if (!mx_try_parse_int(s, &v)) { + *out = (MxStaffLine)0; + return false; + } + *out = (MxStaffLine)v; + return true; +} + +MxStaffLine mx_staff_line_parse(const char *s) { + long v = mx_parse_int(s); + return (MxStaffLine)v; +} + +char *mx_staff_line_to_string(MxStaffLine v) { + return mx_format_int((long)v); +} diff --git a/gen/test/c/mx/mx_staff_line.h b/gen/test/c/mx/mx_staff_line.h new file mode 100644 index 000000000..ca3fe4f02 --- /dev/null +++ b/gen/test/c/mx/mx_staff_line.h @@ -0,0 +1,22 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_STAFF_LINE_H +#define MX_STAFF_LINE_H + +#include + +/* + * The staff-line type indicates the line on a given staff. Staff lines are numbered from bottom to + * top, with 1 being the bottom line on a staff. Staff line values can be used to specify positions + * outside the staff, such as a C clef positioned in the middle of a grand staff. + */ +typedef long MxStaffLine; + +/* Strict parse, then clamps into the declared range. */ +bool mx_staff_line_try_parse(const char *s, MxStaffLine *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxStaffLine mx_staff_line_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_staff_line_to_string(MxStaffLine v); + +#endif /* MX_STAFF_LINE_H */ diff --git a/gen/test/c/mx/mx_staff_number.c b/gen/test/c/mx/mx_staff_number.c new file mode 100644 index 000000000..fc1c9d405 --- /dev/null +++ b/gen/test/c/mx/mx_staff_number.c @@ -0,0 +1,30 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_staff_number.h" + +#include "mx_runtime.h" + +static MxStaffNumber mx_staff_number_clamp(long v) { + if (v < 1) + v = 1; + return (MxStaffNumber)v; +} + +bool mx_staff_number_try_parse(const char *s, MxStaffNumber *out) { + long v; + if (!mx_try_parse_int(s, &v)) { + *out = (MxStaffNumber)0; + return false; + } + *out = mx_staff_number_clamp(v); + return true; +} + +MxStaffNumber mx_staff_number_parse(const char *s) { + long v = mx_parse_int(s); + return mx_staff_number_clamp(v); +} + +char *mx_staff_number_to_string(MxStaffNumber v) { + return mx_format_int((long)v); +} diff --git a/gen/test/c/mx/mx_staff_number.h b/gen/test/c/mx/mx_staff_number.h new file mode 100644 index 000000000..4e03e0b08 --- /dev/null +++ b/gen/test/c/mx/mx_staff_number.h @@ -0,0 +1,21 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_STAFF_NUMBER_H +#define MX_STAFF_NUMBER_H + +#include + +/* + * The staff-number type indicates staff numbers within a multi-staff part. Staves are numbered from + * top to bottom, with 1 being the top staff on a part. + */ +typedef long MxStaffNumber; + +/* Strict parse, then clamps into the declared range. */ +bool mx_staff_number_try_parse(const char *s, MxStaffNumber *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxStaffNumber mx_staff_number_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_staff_number_to_string(MxStaffNumber v); + +#endif /* MX_STAFF_NUMBER_H */ diff --git a/gen/test/c/mx/mx_staff_type.c b/gen/test/c/mx/mx_staff_type.c new file mode 100644 index 000000000..7f1d8a724 --- /dev/null +++ b/gen/test/c/mx/mx_staff_type.c @@ -0,0 +1,36 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_staff_type.h" + +#include + +static const char *const mx_staff_type_values[] = { + "ossia", + "cue", + "editorial", + "regular", + "alternate", +}; + +bool mx_staff_type_try_parse(const char *s, MxStaffType *out) { + for (size_t i = 0; i < sizeof(mx_staff_type_values) / sizeof(mx_staff_type_values[0]); i++) { + if (strcmp(s, mx_staff_type_values[i]) == 0) { + *out = (MxStaffType)i; + return true; + } + } + *out = (MxStaffType)0; + return false; +} + +MxStaffType mx_staff_type_parse(const char *s) { + MxStaffType v; + mx_staff_type_try_parse(s, &v); + return v; +} + +const char *mx_staff_type_to_string(MxStaffType v) { + if ((size_t)v >= sizeof(mx_staff_type_values) / sizeof(mx_staff_type_values[0])) + return mx_staff_type_values[0]; + return mx_staff_type_values[v]; +} diff --git a/gen/test/c/mx/mx_staff_type.h b/gen/test/c/mx/mx_staff_type.h new file mode 100644 index 000000000..cf786150f --- /dev/null +++ b/gen/test/c/mx/mx_staff_type.h @@ -0,0 +1,27 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_STAFF_TYPE_H +#define MX_STAFF_TYPE_H + +#include + +/* + * The staff-type value can be ossia, cue, editorial, regular, or alternate. An alternate staff + * indicates one that shares the same musical data as the prior staff, but displayed differently + * (e.g., treble and bass clef, standard notation and tab). + */ +typedef enum { + MX_STAFF_TYPE_OSSIA = 0, + MX_STAFF_TYPE_CUE = 1, + MX_STAFF_TYPE_EDITORIAL = 2, + MX_STAFF_TYPE_REGULAR = 3, + MX_STAFF_TYPE_ALTERNATE = 4 +} MxStaffType; + +bool mx_staff_type_try_parse(const char *s, MxStaffType *out); +/* Lenient: unknown input falls back to the first variant. */ +MxStaffType mx_staff_type_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_staff_type_to_string(MxStaffType v); + +#endif /* MX_STAFF_TYPE_H */ diff --git a/gen/test/c/mx/mx_start_note.c b/gen/test/c/mx/mx_start_note.c new file mode 100644 index 000000000..4c1dab4bd --- /dev/null +++ b/gen/test/c/mx/mx_start_note.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_start_note.h" + +#include + +static const char *const mx_start_note_values[] = { + "upper", + "main", + "below", +}; + +bool mx_start_note_try_parse(const char *s, MxStartNote *out) { + for (size_t i = 0; i < sizeof(mx_start_note_values) / sizeof(mx_start_note_values[0]); i++) { + if (strcmp(s, mx_start_note_values[i]) == 0) { + *out = (MxStartNote)i; + return true; + } + } + *out = (MxStartNote)0; + return false; +} + +MxStartNote mx_start_note_parse(const char *s) { + MxStartNote v; + mx_start_note_try_parse(s, &v); + return v; +} + +const char *mx_start_note_to_string(MxStartNote v) { + if ((size_t)v >= sizeof(mx_start_note_values) / sizeof(mx_start_note_values[0])) + return mx_start_note_values[0]; + return mx_start_note_values[v]; +} diff --git a/gen/test/c/mx/mx_start_note.h b/gen/test/c/mx/mx_start_note.h new file mode 100644 index 000000000..419d0b2d1 --- /dev/null +++ b/gen/test/c/mx/mx_start_note.h @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_START_NOTE_H +#define MX_START_NOTE_H + +#include + +/* + * The start-note type describes the starting note of trills and mordents for playback, relative to + * the current note. + */ +typedef enum { + MX_START_NOTE_UPPER = 0, + MX_START_NOTE_MAIN = 1, + MX_START_NOTE_BELOW = 2 +} MxStartNote; + +bool mx_start_note_try_parse(const char *s, MxStartNote *out); +/* Lenient: unknown input falls back to the first variant. */ +MxStartNote mx_start_note_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_start_note_to_string(MxStartNote v); + +#endif /* MX_START_NOTE_H */ diff --git a/gen/test/c/mx/mx_start_stop.c b/gen/test/c/mx/mx_start_stop.c new file mode 100644 index 000000000..782c1fadf --- /dev/null +++ b/gen/test/c/mx/mx_start_stop.c @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_start_stop.h" + +#include + +static const char *const mx_start_stop_values[] = { + "start", + "stop", +}; + +bool mx_start_stop_try_parse(const char *s, MxStartStop *out) { + for (size_t i = 0; i < sizeof(mx_start_stop_values) / sizeof(mx_start_stop_values[0]); i++) { + if (strcmp(s, mx_start_stop_values[i]) == 0) { + *out = (MxStartStop)i; + return true; + } + } + *out = (MxStartStop)0; + return false; +} + +MxStartStop mx_start_stop_parse(const char *s) { + MxStartStop v; + mx_start_stop_try_parse(s, &v); + return v; +} + +const char *mx_start_stop_to_string(MxStartStop v) { + if ((size_t)v >= sizeof(mx_start_stop_values) / sizeof(mx_start_stop_values[0])) + return mx_start_stop_values[0]; + return mx_start_stop_values[v]; +} diff --git a/gen/test/c/mx/mx_start_stop.h b/gen/test/c/mx/mx_start_stop.h new file mode 100644 index 000000000..9d2b3adcf --- /dev/null +++ b/gen/test/c/mx/mx_start_stop.h @@ -0,0 +1,27 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_START_STOP_H +#define MX_START_STOP_H + +#include + +/* + * The start-stop type is used for an attribute of musical elements that can either start or stop, + * such as tuplets. The values of start and stop refer to how an element appears in musical score + * order, not in MusicXML document order. An element with a stop attribute may precede the + * corresponding element with a start attribute within a MusicXML document. This is particularly + * common in multi-staff music. For example, the stopping point for a tuplet may appear in staff 1 + * before the starting point for the tuplet appears in staff 2 later in the document. + */ +typedef enum { + MX_START_STOP_START = 0, + MX_START_STOP_STOP = 1 +} MxStartStop; + +bool mx_start_stop_try_parse(const char *s, MxStartStop *out); +/* Lenient: unknown input falls back to the first variant. */ +MxStartStop mx_start_stop_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_start_stop_to_string(MxStartStop v); + +#endif /* MX_START_STOP_H */ diff --git a/gen/test/c/mx/mx_start_stop_continue.c b/gen/test/c/mx/mx_start_stop_continue.c new file mode 100644 index 000000000..d5805df54 --- /dev/null +++ b/gen/test/c/mx/mx_start_stop_continue.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_start_stop_continue.h" + +#include + +static const char *const mx_start_stop_continue_values[] = { + "start", + "stop", + "continue", +}; + +bool mx_start_stop_continue_try_parse(const char *s, MxStartStopContinue *out) { + for (size_t i = 0; i < sizeof(mx_start_stop_continue_values) / sizeof(mx_start_stop_continue_values[0]); i++) { + if (strcmp(s, mx_start_stop_continue_values[i]) == 0) { + *out = (MxStartStopContinue)i; + return true; + } + } + *out = (MxStartStopContinue)0; + return false; +} + +MxStartStopContinue mx_start_stop_continue_parse(const char *s) { + MxStartStopContinue v; + mx_start_stop_continue_try_parse(s, &v); + return v; +} + +const char *mx_start_stop_continue_to_string(MxStartStopContinue v) { + if ((size_t)v >= sizeof(mx_start_stop_continue_values) / sizeof(mx_start_stop_continue_values[0])) + return mx_start_stop_continue_values[0]; + return mx_start_stop_continue_values[v]; +} diff --git a/gen/test/c/mx/mx_start_stop_continue.h b/gen/test/c/mx/mx_start_stop_continue.h new file mode 100644 index 000000000..31da17d12 --- /dev/null +++ b/gen/test/c/mx/mx_start_stop_continue.h @@ -0,0 +1,30 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_START_STOP_CONTINUE_H +#define MX_START_STOP_CONTINUE_H + +#include + +/* + * The start-stop-continue type is used for an attribute of musical elements that can either start + * or stop, but also need to refer to an intermediate point in the symbol, as for complex slurs or + * for formatting of symbols across system breaks. The values of start, stop, and continue refer to + * how an element appears in musical score order, not in MusicXML document order. An element with a + * stop attribute may precede the corresponding element with a start attribute within a MusicXML + * document. This is particularly common in multi-staff music. For example, the stopping point for a + * slur may appear in staff 1 before the starting point for the slur appears in staff 2 later in the + * document. + */ +typedef enum { + MX_START_STOP_CONTINUE_START = 0, + MX_START_STOP_CONTINUE_STOP = 1, + MX_START_STOP_CONTINUE_CONTINUE = 2 +} MxStartStopContinue; + +bool mx_start_stop_continue_try_parse(const char *s, MxStartStopContinue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxStartStopContinue mx_start_stop_continue_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_start_stop_continue_to_string(MxStartStopContinue v); + +#endif /* MX_START_STOP_CONTINUE_H */ diff --git a/gen/test/c/mx/mx_start_stop_discontinue.c b/gen/test/c/mx/mx_start_stop_discontinue.c new file mode 100644 index 000000000..3d1fc98ad --- /dev/null +++ b/gen/test/c/mx/mx_start_stop_discontinue.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_start_stop_discontinue.h" + +#include + +static const char *const mx_start_stop_discontinue_values[] = { + "start", + "stop", + "discontinue", +}; + +bool mx_start_stop_discontinue_try_parse(const char *s, MxStartStopDiscontinue *out) { + for (size_t i = 0; i < sizeof(mx_start_stop_discontinue_values) / sizeof(mx_start_stop_discontinue_values[0]); i++) { + if (strcmp(s, mx_start_stop_discontinue_values[i]) == 0) { + *out = (MxStartStopDiscontinue)i; + return true; + } + } + *out = (MxStartStopDiscontinue)0; + return false; +} + +MxStartStopDiscontinue mx_start_stop_discontinue_parse(const char *s) { + MxStartStopDiscontinue v; + mx_start_stop_discontinue_try_parse(s, &v); + return v; +} + +const char *mx_start_stop_discontinue_to_string(MxStartStopDiscontinue v) { + if ((size_t)v >= sizeof(mx_start_stop_discontinue_values) / sizeof(mx_start_stop_discontinue_values[0])) + return mx_start_stop_discontinue_values[0]; + return mx_start_stop_discontinue_values[v]; +} diff --git a/gen/test/c/mx/mx_start_stop_discontinue.h b/gen/test/c/mx/mx_start_stop_discontinue.h new file mode 100644 index 000000000..903975384 --- /dev/null +++ b/gen/test/c/mx/mx_start_stop_discontinue.h @@ -0,0 +1,28 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_START_STOP_DISCONTINUE_H +#define MX_START_STOP_DISCONTINUE_H + +#include + +/* + * The start-stop-discontinue type is used to specify ending types. Typically, the start type is + * associated with the left barline of the first measure in an ending. The stop and discontinue + * types are associated with the right barline of the last measure in an ending. Stop is used when + * the ending mark concludes with a downward jog, as is typical for first endings. Discontinue is + * used when there is no downward jog, as is typical for second endings that do not conclude a + * piece. + */ +typedef enum { + MX_START_STOP_DISCONTINUE_START = 0, + MX_START_STOP_DISCONTINUE_STOP = 1, + MX_START_STOP_DISCONTINUE_DISCONTINUE = 2 +} MxStartStopDiscontinue; + +bool mx_start_stop_discontinue_try_parse(const char *s, MxStartStopDiscontinue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxStartStopDiscontinue mx_start_stop_discontinue_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_start_stop_discontinue_to_string(MxStartStopDiscontinue v); + +#endif /* MX_START_STOP_DISCONTINUE_H */ diff --git a/gen/test/c/mx/mx_start_stop_single.c b/gen/test/c/mx/mx_start_stop_single.c new file mode 100644 index 000000000..b3e9394b5 --- /dev/null +++ b/gen/test/c/mx/mx_start_stop_single.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_start_stop_single.h" + +#include + +static const char *const mx_start_stop_single_values[] = { + "start", + "stop", + "single", +}; + +bool mx_start_stop_single_try_parse(const char *s, MxStartStopSingle *out) { + for (size_t i = 0; i < sizeof(mx_start_stop_single_values) / sizeof(mx_start_stop_single_values[0]); i++) { + if (strcmp(s, mx_start_stop_single_values[i]) == 0) { + *out = (MxStartStopSingle)i; + return true; + } + } + *out = (MxStartStopSingle)0; + return false; +} + +MxStartStopSingle mx_start_stop_single_parse(const char *s) { + MxStartStopSingle v; + mx_start_stop_single_try_parse(s, &v); + return v; +} + +const char *mx_start_stop_single_to_string(MxStartStopSingle v) { + if ((size_t)v >= sizeof(mx_start_stop_single_values) / sizeof(mx_start_stop_single_values[0])) + return mx_start_stop_single_values[0]; + return mx_start_stop_single_values[v]; +} diff --git a/gen/test/c/mx/mx_start_stop_single.h b/gen/test/c/mx/mx_start_stop_single.h new file mode 100644 index 000000000..38d50a6a5 --- /dev/null +++ b/gen/test/c/mx/mx_start_stop_single.h @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_START_STOP_SINGLE_H +#define MX_START_STOP_SINGLE_H + +#include + +/* + * The start-stop-single type is used for an attribute of musical elements that can be used for + * either multi-note or single-note musical elements, as for groupings. + */ +typedef enum { + MX_START_STOP_SINGLE_START = 0, + MX_START_STOP_SINGLE_STOP = 1, + MX_START_STOP_SINGLE_SINGLE = 2 +} MxStartStopSingle; + +bool mx_start_stop_single_try_parse(const char *s, MxStartStopSingle *out); +/* Lenient: unknown input falls back to the first variant. */ +MxStartStopSingle mx_start_stop_single_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_start_stop_single_to_string(MxStartStopSingle v); + +#endif /* MX_START_STOP_SINGLE_H */ diff --git a/gen/test/c/mx/mx_stem_value.c b/gen/test/c/mx/mx_stem_value.c new file mode 100644 index 000000000..9306f6cec --- /dev/null +++ b/gen/test/c/mx/mx_stem_value.c @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_stem_value.h" + +#include + +static const char *const mx_stem_value_values[] = { + "down", + "up", + "double", + "none", +}; + +bool mx_stem_value_try_parse(const char *s, MxStemValue *out) { + for (size_t i = 0; i < sizeof(mx_stem_value_values) / sizeof(mx_stem_value_values[0]); i++) { + if (strcmp(s, mx_stem_value_values[i]) == 0) { + *out = (MxStemValue)i; + return true; + } + } + *out = (MxStemValue)0; + return false; +} + +MxStemValue mx_stem_value_parse(const char *s) { + MxStemValue v; + mx_stem_value_try_parse(s, &v); + return v; +} + +const char *mx_stem_value_to_string(MxStemValue v) { + if ((size_t)v >= sizeof(mx_stem_value_values) / sizeof(mx_stem_value_values[0])) + return mx_stem_value_values[0]; + return mx_stem_value_values[v]; +} diff --git a/gen/test/c/mx/mx_stem_value.h b/gen/test/c/mx/mx_stem_value.h new file mode 100644 index 000000000..a6c6e3aba --- /dev/null +++ b/gen/test/c/mx/mx_stem_value.h @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_STEM_VALUE_H +#define MX_STEM_VALUE_H + +#include + +/* + * The stem type represents the notated stem direction. + */ +typedef enum { + MX_STEM_VALUE_DOWN = 0, + MX_STEM_VALUE_UP = 1, + MX_STEM_VALUE_DOUBLE = 2, + MX_STEM_VALUE_NONE = 3 +} MxStemValue; + +bool mx_stem_value_try_parse(const char *s, MxStemValue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxStemValue mx_stem_value_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_stem_value_to_string(MxStemValue v); + +#endif /* MX_STEM_VALUE_H */ diff --git a/gen/test/c/mx/mx_step.c b/gen/test/c/mx/mx_step.c new file mode 100644 index 000000000..620c9fc0d --- /dev/null +++ b/gen/test/c/mx/mx_step.c @@ -0,0 +1,38 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_step.h" + +#include + +static const char *const mx_step_values[] = { + "A", + "B", + "C", + "D", + "E", + "F", + "G", +}; + +bool mx_step_try_parse(const char *s, MxStep *out) { + for (size_t i = 0; i < sizeof(mx_step_values) / sizeof(mx_step_values[0]); i++) { + if (strcmp(s, mx_step_values[i]) == 0) { + *out = (MxStep)i; + return true; + } + } + *out = (MxStep)0; + return false; +} + +MxStep mx_step_parse(const char *s) { + MxStep v; + mx_step_try_parse(s, &v); + return v; +} + +const char *mx_step_to_string(MxStep v) { + if ((size_t)v >= sizeof(mx_step_values) / sizeof(mx_step_values[0])) + return mx_step_values[0]; + return mx_step_values[v]; +} diff --git a/gen/test/c/mx/mx_step.h b/gen/test/c/mx/mx_step.h new file mode 100644 index 000000000..fe0b5f330 --- /dev/null +++ b/gen/test/c/mx/mx_step.h @@ -0,0 +1,28 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_STEP_H +#define MX_STEP_H + +#include + +/* + * The step type represents a step of the diatonic scale, represented using the English letters A + * through G. + */ +typedef enum { + MX_STEP_A = 0, + MX_STEP_B = 1, + MX_STEP_C = 2, + MX_STEP_D = 3, + MX_STEP_E = 4, + MX_STEP_F = 5, + MX_STEP_G = 6 +} MxStep; + +bool mx_step_try_parse(const char *s, MxStep *out); +/* Lenient: unknown input falls back to the first variant. */ +MxStep mx_step_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_step_to_string(MxStep v); + +#endif /* MX_STEP_H */ diff --git a/gen/test/c/mx/mx_stick_location.c b/gen/test/c/mx/mx_stick_location.c new file mode 100644 index 000000000..ab7571ad1 --- /dev/null +++ b/gen/test/c/mx/mx_stick_location.c @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_stick_location.h" + +#include + +static const char *const mx_stick_location_values[] = { + "center", + "rim", + "cymbal bell", + "cymbal edge", +}; + +bool mx_stick_location_try_parse(const char *s, MxStickLocation *out) { + for (size_t i = 0; i < sizeof(mx_stick_location_values) / sizeof(mx_stick_location_values[0]); i++) { + if (strcmp(s, mx_stick_location_values[i]) == 0) { + *out = (MxStickLocation)i; + return true; + } + } + *out = (MxStickLocation)0; + return false; +} + +MxStickLocation mx_stick_location_parse(const char *s) { + MxStickLocation v; + mx_stick_location_try_parse(s, &v); + return v; +} + +const char *mx_stick_location_to_string(MxStickLocation v) { + if ((size_t)v >= sizeof(mx_stick_location_values) / sizeof(mx_stick_location_values[0])) + return mx_stick_location_values[0]; + return mx_stick_location_values[v]; +} diff --git a/gen/test/c/mx/mx_stick_location.h b/gen/test/c/mx/mx_stick_location.h new file mode 100644 index 000000000..0a50fee94 --- /dev/null +++ b/gen/test/c/mx/mx_stick_location.h @@ -0,0 +1,25 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_STICK_LOCATION_H +#define MX_STICK_LOCATION_H + +#include + +/* + * The stick-location type represents pictograms for the location of sticks, beaters, or mallets on + * cymbals, gongs, drums, and other instruments. + */ +typedef enum { + MX_STICK_LOCATION_CENTER = 0, + MX_STICK_LOCATION_RIM = 1, + MX_STICK_LOCATION_CYMBAL_BELL = 2, + MX_STICK_LOCATION_CYMBAL_EDGE = 3 +} MxStickLocation; + +bool mx_stick_location_try_parse(const char *s, MxStickLocation *out); +/* Lenient: unknown input falls back to the first variant. */ +MxStickLocation mx_stick_location_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_stick_location_to_string(MxStickLocation v); + +#endif /* MX_STICK_LOCATION_H */ diff --git a/gen/test/c/mx/mx_stick_material.c b/gen/test/c/mx/mx_stick_material.c new file mode 100644 index 000000000..421eea8e8 --- /dev/null +++ b/gen/test/c/mx/mx_stick_material.c @@ -0,0 +1,36 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_stick_material.h" + +#include + +static const char *const mx_stick_material_values[] = { + "soft", + "medium", + "hard", + "shaded", + "x", +}; + +bool mx_stick_material_try_parse(const char *s, MxStickMaterial *out) { + for (size_t i = 0; i < sizeof(mx_stick_material_values) / sizeof(mx_stick_material_values[0]); i++) { + if (strcmp(s, mx_stick_material_values[i]) == 0) { + *out = (MxStickMaterial)i; + return true; + } + } + *out = (MxStickMaterial)0; + return false; +} + +MxStickMaterial mx_stick_material_parse(const char *s) { + MxStickMaterial v; + mx_stick_material_try_parse(s, &v); + return v; +} + +const char *mx_stick_material_to_string(MxStickMaterial v) { + if ((size_t)v >= sizeof(mx_stick_material_values) / sizeof(mx_stick_material_values[0])) + return mx_stick_material_values[0]; + return mx_stick_material_values[v]; +} diff --git a/gen/test/c/mx/mx_stick_material.h b/gen/test/c/mx/mx_stick_material.h new file mode 100644 index 000000000..1606559ff --- /dev/null +++ b/gen/test/c/mx/mx_stick_material.h @@ -0,0 +1,25 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_STICK_MATERIAL_H +#define MX_STICK_MATERIAL_H + +#include + +/* + * The stick-material type represents the material being displayed in a stick pictogram. + */ +typedef enum { + MX_STICK_MATERIAL_SOFT = 0, + MX_STICK_MATERIAL_MEDIUM = 1, + MX_STICK_MATERIAL_HARD = 2, + MX_STICK_MATERIAL_SHADED = 3, + MX_STICK_MATERIAL_X = 4 +} MxStickMaterial; + +bool mx_stick_material_try_parse(const char *s, MxStickMaterial *out); +/* Lenient: unknown input falls back to the first variant. */ +MxStickMaterial mx_stick_material_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_stick_material_to_string(MxStickMaterial v); + +#endif /* MX_STICK_MATERIAL_H */ diff --git a/gen/test/c/mx/mx_stick_type.c b/gen/test/c/mx/mx_stick_type.c new file mode 100644 index 000000000..642fd54b0 --- /dev/null +++ b/gen/test/c/mx/mx_stick_type.c @@ -0,0 +1,41 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_stick_type.h" + +#include + +static const char *const mx_stick_type_values[] = { + "bass drum", + "double bass drum", + "glockenspiel", + "gum", + "hammer", + "superball", + "timpani", + "wound", + "xylophone", + "yarn", +}; + +bool mx_stick_type_try_parse(const char *s, MxStickType *out) { + for (size_t i = 0; i < sizeof(mx_stick_type_values) / sizeof(mx_stick_type_values[0]); i++) { + if (strcmp(s, mx_stick_type_values[i]) == 0) { + *out = (MxStickType)i; + return true; + } + } + *out = (MxStickType)0; + return false; +} + +MxStickType mx_stick_type_parse(const char *s) { + MxStickType v; + mx_stick_type_try_parse(s, &v); + return v; +} + +const char *mx_stick_type_to_string(MxStickType v) { + if ((size_t)v >= sizeof(mx_stick_type_values) / sizeof(mx_stick_type_values[0])) + return mx_stick_type_values[0]; + return mx_stick_type_values[v]; +} diff --git a/gen/test/c/mx/mx_stick_type.h b/gen/test/c/mx/mx_stick_type.h new file mode 100644 index 000000000..d5a311099 --- /dev/null +++ b/gen/test/c/mx/mx_stick_type.h @@ -0,0 +1,31 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_STICK_TYPE_H +#define MX_STICK_TYPE_H + +#include + +/* + * The stick-type type represents the shape of pictograms where the material in the stick, mallet, + * or beater is represented in the pictogram. + */ +typedef enum { + MX_STICK_TYPE_BASS_DRUM = 0, + MX_STICK_TYPE_DOUBLE_BASS_DRUM = 1, + MX_STICK_TYPE_GLOCKENSPIEL = 2, + MX_STICK_TYPE_GUM = 3, + MX_STICK_TYPE_HAMMER = 4, + MX_STICK_TYPE_SUPERBALL = 5, + MX_STICK_TYPE_TIMPANI = 6, + MX_STICK_TYPE_WOUND = 7, + MX_STICK_TYPE_XYLOPHONE = 8, + MX_STICK_TYPE_YARN = 9 +} MxStickType; + +bool mx_stick_type_try_parse(const char *s, MxStickType *out); +/* Lenient: unknown input falls back to the first variant. */ +MxStickType mx_stick_type_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_stick_type_to_string(MxStickType v); + +#endif /* MX_STICK_TYPE_H */ diff --git a/gen/test/c/mx/mx_string_number.c b/gen/test/c/mx/mx_string_number.c new file mode 100644 index 000000000..adf97931b --- /dev/null +++ b/gen/test/c/mx/mx_string_number.c @@ -0,0 +1,30 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_string_number.h" + +#include "mx_runtime.h" + +static MxStringNumber mx_string_number_clamp(long v) { + if (v < 1) + v = 1; + return (MxStringNumber)v; +} + +bool mx_string_number_try_parse(const char *s, MxStringNumber *out) { + long v; + if (!mx_try_parse_int(s, &v)) { + *out = (MxStringNumber)0; + return false; + } + *out = mx_string_number_clamp(v); + return true; +} + +MxStringNumber mx_string_number_parse(const char *s) { + long v = mx_parse_int(s); + return mx_string_number_clamp(v); +} + +char *mx_string_number_to_string(MxStringNumber v) { + return mx_format_int((long)v); +} diff --git a/gen/test/c/mx/mx_string_number.h b/gen/test/c/mx/mx_string_number.h new file mode 100644 index 000000000..b524ca4e2 --- /dev/null +++ b/gen/test/c/mx/mx_string_number.h @@ -0,0 +1,21 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_STRING_NUMBER_H +#define MX_STRING_NUMBER_H + +#include + +/* + * The string-number type indicates a string number. Strings are numbered from high to low, with 1 + * being the highest pitched full-length string. + */ +typedef long MxStringNumber; + +/* Strict parse, then clamps into the declared range. */ +bool mx_string_number_try_parse(const char *s, MxStringNumber *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxStringNumber mx_string_number_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_string_number_to_string(MxStringNumber v); + +#endif /* MX_STRING_NUMBER_H */ diff --git a/gen/test/c/mx/mx_syllabic.c b/gen/test/c/mx/mx_syllabic.c new file mode 100644 index 000000000..9363c44c8 --- /dev/null +++ b/gen/test/c/mx/mx_syllabic.c @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_syllabic.h" + +#include + +static const char *const mx_syllabic_values[] = { + "single", + "begin", + "end", + "middle", +}; + +bool mx_syllabic_try_parse(const char *s, MxSyllabic *out) { + for (size_t i = 0; i < sizeof(mx_syllabic_values) / sizeof(mx_syllabic_values[0]); i++) { + if (strcmp(s, mx_syllabic_values[i]) == 0) { + *out = (MxSyllabic)i; + return true; + } + } + *out = (MxSyllabic)0; + return false; +} + +MxSyllabic mx_syllabic_parse(const char *s) { + MxSyllabic v; + mx_syllabic_try_parse(s, &v); + return v; +} + +const char *mx_syllabic_to_string(MxSyllabic v) { + if ((size_t)v >= sizeof(mx_syllabic_values) / sizeof(mx_syllabic_values[0])) + return mx_syllabic_values[0]; + return mx_syllabic_values[v]; +} diff --git a/gen/test/c/mx/mx_syllabic.h b/gen/test/c/mx/mx_syllabic.h new file mode 100644 index 000000000..65d74e5c8 --- /dev/null +++ b/gen/test/c/mx/mx_syllabic.h @@ -0,0 +1,26 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SYLLABIC_H +#define MX_SYLLABIC_H + +#include + +/* + * Lyric hyphenation is indicated by the syllabic type. The single, begin, end, and middle values + * represent single-syllable words, word-beginning syllables, word-ending syllables, and mid-word + * syllables, respectively. + */ +typedef enum { + MX_SYLLABIC_SINGLE = 0, + MX_SYLLABIC_BEGIN = 1, + MX_SYLLABIC_END = 2, + MX_SYLLABIC_MIDDLE = 3 +} MxSyllabic; + +bool mx_syllabic_try_parse(const char *s, MxSyllabic *out); +/* Lenient: unknown input falls back to the first variant. */ +MxSyllabic mx_syllabic_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_syllabic_to_string(MxSyllabic v); + +#endif /* MX_SYLLABIC_H */ diff --git a/gen/test/c/mx/mx_symbol_size.c b/gen/test/c/mx/mx_symbol_size.c new file mode 100644 index 000000000..333eb8ab5 --- /dev/null +++ b/gen/test/c/mx/mx_symbol_size.c @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_symbol_size.h" + +#include + +static const char *const mx_symbol_size_values[] = { + "full", + "cue", + "grace-cue", + "large", +}; + +bool mx_symbol_size_try_parse(const char *s, MxSymbolSize *out) { + for (size_t i = 0; i < sizeof(mx_symbol_size_values) / sizeof(mx_symbol_size_values[0]); i++) { + if (strcmp(s, mx_symbol_size_values[i]) == 0) { + *out = (MxSymbolSize)i; + return true; + } + } + *out = (MxSymbolSize)0; + return false; +} + +MxSymbolSize mx_symbol_size_parse(const char *s) { + MxSymbolSize v; + mx_symbol_size_try_parse(s, &v); + return v; +} + +const char *mx_symbol_size_to_string(MxSymbolSize v) { + if ((size_t)v >= sizeof(mx_symbol_size_values) / sizeof(mx_symbol_size_values[0])) + return mx_symbol_size_values[0]; + return mx_symbol_size_values[v]; +} diff --git a/gen/test/c/mx/mx_symbol_size.h b/gen/test/c/mx/mx_symbol_size.h new file mode 100644 index 000000000..6f4acdb9f --- /dev/null +++ b/gen/test/c/mx/mx_symbol_size.h @@ -0,0 +1,25 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SYMBOL_SIZE_H +#define MX_SYMBOL_SIZE_H + +#include + +/* + * The symbol-size type is used to distinguish between full, cue sized, grace cue sized, and + * oversized symbols. + */ +typedef enum { + MX_SYMBOL_SIZE_FULL = 0, + MX_SYMBOL_SIZE_CUE = 1, + MX_SYMBOL_SIZE_GRACE_CUE = 2, + MX_SYMBOL_SIZE_LARGE = 3 +} MxSymbolSize; + +bool mx_symbol_size_try_parse(const char *s, MxSymbolSize *out); +/* Lenient: unknown input falls back to the first variant. */ +MxSymbolSize mx_symbol_size_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_symbol_size_to_string(MxSymbolSize v); + +#endif /* MX_SYMBOL_SIZE_H */ diff --git a/gen/test/c/mx/mx_tap_hand.c b/gen/test/c/mx/mx_tap_hand.c new file mode 100644 index 000000000..2aaf69dfa --- /dev/null +++ b/gen/test/c/mx/mx_tap_hand.c @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_tap_hand.h" + +#include + +static const char *const mx_tap_hand_values[] = { + "left", + "right", +}; + +bool mx_tap_hand_try_parse(const char *s, MxTapHand *out) { + for (size_t i = 0; i < sizeof(mx_tap_hand_values) / sizeof(mx_tap_hand_values[0]); i++) { + if (strcmp(s, mx_tap_hand_values[i]) == 0) { + *out = (MxTapHand)i; + return true; + } + } + *out = (MxTapHand)0; + return false; +} + +MxTapHand mx_tap_hand_parse(const char *s) { + MxTapHand v; + mx_tap_hand_try_parse(s, &v); + return v; +} + +const char *mx_tap_hand_to_string(MxTapHand v) { + if ((size_t)v >= sizeof(mx_tap_hand_values) / sizeof(mx_tap_hand_values[0])) + return mx_tap_hand_values[0]; + return mx_tap_hand_values[v]; +} diff --git a/gen/test/c/mx/mx_tap_hand.h b/gen/test/c/mx/mx_tap_hand.h new file mode 100644 index 000000000..edd8ac9e5 --- /dev/null +++ b/gen/test/c/mx/mx_tap_hand.h @@ -0,0 +1,23 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TAP_HAND_H +#define MX_TAP_HAND_H + +#include + +/* + * The tap-hand type represents the symbol to use for a tap element. The left and right values refer + * to the SMuFL guitarLeftHandTapping and guitarRightHandTapping glyphs respectively. + */ +typedef enum { + MX_TAP_HAND_LEFT = 0, + MX_TAP_HAND_RIGHT = 1 +} MxTapHand; + +bool mx_tap_hand_try_parse(const char *s, MxTapHand *out); +/* Lenient: unknown input falls back to the first variant. */ +MxTapHand mx_tap_hand_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_tap_hand_to_string(MxTapHand v); + +#endif /* MX_TAP_HAND_H */ diff --git a/gen/test/c/mx/mx_tenths.c b/gen/test/c/mx/mx_tenths.c new file mode 100644 index 000000000..0c3f9318a --- /dev/null +++ b/gen/test/c/mx/mx_tenths.c @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_tenths.h" + +#include "mx_runtime.h" + +bool mx_tenths_try_parse(const char *s, MxTenths *out) { + double v; + if (!mx_try_parse_decimal(s, &v)) { + *out = (MxTenths)0; + return false; + } + *out = (MxTenths)v; + return true; +} + +MxTenths mx_tenths_parse(const char *s) { + double v = mx_parse_decimal(s); + return (MxTenths)v; +} + +char *mx_tenths_to_string(MxTenths v) { + return mx_format_decimal((double)v); +} diff --git a/gen/test/c/mx/mx_tenths.h b/gen/test/c/mx/mx_tenths.h new file mode 100644 index 000000000..8041a9353 --- /dev/null +++ b/gen/test/c/mx/mx_tenths.h @@ -0,0 +1,27 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TENTHS_H +#define MX_TENTHS_H + +#include + +/* + * The tenths type is a number representing tenths of interline staff space (positive or negative). + * Both integer and decimal values are allowed, such as 5 for a half space and 2.5 for a quarter + * space. Interline space is measured from the middle of a staff line. Distances in a MusicXML file + * are measured in tenths of staff space. Tenths are then scaled to millimeters within the scaling + * element, used in the defaults element at the start of a score. Individual staves can apply a + * scaling factor to adjust staff size. When a MusicXML element or attribute refers to tenths, it + * means the global tenths defined by the scaling element, not the local tenths as adjusted by the + * staff-size element. + */ +typedef double MxTenths; + +/* Strict parse, then clamps into the declared range. */ +bool mx_tenths_try_parse(const char *s, MxTenths *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxTenths mx_tenths_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_tenths_to_string(MxTenths v); + +#endif /* MX_TENTHS_H */ diff --git a/gen/test/c/mx/mx_text_direction.c b/gen/test/c/mx/mx_text_direction.c new file mode 100644 index 000000000..612c86e2b --- /dev/null +++ b/gen/test/c/mx/mx_text_direction.c @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_text_direction.h" + +#include + +static const char *const mx_text_direction_values[] = { + "ltr", + "rtl", + "lro", + "rlo", +}; + +bool mx_text_direction_try_parse(const char *s, MxTextDirection *out) { + for (size_t i = 0; i < sizeof(mx_text_direction_values) / sizeof(mx_text_direction_values[0]); i++) { + if (strcmp(s, mx_text_direction_values[i]) == 0) { + *out = (MxTextDirection)i; + return true; + } + } + *out = (MxTextDirection)0; + return false; +} + +MxTextDirection mx_text_direction_parse(const char *s) { + MxTextDirection v; + mx_text_direction_try_parse(s, &v); + return v; +} + +const char *mx_text_direction_to_string(MxTextDirection v) { + if ((size_t)v >= sizeof(mx_text_direction_values) / sizeof(mx_text_direction_values[0])) + return mx_text_direction_values[0]; + return mx_text_direction_values[v]; +} diff --git a/gen/test/c/mx/mx_text_direction.h b/gen/test/c/mx/mx_text_direction.h new file mode 100644 index 000000000..555619f4d --- /dev/null +++ b/gen/test/c/mx/mx_text_direction.h @@ -0,0 +1,29 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TEXT_DIRECTION_H +#define MX_TEXT_DIRECTION_H + +#include + +/* + * The text-direction type is used to adjust and override the Unicode bidirectional text algorithm, + * similar to the W3C Internationalization Tag Set recommendation. Values are ltr (left-to-right + * embed), rtl (right-to-left embed), lro (left-to-right bidi-override), and rlo (right-to-left + * bidi-override). The default value is ltr. This type is typically used by applications that store + * text in left-to-right visual order rather than logical order. Such applications can use the lro + * value to better communicate with other applications that more fully support bidirectional text. + */ +typedef enum { + MX_TEXT_DIRECTION_LTR = 0, + MX_TEXT_DIRECTION_RTL = 1, + MX_TEXT_DIRECTION_LRO = 2, + MX_TEXT_DIRECTION_RLO = 3 +} MxTextDirection; + +bool mx_text_direction_try_parse(const char *s, MxTextDirection *out); +/* Lenient: unknown input falls back to the first variant. */ +MxTextDirection mx_text_direction_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_text_direction_to_string(MxTextDirection v); + +#endif /* MX_TEXT_DIRECTION_H */ diff --git a/gen/test/c/mx/mx_tied_type.c b/gen/test/c/mx/mx_tied_type.c new file mode 100644 index 000000000..31fbaa2aa --- /dev/null +++ b/gen/test/c/mx/mx_tied_type.c @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_tied_type.h" + +#include + +static const char *const mx_tied_type_values[] = { + "start", + "stop", + "continue", + "let-ring", +}; + +bool mx_tied_type_try_parse(const char *s, MxTiedType *out) { + for (size_t i = 0; i < sizeof(mx_tied_type_values) / sizeof(mx_tied_type_values[0]); i++) { + if (strcmp(s, mx_tied_type_values[i]) == 0) { + *out = (MxTiedType)i; + return true; + } + } + *out = (MxTiedType)0; + return false; +} + +MxTiedType mx_tied_type_parse(const char *s) { + MxTiedType v; + mx_tied_type_try_parse(s, &v); + return v; +} + +const char *mx_tied_type_to_string(MxTiedType v) { + if ((size_t)v >= sizeof(mx_tied_type_values) / sizeof(mx_tied_type_values[0])) + return mx_tied_type_values[0]; + return mx_tied_type_values[v]; +} diff --git a/gen/test/c/mx/mx_tied_type.h b/gen/test/c/mx/mx_tied_type.h new file mode 100644 index 000000000..1bf51dd34 --- /dev/null +++ b/gen/test/c/mx/mx_tied_type.h @@ -0,0 +1,31 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TIED_TYPE_H +#define MX_TIED_TYPE_H + +#include + +/* + * The tied-type type is used as an attribute of the tied element to specify where the visual + * representation of a tie begins and ends. A tied element which joins two notes of the same pitch + * can be specified with tied-type start on the first note and tied-type stop on the second note. To + * indicate a note should be undamped, use a single tied element with tied-type let-ring. For other + * ties that are visually attached to a single note, such as a tie leading into or out of a repeated + * section or coda, use two tied elements on the same note, one start and one stop. In start-stop + * cases, ties can add more elements using a continue type. This is typically used to specify the + * formatting of cross-system ties. + */ +typedef enum { + MX_TIED_TYPE_START = 0, + MX_TIED_TYPE_STOP = 1, + MX_TIED_TYPE_CONTINUE = 2, + MX_TIED_TYPE_LET_RING = 3 +} MxTiedType; + +bool mx_tied_type_try_parse(const char *s, MxTiedType *out); +/* Lenient: unknown input falls back to the first variant. */ +MxTiedType mx_tied_type_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_tied_type_to_string(MxTiedType v); + +#endif /* MX_TIED_TYPE_H */ diff --git a/gen/test/c/mx/mx_time_only.c b/gen/test/c/mx/mx_time_only.c new file mode 100644 index 000000000..8614843a8 --- /dev/null +++ b/gen/test/c/mx/mx_time_only.c @@ -0,0 +1,9 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_time_only.h" + +#include "mx_runtime.h" + +MxTimeOnly mx_time_only_parse(const char *s) { + return mx_strdup(s); +} diff --git a/gen/test/c/mx/mx_time_only.h b/gen/test/c/mx/mx_time_only.h new file mode 100644 index 000000000..4a62e3328 --- /dev/null +++ b/gen/test/c/mx/mx_time_only.h @@ -0,0 +1,17 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TIME_ONLY_H +#define MX_TIME_ONLY_H + +/* + * The time-only type is used to indicate that a particular playback-related element only applies + * particular times through a repeated section. The value is a comma-separated list of positive + * integers arranged in ascending order, indicating which times through the repeated section that + * the element applies. Pattern (not enforced): [1-9][0-9]*(, ?[1-9][0-9]*)* + */ +typedef char *MxTimeOnly; + +/* Malloc'd copy of the wire string; the value IS its spelling. */ +MxTimeOnly mx_time_only_parse(const char *s); + +#endif /* MX_TIME_ONLY_H */ diff --git a/gen/test/c/mx/mx_time_relation.c b/gen/test/c/mx/mx_time_relation.c new file mode 100644 index 000000000..6eab25e70 --- /dev/null +++ b/gen/test/c/mx/mx_time_relation.c @@ -0,0 +1,37 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_time_relation.h" + +#include + +static const char *const mx_time_relation_values[] = { + "parentheses", + "bracket", + "equals", + "slash", + "space", + "hyphen", +}; + +bool mx_time_relation_try_parse(const char *s, MxTimeRelation *out) { + for (size_t i = 0; i < sizeof(mx_time_relation_values) / sizeof(mx_time_relation_values[0]); i++) { + if (strcmp(s, mx_time_relation_values[i]) == 0) { + *out = (MxTimeRelation)i; + return true; + } + } + *out = (MxTimeRelation)0; + return false; +} + +MxTimeRelation mx_time_relation_parse(const char *s) { + MxTimeRelation v; + mx_time_relation_try_parse(s, &v); + return v; +} + +const char *mx_time_relation_to_string(MxTimeRelation v) { + if ((size_t)v >= sizeof(mx_time_relation_values) / sizeof(mx_time_relation_values[0])) + return mx_time_relation_values[0]; + return mx_time_relation_values[v]; +} diff --git a/gen/test/c/mx/mx_time_relation.h b/gen/test/c/mx/mx_time_relation.h new file mode 100644 index 000000000..d70d2c52a --- /dev/null +++ b/gen/test/c/mx/mx_time_relation.h @@ -0,0 +1,27 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TIME_RELATION_H +#define MX_TIME_RELATION_H + +#include + +/* + * The time-relation type indicates the symbol used to represent the interchangeable aspect of dual + * time signatures. + */ +typedef enum { + MX_TIME_RELATION_PARENTHESES = 0, + MX_TIME_RELATION_BRACKET = 1, + MX_TIME_RELATION_EQUALS = 2, + MX_TIME_RELATION_SLASH = 3, + MX_TIME_RELATION_SPACE = 4, + MX_TIME_RELATION_HYPHEN = 5 +} MxTimeRelation; + +bool mx_time_relation_try_parse(const char *s, MxTimeRelation *out); +/* Lenient: unknown input falls back to the first variant. */ +MxTimeRelation mx_time_relation_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_time_relation_to_string(MxTimeRelation v); + +#endif /* MX_TIME_RELATION_H */ diff --git a/gen/test/c/mx/mx_time_separator.c b/gen/test/c/mx/mx_time_separator.c new file mode 100644 index 000000000..8d43079cd --- /dev/null +++ b/gen/test/c/mx/mx_time_separator.c @@ -0,0 +1,36 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_time_separator.h" + +#include + +static const char *const mx_time_separator_values[] = { + "none", + "horizontal", + "diagonal", + "vertical", + "adjacent", +}; + +bool mx_time_separator_try_parse(const char *s, MxTimeSeparator *out) { + for (size_t i = 0; i < sizeof(mx_time_separator_values) / sizeof(mx_time_separator_values[0]); i++) { + if (strcmp(s, mx_time_separator_values[i]) == 0) { + *out = (MxTimeSeparator)i; + return true; + } + } + *out = (MxTimeSeparator)0; + return false; +} + +MxTimeSeparator mx_time_separator_parse(const char *s) { + MxTimeSeparator v; + mx_time_separator_try_parse(s, &v); + return v; +} + +const char *mx_time_separator_to_string(MxTimeSeparator v) { + if ((size_t)v >= sizeof(mx_time_separator_values) / sizeof(mx_time_separator_values[0])) + return mx_time_separator_values[0]; + return mx_time_separator_values[v]; +} diff --git a/gen/test/c/mx/mx_time_separator.h b/gen/test/c/mx/mx_time_separator.h new file mode 100644 index 000000000..0c029a1ca --- /dev/null +++ b/gen/test/c/mx/mx_time_separator.h @@ -0,0 +1,30 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TIME_SEPARATOR_H +#define MX_TIME_SEPARATOR_H + +#include + +/* + * The time-separator type indicates how to display the arrangement between the beats and beat-type + * values in a time signature. The default value is none. The horizontal, diagonal, and vertical + * values represent horizontal, diagonal lower-left to upper-right, and vertical lines respectively. + * For these values, the beats and beat-type values are arranged on either side of the separator + * line. The none value represents no separator with the beats and beat-type arranged vertically. + * The adjacent value represents no separator with the beats and beat-type arranged horizontally. + */ +typedef enum { + MX_TIME_SEPARATOR_NONE = 0, + MX_TIME_SEPARATOR_HORIZONTAL = 1, + MX_TIME_SEPARATOR_DIAGONAL = 2, + MX_TIME_SEPARATOR_VERTICAL = 3, + MX_TIME_SEPARATOR_ADJACENT = 4 +} MxTimeSeparator; + +bool mx_time_separator_try_parse(const char *s, MxTimeSeparator *out); +/* Lenient: unknown input falls back to the first variant. */ +MxTimeSeparator mx_time_separator_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_time_separator_to_string(MxTimeSeparator v); + +#endif /* MX_TIME_SEPARATOR_H */ diff --git a/gen/test/c/mx/mx_time_symbol.c b/gen/test/c/mx/mx_time_symbol.c new file mode 100644 index 000000000..b87043cf1 --- /dev/null +++ b/gen/test/c/mx/mx_time_symbol.c @@ -0,0 +1,37 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_time_symbol.h" + +#include + +static const char *const mx_time_symbol_values[] = { + "common", + "cut", + "single-number", + "note", + "dotted-note", + "normal", +}; + +bool mx_time_symbol_try_parse(const char *s, MxTimeSymbol *out) { + for (size_t i = 0; i < sizeof(mx_time_symbol_values) / sizeof(mx_time_symbol_values[0]); i++) { + if (strcmp(s, mx_time_symbol_values[i]) == 0) { + *out = (MxTimeSymbol)i; + return true; + } + } + *out = (MxTimeSymbol)0; + return false; +} + +MxTimeSymbol mx_time_symbol_parse(const char *s) { + MxTimeSymbol v; + mx_time_symbol_try_parse(s, &v); + return v; +} + +const char *mx_time_symbol_to_string(MxTimeSymbol v) { + if ((size_t)v >= sizeof(mx_time_symbol_values) / sizeof(mx_time_symbol_values[0])) + return mx_time_symbol_values[0]; + return mx_time_symbol_values[v]; +} diff --git a/gen/test/c/mx/mx_time_symbol.h b/gen/test/c/mx/mx_time_symbol.h new file mode 100644 index 000000000..ef3cbb42d --- /dev/null +++ b/gen/test/c/mx/mx_time_symbol.h @@ -0,0 +1,32 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TIME_SYMBOL_H +#define MX_TIME_SYMBOL_H + +#include + +/* + * The time-symbol type indicates how to display a time signature. The normal value is the usual + * fractional display, and is the implied symbol type if none is specified. Other options are the + * common and cut time symbols, as well as a single number with an implied denominator. The note + * symbol indicates that the beat-type should be represented with the corresponding downstem note + * rather than a number. The dotted-note symbol indicates that the beat-type should be represented + * with a dotted downstem note that corresponds to three times the beat-type value, and a numerator + * that is one third the beats value. + */ +typedef enum { + MX_TIME_SYMBOL_COMMON = 0, + MX_TIME_SYMBOL_CUT = 1, + MX_TIME_SYMBOL_SINGLE_NUMBER = 2, + MX_TIME_SYMBOL_NOTE = 3, + MX_TIME_SYMBOL_DOTTED_NOTE = 4, + MX_TIME_SYMBOL_NORMAL = 5 +} MxTimeSymbol; + +bool mx_time_symbol_try_parse(const char *s, MxTimeSymbol *out); +/* Lenient: unknown input falls back to the first variant. */ +MxTimeSymbol mx_time_symbol_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_time_symbol_to_string(MxTimeSymbol v); + +#endif /* MX_TIME_SYMBOL_H */ diff --git a/gen/test/c/mx/mx_tip_direction.c b/gen/test/c/mx/mx_tip_direction.c new file mode 100644 index 000000000..ea8a6a397 --- /dev/null +++ b/gen/test/c/mx/mx_tip_direction.c @@ -0,0 +1,39 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_tip_direction.h" + +#include + +static const char *const mx_tip_direction_values[] = { + "up", + "down", + "left", + "right", + "northwest", + "northeast", + "southeast", + "southwest", +}; + +bool mx_tip_direction_try_parse(const char *s, MxTipDirection *out) { + for (size_t i = 0; i < sizeof(mx_tip_direction_values) / sizeof(mx_tip_direction_values[0]); i++) { + if (strcmp(s, mx_tip_direction_values[i]) == 0) { + *out = (MxTipDirection)i; + return true; + } + } + *out = (MxTipDirection)0; + return false; +} + +MxTipDirection mx_tip_direction_parse(const char *s) { + MxTipDirection v; + mx_tip_direction_try_parse(s, &v); + return v; +} + +const char *mx_tip_direction_to_string(MxTipDirection v) { + if ((size_t)v >= sizeof(mx_tip_direction_values) / sizeof(mx_tip_direction_values[0])) + return mx_tip_direction_values[0]; + return mx_tip_direction_values[v]; +} diff --git a/gen/test/c/mx/mx_tip_direction.h b/gen/test/c/mx/mx_tip_direction.h new file mode 100644 index 000000000..497540143 --- /dev/null +++ b/gen/test/c/mx/mx_tip_direction.h @@ -0,0 +1,29 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TIP_DIRECTION_H +#define MX_TIP_DIRECTION_H + +#include + +/* + * The tip-direction type represents the direction in which the tip of a stick or beater points, + * using Unicode arrow terminology. + */ +typedef enum { + MX_TIP_DIRECTION_UP = 0, + MX_TIP_DIRECTION_DOWN = 1, + MX_TIP_DIRECTION_LEFT = 2, + MX_TIP_DIRECTION_RIGHT = 3, + MX_TIP_DIRECTION_NORTHWEST = 4, + MX_TIP_DIRECTION_NORTHEAST = 5, + MX_TIP_DIRECTION_SOUTHEAST = 6, + MX_TIP_DIRECTION_SOUTHWEST = 7 +} MxTipDirection; + +bool mx_tip_direction_try_parse(const char *s, MxTipDirection *out); +/* Lenient: unknown input falls back to the first variant. */ +MxTipDirection mx_tip_direction_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_tip_direction_to_string(MxTipDirection v); + +#endif /* MX_TIP_DIRECTION_H */ diff --git a/gen/test/c/mx/mx_top_bottom.c b/gen/test/c/mx/mx_top_bottom.c new file mode 100644 index 000000000..9ef213ba9 --- /dev/null +++ b/gen/test/c/mx/mx_top_bottom.c @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_top_bottom.h" + +#include + +static const char *const mx_top_bottom_values[] = { + "top", + "bottom", +}; + +bool mx_top_bottom_try_parse(const char *s, MxTopBottom *out) { + for (size_t i = 0; i < sizeof(mx_top_bottom_values) / sizeof(mx_top_bottom_values[0]); i++) { + if (strcmp(s, mx_top_bottom_values[i]) == 0) { + *out = (MxTopBottom)i; + return true; + } + } + *out = (MxTopBottom)0; + return false; +} + +MxTopBottom mx_top_bottom_parse(const char *s) { + MxTopBottom v; + mx_top_bottom_try_parse(s, &v); + return v; +} + +const char *mx_top_bottom_to_string(MxTopBottom v) { + if ((size_t)v >= sizeof(mx_top_bottom_values) / sizeof(mx_top_bottom_values[0])) + return mx_top_bottom_values[0]; + return mx_top_bottom_values[v]; +} diff --git a/gen/test/c/mx/mx_top_bottom.h b/gen/test/c/mx/mx_top_bottom.h new file mode 100644 index 000000000..53ae3af58 --- /dev/null +++ b/gen/test/c/mx/mx_top_bottom.h @@ -0,0 +1,23 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TOP_BOTTOM_H +#define MX_TOP_BOTTOM_H + +#include + +/* + * The top-bottom type is used to indicate the top or bottom part of a vertical shape like + * non-arpeggiate. + */ +typedef enum { + MX_TOP_BOTTOM_TOP = 0, + MX_TOP_BOTTOM_BOTTOM = 1 +} MxTopBottom; + +bool mx_top_bottom_try_parse(const char *s, MxTopBottom *out); +/* Lenient: unknown input falls back to the first variant. */ +MxTopBottom mx_top_bottom_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_top_bottom_to_string(MxTopBottom v); + +#endif /* MX_TOP_BOTTOM_H */ diff --git a/gen/test/c/mx/mx_tremolo_marks.c b/gen/test/c/mx/mx_tremolo_marks.c new file mode 100644 index 000000000..35748b93f --- /dev/null +++ b/gen/test/c/mx/mx_tremolo_marks.c @@ -0,0 +1,32 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_tremolo_marks.h" + +#include "mx_runtime.h" + +static MxTremoloMarks mx_tremolo_marks_clamp(long v) { + if (v < 0) + v = 0; + if (v > 8) + v = 8; + return (MxTremoloMarks)v; +} + +bool mx_tremolo_marks_try_parse(const char *s, MxTremoloMarks *out) { + long v; + if (!mx_try_parse_int(s, &v)) { + *out = (MxTremoloMarks)0; + return false; + } + *out = mx_tremolo_marks_clamp(v); + return true; +} + +MxTremoloMarks mx_tremolo_marks_parse(const char *s) { + long v = mx_parse_int(s); + return mx_tremolo_marks_clamp(v); +} + +char *mx_tremolo_marks_to_string(MxTremoloMarks v) { + return mx_format_int((long)v); +} diff --git a/gen/test/c/mx/mx_tremolo_marks.h b/gen/test/c/mx/mx_tremolo_marks.h new file mode 100644 index 000000000..d44c33a70 --- /dev/null +++ b/gen/test/c/mx/mx_tremolo_marks.h @@ -0,0 +1,21 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TREMOLO_MARKS_H +#define MX_TREMOLO_MARKS_H + +#include + +/* + * The number of tremolo marks is represented by a number from 0 to 8: the same as beam-level with 0 + * added. + */ +typedef long MxTremoloMarks; + +/* Strict parse, then clamps into the declared range. */ +bool mx_tremolo_marks_try_parse(const char *s, MxTremoloMarks *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxTremoloMarks mx_tremolo_marks_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_tremolo_marks_to_string(MxTremoloMarks v); + +#endif /* MX_TREMOLO_MARKS_H */ diff --git a/gen/test/c/mx/mx_tremolo_type.c b/gen/test/c/mx/mx_tremolo_type.c new file mode 100644 index 000000000..9abf79e14 --- /dev/null +++ b/gen/test/c/mx/mx_tremolo_type.c @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_tremolo_type.h" + +#include + +static const char *const mx_tremolo_type_values[] = { + "start", + "stop", + "single", + "unmeasured", +}; + +bool mx_tremolo_type_try_parse(const char *s, MxTremoloType *out) { + for (size_t i = 0; i < sizeof(mx_tremolo_type_values) / sizeof(mx_tremolo_type_values[0]); i++) { + if (strcmp(s, mx_tremolo_type_values[i]) == 0) { + *out = (MxTremoloType)i; + return true; + } + } + *out = (MxTremoloType)0; + return false; +} + +MxTremoloType mx_tremolo_type_parse(const char *s) { + MxTremoloType v; + mx_tremolo_type_try_parse(s, &v); + return v; +} + +const char *mx_tremolo_type_to_string(MxTremoloType v) { + if ((size_t)v >= sizeof(mx_tremolo_type_values) / sizeof(mx_tremolo_type_values[0])) + return mx_tremolo_type_values[0]; + return mx_tremolo_type_values[v]; +} diff --git a/gen/test/c/mx/mx_tremolo_type.h b/gen/test/c/mx/mx_tremolo_type.h new file mode 100644 index 000000000..88a83a9b6 --- /dev/null +++ b/gen/test/c/mx/mx_tremolo_type.h @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TREMOLO_TYPE_H +#define MX_TREMOLO_TYPE_H + +#include + +/* + * The tremolo-type is used to distinguish multi-note, single-note, and unmeasured tremolos. + */ +typedef enum { + MX_TREMOLO_TYPE_START = 0, + MX_TREMOLO_TYPE_STOP = 1, + MX_TREMOLO_TYPE_SINGLE = 2, + MX_TREMOLO_TYPE_UNMEASURED = 3 +} MxTremoloType; + +bool mx_tremolo_type_try_parse(const char *s, MxTremoloType *out); +/* Lenient: unknown input falls back to the first variant. */ +MxTremoloType mx_tremolo_type_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_tremolo_type_to_string(MxTremoloType v); + +#endif /* MX_TREMOLO_TYPE_H */ diff --git a/gen/test/c/mx/mx_trill_beats.c b/gen/test/c/mx/mx_trill_beats.c new file mode 100644 index 000000000..867ca6474 --- /dev/null +++ b/gen/test/c/mx/mx_trill_beats.c @@ -0,0 +1,30 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_trill_beats.h" + +#include "mx_runtime.h" + +static MxTrillBeats mx_trill_beats_clamp(double v) { + if (v < 2.0) + v = 2.0; + return (MxTrillBeats)v; +} + +bool mx_trill_beats_try_parse(const char *s, MxTrillBeats *out) { + double v; + if (!mx_try_parse_decimal(s, &v)) { + *out = (MxTrillBeats)0; + return false; + } + *out = mx_trill_beats_clamp(v); + return true; +} + +MxTrillBeats mx_trill_beats_parse(const char *s) { + double v = mx_parse_decimal(s); + return mx_trill_beats_clamp(v); +} + +char *mx_trill_beats_to_string(MxTrillBeats v) { + return mx_format_decimal((double)v); +} diff --git a/gen/test/c/mx/mx_trill_beats.h b/gen/test/c/mx/mx_trill_beats.h new file mode 100644 index 000000000..944446c61 --- /dev/null +++ b/gen/test/c/mx/mx_trill_beats.h @@ -0,0 +1,21 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TRILL_BEATS_H +#define MX_TRILL_BEATS_H + +#include + +/* + * The trill-beats type specifies the beats used in a trill-sound or bend-sound attribute group. It + * is a decimal value with a minimum value of 2. + */ +typedef double MxTrillBeats; + +/* Strict parse, then clamps into the declared range. */ +bool mx_trill_beats_try_parse(const char *s, MxTrillBeats *out); +/* Lenient: unparseable input becomes 0, then clamps. */ +MxTrillBeats mx_trill_beats_parse(const char *s); +/* Malloc'd; caller frees. */ +char *mx_trill_beats_to_string(MxTrillBeats v); + +#endif /* MX_TRILL_BEATS_H */ diff --git a/gen/test/c/mx/mx_trill_step.c b/gen/test/c/mx/mx_trill_step.c new file mode 100644 index 000000000..8cca04eb8 --- /dev/null +++ b/gen/test/c/mx/mx_trill_step.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_trill_step.h" + +#include + +static const char *const mx_trill_step_values[] = { + "whole", + "half", + "unison", +}; + +bool mx_trill_step_try_parse(const char *s, MxTrillStep *out) { + for (size_t i = 0; i < sizeof(mx_trill_step_values) / sizeof(mx_trill_step_values[0]); i++) { + if (strcmp(s, mx_trill_step_values[i]) == 0) { + *out = (MxTrillStep)i; + return true; + } + } + *out = (MxTrillStep)0; + return false; +} + +MxTrillStep mx_trill_step_parse(const char *s) { + MxTrillStep v; + mx_trill_step_try_parse(s, &v); + return v; +} + +const char *mx_trill_step_to_string(MxTrillStep v) { + if ((size_t)v >= sizeof(mx_trill_step_values) / sizeof(mx_trill_step_values[0])) + return mx_trill_step_values[0]; + return mx_trill_step_values[v]; +} diff --git a/gen/test/c/mx/mx_trill_step.h b/gen/test/c/mx/mx_trill_step.h new file mode 100644 index 000000000..183ae9ecf --- /dev/null +++ b/gen/test/c/mx/mx_trill_step.h @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TRILL_STEP_H +#define MX_TRILL_STEP_H + +#include + +/* + * The trill-step type describes the alternating note of trills and mordents for playback, relative + * to the current note. + */ +typedef enum { + MX_TRILL_STEP_WHOLE = 0, + MX_TRILL_STEP_HALF = 1, + MX_TRILL_STEP_UNISON = 2 +} MxTrillStep; + +bool mx_trill_step_try_parse(const char *s, MxTrillStep *out); +/* Lenient: unknown input falls back to the first variant. */ +MxTrillStep mx_trill_step_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_trill_step_to_string(MxTrillStep v); + +#endif /* MX_TRILL_STEP_H */ diff --git a/gen/test/c/mx/mx_two_note_turn.c b/gen/test/c/mx/mx_two_note_turn.c new file mode 100644 index 000000000..da9d959a2 --- /dev/null +++ b/gen/test/c/mx/mx_two_note_turn.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_two_note_turn.h" + +#include + +static const char *const mx_two_note_turn_values[] = { + "whole", + "half", + "none", +}; + +bool mx_two_note_turn_try_parse(const char *s, MxTwoNoteTurn *out) { + for (size_t i = 0; i < sizeof(mx_two_note_turn_values) / sizeof(mx_two_note_turn_values[0]); i++) { + if (strcmp(s, mx_two_note_turn_values[i]) == 0) { + *out = (MxTwoNoteTurn)i; + return true; + } + } + *out = (MxTwoNoteTurn)0; + return false; +} + +MxTwoNoteTurn mx_two_note_turn_parse(const char *s) { + MxTwoNoteTurn v; + mx_two_note_turn_try_parse(s, &v); + return v; +} + +const char *mx_two_note_turn_to_string(MxTwoNoteTurn v) { + if ((size_t)v >= sizeof(mx_two_note_turn_values) / sizeof(mx_two_note_turn_values[0])) + return mx_two_note_turn_values[0]; + return mx_two_note_turn_values[v]; +} diff --git a/gen/test/c/mx/mx_two_note_turn.h b/gen/test/c/mx/mx_two_note_turn.h new file mode 100644 index 000000000..e6f6f8d26 --- /dev/null +++ b/gen/test/c/mx/mx_two_note_turn.h @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TWO_NOTE_TURN_H +#define MX_TWO_NOTE_TURN_H + +#include + +/* + * The two-note-turn type describes the ending notes of trills and mordents for playback, relative + * to the current note. + */ +typedef enum { + MX_TWO_NOTE_TURN_WHOLE = 0, + MX_TWO_NOTE_TURN_HALF = 1, + MX_TWO_NOTE_TURN_NONE = 2 +} MxTwoNoteTurn; + +bool mx_two_note_turn_try_parse(const char *s, MxTwoNoteTurn *out); +/* Lenient: unknown input falls back to the first variant. */ +MxTwoNoteTurn mx_two_note_turn_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_two_note_turn_to_string(MxTwoNoteTurn v); + +#endif /* MX_TWO_NOTE_TURN_H */ diff --git a/gen/test/c/mx/mx_up_down.c b/gen/test/c/mx/mx_up_down.c new file mode 100644 index 000000000..fc3c5a174 --- /dev/null +++ b/gen/test/c/mx/mx_up_down.c @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_up_down.h" + +#include + +static const char *const mx_up_down_values[] = { + "up", + "down", +}; + +bool mx_up_down_try_parse(const char *s, MxUpDown *out) { + for (size_t i = 0; i < sizeof(mx_up_down_values) / sizeof(mx_up_down_values[0]); i++) { + if (strcmp(s, mx_up_down_values[i]) == 0) { + *out = (MxUpDown)i; + return true; + } + } + *out = (MxUpDown)0; + return false; +} + +MxUpDown mx_up_down_parse(const char *s) { + MxUpDown v; + mx_up_down_try_parse(s, &v); + return v; +} + +const char *mx_up_down_to_string(MxUpDown v) { + if ((size_t)v >= sizeof(mx_up_down_values) / sizeof(mx_up_down_values[0])) + return mx_up_down_values[0]; + return mx_up_down_values[v]; +} diff --git a/gen/test/c/mx/mx_up_down.h b/gen/test/c/mx/mx_up_down.h new file mode 100644 index 000000000..78fb8419f --- /dev/null +++ b/gen/test/c/mx/mx_up_down.h @@ -0,0 +1,23 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_UP_DOWN_H +#define MX_UP_DOWN_H + +#include + +/* + * The up-down type is used for the direction of arrows and other pointed symbols like vertical + * accents, indicating which way the tip is pointing. + */ +typedef enum { + MX_UP_DOWN_UP = 0, + MX_UP_DOWN_DOWN = 1 +} MxUpDown; + +bool mx_up_down_try_parse(const char *s, MxUpDown *out); +/* Lenient: unknown input falls back to the first variant. */ +MxUpDown mx_up_down_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_up_down_to_string(MxUpDown v); + +#endif /* MX_UP_DOWN_H */ diff --git a/gen/test/c/mx/mx_up_down_stop_continue.c b/gen/test/c/mx/mx_up_down_stop_continue.c new file mode 100644 index 000000000..6bc62961f --- /dev/null +++ b/gen/test/c/mx/mx_up_down_stop_continue.c @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_up_down_stop_continue.h" + +#include + +static const char *const mx_up_down_stop_continue_values[] = { + "up", + "down", + "stop", + "continue", +}; + +bool mx_up_down_stop_continue_try_parse(const char *s, MxUpDownStopContinue *out) { + for (size_t i = 0; i < sizeof(mx_up_down_stop_continue_values) / sizeof(mx_up_down_stop_continue_values[0]); i++) { + if (strcmp(s, mx_up_down_stop_continue_values[i]) == 0) { + *out = (MxUpDownStopContinue)i; + return true; + } + } + *out = (MxUpDownStopContinue)0; + return false; +} + +MxUpDownStopContinue mx_up_down_stop_continue_parse(const char *s) { + MxUpDownStopContinue v; + mx_up_down_stop_continue_try_parse(s, &v); + return v; +} + +const char *mx_up_down_stop_continue_to_string(MxUpDownStopContinue v) { + if ((size_t)v >= sizeof(mx_up_down_stop_continue_values) / sizeof(mx_up_down_stop_continue_values[0])) + return mx_up_down_stop_continue_values[0]; + return mx_up_down_stop_continue_values[v]; +} diff --git a/gen/test/c/mx/mx_up_down_stop_continue.h b/gen/test/c/mx/mx_up_down_stop_continue.h new file mode 100644 index 000000000..51d6f0b34 --- /dev/null +++ b/gen/test/c/mx/mx_up_down_stop_continue.h @@ -0,0 +1,25 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_UP_DOWN_STOP_CONTINUE_H +#define MX_UP_DOWN_STOP_CONTINUE_H + +#include + +/* + * The up-down-stop-continue type is used for octave-shift elements, indicating the direction of the + * shift from their true pitched values because of printing difficulty. + */ +typedef enum { + MX_UP_DOWN_STOP_CONTINUE_UP = 0, + MX_UP_DOWN_STOP_CONTINUE_DOWN = 1, + MX_UP_DOWN_STOP_CONTINUE_STOP = 2, + MX_UP_DOWN_STOP_CONTINUE_CONTINUE = 3 +} MxUpDownStopContinue; + +bool mx_up_down_stop_continue_try_parse(const char *s, MxUpDownStopContinue *out); +/* Lenient: unknown input falls back to the first variant. */ +MxUpDownStopContinue mx_up_down_stop_continue_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_up_down_stop_continue_to_string(MxUpDownStopContinue v); + +#endif /* MX_UP_DOWN_STOP_CONTINUE_H */ diff --git a/gen/test/c/mx/mx_upright_inverted.c b/gen/test/c/mx/mx_upright_inverted.c new file mode 100644 index 000000000..bb7974d34 --- /dev/null +++ b/gen/test/c/mx/mx_upright_inverted.c @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_upright_inverted.h" + +#include + +static const char *const mx_upright_inverted_values[] = { + "upright", + "inverted", +}; + +bool mx_upright_inverted_try_parse(const char *s, MxUprightInverted *out) { + for (size_t i = 0; i < sizeof(mx_upright_inverted_values) / sizeof(mx_upright_inverted_values[0]); i++) { + if (strcmp(s, mx_upright_inverted_values[i]) == 0) { + *out = (MxUprightInverted)i; + return true; + } + } + *out = (MxUprightInverted)0; + return false; +} + +MxUprightInverted mx_upright_inverted_parse(const char *s) { + MxUprightInverted v; + mx_upright_inverted_try_parse(s, &v); + return v; +} + +const char *mx_upright_inverted_to_string(MxUprightInverted v) { + if ((size_t)v >= sizeof(mx_upright_inverted_values) / sizeof(mx_upright_inverted_values[0])) + return mx_upright_inverted_values[0]; + return mx_upright_inverted_values[v]; +} diff --git a/gen/test/c/mx/mx_upright_inverted.h b/gen/test/c/mx/mx_upright_inverted.h new file mode 100644 index 000000000..a41529409 --- /dev/null +++ b/gen/test/c/mx/mx_upright_inverted.h @@ -0,0 +1,23 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_UPRIGHT_INVERTED_H +#define MX_UPRIGHT_INVERTED_H + +#include + +/* + * The upright-inverted type describes the appearance of a fermata element. The value is upright if + * not specified. + */ +typedef enum { + MX_UPRIGHT_INVERTED_UPRIGHT = 0, + MX_UPRIGHT_INVERTED_INVERTED = 1 +} MxUprightInverted; + +bool mx_upright_inverted_try_parse(const char *s, MxUprightInverted *out); +/* Lenient: unknown input falls back to the first variant. */ +MxUprightInverted mx_upright_inverted_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_upright_inverted_to_string(MxUprightInverted v); + +#endif /* MX_UPRIGHT_INVERTED_H */ diff --git a/gen/test/c/mx/mx_valign.c b/gen/test/c/mx/mx_valign.c new file mode 100644 index 000000000..483179f8c --- /dev/null +++ b/gen/test/c/mx/mx_valign.c @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_valign.h" + +#include + +static const char *const mx_valign_values[] = { + "top", + "middle", + "bottom", + "baseline", +}; + +bool mx_valign_try_parse(const char *s, MxValign *out) { + for (size_t i = 0; i < sizeof(mx_valign_values) / sizeof(mx_valign_values[0]); i++) { + if (strcmp(s, mx_valign_values[i]) == 0) { + *out = (MxValign)i; + return true; + } + } + *out = (MxValign)0; + return false; +} + +MxValign mx_valign_parse(const char *s) { + MxValign v; + mx_valign_try_parse(s, &v); + return v; +} + +const char *mx_valign_to_string(MxValign v) { + if ((size_t)v >= sizeof(mx_valign_values) / sizeof(mx_valign_values[0])) + return mx_valign_values[0]; + return mx_valign_values[v]; +} diff --git a/gen/test/c/mx/mx_valign.h b/gen/test/c/mx/mx_valign.h new file mode 100644 index 000000000..05ce289d9 --- /dev/null +++ b/gen/test/c/mx/mx_valign.h @@ -0,0 +1,25 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_VALIGN_H +#define MX_VALIGN_H + +#include + +/* + * The valign type is used to indicate vertical alignment to the top, middle, bottom, or baseline of + * the text. Defaults are implementation-dependent. + */ +typedef enum { + MX_VALIGN_TOP = 0, + MX_VALIGN_MIDDLE = 1, + MX_VALIGN_BOTTOM = 2, + MX_VALIGN_BASELINE = 3 +} MxValign; + +bool mx_valign_try_parse(const char *s, MxValign *out); +/* Lenient: unknown input falls back to the first variant. */ +MxValign mx_valign_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_valign_to_string(MxValign v); + +#endif /* MX_VALIGN_H */ diff --git a/gen/test/c/mx/mx_valign_image.c b/gen/test/c/mx/mx_valign_image.c new file mode 100644 index 000000000..079e9e754 --- /dev/null +++ b/gen/test/c/mx/mx_valign_image.c @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_valign_image.h" + +#include + +static const char *const mx_valign_image_values[] = { + "top", + "middle", + "bottom", +}; + +bool mx_valign_image_try_parse(const char *s, MxValignImage *out) { + for (size_t i = 0; i < sizeof(mx_valign_image_values) / sizeof(mx_valign_image_values[0]); i++) { + if (strcmp(s, mx_valign_image_values[i]) == 0) { + *out = (MxValignImage)i; + return true; + } + } + *out = (MxValignImage)0; + return false; +} + +MxValignImage mx_valign_image_parse(const char *s) { + MxValignImage v; + mx_valign_image_try_parse(s, &v); + return v; +} + +const char *mx_valign_image_to_string(MxValignImage v) { + if ((size_t)v >= sizeof(mx_valign_image_values) / sizeof(mx_valign_image_values[0])) + return mx_valign_image_values[0]; + return mx_valign_image_values[v]; +} diff --git a/gen/test/c/mx/mx_valign_image.h b/gen/test/c/mx/mx_valign_image.h new file mode 100644 index 000000000..b82bf199f --- /dev/null +++ b/gen/test/c/mx/mx_valign_image.h @@ -0,0 +1,24 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_VALIGN_IMAGE_H +#define MX_VALIGN_IMAGE_H + +#include + +/* + * The valign-image type is used to indicate vertical alignment for images and graphics, so it does + * not include a baseline value. Defaults are implementation-dependent. + */ +typedef enum { + MX_VALIGN_IMAGE_TOP = 0, + MX_VALIGN_IMAGE_MIDDLE = 1, + MX_VALIGN_IMAGE_BOTTOM = 2 +} MxValignImage; + +bool mx_valign_image_try_parse(const char *s, MxValignImage *out); +/* Lenient: unknown input falls back to the first variant. */ +MxValignImage mx_valign_image_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_valign_image_to_string(MxValignImage v); + +#endif /* MX_VALIGN_IMAGE_H */ diff --git a/gen/test/c/mx/mx_wedge_type.c b/gen/test/c/mx/mx_wedge_type.c new file mode 100644 index 000000000..d5b54b898 --- /dev/null +++ b/gen/test/c/mx/mx_wedge_type.c @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_wedge_type.h" + +#include + +static const char *const mx_wedge_type_values[] = { + "crescendo", + "diminuendo", + "stop", + "continue", +}; + +bool mx_wedge_type_try_parse(const char *s, MxWedgeType *out) { + for (size_t i = 0; i < sizeof(mx_wedge_type_values) / sizeof(mx_wedge_type_values[0]); i++) { + if (strcmp(s, mx_wedge_type_values[i]) == 0) { + *out = (MxWedgeType)i; + return true; + } + } + *out = (MxWedgeType)0; + return false; +} + +MxWedgeType mx_wedge_type_parse(const char *s) { + MxWedgeType v; + mx_wedge_type_try_parse(s, &v); + return v; +} + +const char *mx_wedge_type_to_string(MxWedgeType v) { + if ((size_t)v >= sizeof(mx_wedge_type_values) / sizeof(mx_wedge_type_values[0])) + return mx_wedge_type_values[0]; + return mx_wedge_type_values[v]; +} diff --git a/gen/test/c/mx/mx_wedge_type.h b/gen/test/c/mx/mx_wedge_type.h new file mode 100644 index 000000000..3c751c8ab --- /dev/null +++ b/gen/test/c/mx/mx_wedge_type.h @@ -0,0 +1,27 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_WEDGE_TYPE_H +#define MX_WEDGE_TYPE_H + +#include + +/* + * The wedge type is crescendo for the start of a wedge that is closed at the left side, diminuendo + * for the start of a wedge that is closed on the right side, and stop for the end of a wedge. The + * continue type is used for formatting wedges over a system break, or for other situations where a + * single wedge is divided into multiple segments. + */ +typedef enum { + MX_WEDGE_TYPE_CRESCENDO = 0, + MX_WEDGE_TYPE_DIMINUENDO = 1, + MX_WEDGE_TYPE_STOP = 2, + MX_WEDGE_TYPE_CONTINUE = 3 +} MxWedgeType; + +bool mx_wedge_type_try_parse(const char *s, MxWedgeType *out); +/* Lenient: unknown input falls back to the first variant. */ +MxWedgeType mx_wedge_type_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_wedge_type_to_string(MxWedgeType v); + +#endif /* MX_WEDGE_TYPE_H */ diff --git a/gen/test/c/mx/mx_winged.c b/gen/test/c/mx/mx_winged.c new file mode 100644 index 000000000..9e0776c6e --- /dev/null +++ b/gen/test/c/mx/mx_winged.c @@ -0,0 +1,36 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_winged.h" + +#include + +static const char *const mx_winged_values[] = { + "none", + "straight", + "curved", + "double-straight", + "double-curved", +}; + +bool mx_winged_try_parse(const char *s, MxWinged *out) { + for (size_t i = 0; i < sizeof(mx_winged_values) / sizeof(mx_winged_values[0]); i++) { + if (strcmp(s, mx_winged_values[i]) == 0) { + *out = (MxWinged)i; + return true; + } + } + *out = (MxWinged)0; + return false; +} + +MxWinged mx_winged_parse(const char *s) { + MxWinged v; + mx_winged_try_parse(s, &v); + return v; +} + +const char *mx_winged_to_string(MxWinged v) { + if ((size_t)v >= sizeof(mx_winged_values) / sizeof(mx_winged_values[0])) + return mx_winged_values[0]; + return mx_winged_values[v]; +} diff --git a/gen/test/c/mx/mx_winged.h b/gen/test/c/mx/mx_winged.h new file mode 100644 index 000000000..7966a88ec --- /dev/null +++ b/gen/test/c/mx/mx_winged.h @@ -0,0 +1,28 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_WINGED_H +#define MX_WINGED_H + +#include + +/* + * The winged attribute indicates whether the repeat has winged extensions that appear above and + * below the barline. The straight and curved values represent single wings, while the + * double-straight and double-curved values represent double wings. The none value indicates no + * wings and is the default. + */ +typedef enum { + MX_WINGED_NONE = 0, + MX_WINGED_STRAIGHT = 1, + MX_WINGED_CURVED = 2, + MX_WINGED_DOUBLE_STRAIGHT = 3, + MX_WINGED_DOUBLE_CURVED = 4 +} MxWinged; + +bool mx_winged_try_parse(const char *s, MxWinged *out); +/* Lenient: unknown input falls back to the first variant. */ +MxWinged mx_winged_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_winged_to_string(MxWinged v); + +#endif /* MX_WINGED_H */ diff --git a/gen/test/c/mx/mx_wood.c b/gen/test/c/mx/mx_wood.c new file mode 100644 index 000000000..ae59f8138 --- /dev/null +++ b/gen/test/c/mx/mx_wood.c @@ -0,0 +1,52 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_wood.h" + +#include + +static const char *const mx_wood_values[] = { + "bamboo scraper", + "board clapper", + "cabasa", + "castanets", + "castanets with handle", + "claves", + "football rattle", + "guiro", + "log drum", + "maraca", + "maracas", + "quijada", + "rainstick", + "ratchet", + "reco-reco", + "sandpaper blocks", + "slit drum", + "temple block", + "vibraslap", + "whip", + "wood block", +}; + +bool mx_wood_try_parse(const char *s, MxWood *out) { + for (size_t i = 0; i < sizeof(mx_wood_values) / sizeof(mx_wood_values[0]); i++) { + if (strcmp(s, mx_wood_values[i]) == 0) { + *out = (MxWood)i; + return true; + } + } + *out = (MxWood)0; + return false; +} + +MxWood mx_wood_parse(const char *s) { + MxWood v; + mx_wood_try_parse(s, &v); + return v; +} + +const char *mx_wood_to_string(MxWood v) { + if ((size_t)v >= sizeof(mx_wood_values) / sizeof(mx_wood_values[0])) + return mx_wood_values[0]; + return mx_wood_values[v]; +} diff --git a/gen/test/c/mx/mx_wood.h b/gen/test/c/mx/mx_wood.h new file mode 100644 index 000000000..ea5fc19d4 --- /dev/null +++ b/gen/test/c/mx/mx_wood.h @@ -0,0 +1,42 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_WOOD_H +#define MX_WOOD_H + +#include + +/* + * The wood type represents pictograms for wood percussion instruments. The maraca and maracas + * values distinguish the one- and two-maraca versions of the pictogram. + */ +typedef enum { + MX_WOOD_BAMBOO_SCRAPER = 0, + MX_WOOD_BOARD_CLAPPER = 1, + MX_WOOD_CABASA = 2, + MX_WOOD_CASTANETS = 3, + MX_WOOD_CASTANETS_WITH_HANDLE = 4, + MX_WOOD_CLAVES = 5, + MX_WOOD_FOOTBALL_RATTLE = 6, + MX_WOOD_GUIRO = 7, + MX_WOOD_LOG_DRUM = 8, + MX_WOOD_MARACA = 9, + MX_WOOD_MARACAS = 10, + MX_WOOD_QUIJADA = 11, + MX_WOOD_RAINSTICK = 12, + MX_WOOD_RATCHET = 13, + MX_WOOD_RECO_RECO = 14, + MX_WOOD_SANDPAPER_BLOCKS = 15, + MX_WOOD_SLIT_DRUM = 16, + MX_WOOD_TEMPLE_BLOCK = 17, + MX_WOOD_VIBRASLAP = 18, + MX_WOOD_WHIP = 19, + MX_WOOD_WOOD_BLOCK = 20 +} MxWood; + +bool mx_wood_try_parse(const char *s, MxWood *out); +/* Lenient: unknown input falls back to the first variant. */ +MxWood mx_wood_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_wood_to_string(MxWood v); + +#endif /* MX_WOOD_H */ diff --git a/gen/test/c/mx/mx_yes_no.c b/gen/test/c/mx/mx_yes_no.c new file mode 100644 index 000000000..4080ef065 --- /dev/null +++ b/gen/test/c/mx/mx_yes_no.c @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_yes_no.h" + +#include + +static const char *const mx_yes_no_values[] = { + "yes", + "no", +}; + +bool mx_yes_no_try_parse(const char *s, MxYesNo *out) { + for (size_t i = 0; i < sizeof(mx_yes_no_values) / sizeof(mx_yes_no_values[0]); i++) { + if (strcmp(s, mx_yes_no_values[i]) == 0) { + *out = (MxYesNo)i; + return true; + } + } + *out = (MxYesNo)0; + return false; +} + +MxYesNo mx_yes_no_parse(const char *s) { + MxYesNo v; + mx_yes_no_try_parse(s, &v); + return v; +} + +const char *mx_yes_no_to_string(MxYesNo v) { + if ((size_t)v >= sizeof(mx_yes_no_values) / sizeof(mx_yes_no_values[0])) + return mx_yes_no_values[0]; + return mx_yes_no_values[v]; +} diff --git a/gen/test/c/mx/mx_yes_no.h b/gen/test/c/mx/mx_yes_no.h new file mode 100644 index 000000000..dbf5c4368 --- /dev/null +++ b/gen/test/c/mx/mx_yes_no.h @@ -0,0 +1,23 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_YES_NO_H +#define MX_YES_NO_H + +#include + +/* + * The yes-no type is used for boolean-like attributes. We cannot use W3C XML Schema booleans due to + * their restrictions on expression of boolean values. + */ +typedef enum { + MX_YES_NO_YES = 0, + MX_YES_NO_NO = 1 +} MxYesNo; + +bool mx_yes_no_try_parse(const char *s, MxYesNo *out); +/* Lenient: unknown input falls back to the first variant. */ +MxYesNo mx_yes_no_parse(const char *s); +/* Returns the wire literal (static storage; do not free). */ +const char *mx_yes_no_to_string(MxYesNo v); + +#endif /* MX_YES_NO_H */ diff --git a/gen/test/c/mx/mx_yes_no_number.c b/gen/test/c/mx/mx_yes_no_number.c new file mode 100644 index 000000000..4b991f110 --- /dev/null +++ b/gen/test/c/mx/mx_yes_no_number.c @@ -0,0 +1,48 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_yes_no_number.h" + +#include "mx_runtime.h" +#include +#include + +bool mx_yes_no_number_try_parse(const char *s, MxYesNoNumber *out) { + MxYesNoNumber v; + memset(&v, 0, sizeof(v)); + if (mx_yes_no_try_parse(s, &v.yes_no)) { + v.kind = MX_YES_NO_NUMBER_KIND_YES_NO; + *out = v; + return true; + } + if (mx_try_parse_decimal(s, &v.decimal)) { + v.kind = MX_YES_NO_NUMBER_KIND_DECIMAL; + *out = v; + return true; + } + *out = v; + return false; +} + +MxYesNoNumber mx_yes_no_number_parse(const char *s) { + MxYesNoNumber v; + if (mx_yes_no_number_try_parse(s, &v)) + return v; + memset(&v, 0, sizeof(v)); + v.kind = MX_YES_NO_NUMBER_KIND_YES_NO; + v.yes_no = mx_yes_no_parse(s); + return v; +} + +char *mx_yes_no_number_to_string(MxYesNoNumber v) { + switch (v.kind) { + case MX_YES_NO_NUMBER_KIND_DECIMAL: + return mx_format_decimal(v.decimal); + default: + return mx_strdup(mx_yes_no_to_string(v.yes_no)); + } +} + +void mx_yes_no_number_free(MxYesNoNumber *v) { + if (!v) + return; +} diff --git a/gen/test/c/mx/mx_yes_no_number.h b/gen/test/c/mx/mx_yes_no_number.h new file mode 100644 index 000000000..e3a89275b --- /dev/null +++ b/gen/test/c/mx/mx_yes_no_number.h @@ -0,0 +1,31 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_YES_NO_NUMBER_H +#define MX_YES_NO_NUMBER_H + +#include +#include "mx_yes_no.h" + +/* + * The yes-no-number type is used for attributes that can be either boolean or numeric values. + */ +typedef enum { + MX_YES_NO_NUMBER_KIND_YES_NO = 0, + MX_YES_NO_NUMBER_KIND_DECIMAL = 1 +} MxYesNoNumberKind; + +typedef struct { + MxYesNoNumberKind kind; + MxYesNo yes_no; + double decimal; +} MxYesNoNumber; + +/* Tries each union member in schema order. */ +bool mx_yes_no_number_try_parse(const char *s, MxYesNoNumber *out); +/* Lenient: an unmatched input is absorbed by the first member. */ +MxYesNoNumber mx_yes_no_number_parse(const char *s); +/* The wire spelling of whichever member is held. Malloc'd. */ +char *mx_yes_no_number_to_string(MxYesNoNumber v); +void mx_yes_no_number_free(MxYesNoNumber *v); + +#endif /* MX_YES_NO_NUMBER_H */ diff --git a/gen/test/c/mx/mx_yyyy_mm_dd.c b/gen/test/c/mx/mx_yyyy_mm_dd.c new file mode 100644 index 000000000..469a0fc05 --- /dev/null +++ b/gen/test/c/mx/mx_yyyy_mm_dd.c @@ -0,0 +1,9 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_yyyy_mm_dd.h" + +#include "mx_runtime.h" + +MxYyyyMmDd mx_yyyy_mm_dd_parse(const char *s) { + return mx_strdup(s); +} diff --git a/gen/test/c/mx/mx_yyyy_mm_dd.h b/gen/test/c/mx/mx_yyyy_mm_dd.h new file mode 100644 index 000000000..350deed6a --- /dev/null +++ b/gen/test/c/mx/mx_yyyy_mm_dd.h @@ -0,0 +1,15 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_YYYY_MM_DD_H +#define MX_YYYY_MM_DD_H + +/* + * Calendar dates are represented yyyy-mm-dd format, following ISO 8601. This is a W3C XML Schema + * date type, but without the optional timezone data. Pattern (not enforced): [^:Z]* + */ +typedef char *MxYyyyMmDd; + +/* Malloc'd copy of the wire string; the value IS its spelling. */ +MxYyyyMmDd mx_yyyy_mm_dd_parse(const char *s); + +#endif /* MX_YYYY_MM_DD_H */ diff --git a/gen/test/c/mx/sources.cmake b/gen/test/c/mx/sources.cmake new file mode 100644 index 000000000..8827aad5e --- /dev/null +++ b/gen/test/c/mx/sources.cmake @@ -0,0 +1,138 @@ +# Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +set(MX_GENERATED_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/mx_above_below.c + ${CMAKE_CURRENT_LIST_DIR}/mx_accidental_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_accordion_middle.c + ${CMAKE_CURRENT_LIST_DIR}/mx_arrow_direction.c + ${CMAKE_CURRENT_LIST_DIR}/mx_arrow_style.c + ${CMAKE_CURRENT_LIST_DIR}/mx_backward_forward.c + ${CMAKE_CURRENT_LIST_DIR}/mx_bar_style.c + ${CMAKE_CURRENT_LIST_DIR}/mx_beam_level.c + ${CMAKE_CURRENT_LIST_DIR}/mx_beam_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_beater_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_breath_mark_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_caesura_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_cancel_location.c + ${CMAKE_CURRENT_LIST_DIR}/mx_circular_arrow.c + ${CMAKE_CURRENT_LIST_DIR}/mx_clef_sign.c + ${CMAKE_CURRENT_LIST_DIR}/mx_color.c + ${CMAKE_CURRENT_LIST_DIR}/mx_comma_separated_text.c + ${CMAKE_CURRENT_LIST_DIR}/mx_css_font_size.c + ${CMAKE_CURRENT_LIST_DIR}/mx_degree_symbol_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_degree_type_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_distance_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_divisions.c + ${CMAKE_CURRENT_LIST_DIR}/mx_effect.c + ${CMAKE_CURRENT_LIST_DIR}/mx_enclosure_shape.c + ${CMAKE_CURRENT_LIST_DIR}/mx_ending_number.c + ${CMAKE_CURRENT_LIST_DIR}/mx_fan.c + ${CMAKE_CURRENT_LIST_DIR}/mx_fermata_shape.c + ${CMAKE_CURRENT_LIST_DIR}/mx_fifths.c + ${CMAKE_CURRENT_LIST_DIR}/mx_font_size.c + ${CMAKE_CURRENT_LIST_DIR}/mx_font_style.c + ${CMAKE_CURRENT_LIST_DIR}/mx_font_weight.c + ${CMAKE_CURRENT_LIST_DIR}/mx_glass_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_glyph_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_group_barline_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_group_symbol_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_handbell_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_harmon_closed_location.c + ${CMAKE_CURRENT_LIST_DIR}/mx_harmon_closed_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_harmony_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_hole_closed_location.c + ${CMAKE_CURRENT_LIST_DIR}/mx_hole_closed_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_instrument_sound.c + ${CMAKE_CURRENT_LIST_DIR}/mx_kind_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_left_center_right.c + ${CMAKE_CURRENT_LIST_DIR}/mx_left_right.c + ${CMAKE_CURRENT_LIST_DIR}/mx_line_end.c + ${CMAKE_CURRENT_LIST_DIR}/mx_line_length.c + ${CMAKE_CURRENT_LIST_DIR}/mx_line_shape.c + ${CMAKE_CURRENT_LIST_DIR}/mx_line_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_line_width_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_margin_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_measure_numbering_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_measure_text.c + ${CMAKE_CURRENT_LIST_DIR}/mx_membrane.c + ${CMAKE_CURRENT_LIST_DIR}/mx_metal.c + ${CMAKE_CURRENT_LIST_DIR}/mx_midi_128.c + ${CMAKE_CURRENT_LIST_DIR}/mx_midi_16.c + ${CMAKE_CURRENT_LIST_DIR}/mx_midi_16384.c + ${CMAKE_CURRENT_LIST_DIR}/mx_millimeters.c + ${CMAKE_CURRENT_LIST_DIR}/mx_mode.c + ${CMAKE_CURRENT_LIST_DIR}/mx_mute.c + ${CMAKE_CURRENT_LIST_DIR}/mx_non_negative_decimal.c + ${CMAKE_CURRENT_LIST_DIR}/mx_note_size_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_note_type_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_notehead_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_number_level.c + ${CMAKE_CURRENT_LIST_DIR}/mx_number_of_lines.c + ${CMAKE_CURRENT_LIST_DIR}/mx_number_or_normal.c + ${CMAKE_CURRENT_LIST_DIR}/mx_octave.c + ${CMAKE_CURRENT_LIST_DIR}/mx_on_off.c + ${CMAKE_CURRENT_LIST_DIR}/mx_over_under.c + ${CMAKE_CURRENT_LIST_DIR}/mx_pedal_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_percent.c + ${CMAKE_CURRENT_LIST_DIR}/mx_pitched_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_positive_divisions.c + ${CMAKE_CURRENT_LIST_DIR}/mx_positive_integer_or_empty.c + ${CMAKE_CURRENT_LIST_DIR}/mx_principal_voice_symbol.c + ${CMAKE_CURRENT_LIST_DIR}/mx_right_left_middle.c + ${CMAKE_CURRENT_LIST_DIR}/mx_rotation_degrees.c + ${CMAKE_CURRENT_LIST_DIR}/mx_runtime.c + ${CMAKE_CURRENT_LIST_DIR}/mx_semi_pitched.c + ${CMAKE_CURRENT_LIST_DIR}/mx_semitones.c + ${CMAKE_CURRENT_LIST_DIR}/mx_show_frets.c + ${CMAKE_CURRENT_LIST_DIR}/mx_show_tuplet.c + ${CMAKE_CURRENT_LIST_DIR}/mx_smufl_accidental_glyph_name.c + ${CMAKE_CURRENT_LIST_DIR}/mx_smufl_coda_glyph_name.c + ${CMAKE_CURRENT_LIST_DIR}/mx_smufl_glyph_name.c + ${CMAKE_CURRENT_LIST_DIR}/mx_smufl_lyrics_glyph_name.c + ${CMAKE_CURRENT_LIST_DIR}/mx_smufl_pictogram_glyph_name.c + ${CMAKE_CURRENT_LIST_DIR}/mx_smufl_segno_glyph_name.c + ${CMAKE_CURRENT_LIST_DIR}/mx_sound_id.c + ${CMAKE_CURRENT_LIST_DIR}/mx_staff_divide_symbol.c + ${CMAKE_CURRENT_LIST_DIR}/mx_staff_line.c + ${CMAKE_CURRENT_LIST_DIR}/mx_staff_number.c + ${CMAKE_CURRENT_LIST_DIR}/mx_staff_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_start_note.c + ${CMAKE_CURRENT_LIST_DIR}/mx_start_stop.c + ${CMAKE_CURRENT_LIST_DIR}/mx_start_stop_continue.c + ${CMAKE_CURRENT_LIST_DIR}/mx_start_stop_discontinue.c + ${CMAKE_CURRENT_LIST_DIR}/mx_start_stop_single.c + ${CMAKE_CURRENT_LIST_DIR}/mx_stem_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_step.c + ${CMAKE_CURRENT_LIST_DIR}/mx_stick_location.c + ${CMAKE_CURRENT_LIST_DIR}/mx_stick_material.c + ${CMAKE_CURRENT_LIST_DIR}/mx_stick_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_string_number.c + ${CMAKE_CURRENT_LIST_DIR}/mx_syllabic.c + ${CMAKE_CURRENT_LIST_DIR}/mx_symbol_size.c + ${CMAKE_CURRENT_LIST_DIR}/mx_tap_hand.c + ${CMAKE_CURRENT_LIST_DIR}/mx_tenths.c + ${CMAKE_CURRENT_LIST_DIR}/mx_text_direction.c + ${CMAKE_CURRENT_LIST_DIR}/mx_tied_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_time_only.c + ${CMAKE_CURRENT_LIST_DIR}/mx_time_relation.c + ${CMAKE_CURRENT_LIST_DIR}/mx_time_separator.c + ${CMAKE_CURRENT_LIST_DIR}/mx_time_symbol.c + ${CMAKE_CURRENT_LIST_DIR}/mx_tip_direction.c + ${CMAKE_CURRENT_LIST_DIR}/mx_top_bottom.c + ${CMAKE_CURRENT_LIST_DIR}/mx_tremolo_marks.c + ${CMAKE_CURRENT_LIST_DIR}/mx_tremolo_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_trill_beats.c + ${CMAKE_CURRENT_LIST_DIR}/mx_trill_step.c + ${CMAKE_CURRENT_LIST_DIR}/mx_two_note_turn.c + ${CMAKE_CURRENT_LIST_DIR}/mx_up_down.c + ${CMAKE_CURRENT_LIST_DIR}/mx_up_down_stop_continue.c + ${CMAKE_CURRENT_LIST_DIR}/mx_upright_inverted.c + ${CMAKE_CURRENT_LIST_DIR}/mx_valign.c + ${CMAKE_CURRENT_LIST_DIR}/mx_valign_image.c + ${CMAKE_CURRENT_LIST_DIR}/mx_wedge_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_winged.c + ${CMAKE_CURRENT_LIST_DIR}/mx_wood.c + ${CMAKE_CURRENT_LIST_DIR}/mx_yes_no.c + ${CMAKE_CURRENT_LIST_DIR}/mx_yes_no_number.c + ${CMAKE_CURRENT_LIST_DIR}/mx_yyyy_mm_dd.c +) diff --git a/gen/test/c/src/values_smoke.c b/gen/test/c/src/values_smoke.c new file mode 100644 index 000000000..f741dbd73 --- /dev/null +++ b/gen/test/c/src/values_smoke.c @@ -0,0 +1,114 @@ +/* Smoke test for the generated value types (the leaf node types): wire -> + typed -> wire, including the leniency policies the corpus fixups encode + (unknown enum literal -> first variant, unparseable number -> 0, clamping + into declared ranges, exclusive-bound epsilon). Mirrors the Go target's + TestValueSmoke. */ + +#include "mx_above_below.h" +#include "mx_beam_value.h" +#include "mx_breath_mark_value.h" +#include "mx_color.h" +#include "mx_font_size.h" +#include "mx_instrument_sound.h" +#include "mx_midi_16.h" +#include "mx_note_type_value.h" +#include "mx_number_or_normal.h" +#include "mx_positive_divisions.h" +#include "mx_positive_integer_or_empty.h" +#include "mx_string_number.h" +#include "mx_tenths.h" + +#include +#include +#include + +static int failures = 0; + +static void expect(const char *name, const char *got, const char *want) { + if (strcmp(got, want) != 0) { + printf("FAIL %s: got \"%s\", want \"%s\"\n", name, got, want); + failures++; + } +} + +static void expect_owned(const char *name, char *got, const char *want) { + expect(name, got, want); + free(got); +} + +int main(void) { + expect("enum round-trip", + mx_above_below_to_string(mx_above_below_parse("below")), "below"); + expect("enum unknown falls back to first variant", + mx_above_below_to_string(mx_above_below_parse("sideways")), "above"); + expect("enum digit-led variant", + mx_note_type_value_to_string(MX_NOTE_TYPE_VALUE_1024TH), "1024th"); + expect("enum space-separated literal", + mx_beam_value_to_string(mx_beam_value_parse("backward hook")), + "backward hook"); + expect("enum empty literal", + mx_breath_mark_value_to_string(mx_breath_mark_value_parse("")), ""); + + expect_owned("number trailing zeros drop on reprint", + mx_tenths_to_string(mx_tenths_parse("8.50")), "8.5"); + expect_owned("number unparseable becomes zero", + mx_tenths_to_string(mx_tenths_parse("abc")), "0"); + expect_owned("number clamps to inclusive min", + mx_midi_16_to_string(mx_midi_16_parse("0")), "1"); + expect_owned("number clamps to inclusive max", + mx_midi_16_to_string(mx_midi_16_parse("99")), "16"); + expect_owned("number int leniently truncates decimals", + mx_midi_16_to_string(mx_midi_16_parse("3.7")), "3"); + expect_owned("number exclusive min clamps to epsilon", + mx_positive_divisions_to_string(mx_positive_divisions_parse("0")), + "0.000001"); + expect_owned("number implied positive-integer min", + mx_string_number_to_string(mx_string_number_parse("")), "1"); + + MxColor color = mx_color_parse("#FF0000"); + expect("string passthrough", color, "#FF0000"); + free(color); + + MxFontSize fs = mx_font_size_parse("small"); + if (fs.kind != MX_FONT_SIZE_KIND_CSS_FONT_SIZE) { + printf("FAIL union kind: got %d, want css-font-size\n", (int)fs.kind); + failures++; + } + expect_owned("union picks enum member", mx_font_size_to_string(fs), "small"); + MxFontSize fs2 = mx_font_size_parse("24"); + expect_owned("union picks numeric member", mx_font_size_to_string(fs2), "24"); + + MxNumberOrNormal nn = mx_number_or_normal_parse("normal"); + expect_owned("union literal member", mx_number_or_normal_to_string(nn), "normal"); + + MxPositiveIntegerOrEmpty pe = mx_positive_integer_or_empty_parse(""); + expect_owned("union empty literal member", + mx_positive_integer_or_empty_to_string(pe), ""); + MxPositiveIntegerOrEmpty p5 = mx_positive_integer_or_empty_parse("5"); + expect_owned("union integer member", + mx_positive_integer_or_empty_to_string(p5), "5"); + + MxInstrumentSound is = mx_instrument_sound_parse("brass.alphorn"); + if (is.kind != MX_INSTRUMENT_SOUND_KIND_SOUND_ID) { + printf("FAIL open enum known id kind\n"); + failures++; + } + expect_owned("open enum known id", mx_instrument_sound_to_string(is), + "brass.alphorn"); + mx_instrument_sound_free(&is); + MxInstrumentSound is2 = mx_instrument_sound_parse("synth.custom-thing"); + if (is2.kind != MX_INSTRUMENT_SOUND_KIND_STRING) { + printf("FAIL open enum fallback kind\n"); + failures++; + } + expect_owned("open enum open string", mx_instrument_sound_to_string(is2), + "synth.custom-thing"); + mx_instrument_sound_free(&is2); + + if (failures == 0) { + printf("values smoke: all checks passed\n"); + return 0; + } + printf("values smoke: %d failure(s)\n", failures); + return 1; +} diff --git a/gen/tests/test_plates.py b/gen/tests/test_plates.py index 6a70a0dc8..974c2535b 100644 --- a/gen/tests/test_plates.py +++ b/gen/tests/test_plates.py @@ -397,6 +397,13 @@ def test_files_unique_and_self_excluded(self): for spec in plates.files: self.assertNotIn(spec.file, spec.includes) + def test_primitive_refs_never_become_includes(self): + # instrument-sound (C target, sounds folded) is sound-id | string; + # the open `string` member is a PRIMITIVE, but a complex type named + # `string` also exists. The include graph must not conflate them. + spec = next(f for f in self.c.files if f.types == ["instrument-sound"]) + self.assertEqual(spec.includes, ["mx_sound_id"]) + def test_derived_plates_expose_both_views(self): derived = [p for p in self.c.complex_types if p.shape == "derived"] self.assertTrue(derived) From 2e572d41e9b94c0d6ccc06cf36b87c6d90eafc30 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 18:58:35 +0000 Subject: [PATCH 21/45] gen/emit: Go complex-type templates; the Go corert suite is green The Go backend now renders all four complex shapes and the document entry points; the corert harness drives the generated model instead of the stub. Every eligible corpus file round-trips: ~777 pass, 52 skip (see gating). Representation (the Go spelling of the plate facts, chosen for round-trip fidelity): - Attributes are presence-tracked pointer fields, required or not: the contract is 'write back exactly what was parsed', and corpus files do omit required attributes. - A composite stores children as ONE ordered list (Children []XChild, a struct of typed pointers where exactly one is non-nil). Interleaved choice content (measure's music-data, note's grace/cue branches, metronome's repeated beat-unit) round-trips in document order for free, which per-member vectors cannot do. No kind discriminator: harmony has a child element literally named 'kind', so a synthetic field would collide. - Parsing is strict about NAMES (unknown attribute/element -> error: the version gate keeps newer documents out, so an unknown name is a generator gap, not data) and lenient about VALUES (the typed Parse* policies). - A derived type embeds its base; Go field promotion gives the flat view and one merged parse/serialize pass. - Document/FromXDoc/ToXDoc are generated from the plates' roots; the root's xmlns declarations are preserved through the model (a few corpus files declare xmlns:xlink). Version gating: Go targets MusicXML 3.1; documents whose root declares a newer version are skipped (reported, not failed) -- MusicXML is backward compatible, so older documents parse; newer ones may use types the model cannot represent. Harness changes (gen/test/go/corert/): - stub package deleted; the generated mx package is the implementation. - Normalization strips whitespace-only character data everywhere (MusicXML has no mixed content; pretty-printing indentation is not content, and an empty holds only its own indentation). Applied to expected and actual alike, so the comparison stays symmetric. - The loader transcodes UTF-16 (BOM-detected) and ISO-8859-1 documents to UTF-8: pugixml and libxml2 auto-detect these; Go's encoding/xml does not. Corpus adjustments, each encoding a documented fact: - data/synthetic/extend.3.0.xml and elision.3.0.xml used 3.0-only attributes (extend lost its font attributes in 3.1; elision lost its text-decoration attributes): MusicXML itself broke backward compatibility there, so no current target schema (3.1 or 4.0) can represent those attributes. The synthetic files now exercise the attribute set valid across 3.0/3.1/4.0. - data/lysuite/ly75a fixup updated to the uniform clamp policy ('', 'test', and '0' all clamp to accordion-middle's minimum 1); the old expectations encoded the legacy implementation's inconsistency (unparseable values escaped the clamp). The policy is now documented in data/README.md. --- data/README.md | 9 + .../ly75a_AccordionRegistrations.fixup.xml | 10 +- data/synthetic/elision.3.0.xml | 2 +- data/synthetic/extend.3.0.xml | 2 +- gen/emit/go/__init__.py | 15 +- gen/emit/go/complexes.py | 312 +++++++++++++ gen/emit/go/document.py | 82 ++++ gen/test/go/corert/corert_test.go | 3 + gen/test/go/corert/normalize.go | 25 ++ gen/test/go/corert/roundtrip.go | 116 ++++- gen/test/go/mx/accidental.go | 144 ++++++ gen/test/go/mx/accidental_mark.go | 142 ++++++ gen/test/go/mx/accidental_text.go | 198 ++++++++ gen/test/go/mx/accord.go | 74 +++ gen/test/go/mx/accordion_registration.go | 160 +++++++ gen/test/go/mx/appearance.go | 96 ++++ gen/test/go/mx/arpeggiate.go | 99 ++++ gen/test/go/mx/arrow.go | 156 +++++++ gen/test/go/mx/articulations.go | 208 +++++++++ gen/test/go/mx/attributes.go | 158 +++++++ gen/test/go/mx/backup.go | 76 ++++ gen/test/go/mx/bar_style_color.go | 43 ++ gen/test/go/mx/barline.go | 178 ++++++++ gen/test/go/mx/barre.go | 49 ++ gen/test/go/mx/bass.go | 68 +++ gen/test/go/mx/bass_alter.go | 117 +++++ gen/test/go/mx/bass_step.go | 108 +++++ gen/test/go/mx/beam.go | 80 ++++ gen/test/go/mx/beat_repeat.go | 96 ++++ gen/test/go/mx/beat_unit_tied.go | 66 +++ gen/test/go/mx/beater.go | 44 ++ gen/test/go/mx/bend.go | 177 ++++++++ gen/test/go/mx/bookmark.go | 61 +++ gen/test/go/mx/bracket.go | 127 ++++++ gen/test/go/mx/breath_mark.go | 106 +++++ gen/test/go/mx/caesura.go | 107 +++++ gen/test/go/mx/cancel.go | 47 ++ gen/test/go/mx/clef.go | 181 ++++++++ gen/test/go/mx/coda.go | 125 ++++++ gen/test/go/mx/credit.go | 127 ++++++ gen/test/go/mx/dashes.go | 103 +++++ gen/test/go/mx/defaults.go | 129 ++++++ gen/test/go/mx/degree.go | 87 ++++ gen/test/go/mx/degree_alter.go | 111 +++++ gen/test/go/mx/degree_type.go | 109 +++++ gen/test/go/mx/degree_value.go | 117 +++++ gen/test/go/mx/direction.go | 133 ++++++ gen/test/go/mx/direction_type.go | 273 +++++++++++ gen/test/go/mx/directive.go | 105 +++++ gen/test/go/mx/distance.go | 45 ++ gen/test/go/mx/document.go | 70 +++ gen/test/go/mx/dynamics.go | 421 +++++++++++++++++ gen/test/go/mx/elision.go | 84 ++++ gen/test/go/mx/empty.go | 34 ++ gen/test/go/mx/empty_font.go | 61 +++ gen/test/go/mx/empty_line.go | 139 ++++++ gen/test/go/mx/empty_placement.go | 103 +++++ gen/test/go/mx/empty_placement_smufl.go | 111 +++++ .../go/mx/empty_print_object_style_align.go | 118 +++++ gen/test/go/mx/empty_print_style_align_id.go | 118 +++++ gen/test/go/mx/empty_trill_sound.go | 153 +++++++ gen/test/go/mx/encoding.go | 87 ++++ gen/test/go/mx/ending.go | 153 +++++++ gen/test/go/mx/extend.go | 77 ++++ gen/test/go/mx/feature.go | 45 ++ gen/test/go/mx/fermata.go | 114 +++++ gen/test/go/mx/figure.go | 102 +++++ gen/test/go/mx/figured_bass.go | 189 ++++++++ gen/test/go/mx/fingering.go | 123 +++++ gen/test/go/mx/first_fret.go | 53 +++ gen/test/go/mx/formatted_symbol_id.go | 184 ++++++++ gen/test/go/mx/formatted_text.go | 190 ++++++++ gen/test/go/mx/formatted_text_id.go | 197 ++++++++ gen/test/go/mx/forward.go | 87 ++++ gen/test/go/mx/frame.go | 159 +++++++ gen/test/go/mx/frame_note.go | 85 ++++ gen/test/go/mx/fret.go | 72 +++ gen/test/go/mx/glass.go | 45 ++ gen/test/go/mx/glissando.go | 144 ++++++ gen/test/go/mx/glyph.go | 49 ++ gen/test/go/mx/grace.go | 67 +++ gen/test/go/mx/group_barline.go | 43 ++ gen/test/go/mx/group_name.go | 108 +++++ gen/test/go/mx/group_symbol.go | 71 +++ gen/test/go/mx/grouping.go | 91 ++++ gen/test/go/mx/hammer_on_pull_off.go | 124 +++++ gen/test/go/mx/handbell.go | 107 +++++ gen/test/go/mx/harmon_closed.go | 45 ++ gen/test/go/mx/harmon_mute.go | 127 ++++++ gen/test/go/mx/harmonic.go | 175 ++++++++ gen/test/go/mx/harmony.go | 250 +++++++++++ gen/test/go/mx/harp_pedals.go | 144 ++++++ gen/test/go/mx/heel_toe.go | 102 +++++ gen/test/go/mx/hole.go | 140 ++++++ gen/test/go/mx/hole_closed.go | 45 ++ gen/test/go/mx/horizontal_turn.go | 161 +++++++ gen/test/go/mx/identification.go | 101 +++++ gen/test/go/mx/image.go | 110 +++++ gen/test/go/mx/instrument.go | 42 ++ gen/test/go/mx/interchangeable.go | 82 ++++ gen/test/go/mx/inversion.go | 100 +++++ gen/test/go/mx/key.go | 186 ++++++++ gen/test/go/mx/key_accidental.go | 45 ++ gen/test/go/mx/key_octave.go | 56 +++ gen/test/go/mx/kind.go | 163 +++++++ gen/test/go/mx/level.go | 68 +++ gen/test/go/mx/line_width.go | 46 ++ gen/test/go/mx/link.go | 126 ++++++ gen/test/go/mx/lyric.go | 228 ++++++++++ gen/test/go/mx/lyric_font.go | 75 ++++ gen/test/go/mx/lyric_language.go | 54 +++ gen/test/go/mx/measure_layout.go | 54 +++ gen/test/go/mx/measure_numbering.go | 116 +++++ gen/test/go/mx/measure_repeat.go | 56 +++ gen/test/go/mx/measure_style.go | 139 ++++++ gen/test/go/mx/metronome.go | 209 +++++++++ gen/test/go/mx/metronome_beam.go | 44 ++ gen/test/go/mx/metronome_note.go | 90 ++++ gen/test/go/mx/metronome_tied.go | 42 ++ gen/test/go/mx/metronome_tuplet.go | 86 ++++ gen/test/go/mx/midi_device.go | 55 +++ gen/test/go/mx/midi_instrument.go | 105 +++++ gen/test/go/mx/miscellaneous.go | 59 +++ gen/test/go/mx/miscellaneous_field.go | 45 ++ gen/test/go/mx/mordent.go | 160 +++++++ gen/test/go/mx/multiple_rest.go | 45 ++ gen/test/go/mx/name_display.go | 76 ++++ gen/test/go/mx/non_arpeggiate.go | 98 ++++ gen/test/go/mx/notations.go | 209 +++++++++ gen/test/go/mx/note.go | 422 ++++++++++++++++++ gen/test/go/mx/note_size.go | 48 ++ gen/test/go/mx/note_type.go | 46 ++ gen/test/go/mx/notehead.go | 100 +++++ gen/test/go/mx/notehead_text.go | 69 +++ gen/test/go/mx/octave_shift.go | 141 ++++++ gen/test/go/mx/offset.go | 49 ++ gen/test/go/mx/opus.go | 76 ++++ gen/test/go/mx/ornaments.go | 201 +++++++++ gen/test/go/mx/other_appearance.go | 45 ++ gen/test/go/mx/other_direction.go | 138 ++++++ gen/test/go/mx/other_notation.go | 146 ++++++ gen/test/go/mx/other_placement_text.go | 115 +++++ gen/test/go/mx/other_play.go | 44 ++ gen/test/go/mx/other_text.go | 45 ++ gen/test/go/mx/page_layout.go | 72 +++ gen/test/go/mx/page_margins.go | 81 ++++ gen/test/go/mx/part_group.go | 152 +++++++ gen/test/go/mx/part_list.go | 71 +++ gen/test/go/mx/part_name.go | 115 +++++ gen/test/go/mx/part_symbol.go | 89 ++++ gen/test/go/mx/partwise_measure.go | 206 +++++++++ gen/test/go/mx/partwise_part.go | 63 +++ gen/test/go/mx/pedal.go | 162 +++++++ gen/test/go/mx/pedal_tuning.go | 60 +++ gen/test/go/mx/per_minute.go | 67 +++ gen/test/go/mx/percussion.go | 226 ++++++++++ gen/test/go/mx/pitch.go | 67 +++ gen/test/go/mx/pitched.go | 45 ++ gen/test/go/mx/placement_text.go | 107 +++++ gen/test/go/mx/play.go | 85 ++++ gen/test/go/mx/principal_voice.go | 138 ++++++ gen/test/go/mx/print.go | 159 +++++++ gen/test/go/mx/repeat.go | 56 +++ gen/test/go/mx/rest.go | 69 +++ gen/test/go/mx/root.go | 69 +++ gen/test/go/mx/root_alter.go | 117 +++++ gen/test/go/mx/root_step.go | 108 +++++ gen/test/go/mx/scaling.go | 64 +++ gen/test/go/mx/scordatura.go | 65 +++ gen/test/go/mx/score_instrument.go | 102 +++++ gen/test/go/mx/score_part.go | 136 ++++++ gen/test/go/mx/score_partwise.go | 120 +++++ gen/test/go/mx/score_timewise.go | 120 +++++ gen/test/go/mx/segno.go | 125 ++++++ gen/test/go/mx/slash.go | 94 ++++ gen/test/go/mx/slide.go | 172 +++++++ gen/test/go/mx/slur.go | 168 +++++++ gen/test/go/mx/sound.go | 245 ++++++++++ gen/test/go/mx/staff_details.go | 114 +++++ gen/test/go/mx/staff_divide.go | 125 ++++++ gen/test/go/mx/staff_layout.go | 65 +++ gen/test/go/mx/staff_tuning.go | 73 +++ gen/test/go/mx/stem.go | 75 ++++ gen/test/go/mx/stick.go | 83 ++++ gen/test/go/mx/string.go | 107 +++++ gen/test/go/mx/string_mute.go | 124 +++++ gen/test/go/mx/strong_accent.go | 102 +++++ gen/test/go/mx/style_text.go | 99 ++++ gen/test/go/mx/supports.go | 66 +++ gen/test/go/mx/system_dividers.go | 72 +++ gen/test/go/mx/system_layout.go | 87 ++++ gen/test/go/mx/system_margins.go | 61 +++ gen/test/go/mx/tap.go | 117 +++++ gen/test/go/mx/technical.go | 334 ++++++++++++++ gen/test/go/mx/text_element_data.go | 122 +++++ gen/test/go/mx/tie.go | 49 ++ gen/test/go/mx/tied.go | 177 ++++++++ gen/test/go/mx/time.go | 194 ++++++++ gen/test/go/mx/time_modification.go | 79 ++++ gen/test/go/mx/timewise_measure.go | 98 ++++ gen/test/go/mx/timewise_part.go | 171 +++++++ gen/test/go/mx/transpose.go | 92 ++++ gen/test/go/mx/tremolo.go | 132 ++++++ gen/test/go/mx/tuplet.go | 162 +++++++ gen/test/go/mx/tuplet_dot.go | 68 +++ gen/test/go/mx/tuplet_number.go | 71 +++ gen/test/go/mx/tuplet_portion.go | 78 ++++ gen/test/go/mx/tuplet_type.go | 72 +++ gen/test/go/mx/typed_text.go | 43 ++ gen/test/go/mx/unpitched.go | 61 +++ gen/test/go/mx/virtual_instrument.go | 61 +++ gen/test/go/mx/wavy_line.go | 139 ++++++ gen/test/go/mx/wedge.go | 130 ++++++ gen/test/go/mx/work.go | 70 +++ gen/test/go/stub/roundtrip.go | 23 - 215 files changed, 22980 insertions(+), 39 deletions(-) create mode 100644 gen/emit/go/complexes.py create mode 100644 gen/emit/go/document.py create mode 100644 gen/test/go/mx/accidental.go create mode 100644 gen/test/go/mx/accidental_mark.go create mode 100644 gen/test/go/mx/accidental_text.go create mode 100644 gen/test/go/mx/accord.go create mode 100644 gen/test/go/mx/accordion_registration.go create mode 100644 gen/test/go/mx/appearance.go create mode 100644 gen/test/go/mx/arpeggiate.go create mode 100644 gen/test/go/mx/arrow.go create mode 100644 gen/test/go/mx/articulations.go create mode 100644 gen/test/go/mx/attributes.go create mode 100644 gen/test/go/mx/backup.go create mode 100644 gen/test/go/mx/bar_style_color.go create mode 100644 gen/test/go/mx/barline.go create mode 100644 gen/test/go/mx/barre.go create mode 100644 gen/test/go/mx/bass.go create mode 100644 gen/test/go/mx/bass_alter.go create mode 100644 gen/test/go/mx/bass_step.go create mode 100644 gen/test/go/mx/beam.go create mode 100644 gen/test/go/mx/beat_repeat.go create mode 100644 gen/test/go/mx/beat_unit_tied.go create mode 100644 gen/test/go/mx/beater.go create mode 100644 gen/test/go/mx/bend.go create mode 100644 gen/test/go/mx/bookmark.go create mode 100644 gen/test/go/mx/bracket.go create mode 100644 gen/test/go/mx/breath_mark.go create mode 100644 gen/test/go/mx/caesura.go create mode 100644 gen/test/go/mx/cancel.go create mode 100644 gen/test/go/mx/clef.go create mode 100644 gen/test/go/mx/coda.go create mode 100644 gen/test/go/mx/credit.go create mode 100644 gen/test/go/mx/dashes.go create mode 100644 gen/test/go/mx/defaults.go create mode 100644 gen/test/go/mx/degree.go create mode 100644 gen/test/go/mx/degree_alter.go create mode 100644 gen/test/go/mx/degree_type.go create mode 100644 gen/test/go/mx/degree_value.go create mode 100644 gen/test/go/mx/direction.go create mode 100644 gen/test/go/mx/direction_type.go create mode 100644 gen/test/go/mx/directive.go create mode 100644 gen/test/go/mx/distance.go create mode 100644 gen/test/go/mx/document.go create mode 100644 gen/test/go/mx/dynamics.go create mode 100644 gen/test/go/mx/elision.go create mode 100644 gen/test/go/mx/empty.go create mode 100644 gen/test/go/mx/empty_font.go create mode 100644 gen/test/go/mx/empty_line.go create mode 100644 gen/test/go/mx/empty_placement.go create mode 100644 gen/test/go/mx/empty_placement_smufl.go create mode 100644 gen/test/go/mx/empty_print_object_style_align.go create mode 100644 gen/test/go/mx/empty_print_style_align_id.go create mode 100644 gen/test/go/mx/empty_trill_sound.go create mode 100644 gen/test/go/mx/encoding.go create mode 100644 gen/test/go/mx/ending.go create mode 100644 gen/test/go/mx/extend.go create mode 100644 gen/test/go/mx/feature.go create mode 100644 gen/test/go/mx/fermata.go create mode 100644 gen/test/go/mx/figure.go create mode 100644 gen/test/go/mx/figured_bass.go create mode 100644 gen/test/go/mx/fingering.go create mode 100644 gen/test/go/mx/first_fret.go create mode 100644 gen/test/go/mx/formatted_symbol_id.go create mode 100644 gen/test/go/mx/formatted_text.go create mode 100644 gen/test/go/mx/formatted_text_id.go create mode 100644 gen/test/go/mx/forward.go create mode 100644 gen/test/go/mx/frame.go create mode 100644 gen/test/go/mx/frame_note.go create mode 100644 gen/test/go/mx/fret.go create mode 100644 gen/test/go/mx/glass.go create mode 100644 gen/test/go/mx/glissando.go create mode 100644 gen/test/go/mx/glyph.go create mode 100644 gen/test/go/mx/grace.go create mode 100644 gen/test/go/mx/group_barline.go create mode 100644 gen/test/go/mx/group_name.go create mode 100644 gen/test/go/mx/group_symbol.go create mode 100644 gen/test/go/mx/grouping.go create mode 100644 gen/test/go/mx/hammer_on_pull_off.go create mode 100644 gen/test/go/mx/handbell.go create mode 100644 gen/test/go/mx/harmon_closed.go create mode 100644 gen/test/go/mx/harmon_mute.go create mode 100644 gen/test/go/mx/harmonic.go create mode 100644 gen/test/go/mx/harmony.go create mode 100644 gen/test/go/mx/harp_pedals.go create mode 100644 gen/test/go/mx/heel_toe.go create mode 100644 gen/test/go/mx/hole.go create mode 100644 gen/test/go/mx/hole_closed.go create mode 100644 gen/test/go/mx/horizontal_turn.go create mode 100644 gen/test/go/mx/identification.go create mode 100644 gen/test/go/mx/image.go create mode 100644 gen/test/go/mx/instrument.go create mode 100644 gen/test/go/mx/interchangeable.go create mode 100644 gen/test/go/mx/inversion.go create mode 100644 gen/test/go/mx/key.go create mode 100644 gen/test/go/mx/key_accidental.go create mode 100644 gen/test/go/mx/key_octave.go create mode 100644 gen/test/go/mx/kind.go create mode 100644 gen/test/go/mx/level.go create mode 100644 gen/test/go/mx/line_width.go create mode 100644 gen/test/go/mx/link.go create mode 100644 gen/test/go/mx/lyric.go create mode 100644 gen/test/go/mx/lyric_font.go create mode 100644 gen/test/go/mx/lyric_language.go create mode 100644 gen/test/go/mx/measure_layout.go create mode 100644 gen/test/go/mx/measure_numbering.go create mode 100644 gen/test/go/mx/measure_repeat.go create mode 100644 gen/test/go/mx/measure_style.go create mode 100644 gen/test/go/mx/metronome.go create mode 100644 gen/test/go/mx/metronome_beam.go create mode 100644 gen/test/go/mx/metronome_note.go create mode 100644 gen/test/go/mx/metronome_tied.go create mode 100644 gen/test/go/mx/metronome_tuplet.go create mode 100644 gen/test/go/mx/midi_device.go create mode 100644 gen/test/go/mx/midi_instrument.go create mode 100644 gen/test/go/mx/miscellaneous.go create mode 100644 gen/test/go/mx/miscellaneous_field.go create mode 100644 gen/test/go/mx/mordent.go create mode 100644 gen/test/go/mx/multiple_rest.go create mode 100644 gen/test/go/mx/name_display.go create mode 100644 gen/test/go/mx/non_arpeggiate.go create mode 100644 gen/test/go/mx/notations.go create mode 100644 gen/test/go/mx/note.go create mode 100644 gen/test/go/mx/note_size.go create mode 100644 gen/test/go/mx/note_type.go create mode 100644 gen/test/go/mx/notehead.go create mode 100644 gen/test/go/mx/notehead_text.go create mode 100644 gen/test/go/mx/octave_shift.go create mode 100644 gen/test/go/mx/offset.go create mode 100644 gen/test/go/mx/opus.go create mode 100644 gen/test/go/mx/ornaments.go create mode 100644 gen/test/go/mx/other_appearance.go create mode 100644 gen/test/go/mx/other_direction.go create mode 100644 gen/test/go/mx/other_notation.go create mode 100644 gen/test/go/mx/other_placement_text.go create mode 100644 gen/test/go/mx/other_play.go create mode 100644 gen/test/go/mx/other_text.go create mode 100644 gen/test/go/mx/page_layout.go create mode 100644 gen/test/go/mx/page_margins.go create mode 100644 gen/test/go/mx/part_group.go create mode 100644 gen/test/go/mx/part_list.go create mode 100644 gen/test/go/mx/part_name.go create mode 100644 gen/test/go/mx/part_symbol.go create mode 100644 gen/test/go/mx/partwise_measure.go create mode 100644 gen/test/go/mx/partwise_part.go create mode 100644 gen/test/go/mx/pedal.go create mode 100644 gen/test/go/mx/pedal_tuning.go create mode 100644 gen/test/go/mx/per_minute.go create mode 100644 gen/test/go/mx/percussion.go create mode 100644 gen/test/go/mx/pitch.go create mode 100644 gen/test/go/mx/pitched.go create mode 100644 gen/test/go/mx/placement_text.go create mode 100644 gen/test/go/mx/play.go create mode 100644 gen/test/go/mx/principal_voice.go create mode 100644 gen/test/go/mx/print.go create mode 100644 gen/test/go/mx/repeat.go create mode 100644 gen/test/go/mx/rest.go create mode 100644 gen/test/go/mx/root.go create mode 100644 gen/test/go/mx/root_alter.go create mode 100644 gen/test/go/mx/root_step.go create mode 100644 gen/test/go/mx/scaling.go create mode 100644 gen/test/go/mx/scordatura.go create mode 100644 gen/test/go/mx/score_instrument.go create mode 100644 gen/test/go/mx/score_part.go create mode 100644 gen/test/go/mx/score_partwise.go create mode 100644 gen/test/go/mx/score_timewise.go create mode 100644 gen/test/go/mx/segno.go create mode 100644 gen/test/go/mx/slash.go create mode 100644 gen/test/go/mx/slide.go create mode 100644 gen/test/go/mx/slur.go create mode 100644 gen/test/go/mx/sound.go create mode 100644 gen/test/go/mx/staff_details.go create mode 100644 gen/test/go/mx/staff_divide.go create mode 100644 gen/test/go/mx/staff_layout.go create mode 100644 gen/test/go/mx/staff_tuning.go create mode 100644 gen/test/go/mx/stem.go create mode 100644 gen/test/go/mx/stick.go create mode 100644 gen/test/go/mx/string.go create mode 100644 gen/test/go/mx/string_mute.go create mode 100644 gen/test/go/mx/strong_accent.go create mode 100644 gen/test/go/mx/style_text.go create mode 100644 gen/test/go/mx/supports.go create mode 100644 gen/test/go/mx/system_dividers.go create mode 100644 gen/test/go/mx/system_layout.go create mode 100644 gen/test/go/mx/system_margins.go create mode 100644 gen/test/go/mx/tap.go create mode 100644 gen/test/go/mx/technical.go create mode 100644 gen/test/go/mx/text_element_data.go create mode 100644 gen/test/go/mx/tie.go create mode 100644 gen/test/go/mx/tied.go create mode 100644 gen/test/go/mx/time.go create mode 100644 gen/test/go/mx/time_modification.go create mode 100644 gen/test/go/mx/timewise_measure.go create mode 100644 gen/test/go/mx/timewise_part.go create mode 100644 gen/test/go/mx/transpose.go create mode 100644 gen/test/go/mx/tremolo.go create mode 100644 gen/test/go/mx/tuplet.go create mode 100644 gen/test/go/mx/tuplet_dot.go create mode 100644 gen/test/go/mx/tuplet_number.go create mode 100644 gen/test/go/mx/tuplet_portion.go create mode 100644 gen/test/go/mx/tuplet_type.go create mode 100644 gen/test/go/mx/typed_text.go create mode 100644 gen/test/go/mx/unpitched.go create mode 100644 gen/test/go/mx/virtual_instrument.go create mode 100644 gen/test/go/mx/wavy_line.go create mode 100644 gen/test/go/mx/wedge.go create mode 100644 gen/test/go/mx/work.go delete mode 100644 gen/test/go/stub/roundtrip.go diff --git a/data/README.md b/data/README.md index 9a9336b22..8cd7f4ba5 100644 --- a/data/README.md +++ b/data/README.md @@ -32,3 +32,12 @@ test by altering values it finds after loading the test file. ``` + +The sidecars encode one uniform leniency policy, shared by every generated target: + +- an unknown enum literal falls back to the enum's first variant (`display-step` `=` -> `A`); +- an unparseable number becomes 0, with decimal-looking integers truncating toward zero; +- every number then clamps into its declared range, including the primitive-implied lower bounds + (`xs:positiveInteger` >= 1, `xs:nonNegativeInteger` >= 0), so `midi-channel` `0` -> `1` and + `accordion-middle` `` -> `1`; +- an exclusive decimal bound clamps to the bound +/- 1e-6 (`duration` `0` -> `0.000001`). diff --git a/data/lysuite/ly75a_AccordionRegistrations.fixup.xml b/data/lysuite/ly75a_AccordionRegistrations.fixup.xml index c9ff2640c..d28f21f4d 100644 --- a/data/lysuite/ly75a_AccordionRegistrations.fixup.xml +++ b/data/lysuite/ly75a_AccordionRegistrations.fixup.xml @@ -4,13 +4,13 @@ element accordion-middle - 0 + 1 element accordion-middle test - 0 + 1 element @@ -18,4 +18,10 @@ 5 3 + + element + accordion-middle + 0 + 1 + diff --git a/data/synthetic/elision.3.0.xml b/data/synthetic/elision.3.0.xml index 6d323cef1..25d8148af 100644 --- a/data/synthetic/elision.3.0.xml +++ b/data/synthetic/elision.3.0.xml @@ -27,7 +27,7 @@ 1 x - x + x x x x diff --git a/data/synthetic/extend.3.0.xml b/data/synthetic/extend.3.0.xml index 9738dc798..f4316c719 100644 --- a/data/synthetic/extend.3.0.xml +++ b/data/synthetic/extend.3.0.xml @@ -29,7 +29,7 @@ x x x - + x x diff --git a/gen/emit/go/__init__.py b/gen/emit/go/__init__.py index 19479336c..a78b9285e 100644 --- a/gen/emit/go/__init__.py +++ b/gen/emit/go/__init__.py @@ -5,10 +5,8 @@ only Go grammar (declarations, parse/serialize bodies, comment syntax) and the runtime support file. -Currently rendered: the four value shapes (the leaf node types) plus the -runtime. Complex types and the document entry points are later phases; until -they land, the emitted package is a compilable library of value types and -the corert harness keeps using its stub. +Rendered: the four value shapes, the four complex shapes, the document +entry points (FromXDoc/ToXDoc), and the runtime support file. """ from __future__ import annotations @@ -18,15 +16,22 @@ import tempfile from pathlib import Path +from gen.emit.go.complexes import complex_file +from gen.emit.go.document import document_file from gen.emit.go.runtime import runtime_file from gen.emit.go.values import value_file from gen.plates.model import Plates def render(plates: Plates) -> dict[str, str]: - files: dict[str, str] = {"runtime.go": runtime_file(plates)} + files: dict[str, str] = { + "runtime.go": runtime_file(plates), + "document.go": document_file(plates), + } for plate in plates.value_types: files[plate.file + ".go"] = value_file(plates, plate) + for plate in plates.complex_types: + files[plate.file + ".go"] = complex_file(plates, plate) return _gofmt(files) diff --git a/gen/emit/go/complexes.py b/gen/emit/go/complexes.py new file mode 100644 index 000000000..613b67343 --- /dev/null +++ b/gen/emit/go/complexes.py @@ -0,0 +1,312 @@ +"""Go templates for the four complex shapes. + +One template per strategy: value-class, composite-class, flag, attrs-class, +inherit. Each complex type renders as a struct plus a parse and a serialize +function against the etree DOM. + +Representation decisions (these are the Go backend's spelling of the plate +facts, chosen for round-trip fidelity): + + - Every attribute is presence-tracked (a pointer field), required or not: + the corert contract is "write back exactly what was parsed", and corpus + files do omit required attributes. + - A composite stores its children as ONE ordered list (`Children + []XChild`, a kind-tagged struct with a typed pointer per possible child + name) rather than one field per member. Interleaved choice elements + (measure's music-data, note's grace/cue branches) round-trip in document + order for free, which per-member vectors cannot do. + - Parsing is strict about NAMES (an unknown attribute or child element is + an error: the version gate keeps incompatible documents out, so an + unknown name is a generator gap, not data) and lenient about VALUES + (the typed Parse* functions degrade deterministically). + - Namespace declarations (xmlns*) are skipped wherever they appear; the + document entry point preserves the root's (see document.py). + - A derived type embeds its base (field promotion gives the flat view) and + parses/serializes the merged member set in one pass, appending children + to the base chain's owner. +""" + +from __future__ import annotations + +from gen.emit.go.common import doc_comment, file_frame, go_string +from gen.plates.model import ComplexPlate, Member, Plates + +# IR primitive -> (Go type, parse expr template, to-string expr template). +_PRIM = { + "decimal": ("float64", "parseDecimal({0})", "formatDecimal({0})"), + "integer": ("int", "parseInt({0})", "formatInt({0})"), + "positive_integer": ("int", "parseInt({0})", "formatInt({0})"), + "non_negative_integer": ("int", "parseInt({0})", "formatInt({0})"), + "string": ("string", "{0}", "{0}"), + "token": ("string", "{0}", "{0}"), + "nmtoken": ("string", "{0}", "{0}"), + "date": ("string", "{0}", "{0}"), +} + + +def _go_type(member: Member) -> str: + if member.type_ref.category == "primitive": + return _PRIM[member.type_ref.wire][0] + return member.type_ref.ident + + +def _parse_expr(member: Member, source: str) -> str: + """The expression turning wire text into the member's Go value.""" + if member.type_ref.category == "primitive": + return _PRIM[member.type_ref.wire][1].format(source) + return f"Parse{member.type_ref.ident}({source})" + + +def _string_expr(member: Member, source: str) -> str: + """The expression turning the member's Go value back into wire text.""" + if member.type_ref.category == "primitive": + return _PRIM[member.type_ref.wire][2].format(source) + return f"{source}.String()" + + +def complex_file(plates: Plates, plate: ComplexPlate) -> str: + if plate.strategy == "inherit": + body = _inherit_body(plates, plate) + else: + body = _class_body(plates, plate) + return file_frame(plates, body, imports=["fmt", "github.com/beevik/etree"]) + + +# --------------------------------------------------------------------------- # +# The shared pieces: struct, attribute loop, child loop +# --------------------------------------------------------------------------- # + + +def _attr_members(members: list[Member]) -> list[Member]: + return [m for m in members if m.kind == "attribute"] + + +def _element_members(members: list[Member]) -> list[Member]: + return [m for m in members if m.kind == "element"] + + +def _value_member(members: list[Member]) -> Member | None: + return next((m for m in members if m.kind == "value"), None) + + +def _struct_lines(plates: Plates, plate: ComplexPlate, members: list[Member], + embed: str | None, children_of: str | None) -> list[str]: + wrap = plates.target.doc_style.wrap + lines = doc_comment(plate.doc, wrap) + lines += [f"type {plate.ident} struct {{"] + if embed: + lines += [f"\t{embed}"] + for m in _attr_members(members): + lines += [f"\t{m.ident} *{_go_type(m)} // attribute {go_string(m.name.wire)}"] + value = _value_member(members) + if value is not None and not embed: + lines += [f"\t{value.ident} {_go_type(value)} // text content"] + if children_of == plate.ident: + lines += [f"\tChildren []{plate.ident}Child // child elements in document order"] + lines += ["}", ""] + return lines + + +def _child_struct_lines(plates: Plates, plate: ComplexPlate) -> list[str]: + elements = _element_members(plate.members) + ident = plate.ident + lines = [ + f"// {ident}Child is one child element of {ident}: exactly one field", + "// is non-nil, and that pointer says which element this is. (No kind", + "// discriminator: schema element names like harmony's would", + "// collide with a synthetic field.)", + f"type {ident}Child struct {{", + ] + for m in elements: + lines += [f"\t{m.ident} *{_go_type(m)}"] + lines += ["}", ""] + return lines + + +def _attr_loop(plates: Plates, members: list[Member]) -> list[str]: + lines = [ + "\tfor _, a := range el.Attr {", + '\t\tif a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") {', + "\t\t\tcontinue", + "\t\t}", + "\t\tswitch a.FullKey() {", + ] + for m in _attr_members(members): + lines += [f"\t\tcase {go_string(m.name.wire)}:"] + if m.type_ref.category == "primitive" and _PRIM[m.type_ref.wire][0] == "string": + lines += ["\t\t\tv := a.Value"] + else: + lines += [f"\t\t\tv := {_parse_expr(m, 'a.Value')}"] + lines += [f"\t\t\tm.{m.ident} = &v"] + lines += [ + "\t\tdefault:", + '\t\t\treturn nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag)', + "\t\t}", + "\t}", + ] + return lines + + +def _attr_writes(plates: Plates, members: list[Member]) -> list[str]: + lines = [] + for m in _attr_members(members): + lines += [ + f"\tif m.{m.ident} != nil {{", + f"\t\tel.CreateAttr({go_string(m.name.wire)}, {_string_expr(m, f'(*m.{m.ident})')})", + "\t}", + ] + return lines + + +def _child_parse_loop(plates: Plates, owner: ComplexPlate) -> list[str]: + """The children dispatch: element name -> typed parse, appended in + document order. `owner` is the plate whose Child struct holds them.""" + ident = owner.ident + lines = ["\tfor _, c := range el.ChildElements() {", "\t\tswitch c.Tag {"] + for m in _element_members(owner.members): + lines += [f"\t\tcase {go_string(m.name.wire)}:"] + if m.type_ref.category == "complex": + lines += [ + f"\t\t\tv, err := parse{m.type_ref.ident}(c)", + "\t\t\tif err != nil {", + "\t\t\t\treturn nil, err", + "\t\t\t}", + f"\t\t\tm.Children = append(m.Children, {ident}Child{{{m.ident}: v}})", + ] + else: + if m.type_ref.category == "primitive" and _PRIM[m.type_ref.wire][0] == "string": + lines += ["\t\t\tv := c.Text()"] + else: + lines += [f"\t\t\tv := {_parse_expr(m, 'c.Text()')}"] + lines += [ + f"\t\t\tm.Children = append(m.Children, {ident}Child{{{m.ident}: &v}})", + ] + lines += [ + "\t\tdefault:", + '\t\t\treturn nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag)', + "\t\t}", + "\t}", + ] + return lines + + +def _child_serialize_loop(plates: Plates, owner: ComplexPlate) -> list[str]: + lines = ["\tfor _, ch := range m.Children {", "\t\tswitch {"] + for m in _element_members(owner.members): + lines += [f"\t\tcase ch.{m.ident} != nil:"] + if m.type_ref.category == "complex": + lines += [f"\t\t\tserialize{m.type_ref.ident}(ch.{m.ident}, el, {go_string(m.name.wire)})"] + else: + lines += [ + f"\t\t\tel.CreateElement({go_string(m.name.wire)}).SetText({_string_expr(m, f'(*ch.{m.ident})')})", + ] + lines += ["\t\t}", "\t}"] + return lines + + +def _reject_children() -> list[str]: + return [ + "\tfor _, c := range el.ChildElements() {", + '\t\treturn nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag)', + "\t}", + ] + + +# --------------------------------------------------------------------------- # +# value-class | flag | attrs-class | composite-class +# --------------------------------------------------------------------------- # + + +def _class_body(plates: Plates, plate: ComplexPlate) -> list[str]: + ident = plate.ident + has_children = plate.strategy == "composite-class" and _element_members(plate.members) + value = _value_member(plate.members) + + lines = _struct_lines( + plates, plate, plate.members, + embed=None, + children_of=ident if has_children else None, + ) + if has_children: + lines += _child_struct_lines(plates, plate) + + lines += [f"func parse{ident}(el *etree.Element) (*{ident}, error) {{"] + lines += [f"\tm := &{ident}{{}}"] + lines += _attr_loop(plates, plate.members) + if value is not None: + if value.type_ref.category == "primitive" and _PRIM[value.type_ref.wire][0] == "string": + lines += [f"\tm.{value.ident} = el.Text()"] + else: + lines += [f"\tm.{value.ident} = {_parse_expr(value, 'el.Text()')}"] + if has_children: + lines += _child_parse_loop(plates, plate) + else: + lines += _reject_children() + lines += ["\treturn m, nil", "}", ""] + + lines += [f"func serialize{ident}(m *{ident}, parent *etree.Element, tag string) {{"] + inert = not _attr_members(plate.members) and value is None and not has_children + if inert: + # Presence is the only information this element carries. + lines += ["\t_ = m", "\tparent.CreateElement(tag)"] + else: + lines += ["\tel := parent.CreateElement(tag)"] + if value is not None: + lines += [f"\tel.SetText({_string_expr(value, f'm.{value.ident}')})"] + lines += _attr_writes(plates, plate.members) + if has_children: + lines += _child_serialize_loop(plates, plate) + lines += ["}"] + return lines + + +# --------------------------------------------------------------------------- # +# inherit (derived): embed the base, parse/serialize the merged view +# --------------------------------------------------------------------------- # + + +def _children_owner(plates: Plates, plate: ComplexPlate) -> ComplexPlate | None: + """The base-chain plate whose Child struct holds this type's children: + the nearest ancestor (or self) with element members.""" + cur = plate + while cur is not None: + if _element_members(cur.members): + return cur + cur = plates.plate(cur.base.wire) if cur.base is not None else None + return None + + +def _inherit_body(plates: Plates, plate: ComplexPlate) -> list[str]: + ident = plate.ident + members = plate.all_members or plate.members + owner = _children_owner(plates, plate) + + lines = _struct_lines( + plates, plate, plate.members, # own attrs only: the base is embedded + embed=plate.base.ident, + children_of=None, + ) + + lines += [f"func parse{ident}(el *etree.Element) (*{ident}, error) {{"] + lines += [f"\tm := &{ident}{{}}"] + # The merged attribute set parses in one pass; field promotion routes + # base attributes through the embedded struct. + lines += _attr_loop(plates, members) + value = _value_member(members) + if value is not None: + lines += [f"\tm.{value.ident} = {_parse_expr(value, 'el.Text()')}"] + if owner is not None: + lines += _child_parse_loop(plates, owner) + else: + lines += _reject_children() + lines += ["\treturn m, nil", "}", ""] + + lines += [f"func serialize{ident}(m *{ident}, parent *etree.Element, tag string) {{"] + lines += ["\tel := parent.CreateElement(tag)"] + if value is not None: + lines += [f"\tel.SetText({_string_expr(value, f'm.{value.ident}')})"] + lines += _attr_writes(plates, members) + if owner is not None: + lines += _child_serialize_loop(plates, owner) + lines += ["}"] + return lines diff --git a/gen/emit/go/document.py b/gen/emit/go/document.py new file mode 100644 index 000000000..52f62ae9c --- /dev/null +++ b/gen/emit/go/document.py @@ -0,0 +1,82 @@ +"""The Go document entry points: Document, FromXDoc, ToXDoc. + +Generated from the plates' roots. The Document also preserves the root +element's namespace declarations (a handful of corpus files declare +xmlns:xlink on the score element); the per-type parsers skip xmlns +declarations wherever they appear, and ToXDoc restores the root's. +""" + +from __future__ import annotations + +from gen.emit.go.common import file_frame, go_string +from gen.plates.model import Plates + + +def document_file(plates: Plates) -> str: + lines = [ + "// ExtraAttr preserves an attribute outside the schema (namespace", + "// declarations on the document root).", + "type ExtraAttr struct {", + "\tKey string", + "\tValue string", + "}", + "", + "// Document is a parsed MusicXML document: exactly one root is set.", + "type Document struct {", + ] + for root in plates.roots: + lines += [f"\t{root.ident} *{root.ident}"] + lines += ["\tRootNamespaces []ExtraAttr", "}", ""] + + lines += [ + "// FromXDoc parses an etree document into the typed MusicXML model.", + "func FromXDoc(doc *etree.Document) (*Document, error) {", + "\troot := doc.Root()", + "\tif root == nil {", + '\t\treturn nil, fmt.Errorf("document has no root element")', + "\t}", + "\td := &Document{}", + "\tfor _, a := range root.Attr {", + '\t\tif a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") {', + "\t\t\td.RootNamespaces = append(d.RootNamespaces, ExtraAttr{a.FullKey(), a.Value})", + "\t\t}", + "\t}", + "\tswitch root.Tag {", + ] + for root in plates.roots: + lines += [ + f"\tcase {go_string(root.wire)}:", + f"\t\tv, err := parse{root.ident}(root)", + "\t\tif err != nil {", + "\t\t\treturn nil, err", + "\t\t}", + f"\t\td.{root.ident} = v", + ] + lines += [ + "\tdefault:", + '\t\treturn nil, fmt.Errorf("unknown root element <%s>", root.Tag)', + "\t}", + "\treturn d, nil", + "}", + "", + "// ToXDoc serializes the typed model back to an etree document.", + "func ToXDoc(d *Document) (*etree.Document, error) {", + "\tdoc := etree.NewDocument()", + "\tswitch {", + ] + for root in plates.roots: + lines += [ + f"\tcase d.{root.ident} != nil:", + f"\t\tserialize{root.ident}(d.{root.ident}, &doc.Element, {go_string(root.wire)})", + ] + lines += [ + "\tdefault:", + '\t\treturn nil, fmt.Errorf("document has no root")', + "\t}", + "\tfor _, a := range d.RootNamespaces {", + "\t\tdoc.Root().CreateAttr(a.Key, a.Value)", + "\t}", + "\treturn doc, nil", + "}", + ] + return file_frame(plates, lines, imports=["fmt", "github.com/beevik/etree"]) diff --git a/gen/test/go/corert/corert_test.go b/gen/test/go/corert/corert_test.go index 87efaee37..83b93396b 100644 --- a/gen/test/go/corert/corert_test.go +++ b/gen/test/go/corert/corert_test.go @@ -46,6 +46,9 @@ func TestCoreRoundtrip(t *testing.T) { name := corert.ToTestName(absPath, dataRoot) t.Run(name, func(t *testing.T) { result := corert.RunCoreRoundtrip(absPath) + if result.Skipped { + t.Skip(result.Message) + } if !result.OK { if result.ExpectedXML != "" || result.ActualXML != "" { corert.WriteFailureFiles(root, name, result.ExpectedXML, result.ActualXML) diff --git a/gen/test/go/corert/normalize.go b/gen/test/go/corert/normalize.go index 65e2e2cb3..dc5d00bda 100644 --- a/gen/test/go/corert/normalize.go +++ b/gen/test/go/corert/normalize.go @@ -28,10 +28,35 @@ func Normalize(doc *etree.Document) { setXMLDeclaration(doc) setDoctypeFromRoot(doc) setRootMusicXMLVersion(doc) + stripInterElementWhitespace(doc.Root()) stripZerosFromDecimalFields(doc.Root()) sortAttributes(doc.Root()) } +// stripInterElementWhitespace removes whitespace-only character data from +// every element: pretty-printing indentation in containers, including +// containers whose optional children are all absent (an empty +// holds only its own indentation). MusicXML has no mixed content, and the +// rule applies to expected and actual alike, so leaf values with real +// content are never touched and the comparison stays symmetric. +func stripInterElementWhitespace(el *etree.Element) { + if el == nil { + return + } + var remove []etree.Token + for _, tok := range el.Child { + if cd, ok := tok.(*etree.CharData); ok && strings.TrimSpace(cd.Data) == "" { + remove = append(remove, tok) + } + } + for _, tok := range remove { + el.RemoveChild(tok) + } + for _, child := range el.ChildElements() { + stripInterElementWhitespace(child) + } +} + func setXMLDeclaration(doc *etree.Document) { doc.CreateProcInst("xml", `version="1.0" encoding="UTF-8" standalone="no"`) } diff --git a/gen/test/go/corert/roundtrip.go b/gen/test/go/corert/roundtrip.go index fbce59073..801312d53 100644 --- a/gen/test/go/corert/roundtrip.go +++ b/gen/test/go/corert/roundtrip.go @@ -1,44 +1,146 @@ package corert import ( + "bytes" "fmt" + "io" "os" "path/filepath" + "strconv" "strings" + "unicode/utf16" + "unicode/utf8" "github.com/beevik/etree" - "github.com/webern/mx/gen/test/go/stub" + "github.com/webern/mx/gen/test/go/mx" ) +// The MusicXML version the generated model supports (the schema pinned in +// this target's config.toml). Documents declaring a NEWER version may use +// elements the model has no types for; MusicXML is backward compatible, so +// older documents are fine. +const maxSupportedVersion = "3.1" + type Result struct { OK bool + Skipped bool Message string ExpectedXML string ActualXML string } +// declaredVersionExceeds reports whether the document's root version +// attribute declares a version newer than max. An absent attribute means +// MusicXML 1.0. +func declaredVersionExceeds(doc *etree.Document, max string) bool { + root := doc.Root() + if root == nil { + return false + } + version := "1.0" + if a := root.SelectAttr("version"); a != nil && a.Value != "" { + version = a.Value + } + parse := func(v string) (int, int) { + parts := strings.SplitN(v, ".", 3) + major, _ := strconv.Atoi(parts[0]) + minor := 0 + if len(parts) > 1 { + minor, _ = strconv.Atoi(parts[1]) + } + return major, minor + } + maj, min := parse(version) + maxMaj, maxMin := parse(max) + return maj > maxMaj || (maj == maxMaj && min > maxMin) +} + +// charsetReader handles the non-UTF-8 encodings the corpus contains. +// ISO-8859-1 is a one-byte-per-rune encoding, so its conversion is total. +// UTF-16 documents were already transcoded whole (see loadDocument), so a +// declared utf-16 passes through as the UTF-8 it now is. Anything else +// passes through to the decoder's UTF-8 validation. (pugixml and libxml2 +// auto-detect these encodings natively; Go's encoding/xml does not.) +func charsetReader(charset string, input io.Reader) (io.Reader, error) { + switch strings.ToLower(charset) { + case "iso-8859-1", "latin1": + raw, err := io.ReadAll(input) + if err != nil { + return nil, err + } + var out []byte + for _, b := range raw { + out = utf8.AppendRune(out, rune(b)) + } + return bytes.NewReader(out), nil + } + return input, nil +} + +// transcodeUTF16 converts a byte-order-marked UTF-16 document to UTF-8; +// anything without a BOM is returned untouched. +func transcodeUTF16(raw []byte) []byte { + if len(raw) < 2 { + return raw + } + little := raw[0] == 0xFF && raw[1] == 0xFE + big := raw[0] == 0xFE && raw[1] == 0xFF + if !little && !big { + return raw + } + raw = raw[2:] + units := make([]uint16, 0, len(raw)/2) + for i := 0; i+1 < len(raw); i += 2 { + if little { + units = append(units, uint16(raw[i])|uint16(raw[i+1])<<8) + } else { + units = append(units, uint16(raw[i])<<8|uint16(raw[i+1])) + } + } + return []byte(string(utf16.Decode(units))) +} + +func loadDocument(absPath string) (*etree.Document, error) { + raw, err := os.ReadFile(absPath) + if err != nil { + return nil, err + } + doc := etree.NewDocument() + doc.ReadSettings.CharsetReader = charsetReader + if err := doc.ReadFromBytes(transcodeUTF16(raw)); err != nil { + return nil, err + } + return doc, nil +} + func RunCoreRoundtrip(absInputPath string) Result { - inputDoc := etree.NewDocument() - if err := inputDoc.ReadFromFile(absInputPath); err != nil { + inputDoc, err := loadDocument(absInputPath) + if err != nil { return Result{Message: fmt.Sprintf("load input: %v", err)} } + if declaredVersionExceeds(inputDoc, maxSupportedVersion) { + return Result{Skipped: true, Message: fmt.Sprintf( + "declares MusicXML > %s; this target generates from the %s schema", + maxSupportedVersion, maxSupportedVersion)} + } + setRootMusicXMLVersion(inputDoc) - model, err := stub.FromXDoc(inputDoc) + model, err := mx.FromXDoc(inputDoc) if err != nil { return Result{Message: fmt.Sprintf("FromXDoc: %v", err)} } - actualDoc, err := stub.ToXDoc(model) + actualDoc, err := mx.ToXDoc(model) if err != nil { return Result{Message: fmt.Sprintf("ToXDoc: %v", err)} } Normalize(actualDoc) - expectedDoc := etree.NewDocument() - if err := expectedDoc.ReadFromFile(absInputPath); err != nil { + expectedDoc, err := loadDocument(absInputPath) + if err != nil { return Result{Message: fmt.Sprintf("load expected: %v", err)} } setRootMusicXMLVersion(expectedDoc) diff --git a/gen/test/go/mx/accidental.go b/gen/test/go/mx/accidental.go new file mode 100644 index 000000000..98c3358ff --- /dev/null +++ b/gen/test/go/mx/accidental.go @@ -0,0 +1,144 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The accidental type represents actual notated accidentals. Editorial and cautionary indications +// are indicated by attributes. Values for these attributes are "no" if not present. Specific +// graphic display such as parentheses, brackets, and size are controlled by the level-display +// attribute group. +type Accidental struct { + Cautionary *YesNo // attribute "cautionary" + Editorial *YesNo // attribute "editorial" + SMUFL *SMUFLAccidentalGlyphName // attribute "smufl" + Parentheses *YesNo // attribute "parentheses" + Bracket *YesNo // attribute "bracket" + Size *SymbolSize // attribute "size" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Value AccidentalValue // text content +} + +func parseAccidental(el *etree.Element) (*Accidental, error) { + m := &Accidental{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "cautionary": + v := ParseYesNo(a.Value) + m.Cautionary = &v + case "editorial": + v := ParseYesNo(a.Value) + m.Editorial = &v + case "smufl": + v := ParseSMUFLAccidentalGlyphName(a.Value) + m.SMUFL = &v + case "parentheses": + v := ParseYesNo(a.Value) + m.Parentheses = &v + case "bracket": + v := ParseYesNo(a.Value) + m.Bracket = &v + case "size": + v := ParseSymbolSize(a.Value) + m.Size = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseAccidentalValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeAccidental(m *Accidental, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Cautionary != nil { + el.CreateAttr("cautionary", (*m.Cautionary).String()) + } + if m.Editorial != nil { + el.CreateAttr("editorial", (*m.Editorial).String()) + } + if m.SMUFL != nil { + el.CreateAttr("smufl", (*m.SMUFL).String()) + } + if m.Parentheses != nil { + el.CreateAttr("parentheses", (*m.Parentheses).String()) + } + if m.Bracket != nil { + el.CreateAttr("bracket", (*m.Bracket).String()) + } + if m.Size != nil { + el.CreateAttr("size", (*m.Size).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/accidental_mark.go b/gen/test/go/mx/accidental_mark.go new file mode 100644 index 000000000..39cca4b64 --- /dev/null +++ b/gen/test/go/mx/accidental_mark.go @@ -0,0 +1,142 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// An accidental-mark can be used as a separate notation or as part of an ornament. When used in an +// ornament, position and placement are relative to the ornament, not relative to the note. +type AccidentalMark struct { + SMUFL *SMUFLAccidentalGlyphName // attribute "smufl" + Parentheses *YesNo // attribute "parentheses" + Bracket *YesNo // attribute "bracket" + Size *SymbolSize // attribute "size" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" + ID *string // attribute "id" + Value AccidentalValue // text content +} + +func parseAccidentalMark(el *etree.Element) (*AccidentalMark, error) { + m := &AccidentalMark{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "smufl": + v := ParseSMUFLAccidentalGlyphName(a.Value) + m.SMUFL = &v + case "parentheses": + v := ParseYesNo(a.Value) + m.Parentheses = &v + case "bracket": + v := ParseYesNo(a.Value) + m.Bracket = &v + case "size": + v := ParseSymbolSize(a.Value) + m.Size = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseAccidentalValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeAccidentalMark(m *AccidentalMark, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.SMUFL != nil { + el.CreateAttr("smufl", (*m.SMUFL).String()) + } + if m.Parentheses != nil { + el.CreateAttr("parentheses", (*m.Parentheses).String()) + } + if m.Bracket != nil { + el.CreateAttr("bracket", (*m.Bracket).String()) + } + if m.Size != nil { + el.CreateAttr("size", (*m.Size).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/accidental_text.go b/gen/test/go/mx/accidental_text.go new file mode 100644 index 000000000..60260754e --- /dev/null +++ b/gen/test/go/mx/accidental_text.go @@ -0,0 +1,198 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The accidental-text type represents an element with an accidental value and text-formatting +// attributes. +type AccidentalText struct { + SMUFL *SMUFLAccidentalGlyphName // attribute "smufl" + XMLLang *string // attribute "xml:lang" + XMLSpace *string // attribute "xml:space" + Justify *LeftCenterRight // attribute "justify" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" + Underline *NumberOfLines // attribute "underline" + Overline *NumberOfLines // attribute "overline" + LineThrough *NumberOfLines // attribute "line-through" + Rotation *RotationDegrees // attribute "rotation" + LetterSpacing *NumberOrNormal // attribute "letter-spacing" + LineHeight *NumberOrNormal // attribute "line-height" + Dir *TextDirection // attribute "dir" + Enclosure *EnclosureShape // attribute "enclosure" + Value AccidentalValue // text content +} + +func parseAccidentalText(el *etree.Element) (*AccidentalText, error) { + m := &AccidentalText{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "smufl": + v := ParseSMUFLAccidentalGlyphName(a.Value) + m.SMUFL = &v + case "xml:lang": + v := a.Value + m.XMLLang = &v + case "xml:space": + v := a.Value + m.XMLSpace = &v + case "justify": + v := ParseLeftCenterRight(a.Value) + m.Justify = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + case "underline": + v := ParseNumberOfLines(a.Value) + m.Underline = &v + case "overline": + v := ParseNumberOfLines(a.Value) + m.Overline = &v + case "line-through": + v := ParseNumberOfLines(a.Value) + m.LineThrough = &v + case "rotation": + v := ParseRotationDegrees(a.Value) + m.Rotation = &v + case "letter-spacing": + v := ParseNumberOrNormal(a.Value) + m.LetterSpacing = &v + case "line-height": + v := ParseNumberOrNormal(a.Value) + m.LineHeight = &v + case "dir": + v := ParseTextDirection(a.Value) + m.Dir = &v + case "enclosure": + v := ParseEnclosureShape(a.Value) + m.Enclosure = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseAccidentalValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeAccidentalText(m *AccidentalText, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.SMUFL != nil { + el.CreateAttr("smufl", (*m.SMUFL).String()) + } + if m.XMLLang != nil { + el.CreateAttr("xml:lang", (*m.XMLLang)) + } + if m.XMLSpace != nil { + el.CreateAttr("xml:space", (*m.XMLSpace)) + } + if m.Justify != nil { + el.CreateAttr("justify", (*m.Justify).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } + if m.Underline != nil { + el.CreateAttr("underline", (*m.Underline).String()) + } + if m.Overline != nil { + el.CreateAttr("overline", (*m.Overline).String()) + } + if m.LineThrough != nil { + el.CreateAttr("line-through", (*m.LineThrough).String()) + } + if m.Rotation != nil { + el.CreateAttr("rotation", (*m.Rotation).String()) + } + if m.LetterSpacing != nil { + el.CreateAttr("letter-spacing", (*m.LetterSpacing).String()) + } + if m.LineHeight != nil { + el.CreateAttr("line-height", (*m.LineHeight).String()) + } + if m.Dir != nil { + el.CreateAttr("dir", (*m.Dir).String()) + } + if m.Enclosure != nil { + el.CreateAttr("enclosure", (*m.Enclosure).String()) + } +} diff --git a/gen/test/go/mx/accord.go b/gen/test/go/mx/accord.go new file mode 100644 index 000000000..30c10ccf9 --- /dev/null +++ b/gen/test/go/mx/accord.go @@ -0,0 +1,74 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The accord type represents the tuning of a single string in the scordatura element. It uses the +// same group of elements as the staff-tuning element. Strings are numbered from high to low. +type Accord struct { + String *StringNumber // attribute "string" + Children []AccordChild // child elements in document order +} + +// AccordChild is one child element of Accord: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type AccordChild struct { + TuningStep *Step + TuningAlter *Semitones + TuningOctave *Octave +} + +func parseAccord(el *etree.Element) (*Accord, error) { + m := &Accord{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "string": + v := ParseStringNumber(a.Value) + m.String = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "tuning-step": + v := ParseStep(c.Text()) + m.Children = append(m.Children, AccordChild{TuningStep: &v}) + case "tuning-alter": + v := ParseSemitones(c.Text()) + m.Children = append(m.Children, AccordChild{TuningAlter: &v}) + case "tuning-octave": + v := ParseOctave(c.Text()) + m.Children = append(m.Children, AccordChild{TuningOctave: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeAccord(m *Accord, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.String != nil { + el.CreateAttr("string", (*m.String).String()) + } + for _, ch := range m.Children { + switch { + case ch.TuningStep != nil: + el.CreateElement("tuning-step").SetText((*ch.TuningStep).String()) + case ch.TuningAlter != nil: + el.CreateElement("tuning-alter").SetText((*ch.TuningAlter).String()) + case ch.TuningOctave != nil: + el.CreateElement("tuning-octave").SetText((*ch.TuningOctave).String()) + } + } +} diff --git a/gen/test/go/mx/accordion_registration.go b/gen/test/go/mx/accordion_registration.go new file mode 100644 index 000000000..4f323cc99 --- /dev/null +++ b/gen/test/go/mx/accordion_registration.go @@ -0,0 +1,160 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The accordion-registration type is use for accordion registration symbols. These are circular +// symbols divided horizontally into high, middle, and low sections that correspond to 4', 8', and +// 16' pipes. Each accordion-high, accordion-middle, and accordion-low element represents the +// presence of one or more dots in the registration diagram. An accordion-registration element needs +// to have at least one of the child elements present. +type AccordionRegistration struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" + ID *string // attribute "id" + Children []AccordionRegistrationChild // child elements in document order +} + +// AccordionRegistrationChild is one child element of AccordionRegistration: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type AccordionRegistrationChild struct { + AccordionHigh *Empty + AccordionMiddle *AccordionMiddle + AccordionLow *Empty +} + +func parseAccordionRegistration(el *etree.Element) (*AccordionRegistration, error) { + m := &AccordionRegistration{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "accordion-high": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, AccordionRegistrationChild{AccordionHigh: v}) + case "accordion-middle": + v := ParseAccordionMiddle(c.Text()) + m.Children = append(m.Children, AccordionRegistrationChild{AccordionMiddle: &v}) + case "accordion-low": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, AccordionRegistrationChild{AccordionLow: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeAccordionRegistration(m *AccordionRegistration, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.AccordionHigh != nil: + serializeEmpty(ch.AccordionHigh, el, "accordion-high") + case ch.AccordionMiddle != nil: + el.CreateElement("accordion-middle").SetText((*ch.AccordionMiddle).String()) + case ch.AccordionLow != nil: + serializeEmpty(ch.AccordionLow, el, "accordion-low") + } + } +} diff --git a/gen/test/go/mx/appearance.go b/gen/test/go/mx/appearance.go new file mode 100644 index 000000000..5d924f170 --- /dev/null +++ b/gen/test/go/mx/appearance.go @@ -0,0 +1,96 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The appearance type controls general graphical settings for the music's final form appearance on +// a printed page of display. This includes support for line widths, definitions for note sizes, and +// standard distances between notation elements, plus an extension element for other aspects of +// appearance. +type Appearance struct { + Children []AppearanceChild // child elements in document order +} + +// AppearanceChild is one child element of Appearance: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type AppearanceChild struct { + LineWidth *LineWidth + NoteSize *NoteSize + Distance *Distance + Glyph *Glyph + OtherAppearance *OtherAppearance +} + +func parseAppearance(el *etree.Element) (*Appearance, error) { + m := &Appearance{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "line-width": + v, err := parseLineWidth(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, AppearanceChild{LineWidth: v}) + case "note-size": + v, err := parseNoteSize(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, AppearanceChild{NoteSize: v}) + case "distance": + v, err := parseDistance(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, AppearanceChild{Distance: v}) + case "glyph": + v, err := parseGlyph(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, AppearanceChild{Glyph: v}) + case "other-appearance": + v, err := parseOtherAppearance(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, AppearanceChild{OtherAppearance: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeAppearance(m *Appearance, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.LineWidth != nil: + serializeLineWidth(ch.LineWidth, el, "line-width") + case ch.NoteSize != nil: + serializeNoteSize(ch.NoteSize, el, "note-size") + case ch.Distance != nil: + serializeDistance(ch.Distance, el, "distance") + case ch.Glyph != nil: + serializeGlyph(ch.Glyph, el, "glyph") + case ch.OtherAppearance != nil: + serializeOtherAppearance(ch.OtherAppearance, el, "other-appearance") + } + } +} diff --git a/gen/test/go/mx/arpeggiate.go b/gen/test/go/mx/arpeggiate.go new file mode 100644 index 000000000..2e783624c --- /dev/null +++ b/gen/test/go/mx/arpeggiate.go @@ -0,0 +1,99 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The arpeggiate type indicates that this note is part of an arpeggiated chord. The number +// attribute can be used to distinguish between two simultaneous chords arpeggiated separately +// (different numbers) or together (same number). The up-down attribute is used if there is an arrow +// on the arpeggio sign. By default, arpeggios go from the lowest to highest note. +type Arpeggiate struct { + Number *NumberLevel // attribute "number" + Direction *UpDown // attribute "direction" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + Placement *AboveBelow // attribute "placement" + Color *Color // attribute "color" + ID *string // attribute "id" +} + +func parseArpeggiate(el *etree.Element) (*Arpeggiate, error) { + m := &Arpeggiate{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "number": + v := ParseNumberLevel(a.Value) + m.Number = &v + case "direction": + v := ParseUpDown(a.Value) + m.Direction = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeArpeggiate(m *Arpeggiate, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.Direction != nil { + el.CreateAttr("direction", (*m.Direction).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/arrow.go b/gen/test/go/mx/arrow.go new file mode 100644 index 000000000..653ed35de --- /dev/null +++ b/gen/test/go/mx/arrow.go @@ -0,0 +1,156 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The arrow element represents an arrow used for a musical technical indication. It can represent +// both Unicode and SMuFL arrows. The presence of an arrowhead element indicates that only the +// arrowhead is displayed, not the arrow stem. The smufl attribute distinguishes different SMuFL +// glyphs that have an arrow appearance such as arrowBlackUp, guitarStrumUp, or handbellsSwingUp. +// The specified glyph should match the descriptive representation. +type Arrow struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" + SMUFL *SMUFLGlyphName // attribute "smufl" + Children []ArrowChild // child elements in document order +} + +// ArrowChild is one child element of Arrow: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type ArrowChild struct { + ArrowDirection *ArrowDirection + ArrowStyle *ArrowStyle + Arrowhead *Empty + CircularArrow *CircularArrow +} + +func parseArrow(el *etree.Element) (*Arrow, error) { + m := &Arrow{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "smufl": + v := ParseSMUFLGlyphName(a.Value) + m.SMUFL = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "arrow-direction": + v := ParseArrowDirection(c.Text()) + m.Children = append(m.Children, ArrowChild{ArrowDirection: &v}) + case "arrow-style": + v := ParseArrowStyle(c.Text()) + m.Children = append(m.Children, ArrowChild{ArrowStyle: &v}) + case "arrowhead": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ArrowChild{Arrowhead: v}) + case "circular-arrow": + v := ParseCircularArrow(c.Text()) + m.Children = append(m.Children, ArrowChild{CircularArrow: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeArrow(m *Arrow, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.SMUFL != nil { + el.CreateAttr("smufl", (*m.SMUFL).String()) + } + for _, ch := range m.Children { + switch { + case ch.ArrowDirection != nil: + el.CreateElement("arrow-direction").SetText((*ch.ArrowDirection).String()) + case ch.ArrowStyle != nil: + el.CreateElement("arrow-style").SetText((*ch.ArrowStyle).String()) + case ch.Arrowhead != nil: + serializeEmpty(ch.Arrowhead, el, "arrowhead") + case ch.CircularArrow != nil: + el.CreateElement("circular-arrow").SetText((*ch.CircularArrow).String()) + } + } +} diff --git a/gen/test/go/mx/articulations.go b/gen/test/go/mx/articulations.go new file mode 100644 index 000000000..4d7585e4d --- /dev/null +++ b/gen/test/go/mx/articulations.go @@ -0,0 +1,208 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Articulations and accents are grouped together here. +type Articulations struct { + ID *string // attribute "id" + Children []ArticulationsChild // child elements in document order +} + +// ArticulationsChild is one child element of Articulations: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type ArticulationsChild struct { + Accent *EmptyPlacement + StrongAccent *StrongAccent + Staccato *EmptyPlacement + Tenuto *EmptyPlacement + DetachedLegato *EmptyPlacement + Staccatissimo *EmptyPlacement + Spiccato *EmptyPlacement + Scoop *EmptyLine + Plop *EmptyLine + Doit *EmptyLine + Falloff *EmptyLine + BreathMark *BreathMark + Caesura *Caesura + Stress *EmptyPlacement + Unstress *EmptyPlacement + SoftAccent *EmptyPlacement + OtherArticulation *OtherPlacementText +} + +func parseArticulations(el *etree.Element) (*Articulations, error) { + m := &Articulations{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "accent": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ArticulationsChild{Accent: v}) + case "strong-accent": + v, err := parseStrongAccent(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ArticulationsChild{StrongAccent: v}) + case "staccato": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ArticulationsChild{Staccato: v}) + case "tenuto": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ArticulationsChild{Tenuto: v}) + case "detached-legato": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ArticulationsChild{DetachedLegato: v}) + case "staccatissimo": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ArticulationsChild{Staccatissimo: v}) + case "spiccato": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ArticulationsChild{Spiccato: v}) + case "scoop": + v, err := parseEmptyLine(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ArticulationsChild{Scoop: v}) + case "plop": + v, err := parseEmptyLine(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ArticulationsChild{Plop: v}) + case "doit": + v, err := parseEmptyLine(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ArticulationsChild{Doit: v}) + case "falloff": + v, err := parseEmptyLine(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ArticulationsChild{Falloff: v}) + case "breath-mark": + v, err := parseBreathMark(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ArticulationsChild{BreathMark: v}) + case "caesura": + v, err := parseCaesura(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ArticulationsChild{Caesura: v}) + case "stress": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ArticulationsChild{Stress: v}) + case "unstress": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ArticulationsChild{Unstress: v}) + case "soft-accent": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ArticulationsChild{SoftAccent: v}) + case "other-articulation": + v, err := parseOtherPlacementText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ArticulationsChild{OtherArticulation: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeArticulations(m *Articulations, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.Accent != nil: + serializeEmptyPlacement(ch.Accent, el, "accent") + case ch.StrongAccent != nil: + serializeStrongAccent(ch.StrongAccent, el, "strong-accent") + case ch.Staccato != nil: + serializeEmptyPlacement(ch.Staccato, el, "staccato") + case ch.Tenuto != nil: + serializeEmptyPlacement(ch.Tenuto, el, "tenuto") + case ch.DetachedLegato != nil: + serializeEmptyPlacement(ch.DetachedLegato, el, "detached-legato") + case ch.Staccatissimo != nil: + serializeEmptyPlacement(ch.Staccatissimo, el, "staccatissimo") + case ch.Spiccato != nil: + serializeEmptyPlacement(ch.Spiccato, el, "spiccato") + case ch.Scoop != nil: + serializeEmptyLine(ch.Scoop, el, "scoop") + case ch.Plop != nil: + serializeEmptyLine(ch.Plop, el, "plop") + case ch.Doit != nil: + serializeEmptyLine(ch.Doit, el, "doit") + case ch.Falloff != nil: + serializeEmptyLine(ch.Falloff, el, "falloff") + case ch.BreathMark != nil: + serializeBreathMark(ch.BreathMark, el, "breath-mark") + case ch.Caesura != nil: + serializeCaesura(ch.Caesura, el, "caesura") + case ch.Stress != nil: + serializeEmptyPlacement(ch.Stress, el, "stress") + case ch.Unstress != nil: + serializeEmptyPlacement(ch.Unstress, el, "unstress") + case ch.SoftAccent != nil: + serializeEmptyPlacement(ch.SoftAccent, el, "soft-accent") + case ch.OtherArticulation != nil: + serializeOtherPlacementText(ch.OtherArticulation, el, "other-articulation") + } + } +} diff --git a/gen/test/go/mx/attributes.go b/gen/test/go/mx/attributes.go new file mode 100644 index 000000000..e51ec9478 --- /dev/null +++ b/gen/test/go/mx/attributes.go @@ -0,0 +1,158 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The attributes element contains musical information that typically changes on measure boundaries. +// This includes key and time signatures, clefs, transpositions, and staving. When attributes are +// changed mid-measure, it affects the music in score order, not in MusicXML document order. +type Attributes struct { + Children []AttributesChild // child elements in document order +} + +// AttributesChild is one child element of Attributes: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type AttributesChild struct { + Footnote *FormattedText + Level *Level + Divisions *PositiveDivisions + Key *Key + Time *Time + Staves *int + PartSymbol *PartSymbol + Instruments *int + Clef *Clef + StaffDetails *StaffDetails + Transpose *Transpose + Directive *Directive + MeasureStyle *MeasureStyle +} + +func parseAttributes(el *etree.Element) (*Attributes, error) { + m := &Attributes{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "footnote": + v, err := parseFormattedText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, AttributesChild{Footnote: v}) + case "level": + v, err := parseLevel(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, AttributesChild{Level: v}) + case "divisions": + v := ParsePositiveDivisions(c.Text()) + m.Children = append(m.Children, AttributesChild{Divisions: &v}) + case "key": + v, err := parseKey(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, AttributesChild{Key: v}) + case "time": + v, err := parseTime(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, AttributesChild{Time: v}) + case "staves": + v := parseInt(c.Text()) + m.Children = append(m.Children, AttributesChild{Staves: &v}) + case "part-symbol": + v, err := parsePartSymbol(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, AttributesChild{PartSymbol: v}) + case "instruments": + v := parseInt(c.Text()) + m.Children = append(m.Children, AttributesChild{Instruments: &v}) + case "clef": + v, err := parseClef(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, AttributesChild{Clef: v}) + case "staff-details": + v, err := parseStaffDetails(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, AttributesChild{StaffDetails: v}) + case "transpose": + v, err := parseTranspose(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, AttributesChild{Transpose: v}) + case "directive": + v, err := parseDirective(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, AttributesChild{Directive: v}) + case "measure-style": + v, err := parseMeasureStyle(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, AttributesChild{MeasureStyle: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeAttributes(m *Attributes, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.Footnote != nil: + serializeFormattedText(ch.Footnote, el, "footnote") + case ch.Level != nil: + serializeLevel(ch.Level, el, "level") + case ch.Divisions != nil: + el.CreateElement("divisions").SetText((*ch.Divisions).String()) + case ch.Key != nil: + serializeKey(ch.Key, el, "key") + case ch.Time != nil: + serializeTime(ch.Time, el, "time") + case ch.Staves != nil: + el.CreateElement("staves").SetText(formatInt((*ch.Staves))) + case ch.PartSymbol != nil: + serializePartSymbol(ch.PartSymbol, el, "part-symbol") + case ch.Instruments != nil: + el.CreateElement("instruments").SetText(formatInt((*ch.Instruments))) + case ch.Clef != nil: + serializeClef(ch.Clef, el, "clef") + case ch.StaffDetails != nil: + serializeStaffDetails(ch.StaffDetails, el, "staff-details") + case ch.Transpose != nil: + serializeTranspose(ch.Transpose, el, "transpose") + case ch.Directive != nil: + serializeDirective(ch.Directive, el, "directive") + case ch.MeasureStyle != nil: + serializeMeasureStyle(ch.MeasureStyle, el, "measure-style") + } + } +} diff --git a/gen/test/go/mx/backup.go b/gen/test/go/mx/backup.go new file mode 100644 index 000000000..afb607156 --- /dev/null +++ b/gen/test/go/mx/backup.go @@ -0,0 +1,76 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The backup and forward elements are required to coordinate multiple voices in one part, including +// music on multiple staves. The backup type is generally used to move between voices and staves. +// Thus the backup element does not include voice or staff elements. Duration values should always +// be positive, and should not cross measure boundaries or mid-measure changes in the divisions +// value. +type Backup struct { + Children []BackupChild // child elements in document order +} + +// BackupChild is one child element of Backup: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type BackupChild struct { + Duration *PositiveDivisions + Footnote *FormattedText + Level *Level +} + +func parseBackup(el *etree.Element) (*Backup, error) { + m := &Backup{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "duration": + v := ParsePositiveDivisions(c.Text()) + m.Children = append(m.Children, BackupChild{Duration: &v}) + case "footnote": + v, err := parseFormattedText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, BackupChild{Footnote: v}) + case "level": + v, err := parseLevel(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, BackupChild{Level: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeBackup(m *Backup, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.Duration != nil: + el.CreateElement("duration").SetText((*ch.Duration).String()) + case ch.Footnote != nil: + serializeFormattedText(ch.Footnote, el, "footnote") + case ch.Level != nil: + serializeLevel(ch.Level, el, "level") + } + } +} diff --git a/gen/test/go/mx/bar_style_color.go b/gen/test/go/mx/bar_style_color.go new file mode 100644 index 000000000..4b1d47120 --- /dev/null +++ b/gen/test/go/mx/bar_style_color.go @@ -0,0 +1,43 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The bar-style-color type contains barline style and color information. +type BarStyleColor struct { + Color *Color // attribute "color" + Value BarStyle // text content +} + +func parseBarStyleColor(el *etree.Element) (*BarStyleColor, error) { + m := &BarStyleColor{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseBarStyle(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeBarStyleColor(m *BarStyleColor, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/barline.go b/gen/test/go/mx/barline.go new file mode 100644 index 000000000..054b8f955 --- /dev/null +++ b/gen/test/go/mx/barline.go @@ -0,0 +1,178 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// If a barline is other than a normal single barline, it should be represented by a barline type +// that describes it. This includes information about repeats and multiple endings, as well as line +// style. Barline data is on the same level as the other musical data in a score - a child of a +// measure in a partwise score, or a part in a timewise score. This allows for barlines within +// measures, as in dotted barlines that subdivide measures in complex meters. The two fermata +// elements allow for fermatas on both sides of the barline (the lower one inverted). Barlines have +// a location attribute to make it easier to process barlines independently of the other musical +// data in a score. It is often easier to set up measures separately from entering notes. The +// location attribute must match where the barline element occurs within the rest of the musical +// data in the score. If location is left, it should be the first element in the measure, aside from +// the print, bookmark, and link elements. If location is right, it should be the last element, +// again with the possible exception of the print, bookmark, and link elements. If no location is +// specified, the right barline is the default. The segno, coda, and divisions attributes work the +// same way as in the sound element. They are used for playback when barline elements contain segno +// or coda child elements. +type Barline struct { + Location *RightLeftMiddle // attribute "location" + SegnoSound *string // attribute "segno" + CodaSound *string // attribute "coda" + Divisions *Divisions // attribute "divisions" + ID *string // attribute "id" + Children []BarlineChild // child elements in document order +} + +// BarlineChild is one child element of Barline: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type BarlineChild struct { + BarStyle *BarStyleColor + Footnote *FormattedText + Level *Level + WavyLine *WavyLine + Segno *Segno + Coda *Coda + Fermata *Fermata + Ending *Ending + Repeat *Repeat +} + +func parseBarline(el *etree.Element) (*Barline, error) { + m := &Barline{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "location": + v := ParseRightLeftMiddle(a.Value) + m.Location = &v + case "segno": + v := a.Value + m.SegnoSound = &v + case "coda": + v := a.Value + m.CodaSound = &v + case "divisions": + v := ParseDivisions(a.Value) + m.Divisions = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "bar-style": + v, err := parseBarStyleColor(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, BarlineChild{BarStyle: v}) + case "footnote": + v, err := parseFormattedText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, BarlineChild{Footnote: v}) + case "level": + v, err := parseLevel(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, BarlineChild{Level: v}) + case "wavy-line": + v, err := parseWavyLine(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, BarlineChild{WavyLine: v}) + case "segno": + v, err := parseSegno(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, BarlineChild{Segno: v}) + case "coda": + v, err := parseCoda(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, BarlineChild{Coda: v}) + case "fermata": + v, err := parseFermata(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, BarlineChild{Fermata: v}) + case "ending": + v, err := parseEnding(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, BarlineChild{Ending: v}) + case "repeat": + v, err := parseRepeat(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, BarlineChild{Repeat: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeBarline(m *Barline, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Location != nil { + el.CreateAttr("location", (*m.Location).String()) + } + if m.SegnoSound != nil { + el.CreateAttr("segno", (*m.SegnoSound)) + } + if m.CodaSound != nil { + el.CreateAttr("coda", (*m.CodaSound)) + } + if m.Divisions != nil { + el.CreateAttr("divisions", (*m.Divisions).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.BarStyle != nil: + serializeBarStyleColor(ch.BarStyle, el, "bar-style") + case ch.Footnote != nil: + serializeFormattedText(ch.Footnote, el, "footnote") + case ch.Level != nil: + serializeLevel(ch.Level, el, "level") + case ch.WavyLine != nil: + serializeWavyLine(ch.WavyLine, el, "wavy-line") + case ch.Segno != nil: + serializeSegno(ch.Segno, el, "segno") + case ch.Coda != nil: + serializeCoda(ch.Coda, el, "coda") + case ch.Fermata != nil: + serializeFermata(ch.Fermata, el, "fermata") + case ch.Ending != nil: + serializeEnding(ch.Ending, el, "ending") + case ch.Repeat != nil: + serializeRepeat(ch.Repeat, el, "repeat") + } + } +} diff --git a/gen/test/go/mx/barre.go b/gen/test/go/mx/barre.go new file mode 100644 index 000000000..0dc02bc87 --- /dev/null +++ b/gen/test/go/mx/barre.go @@ -0,0 +1,49 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The barre element indicates placing a finger over multiple strings on a single fret. The type is +// "start" for the lowest pitched string (e.g., the string with the highest MusicXML number) and is +// "stop" for the highest pitched string. +type Barre struct { + Type *StartStop // attribute "type" + Color *Color // attribute "color" +} + +func parseBarre(el *etree.Element) (*Barre, error) { + m := &Barre{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStartStop(a.Value) + m.Type = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeBarre(m *Barre, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/bass.go b/gen/test/go/mx/bass.go new file mode 100644 index 000000000..3e5cb0dee --- /dev/null +++ b/gen/test/go/mx/bass.go @@ -0,0 +1,68 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The bass type is used to indicate a bass note in popular music chord symbols, e.g. G/C. It is +// generally not used in functional harmony, as inversion is generally not used in pop chord +// symbols. As with root, it is divided into step and alter elements, similar to pitches. +type Bass struct { + Children []BassChild // child elements in document order +} + +// BassChild is one child element of Bass: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type BassChild struct { + BassStep *BassStep + BassAlter *BassAlter +} + +func parseBass(el *etree.Element) (*Bass, error) { + m := &Bass{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "bass-step": + v, err := parseBassStep(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, BassChild{BassStep: v}) + case "bass-alter": + v, err := parseBassAlter(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, BassChild{BassAlter: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeBass(m *Bass, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.BassStep != nil: + serializeBassStep(ch.BassStep, el, "bass-step") + case ch.BassAlter != nil: + serializeBassAlter(ch.BassAlter, el, "bass-alter") + } + } +} diff --git a/gen/test/go/mx/bass_alter.go b/gen/test/go/mx/bass_alter.go new file mode 100644 index 000000000..4939edb1a --- /dev/null +++ b/gen/test/go/mx/bass_alter.go @@ -0,0 +1,117 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The bass-alter type represents the chromatic alteration of the bass of the current chord within +// the harmony element. In some chord styles, the text for the bass-step element may include +// bass-alter information. In that case, the print-object attribute of the bass-alter element can be +// set to no. The location attribute indicates whether the alteration should appear to the left or +// the right of the bass-step; it is right by default. +type BassAlter struct { + Location *LeftRight // attribute "location" + PrintObject *YesNo // attribute "print-object" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Value Semitones // text content +} + +func parseBassAlter(el *etree.Element) (*BassAlter, error) { + m := &BassAlter{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "location": + v := ParseLeftRight(a.Value) + m.Location = &v + case "print-object": + v := ParseYesNo(a.Value) + m.PrintObject = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseSemitones(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeBassAlter(m *BassAlter, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Location != nil { + el.CreateAttr("location", (*m.Location).String()) + } + if m.PrintObject != nil { + el.CreateAttr("print-object", (*m.PrintObject).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/bass_step.go b/gen/test/go/mx/bass_step.go new file mode 100644 index 000000000..f2219616e --- /dev/null +++ b/gen/test/go/mx/bass_step.go @@ -0,0 +1,108 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The bass-step type represents the pitch step of the bass of the current chord within the harmony +// element. The text attribute indicates how the bass should appear in a score if not using the +// element contents. +type BassStep struct { + Text *string // attribute "text" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Value Step // text content +} + +func parseBassStep(el *etree.Element) (*BassStep, error) { + m := &BassStep{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "text": + v := a.Value + m.Text = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseStep(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeBassStep(m *BassStep, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Text != nil { + el.CreateAttr("text", (*m.Text)) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/beam.go b/gen/test/go/mx/beam.go new file mode 100644 index 000000000..f60df24e9 --- /dev/null +++ b/gen/test/go/mx/beam.go @@ -0,0 +1,80 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Beam values include begin, continue, end, forward hook, and backward hook. Up to eight concurrent +// beams are available to cover up to 1024th notes. Each beam in a note is represented with a +// separate beam element, starting with the eighth note beam using a number attribute of 1. Note +// that the beam number does not distinguish sets of beams that overlap, as it does for slur and +// other elements. Beaming groups are distinguished by being in different voices and/or the presence +// or absence of grace and cue elements. Beams that have a begin value can also have a fan attribute +// to indicate accelerandos and ritardandos using fanned beams. The fan attribute may also be used +// with a continue value if the fanning direction changes on that note. The value is "none" if not +// specified. The repeater attribute has been deprecated in MusicXML 3.0. Formerly used for +// tremolos, it needs to be specified with a "yes" value for each beam using it. +type Beam struct { + Number *BeamLevel // attribute "number" + Repeater *YesNo // attribute "repeater" + Fan *Fan // attribute "fan" + Color *Color // attribute "color" + ID *string // attribute "id" + Value BeamValue // text content +} + +func parseBeam(el *etree.Element) (*Beam, error) { + m := &Beam{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "number": + v := ParseBeamLevel(a.Value) + m.Number = &v + case "repeater": + v := ParseYesNo(a.Value) + m.Repeater = &v + case "fan": + v := ParseFan(a.Value) + m.Fan = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseBeamValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeBeam(m *Beam, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.Repeater != nil { + el.CreateAttr("repeater", (*m.Repeater).String()) + } + if m.Fan != nil { + el.CreateAttr("fan", (*m.Fan).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/beat_repeat.go b/gen/test/go/mx/beat_repeat.go new file mode 100644 index 000000000..9709778bc --- /dev/null +++ b/gen/test/go/mx/beat_repeat.go @@ -0,0 +1,96 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The beat-repeat type is used to indicate that a single beat (but possibly many notes) is +// repeated. Both the start and stop of the beat being repeated should be specified. The slashes +// attribute specifies the number of slashes to use in the symbol. The use-dots attribute indicates +// whether or not to use dots as well (for instance, with mixed rhythm patterns). By default, the +// value for slashes is 1 and the value for use-dots is no. The beat-repeat element specifies a +// notation style for repetitions. The actual music being repeated needs to be repeated within the +// MusicXML file. This element specifies the notation that indicates the repeat. +type BeatRepeat struct { + Type *StartStop // attribute "type" + Slashes *int // attribute "slashes" + UseDots *YesNo // attribute "use-dots" + Children []BeatRepeatChild // child elements in document order +} + +// BeatRepeatChild is one child element of BeatRepeat: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type BeatRepeatChild struct { + SlashType *NoteTypeValue + SlashDot *Empty + ExceptVoice *string +} + +func parseBeatRepeat(el *etree.Element) (*BeatRepeat, error) { + m := &BeatRepeat{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStartStop(a.Value) + m.Type = &v + case "slashes": + v := parseInt(a.Value) + m.Slashes = &v + case "use-dots": + v := ParseYesNo(a.Value) + m.UseDots = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "slash-type": + v := ParseNoteTypeValue(c.Text()) + m.Children = append(m.Children, BeatRepeatChild{SlashType: &v}) + case "slash-dot": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, BeatRepeatChild{SlashDot: v}) + case "except-voice": + v := c.Text() + m.Children = append(m.Children, BeatRepeatChild{ExceptVoice: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeBeatRepeat(m *BeatRepeat, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Slashes != nil { + el.CreateAttr("slashes", formatInt((*m.Slashes))) + } + if m.UseDots != nil { + el.CreateAttr("use-dots", (*m.UseDots).String()) + } + for _, ch := range m.Children { + switch { + case ch.SlashType != nil: + el.CreateElement("slash-type").SetText((*ch.SlashType).String()) + case ch.SlashDot != nil: + serializeEmpty(ch.SlashDot, el, "slash-dot") + case ch.ExceptVoice != nil: + el.CreateElement("except-voice").SetText((*ch.ExceptVoice)) + } + } +} diff --git a/gen/test/go/mx/beat_unit_tied.go b/gen/test/go/mx/beat_unit_tied.go new file mode 100644 index 000000000..0a0678de9 --- /dev/null +++ b/gen/test/go/mx/beat_unit_tied.go @@ -0,0 +1,66 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The beat-unit-tied type indicates a beat-unit within a metronome mark that is tied to the +// preceding beat-unit. This allows or two or more tied notes to be associated with a per-minute +// value in a metronome mark, whereas the metronome-tied element is restricted to metric +// relationship marks. +type BeatUnitTied struct { + Children []BeatUnitTiedChild // child elements in document order +} + +// BeatUnitTiedChild is one child element of BeatUnitTied: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type BeatUnitTiedChild struct { + BeatUnit *NoteTypeValue + BeatUnitDot *Empty +} + +func parseBeatUnitTied(el *etree.Element) (*BeatUnitTied, error) { + m := &BeatUnitTied{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "beat-unit": + v := ParseNoteTypeValue(c.Text()) + m.Children = append(m.Children, BeatUnitTiedChild{BeatUnit: &v}) + case "beat-unit-dot": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, BeatUnitTiedChild{BeatUnitDot: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeBeatUnitTied(m *BeatUnitTied, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.BeatUnit != nil: + el.CreateElement("beat-unit").SetText((*ch.BeatUnit).String()) + case ch.BeatUnitDot != nil: + serializeEmpty(ch.BeatUnitDot, el, "beat-unit-dot") + } + } +} diff --git a/gen/test/go/mx/beater.go b/gen/test/go/mx/beater.go new file mode 100644 index 000000000..1a90f9616 --- /dev/null +++ b/gen/test/go/mx/beater.go @@ -0,0 +1,44 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The beater type represents pictograms for beaters, mallets, and sticks that do not have different +// materials represented in the pictogram. +type Beater struct { + Tip *TipDirection // attribute "tip" + Value BeaterValue // text content +} + +func parseBeater(el *etree.Element) (*Beater, error) { + m := &Beater{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "tip": + v := ParseTipDirection(a.Value) + m.Tip = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseBeaterValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeBeater(m *Beater, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Tip != nil { + el.CreateAttr("tip", (*m.Tip).String()) + } +} diff --git a/gen/test/go/mx/bend.go b/gen/test/go/mx/bend.go new file mode 100644 index 000000000..f0e9cb6b5 --- /dev/null +++ b/gen/test/go/mx/bend.go @@ -0,0 +1,177 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The bend type is used in guitar and tablature. The bend-alter element indicates the number of +// steps in the bend, similar to the alter element. As with the alter element, numbers like 0.5 can +// be used to indicate microtones. Negative numbers indicate pre-bends or releases; the pre-bend and +// release elements are used to distinguish what is intended. A with-bar element indicates that the +// bend is to be done at the bridge with a whammy or vibrato bar. The content of the element +// indicates how this should be notated. +type Bend struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Accelerate *YesNo // attribute "accelerate" + Beats *TrillBeats // attribute "beats" + FirstBeat *Percent // attribute "first-beat" + LastBeat *Percent // attribute "last-beat" + Children []BendChild // child elements in document order +} + +// BendChild is one child element of Bend: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type BendChild struct { + BendAlter *Semitones + PreBend *Empty + Release *Empty + WithBar *PlacementText +} + +func parseBend(el *etree.Element) (*Bend, error) { + m := &Bend{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "accelerate": + v := ParseYesNo(a.Value) + m.Accelerate = &v + case "beats": + v := ParseTrillBeats(a.Value) + m.Beats = &v + case "first-beat": + v := ParsePercent(a.Value) + m.FirstBeat = &v + case "last-beat": + v := ParsePercent(a.Value) + m.LastBeat = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "bend-alter": + v := ParseSemitones(c.Text()) + m.Children = append(m.Children, BendChild{BendAlter: &v}) + case "pre-bend": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, BendChild{PreBend: v}) + case "release": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, BendChild{Release: v}) + case "with-bar": + v, err := parsePlacementText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, BendChild{WithBar: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeBend(m *Bend, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Accelerate != nil { + el.CreateAttr("accelerate", (*m.Accelerate).String()) + } + if m.Beats != nil { + el.CreateAttr("beats", (*m.Beats).String()) + } + if m.FirstBeat != nil { + el.CreateAttr("first-beat", (*m.FirstBeat).String()) + } + if m.LastBeat != nil { + el.CreateAttr("last-beat", (*m.LastBeat).String()) + } + for _, ch := range m.Children { + switch { + case ch.BendAlter != nil: + el.CreateElement("bend-alter").SetText((*ch.BendAlter).String()) + case ch.PreBend != nil: + serializeEmpty(ch.PreBend, el, "pre-bend") + case ch.Release != nil: + serializeEmpty(ch.Release, el, "release") + case ch.WithBar != nil: + serializePlacementText(ch.WithBar, el, "with-bar") + } + } +} diff --git a/gen/test/go/mx/bookmark.go b/gen/test/go/mx/bookmark.go new file mode 100644 index 000000000..d50a9e358 --- /dev/null +++ b/gen/test/go/mx/bookmark.go @@ -0,0 +1,61 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The bookmark type serves as a well-defined target for an incoming simple XLink. +type Bookmark struct { + ID *string // attribute "id" + Name *string // attribute "name" + Element *string // attribute "element" + Position *int // attribute "position" +} + +func parseBookmark(el *etree.Element) (*Bookmark, error) { + m := &Bookmark{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "id": + v := a.Value + m.ID = &v + case "name": + v := a.Value + m.Name = &v + case "element": + v := a.Value + m.Element = &v + case "position": + v := parseInt(a.Value) + m.Position = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeBookmark(m *Bookmark, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + if m.Name != nil { + el.CreateAttr("name", (*m.Name)) + } + if m.Element != nil { + el.CreateAttr("element", (*m.Element)) + } + if m.Position != nil { + el.CreateAttr("position", formatInt((*m.Position))) + } +} diff --git a/gen/test/go/mx/bracket.go b/gen/test/go/mx/bracket.go new file mode 100644 index 000000000..1cecc8e7a --- /dev/null +++ b/gen/test/go/mx/bracket.go @@ -0,0 +1,127 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Brackets are combined with words in a variety of modern directions. The line-end attribute +// specifies if there is a jog up or down (or both), an arrow, or nothing at the start or end of the +// bracket. If the line-end is up or down, the length of the jog can be specified using the +// end-length attribute. The line-type is solid by default. +type Bracket struct { + Type *StartStopContinue // attribute "type" + Number *NumberLevel // attribute "number" + LineEnd *LineEnd // attribute "line-end" + EndLength *Tenths // attribute "end-length" + LineType *LineType // attribute "line-type" + DashLength *Tenths // attribute "dash-length" + SpaceLength *Tenths // attribute "space-length" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + Color *Color // attribute "color" + ID *string // attribute "id" +} + +func parseBracket(el *etree.Element) (*Bracket, error) { + m := &Bracket{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStartStopContinue(a.Value) + m.Type = &v + case "number": + v := ParseNumberLevel(a.Value) + m.Number = &v + case "line-end": + v := ParseLineEnd(a.Value) + m.LineEnd = &v + case "end-length": + v := ParseTenths(a.Value) + m.EndLength = &v + case "line-type": + v := ParseLineType(a.Value) + m.LineType = &v + case "dash-length": + v := ParseTenths(a.Value) + m.DashLength = &v + case "space-length": + v := ParseTenths(a.Value) + m.SpaceLength = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeBracket(m *Bracket, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.LineEnd != nil { + el.CreateAttr("line-end", (*m.LineEnd).String()) + } + if m.EndLength != nil { + el.CreateAttr("end-length", (*m.EndLength).String()) + } + if m.LineType != nil { + el.CreateAttr("line-type", (*m.LineType).String()) + } + if m.DashLength != nil { + el.CreateAttr("dash-length", (*m.DashLength).String()) + } + if m.SpaceLength != nil { + el.CreateAttr("space-length", (*m.SpaceLength).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/breath_mark.go b/gen/test/go/mx/breath_mark.go new file mode 100644 index 000000000..d6062ccf4 --- /dev/null +++ b/gen/test/go/mx/breath_mark.go @@ -0,0 +1,106 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The breath-mark element indicates a place to take a breath. +type BreathMark struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" + Value BreathMarkValue // text content +} + +func parseBreathMark(el *etree.Element) (*BreathMark, error) { + m := &BreathMark{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseBreathMarkValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeBreathMark(m *BreathMark, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } +} diff --git a/gen/test/go/mx/caesura.go b/gen/test/go/mx/caesura.go new file mode 100644 index 000000000..6c63b15d0 --- /dev/null +++ b/gen/test/go/mx/caesura.go @@ -0,0 +1,107 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The caesura element indicates a slight pause. It is notated using a "railroad tracks" symbol or +// other variations specified in the element content. +type Caesura struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" + Value CaesuraValue // text content +} + +func parseCaesura(el *etree.Element) (*Caesura, error) { + m := &Caesura{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseCaesuraValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeCaesura(m *Caesura, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } +} diff --git a/gen/test/go/mx/cancel.go b/gen/test/go/mx/cancel.go new file mode 100644 index 000000000..aa4423eca --- /dev/null +++ b/gen/test/go/mx/cancel.go @@ -0,0 +1,47 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// A cancel element indicates that the old key signature should be cancelled before the new one +// appears. This will always happen when changing to C major or A minor and need not be specified +// then. The cancel value matches the fifths value of the cancelled key signature (e.g., a cancel of +// -2 will provide an explicit cancellation for changing from B flat major to F major). The optional +// location attribute indicates where the cancellation appears relative to the new key signature. +type Cancel struct { + Location *CancelLocation // attribute "location" + Value Fifths // text content +} + +func parseCancel(el *etree.Element) (*Cancel, error) { + m := &Cancel{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "location": + v := ParseCancelLocation(a.Value) + m.Location = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseFifths(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeCancel(m *Cancel, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Location != nil { + el.CreateAttr("location", (*m.Location).String()) + } +} diff --git a/gen/test/go/mx/clef.go b/gen/test/go/mx/clef.go new file mode 100644 index 000000000..dfa387243 --- /dev/null +++ b/gen/test/go/mx/clef.go @@ -0,0 +1,181 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Clefs are represented by a combination of sign, line, and clef-octave-change elements. The +// optional number attribute refers to staff numbers within the part. A value of 1 is assumed if not +// present. Sometimes clefs are added to the staff in non-standard line positions, either to +// indicate cue passages, or when there are multiple clefs present simultaneously on one staff. In +// this situation, the additional attribute is set to "yes" and the line value is ignored. The size +// attribute is used for clefs where the additional attribute is "yes". It is typically used to +// indicate cue clefs. Sometimes clefs at the start of a measure need to appear after the barline +// rather than before, as for cues or for use after a repeated section. The after-barline attribute +// is set to "yes" in this situation. The attribute is ignored for mid-measure clefs. Clefs appear +// at the start of each system unless the print-object attribute has been set to "no" or the +// additional attribute has been set to "yes". +type Clef struct { + Number *StaffNumber // attribute "number" + Additional *YesNo // attribute "additional" + Size *SymbolSize // attribute "size" + AfterBarline *YesNo // attribute "after-barline" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + PrintObject *YesNo // attribute "print-object" + ID *string // attribute "id" + Children []ClefChild // child elements in document order +} + +// ClefChild is one child element of Clef: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type ClefChild struct { + Sign *ClefSign + Line *StaffLine + ClefOctaveChange *int +} + +func parseClef(el *etree.Element) (*Clef, error) { + m := &Clef{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "number": + v := ParseStaffNumber(a.Value) + m.Number = &v + case "additional": + v := ParseYesNo(a.Value) + m.Additional = &v + case "size": + v := ParseSymbolSize(a.Value) + m.Size = &v + case "after-barline": + v := ParseYesNo(a.Value) + m.AfterBarline = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "print-object": + v := ParseYesNo(a.Value) + m.PrintObject = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "sign": + v := ParseClefSign(c.Text()) + m.Children = append(m.Children, ClefChild{Sign: &v}) + case "line": + v := ParseStaffLine(c.Text()) + m.Children = append(m.Children, ClefChild{Line: &v}) + case "clef-octave-change": + v := parseInt(c.Text()) + m.Children = append(m.Children, ClefChild{ClefOctaveChange: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeClef(m *Clef, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.Additional != nil { + el.CreateAttr("additional", (*m.Additional).String()) + } + if m.Size != nil { + el.CreateAttr("size", (*m.Size).String()) + } + if m.AfterBarline != nil { + el.CreateAttr("after-barline", (*m.AfterBarline).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.PrintObject != nil { + el.CreateAttr("print-object", (*m.PrintObject).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.Sign != nil: + el.CreateElement("sign").SetText((*ch.Sign).String()) + case ch.Line != nil: + el.CreateElement("line").SetText((*ch.Line).String()) + case ch.ClefOctaveChange != nil: + el.CreateElement("clef-octave-change").SetText(formatInt((*ch.ClefOctaveChange))) + } + } +} diff --git a/gen/test/go/mx/coda.go b/gen/test/go/mx/coda.go new file mode 100644 index 000000000..8c0f69aab --- /dev/null +++ b/gen/test/go/mx/coda.go @@ -0,0 +1,125 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The coda type is the visual indicator of a coda sign. The exact glyph can be specified with the +// smufl attribute. A sound element is also needed to guide playback applications reliably. +type Coda struct { + SMUFL *SMUFLCodaGlyphName // attribute "smufl" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" + ID *string // attribute "id" +} + +func parseCoda(el *etree.Element) (*Coda, error) { + m := &Coda{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "smufl": + v := ParseSMUFLCodaGlyphName(a.Value) + m.SMUFL = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeCoda(m *Coda, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.SMUFL != nil { + el.CreateAttr("smufl", (*m.SMUFL).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/credit.go b/gen/test/go/mx/credit.go new file mode 100644 index 000000000..6d27d3a73 --- /dev/null +++ b/gen/test/go/mx/credit.go @@ -0,0 +1,127 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The credit type represents the appearance of the title, composer, arranger, lyricist, copyright, +// dedication, and other text, symbols, and graphics that commonly appear on the first page of a +// score. The credit-words, credit-symbol, and credit-image elements are similar to the words, +// symbol, and image elements for directions. However, since the credit is not part of a measure, +// the default-x and default-y attributes adjust the origin relative to the bottom left-hand corner +// of the page. The enclosure for credit-words and credit-symbol is none by default. By default, a +// series of credit-words and credit-symbol elements within a single credit element follow one +// another in sequence visually. Non-positional formatting attributes are carried over from the +// previous element by default. The page attribute for the credit element specifies the page number +// where the credit should appear. This is an integer value that starts with 1 for the first page. +// Its value is 1 by default. Since credits occur before the music, these page numbers do not refer +// to the page numbering specified by the print element's page-number attribute. The credit-type +// element indicates the purpose behind a credit. Multiple types of data may be combined in a single +// credit, so multiple elements may be used. Standard values include page number, title, subtitle, +// composer, arranger, lyricist, and rights. +type Credit struct { + Page *int // attribute "page" + ID *string // attribute "id" + Children []CreditChild // child elements in document order +} + +// CreditChild is one child element of Credit: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type CreditChild struct { + CreditType *string + Link *Link + Bookmark *Bookmark + CreditImage *Image + CreditWords *FormattedTextID + CreditSymbol *FormattedSymbolID +} + +func parseCredit(el *etree.Element) (*Credit, error) { + m := &Credit{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "page": + v := parseInt(a.Value) + m.Page = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "credit-type": + v := c.Text() + m.Children = append(m.Children, CreditChild{CreditType: &v}) + case "link": + v, err := parseLink(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, CreditChild{Link: v}) + case "bookmark": + v, err := parseBookmark(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, CreditChild{Bookmark: v}) + case "credit-image": + v, err := parseImage(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, CreditChild{CreditImage: v}) + case "credit-words": + v, err := parseFormattedTextID(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, CreditChild{CreditWords: v}) + case "credit-symbol": + v, err := parseFormattedSymbolID(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, CreditChild{CreditSymbol: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeCredit(m *Credit, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Page != nil { + el.CreateAttr("page", formatInt((*m.Page))) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.CreditType != nil: + el.CreateElement("credit-type").SetText((*ch.CreditType)) + case ch.Link != nil: + serializeLink(ch.Link, el, "link") + case ch.Bookmark != nil: + serializeBookmark(ch.Bookmark, el, "bookmark") + case ch.CreditImage != nil: + serializeImage(ch.CreditImage, el, "credit-image") + case ch.CreditWords != nil: + serializeFormattedTextID(ch.CreditWords, el, "credit-words") + case ch.CreditSymbol != nil: + serializeFormattedSymbolID(ch.CreditSymbol, el, "credit-symbol") + } + } +} diff --git a/gen/test/go/mx/dashes.go b/gen/test/go/mx/dashes.go new file mode 100644 index 000000000..46558fee6 --- /dev/null +++ b/gen/test/go/mx/dashes.go @@ -0,0 +1,103 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The dashes type represents dashes, used for instance with cresc. and dim. marks. +type Dashes struct { + Type *StartStopContinue // attribute "type" + Number *NumberLevel // attribute "number" + DashLength *Tenths // attribute "dash-length" + SpaceLength *Tenths // attribute "space-length" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + Color *Color // attribute "color" + ID *string // attribute "id" +} + +func parseDashes(el *etree.Element) (*Dashes, error) { + m := &Dashes{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStartStopContinue(a.Value) + m.Type = &v + case "number": + v := ParseNumberLevel(a.Value) + m.Number = &v + case "dash-length": + v := ParseTenths(a.Value) + m.DashLength = &v + case "space-length": + v := ParseTenths(a.Value) + m.SpaceLength = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeDashes(m *Dashes, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.DashLength != nil { + el.CreateAttr("dash-length", (*m.DashLength).String()) + } + if m.SpaceLength != nil { + el.CreateAttr("space-length", (*m.SpaceLength).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/defaults.go b/gen/test/go/mx/defaults.go new file mode 100644 index 000000000..31e8d7298 --- /dev/null +++ b/gen/test/go/mx/defaults.go @@ -0,0 +1,129 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The defaults type specifies score-wide defaults for scaling, layout, and appearance. +type Defaults struct { + Children []DefaultsChild // child elements in document order +} + +// DefaultsChild is one child element of Defaults: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type DefaultsChild struct { + Scaling *Scaling + PageLayout *PageLayout + SystemLayout *SystemLayout + StaffLayout *StaffLayout + Appearance *Appearance + MusicFont *EmptyFont + WordFont *EmptyFont + LyricFont *LyricFont + LyricLanguage *LyricLanguage +} + +func parseDefaults(el *etree.Element) (*Defaults, error) { + m := &Defaults{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "scaling": + v, err := parseScaling(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DefaultsChild{Scaling: v}) + case "page-layout": + v, err := parsePageLayout(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DefaultsChild{PageLayout: v}) + case "system-layout": + v, err := parseSystemLayout(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DefaultsChild{SystemLayout: v}) + case "staff-layout": + v, err := parseStaffLayout(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DefaultsChild{StaffLayout: v}) + case "appearance": + v, err := parseAppearance(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DefaultsChild{Appearance: v}) + case "music-font": + v, err := parseEmptyFont(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DefaultsChild{MusicFont: v}) + case "word-font": + v, err := parseEmptyFont(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DefaultsChild{WordFont: v}) + case "lyric-font": + v, err := parseLyricFont(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DefaultsChild{LyricFont: v}) + case "lyric-language": + v, err := parseLyricLanguage(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DefaultsChild{LyricLanguage: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeDefaults(m *Defaults, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.Scaling != nil: + serializeScaling(ch.Scaling, el, "scaling") + case ch.PageLayout != nil: + serializePageLayout(ch.PageLayout, el, "page-layout") + case ch.SystemLayout != nil: + serializeSystemLayout(ch.SystemLayout, el, "system-layout") + case ch.StaffLayout != nil: + serializeStaffLayout(ch.StaffLayout, el, "staff-layout") + case ch.Appearance != nil: + serializeAppearance(ch.Appearance, el, "appearance") + case ch.MusicFont != nil: + serializeEmptyFont(ch.MusicFont, el, "music-font") + case ch.WordFont != nil: + serializeEmptyFont(ch.WordFont, el, "word-font") + case ch.LyricFont != nil: + serializeLyricFont(ch.LyricFont, el, "lyric-font") + case ch.LyricLanguage != nil: + serializeLyricLanguage(ch.LyricLanguage, el, "lyric-language") + } + } +} diff --git a/gen/test/go/mx/degree.go b/gen/test/go/mx/degree.go new file mode 100644 index 000000000..c01b0e30b --- /dev/null +++ b/gen/test/go/mx/degree.go @@ -0,0 +1,87 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The degree type is used to add, alter, or subtract individual notes in the chord. The +// print-object attribute can be used to keep the degree from printing separately when it has +// already taken into account in the text attribute of the kind element. The degree-value and +// degree-type text attributes specify how the value and type of the degree should be displayed. A +// harmony of kind "other" can be spelled explicitly by using a series of degree elements together +// with a root. +type Degree struct { + PrintObject *YesNo // attribute "print-object" + Children []DegreeChild // child elements in document order +} + +// DegreeChild is one child element of Degree: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type DegreeChild struct { + DegreeValue *DegreeValue + DegreeAlter *DegreeAlter + DegreeType *DegreeType +} + +func parseDegree(el *etree.Element) (*Degree, error) { + m := &Degree{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "print-object": + v := ParseYesNo(a.Value) + m.PrintObject = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "degree-value": + v, err := parseDegreeValue(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DegreeChild{DegreeValue: v}) + case "degree-alter": + v, err := parseDegreeAlter(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DegreeChild{DegreeAlter: v}) + case "degree-type": + v, err := parseDegreeType(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DegreeChild{DegreeType: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeDegree(m *Degree, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.PrintObject != nil { + el.CreateAttr("print-object", (*m.PrintObject).String()) + } + for _, ch := range m.Children { + switch { + case ch.DegreeValue != nil: + serializeDegreeValue(ch.DegreeValue, el, "degree-value") + case ch.DegreeAlter != nil: + serializeDegreeAlter(ch.DegreeAlter, el, "degree-alter") + case ch.DegreeType != nil: + serializeDegreeType(ch.DegreeType, el, "degree-type") + } + } +} diff --git a/gen/test/go/mx/degree_alter.go b/gen/test/go/mx/degree_alter.go new file mode 100644 index 000000000..1e87a0ca9 --- /dev/null +++ b/gen/test/go/mx/degree_alter.go @@ -0,0 +1,111 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The degree-alter type represents the chromatic alteration for the current degree. If the +// degree-type value is alter or subtract, the degree-alter value is relative to the degree already +// in the chord based on its kind element. If the degree-type value is add, the degree-alter is +// relative to a dominant chord (major and perfect intervals except for a minor seventh). The +// plus-minus attribute is used to indicate if plus and minus symbols should be used instead of +// sharp and flat symbols to display the degree alteration; it is no by default. +type DegreeAlter struct { + PlusMinus *YesNo // attribute "plus-minus" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Value Semitones // text content +} + +func parseDegreeAlter(el *etree.Element) (*DegreeAlter, error) { + m := &DegreeAlter{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "plus-minus": + v := ParseYesNo(a.Value) + m.PlusMinus = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseSemitones(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeDegreeAlter(m *DegreeAlter, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.PlusMinus != nil { + el.CreateAttr("plus-minus", (*m.PlusMinus).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/degree_type.go b/gen/test/go/mx/degree_type.go new file mode 100644 index 000000000..23442e254 --- /dev/null +++ b/gen/test/go/mx/degree_type.go @@ -0,0 +1,109 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The degree-type type indicates if this degree is an addition, alteration, or subtraction relative +// to the kind of the current chord. The value of the degree-type element affects the interpretation +// of the value of the degree-alter element. The text attribute specifies how the type of the degree +// should be displayed in a score. +type DegreeType struct { + Text *string // attribute "text" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Value DegreeTypeValue // text content +} + +func parseDegreeType(el *etree.Element) (*DegreeType, error) { + m := &DegreeType{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "text": + v := a.Value + m.Text = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseDegreeTypeValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeDegreeType(m *DegreeType, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Text != nil { + el.CreateAttr("text", (*m.Text)) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/degree_value.go b/gen/test/go/mx/degree_value.go new file mode 100644 index 000000000..3fb2f53f0 --- /dev/null +++ b/gen/test/go/mx/degree_value.go @@ -0,0 +1,117 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The content of the degree-value type is a number indicating the degree of the chord (1 for the +// root, 3 for third, etc). The text attribute specifies how the type of the degree should be +// displayed in a score. The degree-value symbol attribute indicates that a symbol should be used in +// specifying the degree. If the symbol attribute is present, the value of the text attribute +// follows the symbol. +type DegreeValue struct { + Symbol *DegreeSymbolValue // attribute "symbol" + Text *string // attribute "text" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Value int // text content +} + +func parseDegreeValue(el *etree.Element) (*DegreeValue, error) { + m := &DegreeValue{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "symbol": + v := ParseDegreeSymbolValue(a.Value) + m.Symbol = &v + case "text": + v := a.Value + m.Text = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = parseInt(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeDegreeValue(m *DegreeValue, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(formatInt(m.Value)) + if m.Symbol != nil { + el.CreateAttr("symbol", (*m.Symbol).String()) + } + if m.Text != nil { + el.CreateAttr("text", (*m.Text)) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/direction.go b/gen/test/go/mx/direction.go new file mode 100644 index 000000000..3d3245db5 --- /dev/null +++ b/gen/test/go/mx/direction.go @@ -0,0 +1,133 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// A direction is a musical indication that is not necessarily attached to a specific note. Two or +// more may be combined to indicate starts and stops of wedges, dashes, etc. For applications where +// a specific direction is indeed attached to a specific note, the direction element can be +// associated with the note element that follows it in score order that is not in a different voice. +// By default, a series of direction-type elements and a series of child elements of a +// direction-type within a single direction element follow one another in sequence visually. For a +// series of direction-type children, non-positional formatting attributes are carried over from the +// previous element by default. +type Direction struct { + Placement *AboveBelow // attribute "placement" + Directive *YesNo // attribute "directive" + ID *string // attribute "id" + Children []DirectionChild // child elements in document order +} + +// DirectionChild is one child element of Direction: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type DirectionChild struct { + DirectionType *DirectionType + Offset *Offset + Footnote *FormattedText + Level *Level + Voice *string + Staff *int + Sound *Sound +} + +func parseDirection(el *etree.Element) (*Direction, error) { + m := &Direction{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "directive": + v := ParseYesNo(a.Value) + m.Directive = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "direction-type": + v, err := parseDirectionType(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionChild{DirectionType: v}) + case "offset": + v, err := parseOffset(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionChild{Offset: v}) + case "footnote": + v, err := parseFormattedText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionChild{Footnote: v}) + case "level": + v, err := parseLevel(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionChild{Level: v}) + case "voice": + v := c.Text() + m.Children = append(m.Children, DirectionChild{Voice: &v}) + case "staff": + v := parseInt(c.Text()) + m.Children = append(m.Children, DirectionChild{Staff: &v}) + case "sound": + v, err := parseSound(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionChild{Sound: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeDirection(m *Direction, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.Directive != nil { + el.CreateAttr("directive", (*m.Directive).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.DirectionType != nil: + serializeDirectionType(ch.DirectionType, el, "direction-type") + case ch.Offset != nil: + serializeOffset(ch.Offset, el, "offset") + case ch.Footnote != nil: + serializeFormattedText(ch.Footnote, el, "footnote") + case ch.Level != nil: + serializeLevel(ch.Level, el, "level") + case ch.Voice != nil: + el.CreateElement("voice").SetText((*ch.Voice)) + case ch.Staff != nil: + el.CreateElement("staff").SetText(formatInt((*ch.Staff))) + case ch.Sound != nil: + serializeSound(ch.Sound, el, "sound") + } + } +} diff --git a/gen/test/go/mx/direction_type.go b/gen/test/go/mx/direction_type.go new file mode 100644 index 000000000..4d5ae18ac --- /dev/null +++ b/gen/test/go/mx/direction_type.go @@ -0,0 +1,273 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Textual direction types may have more than 1 component due to multiple fonts. The dynamics +// element may also be used in the notations element. Attribute groups related to print suggestions +// apply to the individual direction-type, not to the overall direction. +type DirectionType struct { + ID *string // attribute "id" + Children []DirectionTypeChild // child elements in document order +} + +// DirectionTypeChild is one child element of DirectionType: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type DirectionTypeChild struct { + Rehearsal *FormattedTextID + Segno *Segno + Coda *Coda + Words *FormattedTextID + Symbol *FormattedSymbolID + Wedge *Wedge + Dynamics *Dynamics + Dashes *Dashes + Bracket *Bracket + Pedal *Pedal + Metronome *Metronome + OctaveShift *OctaveShift + HarpPedals *HarpPedals + Damp *EmptyPrintStyleAlignID + DampAll *EmptyPrintStyleAlignID + Eyeglasses *EmptyPrintStyleAlignID + StringMute *StringMute + Scordatura *Scordatura + Image *Image + PrincipalVoice *PrincipalVoice + Percussion *Percussion + AccordionRegistration *AccordionRegistration + StaffDivide *StaffDivide + OtherDirection *OtherDirection +} + +func parseDirectionType(el *etree.Element) (*DirectionType, error) { + m := &DirectionType{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "rehearsal": + v, err := parseFormattedTextID(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{Rehearsal: v}) + case "segno": + v, err := parseSegno(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{Segno: v}) + case "coda": + v, err := parseCoda(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{Coda: v}) + case "words": + v, err := parseFormattedTextID(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{Words: v}) + case "symbol": + v, err := parseFormattedSymbolID(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{Symbol: v}) + case "wedge": + v, err := parseWedge(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{Wedge: v}) + case "dynamics": + v, err := parseDynamics(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{Dynamics: v}) + case "dashes": + v, err := parseDashes(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{Dashes: v}) + case "bracket": + v, err := parseBracket(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{Bracket: v}) + case "pedal": + v, err := parsePedal(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{Pedal: v}) + case "metronome": + v, err := parseMetronome(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{Metronome: v}) + case "octave-shift": + v, err := parseOctaveShift(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{OctaveShift: v}) + case "harp-pedals": + v, err := parseHarpPedals(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{HarpPedals: v}) + case "damp": + v, err := parseEmptyPrintStyleAlignID(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{Damp: v}) + case "damp-all": + v, err := parseEmptyPrintStyleAlignID(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{DampAll: v}) + case "eyeglasses": + v, err := parseEmptyPrintStyleAlignID(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{Eyeglasses: v}) + case "string-mute": + v, err := parseStringMute(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{StringMute: v}) + case "scordatura": + v, err := parseScordatura(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{Scordatura: v}) + case "image": + v, err := parseImage(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{Image: v}) + case "principal-voice": + v, err := parsePrincipalVoice(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{PrincipalVoice: v}) + case "percussion": + v, err := parsePercussion(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{Percussion: v}) + case "accordion-registration": + v, err := parseAccordionRegistration(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{AccordionRegistration: v}) + case "staff-divide": + v, err := parseStaffDivide(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{StaffDivide: v}) + case "other-direction": + v, err := parseOtherDirection(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DirectionTypeChild{OtherDirection: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeDirectionType(m *DirectionType, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.Rehearsal != nil: + serializeFormattedTextID(ch.Rehearsal, el, "rehearsal") + case ch.Segno != nil: + serializeSegno(ch.Segno, el, "segno") + case ch.Coda != nil: + serializeCoda(ch.Coda, el, "coda") + case ch.Words != nil: + serializeFormattedTextID(ch.Words, el, "words") + case ch.Symbol != nil: + serializeFormattedSymbolID(ch.Symbol, el, "symbol") + case ch.Wedge != nil: + serializeWedge(ch.Wedge, el, "wedge") + case ch.Dynamics != nil: + serializeDynamics(ch.Dynamics, el, "dynamics") + case ch.Dashes != nil: + serializeDashes(ch.Dashes, el, "dashes") + case ch.Bracket != nil: + serializeBracket(ch.Bracket, el, "bracket") + case ch.Pedal != nil: + serializePedal(ch.Pedal, el, "pedal") + case ch.Metronome != nil: + serializeMetronome(ch.Metronome, el, "metronome") + case ch.OctaveShift != nil: + serializeOctaveShift(ch.OctaveShift, el, "octave-shift") + case ch.HarpPedals != nil: + serializeHarpPedals(ch.HarpPedals, el, "harp-pedals") + case ch.Damp != nil: + serializeEmptyPrintStyleAlignID(ch.Damp, el, "damp") + case ch.DampAll != nil: + serializeEmptyPrintStyleAlignID(ch.DampAll, el, "damp-all") + case ch.Eyeglasses != nil: + serializeEmptyPrintStyleAlignID(ch.Eyeglasses, el, "eyeglasses") + case ch.StringMute != nil: + serializeStringMute(ch.StringMute, el, "string-mute") + case ch.Scordatura != nil: + serializeScordatura(ch.Scordatura, el, "scordatura") + case ch.Image != nil: + serializeImage(ch.Image, el, "image") + case ch.PrincipalVoice != nil: + serializePrincipalVoice(ch.PrincipalVoice, el, "principal-voice") + case ch.Percussion != nil: + serializePercussion(ch.Percussion, el, "percussion") + case ch.AccordionRegistration != nil: + serializeAccordionRegistration(ch.AccordionRegistration, el, "accordion-registration") + case ch.StaffDivide != nil: + serializeStaffDivide(ch.StaffDivide, el, "staff-divide") + case ch.OtherDirection != nil: + serializeOtherDirection(ch.OtherDirection, el, "other-direction") + } + } +} diff --git a/gen/test/go/mx/directive.go b/gen/test/go/mx/directive.go new file mode 100644 index 000000000..7f74c50ca --- /dev/null +++ b/gen/test/go/mx/directive.go @@ -0,0 +1,105 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +type Directive struct { + XMLLang *string // attribute "xml:lang" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Value string // text content +} + +func parseDirective(el *etree.Element) (*Directive, error) { + m := &Directive{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "xml:lang": + v := a.Value + m.XMLLang = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeDirective(m *Directive, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.XMLLang != nil { + el.CreateAttr("xml:lang", (*m.XMLLang)) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/distance.go b/gen/test/go/mx/distance.go new file mode 100644 index 000000000..2254e4569 --- /dev/null +++ b/gen/test/go/mx/distance.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The distance element represents standard distances between notation elements in tenths. The type +// attribute defines what type of distance is being defined. Valid values include hyphen (for +// hyphens in lyrics) and beam. +type Distance struct { + Type *DistanceType // attribute "type" + Value Tenths // text content +} + +func parseDistance(el *etree.Element) (*Distance, error) { + m := &Distance{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseDistanceType(a.Value) + m.Type = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseTenths(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeDistance(m *Distance, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } +} diff --git a/gen/test/go/mx/document.go b/gen/test/go/mx/document.go new file mode 100644 index 000000000..eb02247e3 --- /dev/null +++ b/gen/test/go/mx/document.go @@ -0,0 +1,70 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// ExtraAttr preserves an attribute outside the schema (namespace +// declarations on the document root). +type ExtraAttr struct { + Key string + Value string +} + +// Document is a parsed MusicXML document: exactly one root is set. +type Document struct { + ScorePartwise *ScorePartwise + ScoreTimewise *ScoreTimewise + RootNamespaces []ExtraAttr +} + +// FromXDoc parses an etree document into the typed MusicXML model. +func FromXDoc(doc *etree.Document) (*Document, error) { + root := doc.Root() + if root == nil { + return nil, fmt.Errorf("document has no root element") + } + d := &Document{} + for _, a := range root.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + d.RootNamespaces = append(d.RootNamespaces, ExtraAttr{a.FullKey(), a.Value}) + } + } + switch root.Tag { + case "score-partwise": + v, err := parseScorePartwise(root) + if err != nil { + return nil, err + } + d.ScorePartwise = v + case "score-timewise": + v, err := parseScoreTimewise(root) + if err != nil { + return nil, err + } + d.ScoreTimewise = v + default: + return nil, fmt.Errorf("unknown root element <%s>", root.Tag) + } + return d, nil +} + +// ToXDoc serializes the typed model back to an etree document. +func ToXDoc(d *Document) (*etree.Document, error) { + doc := etree.NewDocument() + switch { + case d.ScorePartwise != nil: + serializeScorePartwise(d.ScorePartwise, &doc.Element, "score-partwise") + case d.ScoreTimewise != nil: + serializeScoreTimewise(d.ScoreTimewise, &doc.Element, "score-timewise") + default: + return nil, fmt.Errorf("document has no root") + } + for _, a := range d.RootNamespaces { + doc.Root().CreateAttr(a.Key, a.Value) + } + return doc, nil +} diff --git a/gen/test/go/mx/dynamics.go b/gen/test/go/mx/dynamics.go new file mode 100644 index 000000000..1f28fe118 --- /dev/null +++ b/gen/test/go/mx/dynamics.go @@ -0,0 +1,421 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Dynamics can be associated either with a note or a general musical direction. To avoid +// inconsistencies between and amongst the letter abbreviations for dynamics (what is sf vs. sfz, +// standing alone or with a trailing dynamic that is not always piano), we use the actual letters as +// the names of these dynamic elements. The other-dynamics element allows other dynamic marks that +// are not covered here, but many of those should perhaps be included in a more general musical +// direction element. Dynamics elements may also be combined to create marks not covered by a single +// element, such as sfmp. These letter dynamic symbols are separated from crescendo, decrescendo, +// and wedge indications. Dynamic representation is inconsistent in scores. Many things are assumed +// by the composer and left out, such as returns to original dynamics. Systematic representations +// are quite complex: for example, Humdrum has at least 3 representation formats related to +// dynamics. The MusicXML format captures what is in the score, but does not try to be optimal for +// analysis or synthesis of dynamics. +type Dynamics struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" + Placement *AboveBelow // attribute "placement" + Underline *NumberOfLines // attribute "underline" + Overline *NumberOfLines // attribute "overline" + LineThrough *NumberOfLines // attribute "line-through" + Enclosure *EnclosureShape // attribute "enclosure" + ID *string // attribute "id" + Children []DynamicsChild // child elements in document order +} + +// DynamicsChild is one child element of Dynamics: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type DynamicsChild struct { + P *Empty + Pp *Empty + Ppp *Empty + Pppp *Empty + Ppppp *Empty + Pppppp *Empty + F *Empty + Ff *Empty + Fff *Empty + Ffff *Empty + Fffff *Empty + Ffffff *Empty + Mp *Empty + Mf *Empty + Sf *Empty + Sfp *Empty + Sfpp *Empty + Fp *Empty + Rf *Empty + Rfz *Empty + Sfz *Empty + Sffz *Empty + Fz *Empty + N *Empty + Pf *Empty + Sfzp *Empty + OtherDynamics *OtherText +} + +func parseDynamics(el *etree.Element) (*Dynamics, error) { + m := &Dynamics{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "underline": + v := ParseNumberOfLines(a.Value) + m.Underline = &v + case "overline": + v := ParseNumberOfLines(a.Value) + m.Overline = &v + case "line-through": + v := ParseNumberOfLines(a.Value) + m.LineThrough = &v + case "enclosure": + v := ParseEnclosureShape(a.Value) + m.Enclosure = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "p": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{P: v}) + case "pp": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Pp: v}) + case "ppp": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Ppp: v}) + case "pppp": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Pppp: v}) + case "ppppp": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Ppppp: v}) + case "pppppp": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Pppppp: v}) + case "f": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{F: v}) + case "ff": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Ff: v}) + case "fff": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Fff: v}) + case "ffff": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Ffff: v}) + case "fffff": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Fffff: v}) + case "ffffff": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Ffffff: v}) + case "mp": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Mp: v}) + case "mf": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Mf: v}) + case "sf": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Sf: v}) + case "sfp": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Sfp: v}) + case "sfpp": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Sfpp: v}) + case "fp": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Fp: v}) + case "rf": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Rf: v}) + case "rfz": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Rfz: v}) + case "sfz": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Sfz: v}) + case "sffz": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Sffz: v}) + case "fz": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Fz: v}) + case "n": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{N: v}) + case "pf": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Pf: v}) + case "sfzp": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{Sfzp: v}) + case "other-dynamics": + v, err := parseOtherText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, DynamicsChild{OtherDynamics: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeDynamics(m *Dynamics, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.Underline != nil { + el.CreateAttr("underline", (*m.Underline).String()) + } + if m.Overline != nil { + el.CreateAttr("overline", (*m.Overline).String()) + } + if m.LineThrough != nil { + el.CreateAttr("line-through", (*m.LineThrough).String()) + } + if m.Enclosure != nil { + el.CreateAttr("enclosure", (*m.Enclosure).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.P != nil: + serializeEmpty(ch.P, el, "p") + case ch.Pp != nil: + serializeEmpty(ch.Pp, el, "pp") + case ch.Ppp != nil: + serializeEmpty(ch.Ppp, el, "ppp") + case ch.Pppp != nil: + serializeEmpty(ch.Pppp, el, "pppp") + case ch.Ppppp != nil: + serializeEmpty(ch.Ppppp, el, "ppppp") + case ch.Pppppp != nil: + serializeEmpty(ch.Pppppp, el, "pppppp") + case ch.F != nil: + serializeEmpty(ch.F, el, "f") + case ch.Ff != nil: + serializeEmpty(ch.Ff, el, "ff") + case ch.Fff != nil: + serializeEmpty(ch.Fff, el, "fff") + case ch.Ffff != nil: + serializeEmpty(ch.Ffff, el, "ffff") + case ch.Fffff != nil: + serializeEmpty(ch.Fffff, el, "fffff") + case ch.Ffffff != nil: + serializeEmpty(ch.Ffffff, el, "ffffff") + case ch.Mp != nil: + serializeEmpty(ch.Mp, el, "mp") + case ch.Mf != nil: + serializeEmpty(ch.Mf, el, "mf") + case ch.Sf != nil: + serializeEmpty(ch.Sf, el, "sf") + case ch.Sfp != nil: + serializeEmpty(ch.Sfp, el, "sfp") + case ch.Sfpp != nil: + serializeEmpty(ch.Sfpp, el, "sfpp") + case ch.Fp != nil: + serializeEmpty(ch.Fp, el, "fp") + case ch.Rf != nil: + serializeEmpty(ch.Rf, el, "rf") + case ch.Rfz != nil: + serializeEmpty(ch.Rfz, el, "rfz") + case ch.Sfz != nil: + serializeEmpty(ch.Sfz, el, "sfz") + case ch.Sffz != nil: + serializeEmpty(ch.Sffz, el, "sffz") + case ch.Fz != nil: + serializeEmpty(ch.Fz, el, "fz") + case ch.N != nil: + serializeEmpty(ch.N, el, "n") + case ch.Pf != nil: + serializeEmpty(ch.Pf, el, "pf") + case ch.Sfzp != nil: + serializeEmpty(ch.Sfzp, el, "sfzp") + case ch.OtherDynamics != nil: + serializeOtherText(ch.OtherDynamics, el, "other-dynamics") + } + } +} diff --git a/gen/test/go/mx/elision.go b/gen/test/go/mx/elision.go new file mode 100644 index 000000000..67751b84e --- /dev/null +++ b/gen/test/go/mx/elision.go @@ -0,0 +1,84 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The elision type represents an elision between lyric syllables. The text content specifies the +// symbol used to display the elision. Common values are a no-break space (Unicode 00A0), an +// underscore (Unicode 005F), or an undertie (Unicode 203F). If the text content is empty, the smufl +// attribute is used to specify the symbol to use. Its value is a SMuFL canonical glyph name that +// starts with lyrics. The SMuFL attribute is ignored if the elision glyph is already specified by +// the text content. If neither text content nor a smufl attribute are present, the elision glyph is +// application-specific. +type Elision struct { + SMUFL *SMUFLLyricsGlyphName // attribute "smufl" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Value string // text content +} + +func parseElision(el *etree.Element) (*Elision, error) { + m := &Elision{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "smufl": + v := ParseSMUFLLyricsGlyphName(a.Value) + m.SMUFL = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeElision(m *Elision, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.SMUFL != nil { + el.CreateAttr("smufl", (*m.SMUFL).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/empty.go b/gen/test/go/mx/empty.go new file mode 100644 index 000000000..ed4cd1dea --- /dev/null +++ b/gen/test/go/mx/empty.go @@ -0,0 +1,34 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The empty type represents an empty element with no attributes. +type Empty struct { +} + +func parseEmpty(el *etree.Element) (*Empty, error) { + m := &Empty{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeEmpty(m *Empty, parent *etree.Element, tag string) { + _ = m + parent.CreateElement(tag) +} diff --git a/gen/test/go/mx/empty_font.go b/gen/test/go/mx/empty_font.go new file mode 100644 index 000000000..0398e4d99 --- /dev/null +++ b/gen/test/go/mx/empty_font.go @@ -0,0 +1,61 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The empty-font type represents an empty element with font attributes. +type EmptyFont struct { + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" +} + +func parseEmptyFont(el *etree.Element) (*EmptyFont, error) { + m := &EmptyFont{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeEmptyFont(m *EmptyFont, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } +} diff --git a/gen/test/go/mx/empty_line.go b/gen/test/go/mx/empty_line.go new file mode 100644 index 000000000..9e4d81437 --- /dev/null +++ b/gen/test/go/mx/empty_line.go @@ -0,0 +1,139 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The empty-line type represents an empty element with line-shape, line-type, line-length, +// dashed-formatting, print-style and placement attributes. +type EmptyLine struct { + LineShape *LineShape // attribute "line-shape" + LineType *LineType // attribute "line-type" + LineLength *LineLength // attribute "line-length" + DashLength *Tenths // attribute "dash-length" + SpaceLength *Tenths // attribute "space-length" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" +} + +func parseEmptyLine(el *etree.Element) (*EmptyLine, error) { + m := &EmptyLine{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "line-shape": + v := ParseLineShape(a.Value) + m.LineShape = &v + case "line-type": + v := ParseLineType(a.Value) + m.LineType = &v + case "line-length": + v := ParseLineLength(a.Value) + m.LineLength = &v + case "dash-length": + v := ParseTenths(a.Value) + m.DashLength = &v + case "space-length": + v := ParseTenths(a.Value) + m.SpaceLength = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeEmptyLine(m *EmptyLine, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.LineShape != nil { + el.CreateAttr("line-shape", (*m.LineShape).String()) + } + if m.LineType != nil { + el.CreateAttr("line-type", (*m.LineType).String()) + } + if m.LineLength != nil { + el.CreateAttr("line-length", (*m.LineLength).String()) + } + if m.DashLength != nil { + el.CreateAttr("dash-length", (*m.DashLength).String()) + } + if m.SpaceLength != nil { + el.CreateAttr("space-length", (*m.SpaceLength).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } +} diff --git a/gen/test/go/mx/empty_placement.go b/gen/test/go/mx/empty_placement.go new file mode 100644 index 000000000..019435924 --- /dev/null +++ b/gen/test/go/mx/empty_placement.go @@ -0,0 +1,103 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The empty-placement type represents an empty element with print-style and placement attributes. +type EmptyPlacement struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" +} + +func parseEmptyPlacement(el *etree.Element) (*EmptyPlacement, error) { + m := &EmptyPlacement{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeEmptyPlacement(m *EmptyPlacement, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } +} diff --git a/gen/test/go/mx/empty_placement_smufl.go b/gen/test/go/mx/empty_placement_smufl.go new file mode 100644 index 000000000..abdf84e0a --- /dev/null +++ b/gen/test/go/mx/empty_placement_smufl.go @@ -0,0 +1,111 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The empty-placement-smufl type represents an empty element with print-style, placement, and smufl +// attributes. +type EmptyPlacementSMUFL struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" + SMUFL *SMUFLGlyphName // attribute "smufl" +} + +func parseEmptyPlacementSMUFL(el *etree.Element) (*EmptyPlacementSMUFL, error) { + m := &EmptyPlacementSMUFL{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "smufl": + v := ParseSMUFLGlyphName(a.Value) + m.SMUFL = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeEmptyPlacementSMUFL(m *EmptyPlacementSMUFL, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.SMUFL != nil { + el.CreateAttr("smufl", (*m.SMUFL).String()) + } +} diff --git a/gen/test/go/mx/empty_print_object_style_align.go b/gen/test/go/mx/empty_print_object_style_align.go new file mode 100644 index 000000000..a20f83cd2 --- /dev/null +++ b/gen/test/go/mx/empty_print_object_style_align.go @@ -0,0 +1,118 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The empty-print-style-align-object type represents an empty element with print-object and +// print-style-align attribute groups. +type EmptyPrintObjectStyleAlign struct { + PrintObject *YesNo // attribute "print-object" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" +} + +func parseEmptyPrintObjectStyleAlign(el *etree.Element) (*EmptyPrintObjectStyleAlign, error) { + m := &EmptyPrintObjectStyleAlign{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "print-object": + v := ParseYesNo(a.Value) + m.PrintObject = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeEmptyPrintObjectStyleAlign(m *EmptyPrintObjectStyleAlign, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.PrintObject != nil { + el.CreateAttr("print-object", (*m.PrintObject).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } +} diff --git a/gen/test/go/mx/empty_print_style_align_id.go b/gen/test/go/mx/empty_print_style_align_id.go new file mode 100644 index 000000000..c8447454f --- /dev/null +++ b/gen/test/go/mx/empty_print_style_align_id.go @@ -0,0 +1,118 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The empty-print-style-align-id type represents an empty element with print-style-align and +// optional-unique-id attribute groups. +type EmptyPrintStyleAlignID struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" + ID *string // attribute "id" +} + +func parseEmptyPrintStyleAlignID(el *etree.Element) (*EmptyPrintStyleAlignID, error) { + m := &EmptyPrintStyleAlignID{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeEmptyPrintStyleAlignID(m *EmptyPrintStyleAlignID, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/empty_trill_sound.go b/gen/test/go/mx/empty_trill_sound.go new file mode 100644 index 000000000..9866c9174 --- /dev/null +++ b/gen/test/go/mx/empty_trill_sound.go @@ -0,0 +1,153 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The empty-trill-sound type represents an empty element with print-style, placement, and +// trill-sound attributes. +type EmptyTrillSound struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" + StartNote *StartNote // attribute "start-note" + TrillStep *TrillStep // attribute "trill-step" + TwoNoteTurn *TwoNoteTurn // attribute "two-note-turn" + Accelerate *YesNo // attribute "accelerate" + Beats *TrillBeats // attribute "beats" + SecondBeat *Percent // attribute "second-beat" + LastBeat *Percent // attribute "last-beat" +} + +func parseEmptyTrillSound(el *etree.Element) (*EmptyTrillSound, error) { + m := &EmptyTrillSound{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "start-note": + v := ParseStartNote(a.Value) + m.StartNote = &v + case "trill-step": + v := ParseTrillStep(a.Value) + m.TrillStep = &v + case "two-note-turn": + v := ParseTwoNoteTurn(a.Value) + m.TwoNoteTurn = &v + case "accelerate": + v := ParseYesNo(a.Value) + m.Accelerate = &v + case "beats": + v := ParseTrillBeats(a.Value) + m.Beats = &v + case "second-beat": + v := ParsePercent(a.Value) + m.SecondBeat = &v + case "last-beat": + v := ParsePercent(a.Value) + m.LastBeat = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeEmptyTrillSound(m *EmptyTrillSound, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.StartNote != nil { + el.CreateAttr("start-note", (*m.StartNote).String()) + } + if m.TrillStep != nil { + el.CreateAttr("trill-step", (*m.TrillStep).String()) + } + if m.TwoNoteTurn != nil { + el.CreateAttr("two-note-turn", (*m.TwoNoteTurn).String()) + } + if m.Accelerate != nil { + el.CreateAttr("accelerate", (*m.Accelerate).String()) + } + if m.Beats != nil { + el.CreateAttr("beats", (*m.Beats).String()) + } + if m.SecondBeat != nil { + el.CreateAttr("second-beat", (*m.SecondBeat).String()) + } + if m.LastBeat != nil { + el.CreateAttr("last-beat", (*m.LastBeat).String()) + } +} diff --git a/gen/test/go/mx/encoding.go b/gen/test/go/mx/encoding.go new file mode 100644 index 000000000..00856a3e2 --- /dev/null +++ b/gen/test/go/mx/encoding.go @@ -0,0 +1,87 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The encoding element contains information about who did the digital encoding, when, with what +// software, and in what aspects. Standard type values for the encoder element are music, words, and +// arrangement, but other types may be used. The type attribute is only needed when there are +// multiple encoder elements. +type Encoding struct { + Children []EncodingChild // child elements in document order +} + +// EncodingChild is one child element of Encoding: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type EncodingChild struct { + EncodingDate *YyyyMmDd + Encoder *TypedText + Software *string + EncodingDescription *string + Supports *Supports +} + +func parseEncoding(el *etree.Element) (*Encoding, error) { + m := &Encoding{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "encoding-date": + v := ParseYyyyMmDd(c.Text()) + m.Children = append(m.Children, EncodingChild{EncodingDate: &v}) + case "encoder": + v, err := parseTypedText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, EncodingChild{Encoder: v}) + case "software": + v := c.Text() + m.Children = append(m.Children, EncodingChild{Software: &v}) + case "encoding-description": + v := c.Text() + m.Children = append(m.Children, EncodingChild{EncodingDescription: &v}) + case "supports": + v, err := parseSupports(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, EncodingChild{Supports: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeEncoding(m *Encoding, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.EncodingDate != nil: + el.CreateElement("encoding-date").SetText((*ch.EncodingDate).String()) + case ch.Encoder != nil: + serializeTypedText(ch.Encoder, el, "encoder") + case ch.Software != nil: + el.CreateElement("software").SetText((*ch.Software)) + case ch.EncodingDescription != nil: + el.CreateElement("encoding-description").SetText((*ch.EncodingDescription)) + case ch.Supports != nil: + serializeSupports(ch.Supports, el, "supports") + } + } +} diff --git a/gen/test/go/mx/ending.go b/gen/test/go/mx/ending.go new file mode 100644 index 000000000..8dcc8c524 --- /dev/null +++ b/gen/test/go/mx/ending.go @@ -0,0 +1,153 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The ending type represents multiple (e.g. first and second) endings. Typically, the start type is +// associated with the left barline of the first measure in an ending. The stop and discontinue +// types are associated with the right barline of the last measure in an ending. Stop is used when +// the ending mark concludes with a downward jog, as is typical for first endings. Discontinue is +// used when there is no downward jog, as is typical for second endings that do not conclude a +// piece. The length of the jog can be specified using the end-length attribute. The text-x and +// text-y attributes are offsets that specify where the baseline of the start of the ending text +// appears, relative to the start of the ending line. The number attribute reflects the numeric +// values of what is under the ending line. Single endings such as "1" or comma-separated multiple +// endings such as "1,2" may be used. The ending element text is used when the text displayed in the +// ending is different than what appears in the number attribute. The print-object element is used +// to indicate when an ending is present but not printed, as is often the case for many parts in a +// full score. +type Ending struct { + Number *EndingNumber // attribute "number" + Type *StartStopDiscontinue // attribute "type" + EndLength *Tenths // attribute "end-length" + TextX *Tenths // attribute "text-x" + TextY *Tenths // attribute "text-y" + PrintObject *YesNo // attribute "print-object" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Value string // text content +} + +func parseEnding(el *etree.Element) (*Ending, error) { + m := &Ending{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "number": + v := ParseEndingNumber(a.Value) + m.Number = &v + case "type": + v := ParseStartStopDiscontinue(a.Value) + m.Type = &v + case "end-length": + v := ParseTenths(a.Value) + m.EndLength = &v + case "text-x": + v := ParseTenths(a.Value) + m.TextX = &v + case "text-y": + v := ParseTenths(a.Value) + m.TextY = &v + case "print-object": + v := ParseYesNo(a.Value) + m.PrintObject = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeEnding(m *Ending, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.EndLength != nil { + el.CreateAttr("end-length", (*m.EndLength).String()) + } + if m.TextX != nil { + el.CreateAttr("text-x", (*m.TextX).String()) + } + if m.TextY != nil { + el.CreateAttr("text-y", (*m.TextY).String()) + } + if m.PrintObject != nil { + el.CreateAttr("print-object", (*m.PrintObject).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/extend.go b/gen/test/go/mx/extend.go new file mode 100644 index 000000000..981e86675 --- /dev/null +++ b/gen/test/go/mx/extend.go @@ -0,0 +1,77 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The extend type represents lyric word extension / melisma lines as well as figured bass +// extensions. The optional type and position attributes are added in Version 3.0 to provide better +// formatting control. +type Extend struct { + Type *StartStopContinue // attribute "type" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + Color *Color // attribute "color" +} + +func parseExtend(el *etree.Element) (*Extend, error) { + m := &Extend{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStartStopContinue(a.Value) + m.Type = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeExtend(m *Extend, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/feature.go b/gen/test/go/mx/feature.go new file mode 100644 index 000000000..53aa93287 --- /dev/null +++ b/gen/test/go/mx/feature.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The feature type is a part of the grouping element used for musical analysis. The type attribute +// represents the type of the feature and the element content represents its value. This type is +// flexible to allow for different analyses. +type Feature struct { + Type *string // attribute "type" + Value string // text content +} + +func parseFeature(el *etree.Element) (*Feature, error) { + m := &Feature{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := a.Value + m.Type = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeFeature(m *Feature, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.Type != nil { + el.CreateAttr("type", (*m.Type)) + } +} diff --git a/gen/test/go/mx/fermata.go b/gen/test/go/mx/fermata.go new file mode 100644 index 000000000..e17b12a6c --- /dev/null +++ b/gen/test/go/mx/fermata.go @@ -0,0 +1,114 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The fermata text content represents the shape of the fermata sign. An empty fermata element +// represents a normal fermata. The fermata type is upright if not specified. +type Fermata struct { + Type *UprightInverted // attribute "type" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + ID *string // attribute "id" + Value FermataShape // text content +} + +func parseFermata(el *etree.Element) (*Fermata, error) { + m := &Fermata{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseUprightInverted(a.Value) + m.Type = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseFermataShape(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeFermata(m *Fermata, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/figure.go b/gen/test/go/mx/figure.go new file mode 100644 index 000000000..33fd23a24 --- /dev/null +++ b/gen/test/go/mx/figure.go @@ -0,0 +1,102 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The figure type represents a single figure within a figured-bass element. +type Figure struct { + Children []FigureChild // child elements in document order +} + +// FigureChild is one child element of Figure: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type FigureChild struct { + Prefix *StyleText + FigureNumber *StyleText + Suffix *StyleText + Extend *Extend + Footnote *FormattedText + Level *Level +} + +func parseFigure(el *etree.Element) (*Figure, error) { + m := &Figure{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "prefix": + v, err := parseStyleText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, FigureChild{Prefix: v}) + case "figure-number": + v, err := parseStyleText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, FigureChild{FigureNumber: v}) + case "suffix": + v, err := parseStyleText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, FigureChild{Suffix: v}) + case "extend": + v, err := parseExtend(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, FigureChild{Extend: v}) + case "footnote": + v, err := parseFormattedText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, FigureChild{Footnote: v}) + case "level": + v, err := parseLevel(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, FigureChild{Level: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeFigure(m *Figure, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.Prefix != nil: + serializeStyleText(ch.Prefix, el, "prefix") + case ch.FigureNumber != nil: + serializeStyleText(ch.FigureNumber, el, "figure-number") + case ch.Suffix != nil: + serializeStyleText(ch.Suffix, el, "suffix") + case ch.Extend != nil: + serializeExtend(ch.Extend, el, "extend") + case ch.Footnote != nil: + serializeFormattedText(ch.Footnote, el, "footnote") + case ch.Level != nil: + serializeLevel(ch.Level, el, "level") + } + } +} diff --git a/gen/test/go/mx/figured_bass.go b/gen/test/go/mx/figured_bass.go new file mode 100644 index 000000000..19652f768 --- /dev/null +++ b/gen/test/go/mx/figured_bass.go @@ -0,0 +1,189 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The figured-bass element represents figured bass notation. Figured bass elements take their +// position from the first regular note (not a grace note or chord note) that follows in score +// order. The optional duration element is used to indicate changes of figures under a note. Figures +// are ordered from top to bottom. The value of parentheses is "no" if not present. +type FiguredBass struct { + Parentheses *YesNo // attribute "parentheses" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + PrintDot *YesNo // attribute "print-dot" + PrintLyric *YesNo // attribute "print-lyric" + PrintObject *YesNo // attribute "print-object" + PrintSpacing *YesNo // attribute "print-spacing" + ID *string // attribute "id" + Children []FiguredBassChild // child elements in document order +} + +// FiguredBassChild is one child element of FiguredBass: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type FiguredBassChild struct { + Figure *Figure + Duration *PositiveDivisions + Footnote *FormattedText + Level *Level +} + +func parseFiguredBass(el *etree.Element) (*FiguredBass, error) { + m := &FiguredBass{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "parentheses": + v := ParseYesNo(a.Value) + m.Parentheses = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "print-dot": + v := ParseYesNo(a.Value) + m.PrintDot = &v + case "print-lyric": + v := ParseYesNo(a.Value) + m.PrintLyric = &v + case "print-object": + v := ParseYesNo(a.Value) + m.PrintObject = &v + case "print-spacing": + v := ParseYesNo(a.Value) + m.PrintSpacing = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "figure": + v, err := parseFigure(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, FiguredBassChild{Figure: v}) + case "duration": + v := ParsePositiveDivisions(c.Text()) + m.Children = append(m.Children, FiguredBassChild{Duration: &v}) + case "footnote": + v, err := parseFormattedText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, FiguredBassChild{Footnote: v}) + case "level": + v, err := parseLevel(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, FiguredBassChild{Level: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeFiguredBass(m *FiguredBass, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Parentheses != nil { + el.CreateAttr("parentheses", (*m.Parentheses).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.PrintDot != nil { + el.CreateAttr("print-dot", (*m.PrintDot).String()) + } + if m.PrintLyric != nil { + el.CreateAttr("print-lyric", (*m.PrintLyric).String()) + } + if m.PrintObject != nil { + el.CreateAttr("print-object", (*m.PrintObject).String()) + } + if m.PrintSpacing != nil { + el.CreateAttr("print-spacing", (*m.PrintSpacing).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.Figure != nil: + serializeFigure(ch.Figure, el, "figure") + case ch.Duration != nil: + el.CreateElement("duration").SetText((*ch.Duration).String()) + case ch.Footnote != nil: + serializeFormattedText(ch.Footnote, el, "footnote") + case ch.Level != nil: + serializeLevel(ch.Level, el, "level") + } + } +} diff --git a/gen/test/go/mx/fingering.go b/gen/test/go/mx/fingering.go new file mode 100644 index 000000000..e6555f24f --- /dev/null +++ b/gen/test/go/mx/fingering.go @@ -0,0 +1,123 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Fingering is typically indicated 1,2,3,4,5. Multiple fingerings may be given, typically to +// substitute fingerings in the middle of a note. The substitution and alternate values are "no" if +// the attribute is not present. For guitar and other fretted instruments, the fingering element +// represents the fretting finger; the pluck element represents the plucking finger. +type Fingering struct { + Substitution *YesNo // attribute "substitution" + Alternate *YesNo // attribute "alternate" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" + Value string // text content +} + +func parseFingering(el *etree.Element) (*Fingering, error) { + m := &Fingering{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "substitution": + v := ParseYesNo(a.Value) + m.Substitution = &v + case "alternate": + v := ParseYesNo(a.Value) + m.Alternate = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeFingering(m *Fingering, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.Substitution != nil { + el.CreateAttr("substitution", (*m.Substitution).String()) + } + if m.Alternate != nil { + el.CreateAttr("alternate", (*m.Alternate).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } +} diff --git a/gen/test/go/mx/first_fret.go b/gen/test/go/mx/first_fret.go new file mode 100644 index 000000000..c1e7b17aa --- /dev/null +++ b/gen/test/go/mx/first_fret.go @@ -0,0 +1,53 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The first-fret type indicates which fret is shown in the top space of the frame; it is fret 1 if +// the element is not present. The optional text attribute indicates how this is represented in the +// fret diagram, while the location attribute indicates whether the text appears to the left or +// right of the frame. +type FirstFret struct { + Text *string // attribute "text" + Location *LeftRight // attribute "location" + Value int // text content +} + +func parseFirstFret(el *etree.Element) (*FirstFret, error) { + m := &FirstFret{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "text": + v := a.Value + m.Text = &v + case "location": + v := ParseLeftRight(a.Value) + m.Location = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = parseInt(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeFirstFret(m *FirstFret, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(formatInt(m.Value)) + if m.Text != nil { + el.CreateAttr("text", (*m.Text)) + } + if m.Location != nil { + el.CreateAttr("location", (*m.Location).String()) + } +} diff --git a/gen/test/go/mx/formatted_symbol_id.go b/gen/test/go/mx/formatted_symbol_id.go new file mode 100644 index 000000000..05b566944 --- /dev/null +++ b/gen/test/go/mx/formatted_symbol_id.go @@ -0,0 +1,184 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The formatted-symbol-id type represents a SMuFL musical symbol element with formatting and id +// attributes. +type FormattedSymbolID struct { + Justify *LeftCenterRight // attribute "justify" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" + Underline *NumberOfLines // attribute "underline" + Overline *NumberOfLines // attribute "overline" + LineThrough *NumberOfLines // attribute "line-through" + Rotation *RotationDegrees // attribute "rotation" + LetterSpacing *NumberOrNormal // attribute "letter-spacing" + LineHeight *NumberOrNormal // attribute "line-height" + Dir *TextDirection // attribute "dir" + Enclosure *EnclosureShape // attribute "enclosure" + ID *string // attribute "id" + Value SMUFLGlyphName // text content +} + +func parseFormattedSymbolID(el *etree.Element) (*FormattedSymbolID, error) { + m := &FormattedSymbolID{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "justify": + v := ParseLeftCenterRight(a.Value) + m.Justify = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + case "underline": + v := ParseNumberOfLines(a.Value) + m.Underline = &v + case "overline": + v := ParseNumberOfLines(a.Value) + m.Overline = &v + case "line-through": + v := ParseNumberOfLines(a.Value) + m.LineThrough = &v + case "rotation": + v := ParseRotationDegrees(a.Value) + m.Rotation = &v + case "letter-spacing": + v := ParseNumberOrNormal(a.Value) + m.LetterSpacing = &v + case "line-height": + v := ParseNumberOrNormal(a.Value) + m.LineHeight = &v + case "dir": + v := ParseTextDirection(a.Value) + m.Dir = &v + case "enclosure": + v := ParseEnclosureShape(a.Value) + m.Enclosure = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseSMUFLGlyphName(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeFormattedSymbolID(m *FormattedSymbolID, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Justify != nil { + el.CreateAttr("justify", (*m.Justify).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } + if m.Underline != nil { + el.CreateAttr("underline", (*m.Underline).String()) + } + if m.Overline != nil { + el.CreateAttr("overline", (*m.Overline).String()) + } + if m.LineThrough != nil { + el.CreateAttr("line-through", (*m.LineThrough).String()) + } + if m.Rotation != nil { + el.CreateAttr("rotation", (*m.Rotation).String()) + } + if m.LetterSpacing != nil { + el.CreateAttr("letter-spacing", (*m.LetterSpacing).String()) + } + if m.LineHeight != nil { + el.CreateAttr("line-height", (*m.LineHeight).String()) + } + if m.Dir != nil { + el.CreateAttr("dir", (*m.Dir).String()) + } + if m.Enclosure != nil { + el.CreateAttr("enclosure", (*m.Enclosure).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/formatted_text.go b/gen/test/go/mx/formatted_text.go new file mode 100644 index 000000000..e4a8335bd --- /dev/null +++ b/gen/test/go/mx/formatted_text.go @@ -0,0 +1,190 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The formatted-text type represents a text element with text-formatting attributes. +type FormattedText struct { + XMLLang *string // attribute "xml:lang" + XMLSpace *string // attribute "xml:space" + Justify *LeftCenterRight // attribute "justify" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" + Underline *NumberOfLines // attribute "underline" + Overline *NumberOfLines // attribute "overline" + LineThrough *NumberOfLines // attribute "line-through" + Rotation *RotationDegrees // attribute "rotation" + LetterSpacing *NumberOrNormal // attribute "letter-spacing" + LineHeight *NumberOrNormal // attribute "line-height" + Dir *TextDirection // attribute "dir" + Enclosure *EnclosureShape // attribute "enclosure" + Value string // text content +} + +func parseFormattedText(el *etree.Element) (*FormattedText, error) { + m := &FormattedText{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "xml:lang": + v := a.Value + m.XMLLang = &v + case "xml:space": + v := a.Value + m.XMLSpace = &v + case "justify": + v := ParseLeftCenterRight(a.Value) + m.Justify = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + case "underline": + v := ParseNumberOfLines(a.Value) + m.Underline = &v + case "overline": + v := ParseNumberOfLines(a.Value) + m.Overline = &v + case "line-through": + v := ParseNumberOfLines(a.Value) + m.LineThrough = &v + case "rotation": + v := ParseRotationDegrees(a.Value) + m.Rotation = &v + case "letter-spacing": + v := ParseNumberOrNormal(a.Value) + m.LetterSpacing = &v + case "line-height": + v := ParseNumberOrNormal(a.Value) + m.LineHeight = &v + case "dir": + v := ParseTextDirection(a.Value) + m.Dir = &v + case "enclosure": + v := ParseEnclosureShape(a.Value) + m.Enclosure = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeFormattedText(m *FormattedText, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.XMLLang != nil { + el.CreateAttr("xml:lang", (*m.XMLLang)) + } + if m.XMLSpace != nil { + el.CreateAttr("xml:space", (*m.XMLSpace)) + } + if m.Justify != nil { + el.CreateAttr("justify", (*m.Justify).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } + if m.Underline != nil { + el.CreateAttr("underline", (*m.Underline).String()) + } + if m.Overline != nil { + el.CreateAttr("overline", (*m.Overline).String()) + } + if m.LineThrough != nil { + el.CreateAttr("line-through", (*m.LineThrough).String()) + } + if m.Rotation != nil { + el.CreateAttr("rotation", (*m.Rotation).String()) + } + if m.LetterSpacing != nil { + el.CreateAttr("letter-spacing", (*m.LetterSpacing).String()) + } + if m.LineHeight != nil { + el.CreateAttr("line-height", (*m.LineHeight).String()) + } + if m.Dir != nil { + el.CreateAttr("dir", (*m.Dir).String()) + } + if m.Enclosure != nil { + el.CreateAttr("enclosure", (*m.Enclosure).String()) + } +} diff --git a/gen/test/go/mx/formatted_text_id.go b/gen/test/go/mx/formatted_text_id.go new file mode 100644 index 000000000..2040e0efb --- /dev/null +++ b/gen/test/go/mx/formatted_text_id.go @@ -0,0 +1,197 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The formatted-text-id type represents a text element with text-formatting and id attributes. +type FormattedTextID struct { + XMLLang *string // attribute "xml:lang" + XMLSpace *string // attribute "xml:space" + Justify *LeftCenterRight // attribute "justify" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" + Underline *NumberOfLines // attribute "underline" + Overline *NumberOfLines // attribute "overline" + LineThrough *NumberOfLines // attribute "line-through" + Rotation *RotationDegrees // attribute "rotation" + LetterSpacing *NumberOrNormal // attribute "letter-spacing" + LineHeight *NumberOrNormal // attribute "line-height" + Dir *TextDirection // attribute "dir" + Enclosure *EnclosureShape // attribute "enclosure" + ID *string // attribute "id" + Value string // text content +} + +func parseFormattedTextID(el *etree.Element) (*FormattedTextID, error) { + m := &FormattedTextID{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "xml:lang": + v := a.Value + m.XMLLang = &v + case "xml:space": + v := a.Value + m.XMLSpace = &v + case "justify": + v := ParseLeftCenterRight(a.Value) + m.Justify = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + case "underline": + v := ParseNumberOfLines(a.Value) + m.Underline = &v + case "overline": + v := ParseNumberOfLines(a.Value) + m.Overline = &v + case "line-through": + v := ParseNumberOfLines(a.Value) + m.LineThrough = &v + case "rotation": + v := ParseRotationDegrees(a.Value) + m.Rotation = &v + case "letter-spacing": + v := ParseNumberOrNormal(a.Value) + m.LetterSpacing = &v + case "line-height": + v := ParseNumberOrNormal(a.Value) + m.LineHeight = &v + case "dir": + v := ParseTextDirection(a.Value) + m.Dir = &v + case "enclosure": + v := ParseEnclosureShape(a.Value) + m.Enclosure = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeFormattedTextID(m *FormattedTextID, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.XMLLang != nil { + el.CreateAttr("xml:lang", (*m.XMLLang)) + } + if m.XMLSpace != nil { + el.CreateAttr("xml:space", (*m.XMLSpace)) + } + if m.Justify != nil { + el.CreateAttr("justify", (*m.Justify).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } + if m.Underline != nil { + el.CreateAttr("underline", (*m.Underline).String()) + } + if m.Overline != nil { + el.CreateAttr("overline", (*m.Overline).String()) + } + if m.LineThrough != nil { + el.CreateAttr("line-through", (*m.LineThrough).String()) + } + if m.Rotation != nil { + el.CreateAttr("rotation", (*m.Rotation).String()) + } + if m.LetterSpacing != nil { + el.CreateAttr("letter-spacing", (*m.LetterSpacing).String()) + } + if m.LineHeight != nil { + el.CreateAttr("line-height", (*m.LineHeight).String()) + } + if m.Dir != nil { + el.CreateAttr("dir", (*m.Dir).String()) + } + if m.Enclosure != nil { + el.CreateAttr("enclosure", (*m.Enclosure).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/forward.go b/gen/test/go/mx/forward.go new file mode 100644 index 000000000..616107431 --- /dev/null +++ b/gen/test/go/mx/forward.go @@ -0,0 +1,87 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The backup and forward elements are required to coordinate multiple voices in one part, including +// music on multiple staves. The forward element is generally used within voices and staves. +// Duration values should always be positive, and should not cross measure boundaries or mid-measure +// changes in the divisions value. +type Forward struct { + Children []ForwardChild // child elements in document order +} + +// ForwardChild is one child element of Forward: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type ForwardChild struct { + Duration *PositiveDivisions + Footnote *FormattedText + Level *Level + Voice *string + Staff *int +} + +func parseForward(el *etree.Element) (*Forward, error) { + m := &Forward{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "duration": + v := ParsePositiveDivisions(c.Text()) + m.Children = append(m.Children, ForwardChild{Duration: &v}) + case "footnote": + v, err := parseFormattedText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ForwardChild{Footnote: v}) + case "level": + v, err := parseLevel(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ForwardChild{Level: v}) + case "voice": + v := c.Text() + m.Children = append(m.Children, ForwardChild{Voice: &v}) + case "staff": + v := parseInt(c.Text()) + m.Children = append(m.Children, ForwardChild{Staff: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeForward(m *Forward, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.Duration != nil: + el.CreateElement("duration").SetText((*ch.Duration).String()) + case ch.Footnote != nil: + serializeFormattedText(ch.Footnote, el, "footnote") + case ch.Level != nil: + serializeLevel(ch.Level, el, "level") + case ch.Voice != nil: + el.CreateElement("voice").SetText((*ch.Voice)) + case ch.Staff != nil: + el.CreateElement("staff").SetText(formatInt((*ch.Staff))) + } + } +} diff --git a/gen/test/go/mx/frame.go b/gen/test/go/mx/frame.go new file mode 100644 index 000000000..039cddee8 --- /dev/null +++ b/gen/test/go/mx/frame.go @@ -0,0 +1,159 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The frame type represents a frame or fretboard diagram used together with a chord symbol. The +// representation is based on the NIFF guitar grid with additional information. The frame type's +// unplayed attribute indicates what to display above a string that has no associated frame-note +// element. Typical values are x and the empty string. If the attribute is not present, the display +// of the unplayed string is application-defined. +type Frame struct { + Height *Tenths // attribute "height" + Width *Tenths // attribute "width" + Unplayed *string // attribute "unplayed" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *ValignImage // attribute "valign" + ID *string // attribute "id" + Children []FrameChild // child elements in document order +} + +// FrameChild is one child element of Frame: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type FrameChild struct { + FrameStrings *int + FrameFrets *int + FirstFret *FirstFret + FrameNote *FrameNote +} + +func parseFrame(el *etree.Element) (*Frame, error) { + m := &Frame{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "height": + v := ParseTenths(a.Value) + m.Height = &v + case "width": + v := ParseTenths(a.Value) + m.Width = &v + case "unplayed": + v := a.Value + m.Unplayed = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValignImage(a.Value) + m.Valign = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "frame-strings": + v := parseInt(c.Text()) + m.Children = append(m.Children, FrameChild{FrameStrings: &v}) + case "frame-frets": + v := parseInt(c.Text()) + m.Children = append(m.Children, FrameChild{FrameFrets: &v}) + case "first-fret": + v, err := parseFirstFret(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, FrameChild{FirstFret: v}) + case "frame-note": + v, err := parseFrameNote(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, FrameChild{FrameNote: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeFrame(m *Frame, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Height != nil { + el.CreateAttr("height", (*m.Height).String()) + } + if m.Width != nil { + el.CreateAttr("width", (*m.Width).String()) + } + if m.Unplayed != nil { + el.CreateAttr("unplayed", (*m.Unplayed)) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.FrameStrings != nil: + el.CreateElement("frame-strings").SetText(formatInt((*ch.FrameStrings))) + case ch.FrameFrets != nil: + el.CreateElement("frame-frets").SetText(formatInt((*ch.FrameFrets))) + case ch.FirstFret != nil: + serializeFirstFret(ch.FirstFret, el, "first-fret") + case ch.FrameNote != nil: + serializeFrameNote(ch.FrameNote, el, "frame-note") + } + } +} diff --git a/gen/test/go/mx/frame_note.go b/gen/test/go/mx/frame_note.go new file mode 100644 index 000000000..eadd5af17 --- /dev/null +++ b/gen/test/go/mx/frame_note.go @@ -0,0 +1,85 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The frame-note type represents each note included in the frame. An open string will have a fret +// value of 0, while a muted string will not be associated with a frame-note element. +type FrameNote struct { + Children []FrameNoteChild // child elements in document order +} + +// FrameNoteChild is one child element of FrameNote: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type FrameNoteChild struct { + String *String + Fret *Fret + Fingering *Fingering + Barre *Barre +} + +func parseFrameNote(el *etree.Element) (*FrameNote, error) { + m := &FrameNote{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "string": + v, err := parseString(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, FrameNoteChild{String: v}) + case "fret": + v, err := parseFret(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, FrameNoteChild{Fret: v}) + case "fingering": + v, err := parseFingering(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, FrameNoteChild{Fingering: v}) + case "barre": + v, err := parseBarre(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, FrameNoteChild{Barre: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeFrameNote(m *FrameNote, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.String != nil: + serializeString(ch.String, el, "string") + case ch.Fret != nil: + serializeFret(ch.Fret, el, "fret") + case ch.Fingering != nil: + serializeFingering(ch.Fingering, el, "fingering") + case ch.Barre != nil: + serializeBarre(ch.Barre, el, "barre") + } + } +} diff --git a/gen/test/go/mx/fret.go b/gen/test/go/mx/fret.go new file mode 100644 index 000000000..a524836a7 --- /dev/null +++ b/gen/test/go/mx/fret.go @@ -0,0 +1,72 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The fret element is used with tablature notation and chord diagrams. Fret numbers start with 0 +// for an open string and 1 for the first fret. +type Fret struct { + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Value int // text content +} + +func parseFret(el *etree.Element) (*Fret, error) { + m := &Fret{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = parseInt(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeFret(m *Fret, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(formatInt(m.Value)) + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/glass.go b/gen/test/go/mx/glass.go new file mode 100644 index 000000000..dc3c320f3 --- /dev/null +++ b/gen/test/go/mx/glass.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The glass type represents pictograms for glass percussion instruments. The smufl attribute is +// used to distinguish different SMuFL glyphs for wind chimes in the chimes pictograms range, +// including those made of materials other than glass. +type Glass struct { + SMUFL *SMUFLPictogramGlyphName // attribute "smufl" + Value GlassValue // text content +} + +func parseGlass(el *etree.Element) (*Glass, error) { + m := &Glass{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "smufl": + v := ParseSMUFLPictogramGlyphName(a.Value) + m.SMUFL = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseGlassValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeGlass(m *Glass, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.SMUFL != nil { + el.CreateAttr("smufl", (*m.SMUFL).String()) + } +} diff --git a/gen/test/go/mx/glissando.go b/gen/test/go/mx/glissando.go new file mode 100644 index 000000000..4fd080dfe --- /dev/null +++ b/gen/test/go/mx/glissando.go @@ -0,0 +1,144 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Glissando and slide types both indicate rapidly moving from one pitch to the other so that +// individual notes are not discerned. The distinction is similar to that between NIFF's glissando +// and portamento elements. A glissando sounds the half notes in between the slide and defaults to a +// wavy line. The optional text is printed alongside the line. +type Glissando struct { + Type *StartStop // attribute "type" + Number *NumberLevel // attribute "number" + LineType *LineType // attribute "line-type" + DashLength *Tenths // attribute "dash-length" + SpaceLength *Tenths // attribute "space-length" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + ID *string // attribute "id" + Value string // text content +} + +func parseGlissando(el *etree.Element) (*Glissando, error) { + m := &Glissando{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStartStop(a.Value) + m.Type = &v + case "number": + v := ParseNumberLevel(a.Value) + m.Number = &v + case "line-type": + v := ParseLineType(a.Value) + m.LineType = &v + case "dash-length": + v := ParseTenths(a.Value) + m.DashLength = &v + case "space-length": + v := ParseTenths(a.Value) + m.SpaceLength = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeGlissando(m *Glissando, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.LineType != nil { + el.CreateAttr("line-type", (*m.LineType).String()) + } + if m.DashLength != nil { + el.CreateAttr("dash-length", (*m.DashLength).String()) + } + if m.SpaceLength != nil { + el.CreateAttr("space-length", (*m.SpaceLength).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/glyph.go b/gen/test/go/mx/glyph.go new file mode 100644 index 000000000..e42d7ad45 --- /dev/null +++ b/gen/test/go/mx/glyph.go @@ -0,0 +1,49 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The glyph element represents what SMuFL glyph should be used for different variations of symbols +// that are semantically identical. The type attribute specifies what type of glyph is being +// defined. The element value specifies what SMuFL glyph to use, including recommended stylistic +// alternates. The SMuFL glyph name should match the type. For instance, a type of quarter-rest +// would use values restQuarter, restQuarterOld, or restQuarterZ. A type of g-clef-ottava-bassa +// would use values gClef8vb, gClef8vbOld, or gClef8vbCClef. A type of octave-shift-up-8 would use +// values ottava, ottavaBassa, ottavaBassaBa, ottavaBassaVb, or octaveBassa. +type Glyph struct { + Type *GlyphType // attribute "type" + Value SMUFLGlyphName // text content +} + +func parseGlyph(el *etree.Element) (*Glyph, error) { + m := &Glyph{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseGlyphType(a.Value) + m.Type = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseSMUFLGlyphName(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeGlyph(m *Glyph, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } +} diff --git a/gen/test/go/mx/grace.go b/gen/test/go/mx/grace.go new file mode 100644 index 000000000..5e634380b --- /dev/null +++ b/gen/test/go/mx/grace.go @@ -0,0 +1,67 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The grace type indicates the presence of a grace note. The slash attribute for a grace note is +// yes for slashed eighth notes. The other grace note attributes come from MuseData sound +// suggestions. The steal-time-previous attribute indicates the percentage of time to steal from the +// previous note for the grace note. The steal-time-following attribute indicates the percentage of +// time to steal from the following note for the grace note, as for appoggiaturas. The make-time +// attribute indicates to make time, not steal time; the units are in real-time divisions for the +// grace note. +type Grace struct { + StealTimePrevious *Percent // attribute "steal-time-previous" + StealTimeFollowing *Percent // attribute "steal-time-following" + MakeTime *Divisions // attribute "make-time" + Slash *YesNo // attribute "slash" +} + +func parseGrace(el *etree.Element) (*Grace, error) { + m := &Grace{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "steal-time-previous": + v := ParsePercent(a.Value) + m.StealTimePrevious = &v + case "steal-time-following": + v := ParsePercent(a.Value) + m.StealTimeFollowing = &v + case "make-time": + v := ParseDivisions(a.Value) + m.MakeTime = &v + case "slash": + v := ParseYesNo(a.Value) + m.Slash = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeGrace(m *Grace, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.StealTimePrevious != nil { + el.CreateAttr("steal-time-previous", (*m.StealTimePrevious).String()) + } + if m.StealTimeFollowing != nil { + el.CreateAttr("steal-time-following", (*m.StealTimeFollowing).String()) + } + if m.MakeTime != nil { + el.CreateAttr("make-time", (*m.MakeTime).String()) + } + if m.Slash != nil { + el.CreateAttr("slash", (*m.Slash).String()) + } +} diff --git a/gen/test/go/mx/group_barline.go b/gen/test/go/mx/group_barline.go new file mode 100644 index 000000000..0e70bef50 --- /dev/null +++ b/gen/test/go/mx/group_barline.go @@ -0,0 +1,43 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The group-barline type indicates if the group should have common barlines. +type GroupBarline struct { + Color *Color // attribute "color" + Value GroupBarlineValue // text content +} + +func parseGroupBarline(el *etree.Element) (*GroupBarline, error) { + m := &GroupBarline{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseGroupBarlineValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeGroupBarline(m *GroupBarline, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/group_name.go b/gen/test/go/mx/group_name.go new file mode 100644 index 000000000..5792b6b26 --- /dev/null +++ b/gen/test/go/mx/group_name.go @@ -0,0 +1,108 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The group-name type describes the name or abbreviation of a part-group element. Formatting +// attributes in the group-name type are deprecated in Version 2.0 in favor of the new +// group-name-display and group-abbreviation-display elements. +type GroupName struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Justify *LeftCenterRight // attribute "justify" + Value string // text content +} + +func parseGroupName(el *etree.Element) (*GroupName, error) { + m := &GroupName{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "justify": + v := ParseLeftCenterRight(a.Value) + m.Justify = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeGroupName(m *GroupName, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Justify != nil { + el.CreateAttr("justify", (*m.Justify).String()) + } +} diff --git a/gen/test/go/mx/group_symbol.go b/gen/test/go/mx/group_symbol.go new file mode 100644 index 000000000..b6088e5ff --- /dev/null +++ b/gen/test/go/mx/group_symbol.go @@ -0,0 +1,71 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The group-symbol type indicates how the symbol for a group is indicated in the score. +type GroupSymbol struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + Color *Color // attribute "color" + Value GroupSymbolValue // text content +} + +func parseGroupSymbol(el *etree.Element) (*GroupSymbol, error) { + m := &GroupSymbol{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseGroupSymbolValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeGroupSymbol(m *GroupSymbol, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/grouping.go b/gen/test/go/mx/grouping.go new file mode 100644 index 000000000..3c8868048 --- /dev/null +++ b/gen/test/go/mx/grouping.go @@ -0,0 +1,91 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The grouping type is used for musical analysis. When the type attribute is "start" or "single", +// it usually contains one or more feature elements. The number attribute is used for distinguishing +// between overlapping and hierarchical groupings. The member-of attribute allows for easy +// distinguishing of what grouping elements are in what hierarchy. Feature elements contained within +// a "stop" type of grouping may be ignored. This element is flexible to allow for different types +// of analyses. Future versions of the MusicXML format may add elements that can represent more +// standardized categories of analysis data, allowing for easier data sharing. +type Grouping struct { + Type *StartStopSingle // attribute "type" + Number *string // attribute "number" + MemberOf *string // attribute "member-of" + ID *string // attribute "id" + Children []GroupingChild // child elements in document order +} + +// GroupingChild is one child element of Grouping: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type GroupingChild struct { + Feature *Feature +} + +func parseGrouping(el *etree.Element) (*Grouping, error) { + m := &Grouping{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStartStopSingle(a.Value) + m.Type = &v + case "number": + v := a.Value + m.Number = &v + case "member-of": + v := a.Value + m.MemberOf = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "feature": + v, err := parseFeature(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, GroupingChild{Feature: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeGrouping(m *Grouping, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Number != nil { + el.CreateAttr("number", (*m.Number)) + } + if m.MemberOf != nil { + el.CreateAttr("member-of", (*m.MemberOf)) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.Feature != nil: + serializeFeature(ch.Feature, el, "feature") + } + } +} diff --git a/gen/test/go/mx/hammer_on_pull_off.go b/gen/test/go/mx/hammer_on_pull_off.go new file mode 100644 index 000000000..4f778e712 --- /dev/null +++ b/gen/test/go/mx/hammer_on_pull_off.go @@ -0,0 +1,124 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The hammer-on and pull-off elements are used in guitar and fretted instrument notation. Since a +// single slur can be marked over many notes, the hammer-on and pull-off elements are separate so +// the individual pair of notes can be specified. The element content can be used to specify how the +// hammer-on or pull-off should be notated. An empty element leaves this choice up to the +// application. +type HammerOnPullOff struct { + Type *StartStop // attribute "type" + Number *NumberLevel // attribute "number" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" + Value string // text content +} + +func parseHammerOnPullOff(el *etree.Element) (*HammerOnPullOff, error) { + m := &HammerOnPullOff{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStartStop(a.Value) + m.Type = &v + case "number": + v := ParseNumberLevel(a.Value) + m.Number = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeHammerOnPullOff(m *HammerOnPullOff, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } +} diff --git a/gen/test/go/mx/handbell.go b/gen/test/go/mx/handbell.go new file mode 100644 index 000000000..4754d0207 --- /dev/null +++ b/gen/test/go/mx/handbell.go @@ -0,0 +1,107 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The handbell element represents notation for various techniques used in handbell and handchime +// music. +type Handbell struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" + Value HandbellValue // text content +} + +func parseHandbell(el *etree.Element) (*Handbell, error) { + m := &Handbell{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseHandbellValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeHandbell(m *Handbell, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } +} diff --git a/gen/test/go/mx/harmon_closed.go b/gen/test/go/mx/harmon_closed.go new file mode 100644 index 000000000..2e2d1d3c9 --- /dev/null +++ b/gen/test/go/mx/harmon_closed.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The harmon-closed type represents whether the harmon mute is closed, open, or half-open. The +// optional location attribute indicates which portion of the symbol is filled in when the element +// value is half. +type HarmonClosed struct { + Location *HarmonClosedLocation // attribute "location" + Value HarmonClosedValue // text content +} + +func parseHarmonClosed(el *etree.Element) (*HarmonClosed, error) { + m := &HarmonClosed{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "location": + v := ParseHarmonClosedLocation(a.Value) + m.Location = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseHarmonClosedValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeHarmonClosed(m *HarmonClosed, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Location != nil { + el.CreateAttr("location", (*m.Location).String()) + } +} diff --git a/gen/test/go/mx/harmon_mute.go b/gen/test/go/mx/harmon_mute.go new file mode 100644 index 000000000..d47a2e414 --- /dev/null +++ b/gen/test/go/mx/harmon_mute.go @@ -0,0 +1,127 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The harmon-mute type represents the symbols used for harmon mutes in brass notation. +type HarmonMute struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" + Children []HarmonMuteChild // child elements in document order +} + +// HarmonMuteChild is one child element of HarmonMute: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type HarmonMuteChild struct { + HarmonClosed *HarmonClosed +} + +func parseHarmonMute(el *etree.Element) (*HarmonMute, error) { + m := &HarmonMute{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "harmon-closed": + v, err := parseHarmonClosed(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, HarmonMuteChild{HarmonClosed: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeHarmonMute(m *HarmonMute, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + for _, ch := range m.Children { + switch { + case ch.HarmonClosed != nil: + serializeHarmonClosed(ch.HarmonClosed, el, "harmon-closed") + } + } +} diff --git a/gen/test/go/mx/harmonic.go b/gen/test/go/mx/harmonic.go new file mode 100644 index 000000000..fdcef056f --- /dev/null +++ b/gen/test/go/mx/harmonic.go @@ -0,0 +1,175 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The harmonic type indicates natural and artificial harmonics. Allowing the type of pitch to be +// specified, combined with controls for appearance/playback differences, allows both the notation +// and the sound to be represented. Artificial harmonics can add a notated touching-pitch; +// artificial pinch harmonics will usually not notate a touching pitch. The attributes for the +// harmonic element refer to the use of the circular harmonic symbol, typically but not always used +// with natural harmonics. +type Harmonic struct { + PrintObject *YesNo // attribute "print-object" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" + Children []HarmonicChild // child elements in document order +} + +// HarmonicChild is one child element of Harmonic: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type HarmonicChild struct { + Natural *Empty + Artificial *Empty + BasePitch *Empty + TouchingPitch *Empty + SoundingPitch *Empty +} + +func parseHarmonic(el *etree.Element) (*Harmonic, error) { + m := &Harmonic{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "print-object": + v := ParseYesNo(a.Value) + m.PrintObject = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "natural": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, HarmonicChild{Natural: v}) + case "artificial": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, HarmonicChild{Artificial: v}) + case "base-pitch": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, HarmonicChild{BasePitch: v}) + case "touching-pitch": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, HarmonicChild{TouchingPitch: v}) + case "sounding-pitch": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, HarmonicChild{SoundingPitch: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeHarmonic(m *Harmonic, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.PrintObject != nil { + el.CreateAttr("print-object", (*m.PrintObject).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + for _, ch := range m.Children { + switch { + case ch.Natural != nil: + serializeEmpty(ch.Natural, el, "natural") + case ch.Artificial != nil: + serializeEmpty(ch.Artificial, el, "artificial") + case ch.BasePitch != nil: + serializeEmpty(ch.BasePitch, el, "base-pitch") + case ch.TouchingPitch != nil: + serializeEmpty(ch.TouchingPitch, el, "touching-pitch") + case ch.SoundingPitch != nil: + serializeEmpty(ch.SoundingPitch, el, "sounding-pitch") + } + } +} diff --git a/gen/test/go/mx/harmony.go b/gen/test/go/mx/harmony.go new file mode 100644 index 000000000..fcb1e636c --- /dev/null +++ b/gen/test/go/mx/harmony.go @@ -0,0 +1,250 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The harmony type is based on Humdrum's **harm encoding, extended to support chord symbols in +// popular music as well as functional harmony analysis in classical music. If there are alternate +// harmonies possible, this can be specified using multiple harmony elements differentiated by type. +// Explicit harmonies have all note present in the music; implied have some notes missing but +// implied; alternate represents alternate analyses. The harmony object may be used for analysis or +// for chord symbols. The print-object attribute controls whether or not anything is printed due to +// the harmony element. The print-frame attribute controls printing of a frame or fretboard diagram. +// The print-style attribute group sets the default for the harmony, but individual elements can +// override this with their own print-style values. +type Harmony struct { + Type *HarmonyType // attribute "type" + PrintFrame *YesNo // attribute "print-frame" + PrintObject *YesNo // attribute "print-object" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" + ID *string // attribute "id" + Children []HarmonyChild // child elements in document order +} + +// HarmonyChild is one child element of Harmony: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type HarmonyChild struct { + Root *Root + Function *StyleText + Kind *Kind + Inversion *Inversion + Bass *Bass + Degree *Degree + Frame *Frame + Offset *Offset + Footnote *FormattedText + Level *Level + Staff *int +} + +func parseHarmony(el *etree.Element) (*Harmony, error) { + m := &Harmony{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseHarmonyType(a.Value) + m.Type = &v + case "print-frame": + v := ParseYesNo(a.Value) + m.PrintFrame = &v + case "print-object": + v := ParseYesNo(a.Value) + m.PrintObject = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "root": + v, err := parseRoot(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, HarmonyChild{Root: v}) + case "function": + v, err := parseStyleText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, HarmonyChild{Function: v}) + case "kind": + v, err := parseKind(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, HarmonyChild{Kind: v}) + case "inversion": + v, err := parseInversion(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, HarmonyChild{Inversion: v}) + case "bass": + v, err := parseBass(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, HarmonyChild{Bass: v}) + case "degree": + v, err := parseDegree(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, HarmonyChild{Degree: v}) + case "frame": + v, err := parseFrame(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, HarmonyChild{Frame: v}) + case "offset": + v, err := parseOffset(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, HarmonyChild{Offset: v}) + case "footnote": + v, err := parseFormattedText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, HarmonyChild{Footnote: v}) + case "level": + v, err := parseLevel(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, HarmonyChild{Level: v}) + case "staff": + v := parseInt(c.Text()) + m.Children = append(m.Children, HarmonyChild{Staff: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeHarmony(m *Harmony, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.PrintFrame != nil { + el.CreateAttr("print-frame", (*m.PrintFrame).String()) + } + if m.PrintObject != nil { + el.CreateAttr("print-object", (*m.PrintObject).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.Root != nil: + serializeRoot(ch.Root, el, "root") + case ch.Function != nil: + serializeStyleText(ch.Function, el, "function") + case ch.Kind != nil: + serializeKind(ch.Kind, el, "kind") + case ch.Inversion != nil: + serializeInversion(ch.Inversion, el, "inversion") + case ch.Bass != nil: + serializeBass(ch.Bass, el, "bass") + case ch.Degree != nil: + serializeDegree(ch.Degree, el, "degree") + case ch.Frame != nil: + serializeFrame(ch.Frame, el, "frame") + case ch.Offset != nil: + serializeOffset(ch.Offset, el, "offset") + case ch.Footnote != nil: + serializeFormattedText(ch.Footnote, el, "footnote") + case ch.Level != nil: + serializeLevel(ch.Level, el, "level") + case ch.Staff != nil: + el.CreateElement("staff").SetText(formatInt((*ch.Staff))) + } + } +} diff --git a/gen/test/go/mx/harp_pedals.go b/gen/test/go/mx/harp_pedals.go new file mode 100644 index 000000000..f634f4ec8 --- /dev/null +++ b/gen/test/go/mx/harp_pedals.go @@ -0,0 +1,144 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The harp-pedals type is used to create harp pedal diagrams. The pedal-step and pedal-alter +// elements use the same values as the step and alter elements. For easiest reading, the +// pedal-tuning elements should follow standard harp pedal order, with pedal-step values of D, C, B, +// E, F, G, and A. +type HarpPedals struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" + ID *string // attribute "id" + Children []HarpPedalsChild // child elements in document order +} + +// HarpPedalsChild is one child element of HarpPedals: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type HarpPedalsChild struct { + PedalTuning *PedalTuning +} + +func parseHarpPedals(el *etree.Element) (*HarpPedals, error) { + m := &HarpPedals{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "pedal-tuning": + v, err := parsePedalTuning(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, HarpPedalsChild{PedalTuning: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeHarpPedals(m *HarpPedals, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.PedalTuning != nil: + serializePedalTuning(ch.PedalTuning, el, "pedal-tuning") + } + } +} diff --git a/gen/test/go/mx/heel_toe.go b/gen/test/go/mx/heel_toe.go new file mode 100644 index 000000000..4f4ce0923 --- /dev/null +++ b/gen/test/go/mx/heel_toe.go @@ -0,0 +1,102 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The heel and toe elements are used with organ pedals. The substitution value is "no" if the +// attribute is not present. +type HeelToe struct { + EmptyPlacement + Substitution *YesNo // attribute "substitution" +} + +func parseHeelToe(el *etree.Element) (*HeelToe, error) { + m := &HeelToe{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "substitution": + v := ParseYesNo(a.Value) + m.Substitution = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeHeelToe(m *HeelToe, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.Substitution != nil { + el.CreateAttr("substitution", (*m.Substitution).String()) + } +} diff --git a/gen/test/go/mx/hole.go b/gen/test/go/mx/hole.go new file mode 100644 index 000000000..84a5db0d1 --- /dev/null +++ b/gen/test/go/mx/hole.go @@ -0,0 +1,140 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The hole type represents the symbols used for woodwind and brass fingerings as well as other +// notations. +type Hole struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" + Children []HoleChild // child elements in document order +} + +// HoleChild is one child element of Hole: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type HoleChild struct { + HoleType *string + HoleClosed *HoleClosed + HoleShape *string +} + +func parseHole(el *etree.Element) (*Hole, error) { + m := &Hole{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "hole-type": + v := c.Text() + m.Children = append(m.Children, HoleChild{HoleType: &v}) + case "hole-closed": + v, err := parseHoleClosed(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, HoleChild{HoleClosed: v}) + case "hole-shape": + v := c.Text() + m.Children = append(m.Children, HoleChild{HoleShape: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeHole(m *Hole, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + for _, ch := range m.Children { + switch { + case ch.HoleType != nil: + el.CreateElement("hole-type").SetText((*ch.HoleType)) + case ch.HoleClosed != nil: + serializeHoleClosed(ch.HoleClosed, el, "hole-closed") + case ch.HoleShape != nil: + el.CreateElement("hole-shape").SetText((*ch.HoleShape)) + } + } +} diff --git a/gen/test/go/mx/hole_closed.go b/gen/test/go/mx/hole_closed.go new file mode 100644 index 000000000..54f583f00 --- /dev/null +++ b/gen/test/go/mx/hole_closed.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The hole-closed type represents whether the hole is closed, open, or half-open. The optional +// location attribute indicates which portion of the hole is filled in when the element value is +// half. +type HoleClosed struct { + Location *HoleClosedLocation // attribute "location" + Value HoleClosedValue // text content +} + +func parseHoleClosed(el *etree.Element) (*HoleClosed, error) { + m := &HoleClosed{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "location": + v := ParseHoleClosedLocation(a.Value) + m.Location = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseHoleClosedValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeHoleClosed(m *HoleClosed, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Location != nil { + el.CreateAttr("location", (*m.Location).String()) + } +} diff --git a/gen/test/go/mx/horizontal_turn.go b/gen/test/go/mx/horizontal_turn.go new file mode 100644 index 000000000..94fc34e9a --- /dev/null +++ b/gen/test/go/mx/horizontal_turn.go @@ -0,0 +1,161 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The horizontal-turn type represents turn elements that are horizontal rather than vertical. These +// are empty elements with print-style, placement, trill-sound, and slash attributes. If the slash +// attribute is yes, then a vertical line is used to slash the turn; it is no by default. +type HorizontalTurn struct { + Slash *YesNo // attribute "slash" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" + StartNote *StartNote // attribute "start-note" + TrillStep *TrillStep // attribute "trill-step" + TwoNoteTurn *TwoNoteTurn // attribute "two-note-turn" + Accelerate *YesNo // attribute "accelerate" + Beats *TrillBeats // attribute "beats" + SecondBeat *Percent // attribute "second-beat" + LastBeat *Percent // attribute "last-beat" +} + +func parseHorizontalTurn(el *etree.Element) (*HorizontalTurn, error) { + m := &HorizontalTurn{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "slash": + v := ParseYesNo(a.Value) + m.Slash = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "start-note": + v := ParseStartNote(a.Value) + m.StartNote = &v + case "trill-step": + v := ParseTrillStep(a.Value) + m.TrillStep = &v + case "two-note-turn": + v := ParseTwoNoteTurn(a.Value) + m.TwoNoteTurn = &v + case "accelerate": + v := ParseYesNo(a.Value) + m.Accelerate = &v + case "beats": + v := ParseTrillBeats(a.Value) + m.Beats = &v + case "second-beat": + v := ParsePercent(a.Value) + m.SecondBeat = &v + case "last-beat": + v := ParsePercent(a.Value) + m.LastBeat = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeHorizontalTurn(m *HorizontalTurn, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Slash != nil { + el.CreateAttr("slash", (*m.Slash).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.StartNote != nil { + el.CreateAttr("start-note", (*m.StartNote).String()) + } + if m.TrillStep != nil { + el.CreateAttr("trill-step", (*m.TrillStep).String()) + } + if m.TwoNoteTurn != nil { + el.CreateAttr("two-note-turn", (*m.TwoNoteTurn).String()) + } + if m.Accelerate != nil { + el.CreateAttr("accelerate", (*m.Accelerate).String()) + } + if m.Beats != nil { + el.CreateAttr("beats", (*m.Beats).String()) + } + if m.SecondBeat != nil { + el.CreateAttr("second-beat", (*m.SecondBeat).String()) + } + if m.LastBeat != nil { + el.CreateAttr("last-beat", (*m.LastBeat).String()) + } +} diff --git a/gen/test/go/mx/identification.go b/gen/test/go/mx/identification.go new file mode 100644 index 000000000..1ed8f860c --- /dev/null +++ b/gen/test/go/mx/identification.go @@ -0,0 +1,101 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Identification contains basic metadata about the score. It includes the information in MuseData +// headers that may apply at a score-wide, movement-wide, or part-wide level. The creator, rights, +// source, and relation elements are based on Dublin Core. +type Identification struct { + Children []IdentificationChild // child elements in document order +} + +// IdentificationChild is one child element of Identification: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type IdentificationChild struct { + Creator *TypedText + Rights *TypedText + Encoding *Encoding + Source *string + Relation *TypedText + Miscellaneous *Miscellaneous +} + +func parseIdentification(el *etree.Element) (*Identification, error) { + m := &Identification{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "creator": + v, err := parseTypedText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, IdentificationChild{Creator: v}) + case "rights": + v, err := parseTypedText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, IdentificationChild{Rights: v}) + case "encoding": + v, err := parseEncoding(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, IdentificationChild{Encoding: v}) + case "source": + v := c.Text() + m.Children = append(m.Children, IdentificationChild{Source: &v}) + case "relation": + v, err := parseTypedText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, IdentificationChild{Relation: v}) + case "miscellaneous": + v, err := parseMiscellaneous(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, IdentificationChild{Miscellaneous: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeIdentification(m *Identification, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.Creator != nil: + serializeTypedText(ch.Creator, el, "creator") + case ch.Rights != nil: + serializeTypedText(ch.Rights, el, "rights") + case ch.Encoding != nil: + serializeEncoding(ch.Encoding, el, "encoding") + case ch.Source != nil: + el.CreateElement("source").SetText((*ch.Source)) + case ch.Relation != nil: + serializeTypedText(ch.Relation, el, "relation") + case ch.Miscellaneous != nil: + serializeMiscellaneous(ch.Miscellaneous, el, "miscellaneous") + } + } +} diff --git a/gen/test/go/mx/image.go b/gen/test/go/mx/image.go new file mode 100644 index 000000000..f51fe0a93 --- /dev/null +++ b/gen/test/go/mx/image.go @@ -0,0 +1,110 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The image type is used to include graphical images in a score. +type Image struct { + Source *string // attribute "source" + Type *string // attribute "type" + Height *Tenths // attribute "height" + Width *Tenths // attribute "width" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + Halign *LeftCenterRight // attribute "halign" + Valign *ValignImage // attribute "valign" + ID *string // attribute "id" +} + +func parseImage(el *etree.Element) (*Image, error) { + m := &Image{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "source": + v := a.Value + m.Source = &v + case "type": + v := a.Value + m.Type = &v + case "height": + v := ParseTenths(a.Value) + m.Height = &v + case "width": + v := ParseTenths(a.Value) + m.Width = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValignImage(a.Value) + m.Valign = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeImage(m *Image, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Source != nil { + el.CreateAttr("source", (*m.Source)) + } + if m.Type != nil { + el.CreateAttr("type", (*m.Type)) + } + if m.Height != nil { + el.CreateAttr("height", (*m.Height).String()) + } + if m.Width != nil { + el.CreateAttr("width", (*m.Width).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/instrument.go b/gen/test/go/mx/instrument.go new file mode 100644 index 000000000..9b517300e --- /dev/null +++ b/gen/test/go/mx/instrument.go @@ -0,0 +1,42 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The instrument type distinguishes between score-instrument elements in a score-part. The id +// attribute is an IDREF back to the score-instrument ID. If multiple score-instruments are +// specified on a score-part, there should be an instrument element for each note in the part. +type Instrument struct { + ID *string // attribute "id" +} + +func parseInstrument(el *etree.Element) (*Instrument, error) { + m := &Instrument{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeInstrument(m *Instrument, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/interchangeable.go b/gen/test/go/mx/interchangeable.go new file mode 100644 index 000000000..b19a28445 --- /dev/null +++ b/gen/test/go/mx/interchangeable.go @@ -0,0 +1,82 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The interchangeable type is used to represent the second in a pair of interchangeable dual time +// signatures, such as the 6/8 in 3/4 (6/8). A separate symbol attribute value is available compared +// to the time element's symbol attribute, which applies to the first of the dual time signatures. +type Interchangeable struct { + Symbol *TimeSymbol // attribute "symbol" + Separator *TimeSeparator // attribute "separator" + Children []InterchangeableChild // child elements in document order +} + +// InterchangeableChild is one child element of Interchangeable: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type InterchangeableChild struct { + TimeRelation *TimeRelation + Beats *string + BeatType *string +} + +func parseInterchangeable(el *etree.Element) (*Interchangeable, error) { + m := &Interchangeable{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "symbol": + v := ParseTimeSymbol(a.Value) + m.Symbol = &v + case "separator": + v := ParseTimeSeparator(a.Value) + m.Separator = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "time-relation": + v := ParseTimeRelation(c.Text()) + m.Children = append(m.Children, InterchangeableChild{TimeRelation: &v}) + case "beats": + v := c.Text() + m.Children = append(m.Children, InterchangeableChild{Beats: &v}) + case "beat-type": + v := c.Text() + m.Children = append(m.Children, InterchangeableChild{BeatType: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeInterchangeable(m *Interchangeable, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Symbol != nil { + el.CreateAttr("symbol", (*m.Symbol).String()) + } + if m.Separator != nil { + el.CreateAttr("separator", (*m.Separator).String()) + } + for _, ch := range m.Children { + switch { + case ch.TimeRelation != nil: + el.CreateElement("time-relation").SetText((*ch.TimeRelation).String()) + case ch.Beats != nil: + el.CreateElement("beats").SetText((*ch.Beats)) + case ch.BeatType != nil: + el.CreateElement("beat-type").SetText((*ch.BeatType)) + } + } +} diff --git a/gen/test/go/mx/inversion.go b/gen/test/go/mx/inversion.go new file mode 100644 index 000000000..b9b466a60 --- /dev/null +++ b/gen/test/go/mx/inversion.go @@ -0,0 +1,100 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The inversion type represents harmony inversions. The value is a number indicating which +// inversion is used: 0 for root position, 1 for first inversion, etc. +type Inversion struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Value int // text content +} + +func parseInversion(el *etree.Element) (*Inversion, error) { + m := &Inversion{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = parseInt(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeInversion(m *Inversion, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(formatInt(m.Value)) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/key.go b/gen/test/go/mx/key.go new file mode 100644 index 000000000..4d6cfb074 --- /dev/null +++ b/gen/test/go/mx/key.go @@ -0,0 +1,186 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The key type represents a key signature. Both traditional and non-traditional key signatures are +// supported. The optional number attribute refers to staff numbers. If absent, the key signature +// applies to all staves in the part. Key signatures appear at the start of each system unless the +// print-object attribute has been set to "no". +type Key struct { + Number *StaffNumber // attribute "number" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + PrintObject *YesNo // attribute "print-object" + ID *string // attribute "id" + Children []KeyChild // child elements in document order +} + +// KeyChild is one child element of Key: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type KeyChild struct { + Cancel *Cancel + Fifths *Fifths + Mode *Mode + KeyStep *Step + KeyAlter *Semitones + KeyAccidental *KeyAccidental + KeyOctave *KeyOctave +} + +func parseKey(el *etree.Element) (*Key, error) { + m := &Key{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "number": + v := ParseStaffNumber(a.Value) + m.Number = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "print-object": + v := ParseYesNo(a.Value) + m.PrintObject = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "cancel": + v, err := parseCancel(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, KeyChild{Cancel: v}) + case "fifths": + v := ParseFifths(c.Text()) + m.Children = append(m.Children, KeyChild{Fifths: &v}) + case "mode": + v := ParseMode(c.Text()) + m.Children = append(m.Children, KeyChild{Mode: &v}) + case "key-step": + v := ParseStep(c.Text()) + m.Children = append(m.Children, KeyChild{KeyStep: &v}) + case "key-alter": + v := ParseSemitones(c.Text()) + m.Children = append(m.Children, KeyChild{KeyAlter: &v}) + case "key-accidental": + v, err := parseKeyAccidental(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, KeyChild{KeyAccidental: v}) + case "key-octave": + v, err := parseKeyOctave(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, KeyChild{KeyOctave: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeKey(m *Key, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.PrintObject != nil { + el.CreateAttr("print-object", (*m.PrintObject).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.Cancel != nil: + serializeCancel(ch.Cancel, el, "cancel") + case ch.Fifths != nil: + el.CreateElement("fifths").SetText((*ch.Fifths).String()) + case ch.Mode != nil: + el.CreateElement("mode").SetText((*ch.Mode).String()) + case ch.KeyStep != nil: + el.CreateElement("key-step").SetText((*ch.KeyStep).String()) + case ch.KeyAlter != nil: + el.CreateElement("key-alter").SetText((*ch.KeyAlter).String()) + case ch.KeyAccidental != nil: + serializeKeyAccidental(ch.KeyAccidental, el, "key-accidental") + case ch.KeyOctave != nil: + serializeKeyOctave(ch.KeyOctave, el, "key-octave") + } + } +} diff --git a/gen/test/go/mx/key_accidental.go b/gen/test/go/mx/key_accidental.go new file mode 100644 index 000000000..89a3f4fca --- /dev/null +++ b/gen/test/go/mx/key_accidental.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The key-accidental type indicates the accidental to be displayed in a non-traditional key +// signature, represented in the same manner as the accidental type without the formatting +// attributes. +type KeyAccidental struct { + SMUFL *SMUFLAccidentalGlyphName // attribute "smufl" + Value AccidentalValue // text content +} + +func parseKeyAccidental(el *etree.Element) (*KeyAccidental, error) { + m := &KeyAccidental{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "smufl": + v := ParseSMUFLAccidentalGlyphName(a.Value) + m.SMUFL = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseAccidentalValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeKeyAccidental(m *KeyAccidental, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.SMUFL != nil { + el.CreateAttr("smufl", (*m.SMUFL).String()) + } +} diff --git a/gen/test/go/mx/key_octave.go b/gen/test/go/mx/key_octave.go new file mode 100644 index 000000000..3719bb467 --- /dev/null +++ b/gen/test/go/mx/key_octave.go @@ -0,0 +1,56 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The key-octave element specifies in which octave an element of a key signature appears. The +// content specifies the octave value using the same values as the display-octave element. The +// number attribute is a positive integer that refers to the key signature element in left-to-right +// order. If the cancel attribute is set to yes, then this number refers to the canceling key +// signature specified by the cancel element in the parent key element. The cancel attribute cannot +// be set to yes if there is no corresponding cancel element within the parent key element. It is no +// by default. +type KeyOctave struct { + Number *int // attribute "number" + Cancel *YesNo // attribute "cancel" + Value Octave // text content +} + +func parseKeyOctave(el *etree.Element) (*KeyOctave, error) { + m := &KeyOctave{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "number": + v := parseInt(a.Value) + m.Number = &v + case "cancel": + v := ParseYesNo(a.Value) + m.Cancel = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseOctave(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeKeyOctave(m *KeyOctave, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Number != nil { + el.CreateAttr("number", formatInt((*m.Number))) + } + if m.Cancel != nil { + el.CreateAttr("cancel", (*m.Cancel).String()) + } +} diff --git a/gen/test/go/mx/kind.go b/gen/test/go/mx/kind.go new file mode 100644 index 000000000..8d66a300f --- /dev/null +++ b/gen/test/go/mx/kind.go @@ -0,0 +1,163 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Kind indicates the type of chord. Degree elements can then add, subtract, or alter from these +// starting points The attributes are used to indicate the formatting of the symbol. Since the kind +// element is the constant in all the harmony-chord groups that can make up a polychord, many +// formatting attributes are here. The use-symbols attribute is yes if the kind should be +// represented when possible with harmony symbols rather than letters and numbers. These symbols +// include: major: a triangle, like Unicode 25B3 minor: -, like Unicode 002D augmented: +, like +// Unicode 002B diminished: °, like Unicode 00B0 half-diminished: ø, like Unicode 00F8 For the +// major-minor kind, only the minor symbol is used when use-symbols is yes. The major symbol is set +// using the symbol attribute in the degree-value element. The corresponding degree-alter value will +// usually be 0 in this case. The text attribute describes how the kind should be spelled in a +// score. If use-symbols is yes, the value of the text attribute follows the symbol. The +// stack-degrees attribute is yes if the degree elements should be stacked above each other. The +// parentheses-degrees attribute is yes if all the degrees should be in parentheses. The +// bracket-degrees attribute is yes if all the degrees should be in a bracket. If not specified, +// these values are implementation-specific. The alignment attributes are for the entire +// harmony-chord group of which this kind element is a part. +type Kind struct { + UseSymbols *YesNo // attribute "use-symbols" + Text *string // attribute "text" + StackDegrees *YesNo // attribute "stack-degrees" + ParenthesesDegrees *YesNo // attribute "parentheses-degrees" + BracketDegrees *YesNo // attribute "bracket-degrees" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" + Value KindValue // text content +} + +func parseKind(el *etree.Element) (*Kind, error) { + m := &Kind{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "use-symbols": + v := ParseYesNo(a.Value) + m.UseSymbols = &v + case "text": + v := a.Value + m.Text = &v + case "stack-degrees": + v := ParseYesNo(a.Value) + m.StackDegrees = &v + case "parentheses-degrees": + v := ParseYesNo(a.Value) + m.ParenthesesDegrees = &v + case "bracket-degrees": + v := ParseYesNo(a.Value) + m.BracketDegrees = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseKindValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeKind(m *Kind, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.UseSymbols != nil { + el.CreateAttr("use-symbols", (*m.UseSymbols).String()) + } + if m.Text != nil { + el.CreateAttr("text", (*m.Text)) + } + if m.StackDegrees != nil { + el.CreateAttr("stack-degrees", (*m.StackDegrees).String()) + } + if m.ParenthesesDegrees != nil { + el.CreateAttr("parentheses-degrees", (*m.ParenthesesDegrees).String()) + } + if m.BracketDegrees != nil { + el.CreateAttr("bracket-degrees", (*m.BracketDegrees).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } +} diff --git a/gen/test/go/mx/level.go b/gen/test/go/mx/level.go new file mode 100644 index 000000000..23fc13535 --- /dev/null +++ b/gen/test/go/mx/level.go @@ -0,0 +1,68 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The level type is used to specify editorial information for different MusicXML elements. If the +// reference attribute for the level element is yes, this indicates editorial information that is +// for display only and should not affect playback. For instance, a modern edition of older music +// may set reference="yes" on the attributes containing the music's original clef, key, and time +// signature. It is no by default. +type Level struct { + Reference *YesNo // attribute "reference" + Parentheses *YesNo // attribute "parentheses" + Bracket *YesNo // attribute "bracket" + Size *SymbolSize // attribute "size" + Value string // text content +} + +func parseLevel(el *etree.Element) (*Level, error) { + m := &Level{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "reference": + v := ParseYesNo(a.Value) + m.Reference = &v + case "parentheses": + v := ParseYesNo(a.Value) + m.Parentheses = &v + case "bracket": + v := ParseYesNo(a.Value) + m.Bracket = &v + case "size": + v := ParseSymbolSize(a.Value) + m.Size = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeLevel(m *Level, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.Reference != nil { + el.CreateAttr("reference", (*m.Reference).String()) + } + if m.Parentheses != nil { + el.CreateAttr("parentheses", (*m.Parentheses).String()) + } + if m.Bracket != nil { + el.CreateAttr("bracket", (*m.Bracket).String()) + } + if m.Size != nil { + el.CreateAttr("size", (*m.Size).String()) + } +} diff --git a/gen/test/go/mx/line_width.go b/gen/test/go/mx/line_width.go new file mode 100644 index 000000000..7128d90e5 --- /dev/null +++ b/gen/test/go/mx/line_width.go @@ -0,0 +1,46 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The line-width type indicates the width of a line type in tenths. The type attribute defines what +// type of line is being defined. Values include beam, bracket, dashes, enclosure, ending, extend, +// heavy barline, leger, light barline, octave shift, pedal, slur middle, slur tip, staff, stem, tie +// middle, tie tip, tuplet bracket, and wedge. The text content is expressed in tenths. +type LineWidth struct { + Type *LineWidthType // attribute "type" + Value Tenths // text content +} + +func parseLineWidth(el *etree.Element) (*LineWidth, error) { + m := &LineWidth{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseLineWidthType(a.Value) + m.Type = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseTenths(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeLineWidth(m *LineWidth, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } +} diff --git a/gen/test/go/mx/link.go b/gen/test/go/mx/link.go new file mode 100644 index 000000000..b063b324f --- /dev/null +++ b/gen/test/go/mx/link.go @@ -0,0 +1,126 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The link type serves as an outgoing simple XLink. It is also used to connect a MusicXML score +// with a MusicXML opus. If a relative link is used within a document that is part of a compressed +// MusicXML file, the link is relative to the root folder of the zip file. +type Link struct { + Name *string // attribute "name" + XlinkHref *string // attribute "xlink:href" + XlinkType *string // attribute "xlink:type" + XlinkRole *string // attribute "xlink:role" + XlinkTitle *string // attribute "xlink:title" + XlinkShow *string // attribute "xlink:show" + XlinkActuate *string // attribute "xlink:actuate" + Element *string // attribute "element" + Position *int // attribute "position" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" +} + +func parseLink(el *etree.Element) (*Link, error) { + m := &Link{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "name": + v := a.Value + m.Name = &v + case "xlink:href": + v := a.Value + m.XlinkHref = &v + case "xlink:type": + v := a.Value + m.XlinkType = &v + case "xlink:role": + v := a.Value + m.XlinkRole = &v + case "xlink:title": + v := a.Value + m.XlinkTitle = &v + case "xlink:show": + v := a.Value + m.XlinkShow = &v + case "xlink:actuate": + v := a.Value + m.XlinkActuate = &v + case "element": + v := a.Value + m.Element = &v + case "position": + v := parseInt(a.Value) + m.Position = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeLink(m *Link, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Name != nil { + el.CreateAttr("name", (*m.Name)) + } + if m.XlinkHref != nil { + el.CreateAttr("xlink:href", (*m.XlinkHref)) + } + if m.XlinkType != nil { + el.CreateAttr("xlink:type", (*m.XlinkType)) + } + if m.XlinkRole != nil { + el.CreateAttr("xlink:role", (*m.XlinkRole)) + } + if m.XlinkTitle != nil { + el.CreateAttr("xlink:title", (*m.XlinkTitle)) + } + if m.XlinkShow != nil { + el.CreateAttr("xlink:show", (*m.XlinkShow)) + } + if m.XlinkActuate != nil { + el.CreateAttr("xlink:actuate", (*m.XlinkActuate)) + } + if m.Element != nil { + el.CreateAttr("element", (*m.Element)) + } + if m.Position != nil { + el.CreateAttr("position", formatInt((*m.Position))) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } +} diff --git a/gen/test/go/mx/lyric.go b/gen/test/go/mx/lyric.go new file mode 100644 index 000000000..90589aa90 --- /dev/null +++ b/gen/test/go/mx/lyric.go @@ -0,0 +1,228 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The lyric type represents text underlays for lyrics, based on Humdrum with support for other +// formats. Two text elements that are not separated by an elision element are part of the same +// syllable, but may have different text formatting. The MusicXML XSD is more strict than the DTD in +// enforcing this by disallowing a second syllabic element unless preceded by an elision element. +// The lyric number indicates multiple lines, though a name can be used as well (as in Finale's +// verse / chorus / section specification). Justification is center by default; placement is below +// by default. The print-object attribute can override a note's print-lyric attribute in cases where +// only some lyrics on a note are printed, as when lyrics for later verses are printed in a block of +// text rather than with each note. The time-only attribute precisely specifies which lyrics are to +// be sung which time through a repeated section. +type Lyric struct { + Number *string // attribute "number" + Name *string // attribute "name" + TimeOnly *TimeOnly // attribute "time-only" + Justify *LeftCenterRight // attribute "justify" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + Placement *AboveBelow // attribute "placement" + Color *Color // attribute "color" + PrintObject *YesNo // attribute "print-object" + ID *string // attribute "id" + Children []LyricChild // child elements in document order +} + +// LyricChild is one child element of Lyric: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type LyricChild struct { + Syllabic *Syllabic + Text *TextElementData + Elision *Elision + Extend *Extend + Laughing *Empty + Humming *Empty + EndLine *Empty + EndParagraph *Empty + Footnote *FormattedText + Level *Level +} + +func parseLyric(el *etree.Element) (*Lyric, error) { + m := &Lyric{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "number": + v := a.Value + m.Number = &v + case "name": + v := a.Value + m.Name = &v + case "time-only": + v := ParseTimeOnly(a.Value) + m.TimeOnly = &v + case "justify": + v := ParseLeftCenterRight(a.Value) + m.Justify = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "print-object": + v := ParseYesNo(a.Value) + m.PrintObject = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "syllabic": + v := ParseSyllabic(c.Text()) + m.Children = append(m.Children, LyricChild{Syllabic: &v}) + case "text": + v, err := parseTextElementData(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, LyricChild{Text: v}) + case "elision": + v, err := parseElision(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, LyricChild{Elision: v}) + case "extend": + v, err := parseExtend(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, LyricChild{Extend: v}) + case "laughing": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, LyricChild{Laughing: v}) + case "humming": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, LyricChild{Humming: v}) + case "end-line": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, LyricChild{EndLine: v}) + case "end-paragraph": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, LyricChild{EndParagraph: v}) + case "footnote": + v, err := parseFormattedText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, LyricChild{Footnote: v}) + case "level": + v, err := parseLevel(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, LyricChild{Level: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeLyric(m *Lyric, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Number != nil { + el.CreateAttr("number", (*m.Number)) + } + if m.Name != nil { + el.CreateAttr("name", (*m.Name)) + } + if m.TimeOnly != nil { + el.CreateAttr("time-only", (*m.TimeOnly).String()) + } + if m.Justify != nil { + el.CreateAttr("justify", (*m.Justify).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.PrintObject != nil { + el.CreateAttr("print-object", (*m.PrintObject).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.Syllabic != nil: + el.CreateElement("syllabic").SetText((*ch.Syllabic).String()) + case ch.Text != nil: + serializeTextElementData(ch.Text, el, "text") + case ch.Elision != nil: + serializeElision(ch.Elision, el, "elision") + case ch.Extend != nil: + serializeExtend(ch.Extend, el, "extend") + case ch.Laughing != nil: + serializeEmpty(ch.Laughing, el, "laughing") + case ch.Humming != nil: + serializeEmpty(ch.Humming, el, "humming") + case ch.EndLine != nil: + serializeEmpty(ch.EndLine, el, "end-line") + case ch.EndParagraph != nil: + serializeEmpty(ch.EndParagraph, el, "end-paragraph") + case ch.Footnote != nil: + serializeFormattedText(ch.Footnote, el, "footnote") + case ch.Level != nil: + serializeLevel(ch.Level, el, "level") + } + } +} diff --git a/gen/test/go/mx/lyric_font.go b/gen/test/go/mx/lyric_font.go new file mode 100644 index 000000000..fb90e5f3a --- /dev/null +++ b/gen/test/go/mx/lyric_font.go @@ -0,0 +1,75 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The lyric-font type specifies the default font for a particular name and number of lyric. +type LyricFont struct { + Number *string // attribute "number" + Name *string // attribute "name" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" +} + +func parseLyricFont(el *etree.Element) (*LyricFont, error) { + m := &LyricFont{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "number": + v := a.Value + m.Number = &v + case "name": + v := a.Value + m.Name = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeLyricFont(m *LyricFont, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Number != nil { + el.CreateAttr("number", (*m.Number)) + } + if m.Name != nil { + el.CreateAttr("name", (*m.Name)) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } +} diff --git a/gen/test/go/mx/lyric_language.go b/gen/test/go/mx/lyric_language.go new file mode 100644 index 000000000..a18d31dff --- /dev/null +++ b/gen/test/go/mx/lyric_language.go @@ -0,0 +1,54 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The lyric-language type specifies the default language for a particular name and number of lyric. +type LyricLanguage struct { + Number *string // attribute "number" + Name *string // attribute "name" + XMLLang *string // attribute "xml:lang" +} + +func parseLyricLanguage(el *etree.Element) (*LyricLanguage, error) { + m := &LyricLanguage{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "number": + v := a.Value + m.Number = &v + case "name": + v := a.Value + m.Name = &v + case "xml:lang": + v := a.Value + m.XMLLang = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeLyricLanguage(m *LyricLanguage, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Number != nil { + el.CreateAttr("number", (*m.Number)) + } + if m.Name != nil { + el.CreateAttr("name", (*m.Name)) + } + if m.XMLLang != nil { + el.CreateAttr("xml:lang", (*m.XMLLang)) + } +} diff --git a/gen/test/go/mx/measure_layout.go b/gen/test/go/mx/measure_layout.go new file mode 100644 index 000000000..73c230fe9 --- /dev/null +++ b/gen/test/go/mx/measure_layout.go @@ -0,0 +1,54 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The measure-layout type includes the horizontal distance from the previous measure. +type MeasureLayout struct { + Children []MeasureLayoutChild // child elements in document order +} + +// MeasureLayoutChild is one child element of MeasureLayout: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type MeasureLayoutChild struct { + MeasureDistance *Tenths +} + +func parseMeasureLayout(el *etree.Element) (*MeasureLayout, error) { + m := &MeasureLayout{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "measure-distance": + v := ParseTenths(c.Text()) + m.Children = append(m.Children, MeasureLayoutChild{MeasureDistance: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeMeasureLayout(m *MeasureLayout, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.MeasureDistance != nil: + el.CreateElement("measure-distance").SetText((*ch.MeasureDistance).String()) + } + } +} diff --git a/gen/test/go/mx/measure_numbering.go b/gen/test/go/mx/measure_numbering.go new file mode 100644 index 000000000..10e2c5b28 --- /dev/null +++ b/gen/test/go/mx/measure_numbering.go @@ -0,0 +1,116 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The measure-numbering type describes how frequently measure numbers are displayed on this part. +// The number attribute from the measure element is used for printing. Measures with an implicit +// attribute set to "yes" never display a measure number, regardless of the measure-numbering +// setting. +type MeasureNumbering struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" + Value MeasureNumberingValue // text content +} + +func parseMeasureNumbering(el *etree.Element) (*MeasureNumbering, error) { + m := &MeasureNumbering{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseMeasureNumberingValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeMeasureNumbering(m *MeasureNumbering, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } +} diff --git a/gen/test/go/mx/measure_repeat.go b/gen/test/go/mx/measure_repeat.go new file mode 100644 index 000000000..63aa22b47 --- /dev/null +++ b/gen/test/go/mx/measure_repeat.go @@ -0,0 +1,56 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The measure-repeat type is used for both single and multiple measure repeats. The text of the +// element indicates the number of measures to be repeated in a single pattern. The slashes +// attribute specifies the number of slashes to use in the repeat sign. It is 1 if not specified. +// Both the start and the stop of the measure-repeat must be specified. The text of the element is +// ignored when the type is stop. The measure-repeat element specifies a notation style for +// repetitions. The actual music being repeated needs to be repeated within the MusicXML file. This +// element specifies the notation that indicates the repeat. +type MeasureRepeat struct { + Type *StartStop // attribute "type" + Slashes *int // attribute "slashes" + Value PositiveIntegerOrEmpty // text content +} + +func parseMeasureRepeat(el *etree.Element) (*MeasureRepeat, error) { + m := &MeasureRepeat{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStartStop(a.Value) + m.Type = &v + case "slashes": + v := parseInt(a.Value) + m.Slashes = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParsePositiveIntegerOrEmpty(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeMeasureRepeat(m *MeasureRepeat, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Slashes != nil { + el.CreateAttr("slashes", formatInt((*m.Slashes))) + } +} diff --git a/gen/test/go/mx/measure_style.go b/gen/test/go/mx/measure_style.go new file mode 100644 index 000000000..b8810395c --- /dev/null +++ b/gen/test/go/mx/measure_style.go @@ -0,0 +1,139 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// A measure-style indicates a special way to print partial to multiple measures within a part. This +// includes multiple rests over several measures, repeats of beats, single, or multiple measures, +// and use of slash notation. The multiple-rest and measure-repeat symbols indicate the number of +// measures covered in the element content. The beat-repeat and slash elements can cover partial +// measures. All but the multiple-rest element use a type attribute to indicate starting and +// stopping the use of the style. The optional number attribute specifies the staff number from top +// to bottom on the system, as with clef. +type MeasureStyle struct { + Number *StaffNumber // attribute "number" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + ID *string // attribute "id" + Children []MeasureStyleChild // child elements in document order +} + +// MeasureStyleChild is one child element of MeasureStyle: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type MeasureStyleChild struct { + MultipleRest *MultipleRest + MeasureRepeat *MeasureRepeat + BeatRepeat *BeatRepeat + Slash *Slash +} + +func parseMeasureStyle(el *etree.Element) (*MeasureStyle, error) { + m := &MeasureStyle{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "number": + v := ParseStaffNumber(a.Value) + m.Number = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "multiple-rest": + v, err := parseMultipleRest(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, MeasureStyleChild{MultipleRest: v}) + case "measure-repeat": + v, err := parseMeasureRepeat(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, MeasureStyleChild{MeasureRepeat: v}) + case "beat-repeat": + v, err := parseBeatRepeat(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, MeasureStyleChild{BeatRepeat: v}) + case "slash": + v, err := parseSlash(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, MeasureStyleChild{Slash: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeMeasureStyle(m *MeasureStyle, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.MultipleRest != nil: + serializeMultipleRest(ch.MultipleRest, el, "multiple-rest") + case ch.MeasureRepeat != nil: + serializeMeasureRepeat(ch.MeasureRepeat, el, "measure-repeat") + case ch.BeatRepeat != nil: + serializeBeatRepeat(ch.BeatRepeat, el, "beat-repeat") + case ch.Slash != nil: + serializeSlash(ch.Slash, el, "slash") + } + } +} diff --git a/gen/test/go/mx/metronome.go b/gen/test/go/mx/metronome.go new file mode 100644 index 000000000..72e821f91 --- /dev/null +++ b/gen/test/go/mx/metronome.go @@ -0,0 +1,209 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The metronome type represents metronome marks and other metric relationships. The beat-unit group +// and per-minute element specify regular metronome marks. The metronome-note and metronome-relation +// elements allow for the specification of metric modulations and other metric relationships, such +// as swing tempo marks where two eighths are equated to a quarter note / eighth note triplet. Tied +// notes can be represented in both types of metronome marks by using the beat-unit-tied and +// metronome-tied elements. The parentheses attribute indicates whether or not to put the metronome +// mark in parentheses; its value is no if not specified. +type Metronome struct { + Parentheses *YesNo // attribute "parentheses" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" + Justify *LeftCenterRight // attribute "justify" + ID *string // attribute "id" + Children []MetronomeChild // child elements in document order +} + +// MetronomeChild is one child element of Metronome: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type MetronomeChild struct { + BeatUnit *NoteTypeValue + BeatUnitDot *Empty + BeatUnitTied *BeatUnitTied + PerMinute *PerMinute + MetronomeArrows *Empty + MetronomeNote *MetronomeNote + MetronomeRelation *string +} + +func parseMetronome(el *etree.Element) (*Metronome, error) { + m := &Metronome{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "parentheses": + v := ParseYesNo(a.Value) + m.Parentheses = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + case "justify": + v := ParseLeftCenterRight(a.Value) + m.Justify = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "beat-unit": + v := ParseNoteTypeValue(c.Text()) + m.Children = append(m.Children, MetronomeChild{BeatUnit: &v}) + case "beat-unit-dot": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, MetronomeChild{BeatUnitDot: v}) + case "beat-unit-tied": + v, err := parseBeatUnitTied(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, MetronomeChild{BeatUnitTied: v}) + case "per-minute": + v, err := parsePerMinute(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, MetronomeChild{PerMinute: v}) + case "metronome-arrows": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, MetronomeChild{MetronomeArrows: v}) + case "metronome-note": + v, err := parseMetronomeNote(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, MetronomeChild{MetronomeNote: v}) + case "metronome-relation": + v := c.Text() + m.Children = append(m.Children, MetronomeChild{MetronomeRelation: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeMetronome(m *Metronome, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Parentheses != nil { + el.CreateAttr("parentheses", (*m.Parentheses).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } + if m.Justify != nil { + el.CreateAttr("justify", (*m.Justify).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.BeatUnit != nil: + el.CreateElement("beat-unit").SetText((*ch.BeatUnit).String()) + case ch.BeatUnitDot != nil: + serializeEmpty(ch.BeatUnitDot, el, "beat-unit-dot") + case ch.BeatUnitTied != nil: + serializeBeatUnitTied(ch.BeatUnitTied, el, "beat-unit-tied") + case ch.PerMinute != nil: + serializePerMinute(ch.PerMinute, el, "per-minute") + case ch.MetronomeArrows != nil: + serializeEmpty(ch.MetronomeArrows, el, "metronome-arrows") + case ch.MetronomeNote != nil: + serializeMetronomeNote(ch.MetronomeNote, el, "metronome-note") + case ch.MetronomeRelation != nil: + el.CreateElement("metronome-relation").SetText((*ch.MetronomeRelation)) + } + } +} diff --git a/gen/test/go/mx/metronome_beam.go b/gen/test/go/mx/metronome_beam.go new file mode 100644 index 000000000..aeb34bbbd --- /dev/null +++ b/gen/test/go/mx/metronome_beam.go @@ -0,0 +1,44 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The metronome-beam type works like the beam type in defining metric relationships, but does not +// include all the attributes available in the beam type. +type MetronomeBeam struct { + Number *BeamLevel // attribute "number" + Value BeamValue // text content +} + +func parseMetronomeBeam(el *etree.Element) (*MetronomeBeam, error) { + m := &MetronomeBeam{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "number": + v := ParseBeamLevel(a.Value) + m.Number = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseBeamValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeMetronomeBeam(m *MetronomeBeam, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } +} diff --git a/gen/test/go/mx/metronome_note.go b/gen/test/go/mx/metronome_note.go new file mode 100644 index 000000000..577483934 --- /dev/null +++ b/gen/test/go/mx/metronome_note.go @@ -0,0 +1,90 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The metronome-note type defines the appearance of a note within a metric relationship mark. +type MetronomeNote struct { + Children []MetronomeNoteChild // child elements in document order +} + +// MetronomeNoteChild is one child element of MetronomeNote: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type MetronomeNoteChild struct { + MetronomeType *NoteTypeValue + MetronomeDot *Empty + MetronomeBeam *MetronomeBeam + MetronomeTied *MetronomeTied + MetronomeTuplet *MetronomeTuplet +} + +func parseMetronomeNote(el *etree.Element) (*MetronomeNote, error) { + m := &MetronomeNote{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "metronome-type": + v := ParseNoteTypeValue(c.Text()) + m.Children = append(m.Children, MetronomeNoteChild{MetronomeType: &v}) + case "metronome-dot": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, MetronomeNoteChild{MetronomeDot: v}) + case "metronome-beam": + v, err := parseMetronomeBeam(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, MetronomeNoteChild{MetronomeBeam: v}) + case "metronome-tied": + v, err := parseMetronomeTied(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, MetronomeNoteChild{MetronomeTied: v}) + case "metronome-tuplet": + v, err := parseMetronomeTuplet(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, MetronomeNoteChild{MetronomeTuplet: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeMetronomeNote(m *MetronomeNote, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.MetronomeType != nil: + el.CreateElement("metronome-type").SetText((*ch.MetronomeType).String()) + case ch.MetronomeDot != nil: + serializeEmpty(ch.MetronomeDot, el, "metronome-dot") + case ch.MetronomeBeam != nil: + serializeMetronomeBeam(ch.MetronomeBeam, el, "metronome-beam") + case ch.MetronomeTied != nil: + serializeMetronomeTied(ch.MetronomeTied, el, "metronome-tied") + case ch.MetronomeTuplet != nil: + serializeMetronomeTuplet(ch.MetronomeTuplet, el, "metronome-tuplet") + } + } +} diff --git a/gen/test/go/mx/metronome_tied.go b/gen/test/go/mx/metronome_tied.go new file mode 100644 index 000000000..c1c5dd098 --- /dev/null +++ b/gen/test/go/mx/metronome_tied.go @@ -0,0 +1,42 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The metronome-tied indicates the presence of a tie within a metric relationship mark. As with the +// tied element, both the start and stop of the tie should be specified, in this case within +// separate metronome-note elements. +type MetronomeTied struct { + Type *StartStop // attribute "type" +} + +func parseMetronomeTied(el *etree.Element) (*MetronomeTied, error) { + m := &MetronomeTied{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStartStop(a.Value) + m.Type = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeMetronomeTied(m *MetronomeTied, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } +} diff --git a/gen/test/go/mx/metronome_tuplet.go b/gen/test/go/mx/metronome_tuplet.go new file mode 100644 index 000000000..3d6a96976 --- /dev/null +++ b/gen/test/go/mx/metronome_tuplet.go @@ -0,0 +1,86 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The metronome-tuplet type uses the same element structure as the time-modification element along +// with some attributes from the tuplet element. +type MetronomeTuplet struct { + TimeModification + Type *StartStop // attribute "type" + Bracket *YesNo // attribute "bracket" + ShowNumber *ShowTuplet // attribute "show-number" +} + +func parseMetronomeTuplet(el *etree.Element) (*MetronomeTuplet, error) { + m := &MetronomeTuplet{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStartStop(a.Value) + m.Type = &v + case "bracket": + v := ParseYesNo(a.Value) + m.Bracket = &v + case "show-number": + v := ParseShowTuplet(a.Value) + m.ShowNumber = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "actual-notes": + v := parseInt(c.Text()) + m.Children = append(m.Children, TimeModificationChild{ActualNotes: &v}) + case "normal-notes": + v := parseInt(c.Text()) + m.Children = append(m.Children, TimeModificationChild{NormalNotes: &v}) + case "normal-type": + v := ParseNoteTypeValue(c.Text()) + m.Children = append(m.Children, TimeModificationChild{NormalType: &v}) + case "normal-dot": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TimeModificationChild{NormalDot: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeMetronomeTuplet(m *MetronomeTuplet, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Bracket != nil { + el.CreateAttr("bracket", (*m.Bracket).String()) + } + if m.ShowNumber != nil { + el.CreateAttr("show-number", (*m.ShowNumber).String()) + } + for _, ch := range m.Children { + switch { + case ch.ActualNotes != nil: + el.CreateElement("actual-notes").SetText(formatInt((*ch.ActualNotes))) + case ch.NormalNotes != nil: + el.CreateElement("normal-notes").SetText(formatInt((*ch.NormalNotes))) + case ch.NormalType != nil: + el.CreateElement("normal-type").SetText((*ch.NormalType).String()) + case ch.NormalDot != nil: + serializeEmpty(ch.NormalDot, el, "normal-dot") + } + } +} diff --git a/gen/test/go/mx/midi_device.go b/gen/test/go/mx/midi_device.go new file mode 100644 index 000000000..95bb445ae --- /dev/null +++ b/gen/test/go/mx/midi_device.go @@ -0,0 +1,55 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The midi-device type corresponds to the DeviceName meta event in Standard MIDI Files. The +// optional port attribute is a number from 1 to 16 that can be used with the unofficial MIDI port +// (or cable) meta event. Unlike the DeviceName meta event, there can be multiple midi-device +// elements per MusicXML part starting in MusicXML 3.0. The optional id attribute refers to the +// score-instrument assigned to this device. If missing, the device assignment affects all +// score-instrument elements in the score-part. +type MIDIDevice struct { + Port *MIDI16 // attribute "port" + ID *string // attribute "id" + Value string // text content +} + +func parseMIDIDevice(el *etree.Element) (*MIDIDevice, error) { + m := &MIDIDevice{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "port": + v := ParseMIDI16(a.Value) + m.Port = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeMIDIDevice(m *MIDIDevice, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.Port != nil { + el.CreateAttr("port", (*m.Port).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/midi_instrument.go b/gen/test/go/mx/midi_instrument.go new file mode 100644 index 000000000..a3cf1030e --- /dev/null +++ b/gen/test/go/mx/midi_instrument.go @@ -0,0 +1,105 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The midi-instrument type defines MIDI 1.0 instrument playback. The midi-instrument element can be +// a part of either the score-instrument element at the start of a part, or the sound element within +// a part. The id attribute refers to the score-instrument affected by the change. +type MIDIInstrument struct { + ID *string // attribute "id" + Children []MIDIInstrumentChild // child elements in document order +} + +// MIDIInstrumentChild is one child element of MIDIInstrument: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type MIDIInstrumentChild struct { + MIDIChannel *MIDI16 + MIDIName *string + MIDIBank *MIDI16384 + MIDIProgram *MIDI128 + MIDIUnpitched *MIDI128 + Volume *Percent + Pan *RotationDegrees + Elevation *RotationDegrees +} + +func parseMIDIInstrument(el *etree.Element) (*MIDIInstrument, error) { + m := &MIDIInstrument{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "midi-channel": + v := ParseMIDI16(c.Text()) + m.Children = append(m.Children, MIDIInstrumentChild{MIDIChannel: &v}) + case "midi-name": + v := c.Text() + m.Children = append(m.Children, MIDIInstrumentChild{MIDIName: &v}) + case "midi-bank": + v := ParseMIDI16384(c.Text()) + m.Children = append(m.Children, MIDIInstrumentChild{MIDIBank: &v}) + case "midi-program": + v := ParseMIDI128(c.Text()) + m.Children = append(m.Children, MIDIInstrumentChild{MIDIProgram: &v}) + case "midi-unpitched": + v := ParseMIDI128(c.Text()) + m.Children = append(m.Children, MIDIInstrumentChild{MIDIUnpitched: &v}) + case "volume": + v := ParsePercent(c.Text()) + m.Children = append(m.Children, MIDIInstrumentChild{Volume: &v}) + case "pan": + v := ParseRotationDegrees(c.Text()) + m.Children = append(m.Children, MIDIInstrumentChild{Pan: &v}) + case "elevation": + v := ParseRotationDegrees(c.Text()) + m.Children = append(m.Children, MIDIInstrumentChild{Elevation: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeMIDIInstrument(m *MIDIInstrument, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.MIDIChannel != nil: + el.CreateElement("midi-channel").SetText((*ch.MIDIChannel).String()) + case ch.MIDIName != nil: + el.CreateElement("midi-name").SetText((*ch.MIDIName)) + case ch.MIDIBank != nil: + el.CreateElement("midi-bank").SetText((*ch.MIDIBank).String()) + case ch.MIDIProgram != nil: + el.CreateElement("midi-program").SetText((*ch.MIDIProgram).String()) + case ch.MIDIUnpitched != nil: + el.CreateElement("midi-unpitched").SetText((*ch.MIDIUnpitched).String()) + case ch.Volume != nil: + el.CreateElement("volume").SetText((*ch.Volume).String()) + case ch.Pan != nil: + el.CreateElement("pan").SetText((*ch.Pan).String()) + case ch.Elevation != nil: + el.CreateElement("elevation").SetText((*ch.Elevation).String()) + } + } +} diff --git a/gen/test/go/mx/miscellaneous.go b/gen/test/go/mx/miscellaneous.go new file mode 100644 index 000000000..64547405e --- /dev/null +++ b/gen/test/go/mx/miscellaneous.go @@ -0,0 +1,59 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// If a program has other metadata not yet supported in the MusicXML format, it can go in the +// miscellaneous element. The miscellaneous type puts each separate part of metadata into its own +// miscellaneous-field type. +type Miscellaneous struct { + Children []MiscellaneousChild // child elements in document order +} + +// MiscellaneousChild is one child element of Miscellaneous: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type MiscellaneousChild struct { + MiscellaneousField *MiscellaneousField +} + +func parseMiscellaneous(el *etree.Element) (*Miscellaneous, error) { + m := &Miscellaneous{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "miscellaneous-field": + v, err := parseMiscellaneousField(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, MiscellaneousChild{MiscellaneousField: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeMiscellaneous(m *Miscellaneous, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.MiscellaneousField != nil: + serializeMiscellaneousField(ch.MiscellaneousField, el, "miscellaneous-field") + } + } +} diff --git a/gen/test/go/mx/miscellaneous_field.go b/gen/test/go/mx/miscellaneous_field.go new file mode 100644 index 000000000..30ce8a38e --- /dev/null +++ b/gen/test/go/mx/miscellaneous_field.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// If a program has other metadata not yet supported in the MusicXML format, each type of metadata +// can go in a miscellaneous-field element. The required name attribute indicates the type of +// metadata the element content represents. +type MiscellaneousField struct { + Name *string // attribute "name" + Value string // text content +} + +func parseMiscellaneousField(el *etree.Element) (*MiscellaneousField, error) { + m := &MiscellaneousField{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "name": + v := a.Value + m.Name = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeMiscellaneousField(m *MiscellaneousField, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.Name != nil { + el.CreateAttr("name", (*m.Name)) + } +} diff --git a/gen/test/go/mx/mordent.go b/gen/test/go/mx/mordent.go new file mode 100644 index 000000000..1c9f25ecb --- /dev/null +++ b/gen/test/go/mx/mordent.go @@ -0,0 +1,160 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The mordent type is used for both represents the mordent sign with the vertical line and the +// inverted-mordent sign without the line. The long attribute is "no" by default. The approach and +// departure attributes are used for compound ornaments, indicating how the beginning and ending of +// the ornament look relative to the main part of the mordent. +type Mordent struct { + EmptyTrillSound + Long *YesNo // attribute "long" + Approach *AboveBelow // attribute "approach" + Departure *AboveBelow // attribute "departure" +} + +func parseMordent(el *etree.Element) (*Mordent, error) { + m := &Mordent{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "start-note": + v := ParseStartNote(a.Value) + m.StartNote = &v + case "trill-step": + v := ParseTrillStep(a.Value) + m.TrillStep = &v + case "two-note-turn": + v := ParseTwoNoteTurn(a.Value) + m.TwoNoteTurn = &v + case "accelerate": + v := ParseYesNo(a.Value) + m.Accelerate = &v + case "beats": + v := ParseTrillBeats(a.Value) + m.Beats = &v + case "second-beat": + v := ParsePercent(a.Value) + m.SecondBeat = &v + case "last-beat": + v := ParsePercent(a.Value) + m.LastBeat = &v + case "long": + v := ParseYesNo(a.Value) + m.Long = &v + case "approach": + v := ParseAboveBelow(a.Value) + m.Approach = &v + case "departure": + v := ParseAboveBelow(a.Value) + m.Departure = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeMordent(m *Mordent, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.StartNote != nil { + el.CreateAttr("start-note", (*m.StartNote).String()) + } + if m.TrillStep != nil { + el.CreateAttr("trill-step", (*m.TrillStep).String()) + } + if m.TwoNoteTurn != nil { + el.CreateAttr("two-note-turn", (*m.TwoNoteTurn).String()) + } + if m.Accelerate != nil { + el.CreateAttr("accelerate", (*m.Accelerate).String()) + } + if m.Beats != nil { + el.CreateAttr("beats", (*m.Beats).String()) + } + if m.SecondBeat != nil { + el.CreateAttr("second-beat", (*m.SecondBeat).String()) + } + if m.LastBeat != nil { + el.CreateAttr("last-beat", (*m.LastBeat).String()) + } + if m.Long != nil { + el.CreateAttr("long", (*m.Long).String()) + } + if m.Approach != nil { + el.CreateAttr("approach", (*m.Approach).String()) + } + if m.Departure != nil { + el.CreateAttr("departure", (*m.Departure).String()) + } +} diff --git a/gen/test/go/mx/multiple_rest.go b/gen/test/go/mx/multiple_rest.go new file mode 100644 index 000000000..89dfb206d --- /dev/null +++ b/gen/test/go/mx/multiple_rest.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The text of the multiple-rest type indicates the number of measures in the multiple rest. +// Multiple rests may use the 1-bar / 2-bar / 4-bar rest symbols, or a single shape. The use-symbols +// attribute indicates which to use; it is no if not specified. +type MultipleRest struct { + UseSymbols *YesNo // attribute "use-symbols" + Value PositiveIntegerOrEmpty // text content +} + +func parseMultipleRest(el *etree.Element) (*MultipleRest, error) { + m := &MultipleRest{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "use-symbols": + v := ParseYesNo(a.Value) + m.UseSymbols = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParsePositiveIntegerOrEmpty(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeMultipleRest(m *MultipleRest, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.UseSymbols != nil { + el.CreateAttr("use-symbols", (*m.UseSymbols).String()) + } +} diff --git a/gen/test/go/mx/name_display.go b/gen/test/go/mx/name_display.go new file mode 100644 index 000000000..a7f2f014f --- /dev/null +++ b/gen/test/go/mx/name_display.go @@ -0,0 +1,76 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The name-display type is used for exact formatting of multi-font text in part and group names to +// the left of the system. The print-object attribute can be used to determine what, if anything, is +// printed at the start of each system. Enclosure for the display-text element is none by default. +// Language for the display-text element is Italian ("it") by default. +type NameDisplay struct { + PrintObject *YesNo // attribute "print-object" + Children []NameDisplayChild // child elements in document order +} + +// NameDisplayChild is one child element of NameDisplay: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type NameDisplayChild struct { + DisplayText *FormattedText + AccidentalText *AccidentalText +} + +func parseNameDisplay(el *etree.Element) (*NameDisplay, error) { + m := &NameDisplay{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "print-object": + v := ParseYesNo(a.Value) + m.PrintObject = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "display-text": + v, err := parseFormattedText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NameDisplayChild{DisplayText: v}) + case "accidental-text": + v, err := parseAccidentalText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NameDisplayChild{AccidentalText: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeNameDisplay(m *NameDisplay, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.PrintObject != nil { + el.CreateAttr("print-object", (*m.PrintObject).String()) + } + for _, ch := range m.Children { + switch { + case ch.DisplayText != nil: + serializeFormattedText(ch.DisplayText, el, "display-text") + case ch.AccidentalText != nil: + serializeAccidentalText(ch.AccidentalText, el, "accidental-text") + } + } +} diff --git a/gen/test/go/mx/non_arpeggiate.go b/gen/test/go/mx/non_arpeggiate.go new file mode 100644 index 000000000..d86bcc1d2 --- /dev/null +++ b/gen/test/go/mx/non_arpeggiate.go @@ -0,0 +1,98 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The non-arpeggiate type indicates that this note is at the top or bottom of a bracket indicating +// to not arpeggiate these notes. Since this does not involve playback, it is only used on the top +// or bottom notes, not on each note as for the arpeggiate type. +type NonArpeggiate struct { + Type *TopBottom // attribute "type" + Number *NumberLevel // attribute "number" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + Placement *AboveBelow // attribute "placement" + Color *Color // attribute "color" + ID *string // attribute "id" +} + +func parseNonArpeggiate(el *etree.Element) (*NonArpeggiate, error) { + m := &NonArpeggiate{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseTopBottom(a.Value) + m.Type = &v + case "number": + v := ParseNumberLevel(a.Value) + m.Number = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeNonArpeggiate(m *NonArpeggiate, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/notations.go b/gen/test/go/mx/notations.go new file mode 100644 index 000000000..19d850b0c --- /dev/null +++ b/gen/test/go/mx/notations.go @@ -0,0 +1,209 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Notations refer to musical notations, not XML notations. Multiple notations are allowed in order +// to represent multiple editorial levels. The print-object attribute, added in Version 3.0, allows +// notations to represent details of performance technique, such as fingerings, without having them +// appear in the score. +type Notations struct { + PrintObject *YesNo // attribute "print-object" + ID *string // attribute "id" + Children []NotationsChild // child elements in document order +} + +// NotationsChild is one child element of Notations: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type NotationsChild struct { + Footnote *FormattedText + Level *Level + Tied *Tied + Slur *Slur + Tuplet *Tuplet + Glissando *Glissando + Slide *Slide + Ornaments *Ornaments + Technical *Technical + Articulations *Articulations + Dynamics *Dynamics + Fermata *Fermata + Arpeggiate *Arpeggiate + NonArpeggiate *NonArpeggiate + AccidentalMark *AccidentalMark + OtherNotation *OtherNotation +} + +func parseNotations(el *etree.Element) (*Notations, error) { + m := &Notations{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "print-object": + v := ParseYesNo(a.Value) + m.PrintObject = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "footnote": + v, err := parseFormattedText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NotationsChild{Footnote: v}) + case "level": + v, err := parseLevel(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NotationsChild{Level: v}) + case "tied": + v, err := parseTied(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NotationsChild{Tied: v}) + case "slur": + v, err := parseSlur(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NotationsChild{Slur: v}) + case "tuplet": + v, err := parseTuplet(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NotationsChild{Tuplet: v}) + case "glissando": + v, err := parseGlissando(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NotationsChild{Glissando: v}) + case "slide": + v, err := parseSlide(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NotationsChild{Slide: v}) + case "ornaments": + v, err := parseOrnaments(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NotationsChild{Ornaments: v}) + case "technical": + v, err := parseTechnical(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NotationsChild{Technical: v}) + case "articulations": + v, err := parseArticulations(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NotationsChild{Articulations: v}) + case "dynamics": + v, err := parseDynamics(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NotationsChild{Dynamics: v}) + case "fermata": + v, err := parseFermata(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NotationsChild{Fermata: v}) + case "arpeggiate": + v, err := parseArpeggiate(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NotationsChild{Arpeggiate: v}) + case "non-arpeggiate": + v, err := parseNonArpeggiate(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NotationsChild{NonArpeggiate: v}) + case "accidental-mark": + v, err := parseAccidentalMark(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NotationsChild{AccidentalMark: v}) + case "other-notation": + v, err := parseOtherNotation(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NotationsChild{OtherNotation: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeNotations(m *Notations, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.PrintObject != nil { + el.CreateAttr("print-object", (*m.PrintObject).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.Footnote != nil: + serializeFormattedText(ch.Footnote, el, "footnote") + case ch.Level != nil: + serializeLevel(ch.Level, el, "level") + case ch.Tied != nil: + serializeTied(ch.Tied, el, "tied") + case ch.Slur != nil: + serializeSlur(ch.Slur, el, "slur") + case ch.Tuplet != nil: + serializeTuplet(ch.Tuplet, el, "tuplet") + case ch.Glissando != nil: + serializeGlissando(ch.Glissando, el, "glissando") + case ch.Slide != nil: + serializeSlide(ch.Slide, el, "slide") + case ch.Ornaments != nil: + serializeOrnaments(ch.Ornaments, el, "ornaments") + case ch.Technical != nil: + serializeTechnical(ch.Technical, el, "technical") + case ch.Articulations != nil: + serializeArticulations(ch.Articulations, el, "articulations") + case ch.Dynamics != nil: + serializeDynamics(ch.Dynamics, el, "dynamics") + case ch.Fermata != nil: + serializeFermata(ch.Fermata, el, "fermata") + case ch.Arpeggiate != nil: + serializeArpeggiate(ch.Arpeggiate, el, "arpeggiate") + case ch.NonArpeggiate != nil: + serializeNonArpeggiate(ch.NonArpeggiate, el, "non-arpeggiate") + case ch.AccidentalMark != nil: + serializeAccidentalMark(ch.AccidentalMark, el, "accidental-mark") + case ch.OtherNotation != nil: + serializeOtherNotation(ch.OtherNotation, el, "other-notation") + } + } +} diff --git a/gen/test/go/mx/note.go b/gen/test/go/mx/note.go new file mode 100644 index 000000000..fde796c8c --- /dev/null +++ b/gen/test/go/mx/note.go @@ -0,0 +1,422 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Notes are the most common type of MusicXML data. The MusicXML format keeps the MuseData +// distinction between elements used for sound information and elements used for notation +// information (e.g., tie is used for sound, tied for notation). Thus grace notes do not have a +// duration element. Cue notes have a duration element, as do forward elements, but no tie elements. +// Having these two types of information available can make interchange considerably easier, as some +// programs handle one type of information much more readily than the other. The print-leger +// attribute is used to indicate whether leger lines are printed. Notes without leger lines are used +// to indicate indeterminate high and low notes. By default, it is set to yes. If print-object is +// set to no, print-leger is interpreted to also be set to no if not present. This attribute is +// ignored for rests. The dynamics and end-dynamics attributes correspond to MIDI 1.0's Note On and +// Note Off velocities, respectively. They are expressed in terms of percentages of the default +// forte value (90 for MIDI 1.0). The attack and release attributes are used to alter the starting +// and stopping time of the note from when it would otherwise occur based on the flow of durations - +// information that is specific to a performance. They are expressed in terms of divisions, either +// positive or negative. A note that starts a tie should not have a release attribute, and a note +// that stops a tie should not have an attack attribute. The attack and release attributes are +// independent of each other. The attack attribute only changes the starting time of a note, and the +// release attribute only changes the stopping time of a note. If a note is played only particular +// times through a repeat, the time-only attribute shows which times to play the note. The pizzicato +// attribute is used when just this note is sounded pizzicato, vs. the pizzicato element which +// changes overall playback between pizzicato and arco. +type Note struct { + PrintLeger *YesNo // attribute "print-leger" + Dynamics *NonNegativeDecimal // attribute "dynamics" + EndDynamics *NonNegativeDecimal // attribute "end-dynamics" + Attack *Divisions // attribute "attack" + Release *Divisions // attribute "release" + TimeOnly *TimeOnly // attribute "time-only" + Pizzicato *YesNo // attribute "pizzicato" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + PrintDot *YesNo // attribute "print-dot" + PrintLyric *YesNo // attribute "print-lyric" + PrintObject *YesNo // attribute "print-object" + PrintSpacing *YesNo // attribute "print-spacing" + ID *string // attribute "id" + Children []NoteChild // child elements in document order +} + +// NoteChild is one child element of Note: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type NoteChild struct { + Grace *Grace + Chord *Empty + Pitch *Pitch + Unpitched *Unpitched + Rest *Rest + Tie *Tie + Cue *Empty + Duration *PositiveDivisions + Instrument *Instrument + Footnote *FormattedText + Level *Level + Voice *string + Type *NoteType + Dot *EmptyPlacement + Accidental *Accidental + TimeModification *TimeModification + Stem *Stem + Notehead *Notehead + NoteheadText *NoteheadText + Staff *int + Beam *Beam + Notations *Notations + Lyric *Lyric + Play *Play +} + +func parseNote(el *etree.Element) (*Note, error) { + m := &Note{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "print-leger": + v := ParseYesNo(a.Value) + m.PrintLeger = &v + case "dynamics": + v := ParseNonNegativeDecimal(a.Value) + m.Dynamics = &v + case "end-dynamics": + v := ParseNonNegativeDecimal(a.Value) + m.EndDynamics = &v + case "attack": + v := ParseDivisions(a.Value) + m.Attack = &v + case "release": + v := ParseDivisions(a.Value) + m.Release = &v + case "time-only": + v := ParseTimeOnly(a.Value) + m.TimeOnly = &v + case "pizzicato": + v := ParseYesNo(a.Value) + m.Pizzicato = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "print-dot": + v := ParseYesNo(a.Value) + m.PrintDot = &v + case "print-lyric": + v := ParseYesNo(a.Value) + m.PrintLyric = &v + case "print-object": + v := ParseYesNo(a.Value) + m.PrintObject = &v + case "print-spacing": + v := ParseYesNo(a.Value) + m.PrintSpacing = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "grace": + v, err := parseGrace(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{Grace: v}) + case "chord": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{Chord: v}) + case "pitch": + v, err := parsePitch(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{Pitch: v}) + case "unpitched": + v, err := parseUnpitched(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{Unpitched: v}) + case "rest": + v, err := parseRest(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{Rest: v}) + case "tie": + v, err := parseTie(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{Tie: v}) + case "cue": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{Cue: v}) + case "duration": + v := ParsePositiveDivisions(c.Text()) + m.Children = append(m.Children, NoteChild{Duration: &v}) + case "instrument": + v, err := parseInstrument(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{Instrument: v}) + case "footnote": + v, err := parseFormattedText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{Footnote: v}) + case "level": + v, err := parseLevel(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{Level: v}) + case "voice": + v := c.Text() + m.Children = append(m.Children, NoteChild{Voice: &v}) + case "type": + v, err := parseNoteType(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{Type: v}) + case "dot": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{Dot: v}) + case "accidental": + v, err := parseAccidental(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{Accidental: v}) + case "time-modification": + v, err := parseTimeModification(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{TimeModification: v}) + case "stem": + v, err := parseStem(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{Stem: v}) + case "notehead": + v, err := parseNotehead(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{Notehead: v}) + case "notehead-text": + v, err := parseNoteheadText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{NoteheadText: v}) + case "staff": + v := parseInt(c.Text()) + m.Children = append(m.Children, NoteChild{Staff: &v}) + case "beam": + v, err := parseBeam(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{Beam: v}) + case "notations": + v, err := parseNotations(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{Notations: v}) + case "lyric": + v, err := parseLyric(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{Lyric: v}) + case "play": + v, err := parsePlay(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteChild{Play: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeNote(m *Note, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.PrintLeger != nil { + el.CreateAttr("print-leger", (*m.PrintLeger).String()) + } + if m.Dynamics != nil { + el.CreateAttr("dynamics", (*m.Dynamics).String()) + } + if m.EndDynamics != nil { + el.CreateAttr("end-dynamics", (*m.EndDynamics).String()) + } + if m.Attack != nil { + el.CreateAttr("attack", (*m.Attack).String()) + } + if m.Release != nil { + el.CreateAttr("release", (*m.Release).String()) + } + if m.TimeOnly != nil { + el.CreateAttr("time-only", (*m.TimeOnly).String()) + } + if m.Pizzicato != nil { + el.CreateAttr("pizzicato", (*m.Pizzicato).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.PrintDot != nil { + el.CreateAttr("print-dot", (*m.PrintDot).String()) + } + if m.PrintLyric != nil { + el.CreateAttr("print-lyric", (*m.PrintLyric).String()) + } + if m.PrintObject != nil { + el.CreateAttr("print-object", (*m.PrintObject).String()) + } + if m.PrintSpacing != nil { + el.CreateAttr("print-spacing", (*m.PrintSpacing).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.Grace != nil: + serializeGrace(ch.Grace, el, "grace") + case ch.Chord != nil: + serializeEmpty(ch.Chord, el, "chord") + case ch.Pitch != nil: + serializePitch(ch.Pitch, el, "pitch") + case ch.Unpitched != nil: + serializeUnpitched(ch.Unpitched, el, "unpitched") + case ch.Rest != nil: + serializeRest(ch.Rest, el, "rest") + case ch.Tie != nil: + serializeTie(ch.Tie, el, "tie") + case ch.Cue != nil: + serializeEmpty(ch.Cue, el, "cue") + case ch.Duration != nil: + el.CreateElement("duration").SetText((*ch.Duration).String()) + case ch.Instrument != nil: + serializeInstrument(ch.Instrument, el, "instrument") + case ch.Footnote != nil: + serializeFormattedText(ch.Footnote, el, "footnote") + case ch.Level != nil: + serializeLevel(ch.Level, el, "level") + case ch.Voice != nil: + el.CreateElement("voice").SetText((*ch.Voice)) + case ch.Type != nil: + serializeNoteType(ch.Type, el, "type") + case ch.Dot != nil: + serializeEmptyPlacement(ch.Dot, el, "dot") + case ch.Accidental != nil: + serializeAccidental(ch.Accidental, el, "accidental") + case ch.TimeModification != nil: + serializeTimeModification(ch.TimeModification, el, "time-modification") + case ch.Stem != nil: + serializeStem(ch.Stem, el, "stem") + case ch.Notehead != nil: + serializeNotehead(ch.Notehead, el, "notehead") + case ch.NoteheadText != nil: + serializeNoteheadText(ch.NoteheadText, el, "notehead-text") + case ch.Staff != nil: + el.CreateElement("staff").SetText(formatInt((*ch.Staff))) + case ch.Beam != nil: + serializeBeam(ch.Beam, el, "beam") + case ch.Notations != nil: + serializeNotations(ch.Notations, el, "notations") + case ch.Lyric != nil: + serializeLyric(ch.Lyric, el, "lyric") + case ch.Play != nil: + serializePlay(ch.Play, el, "play") + } + } +} diff --git a/gen/test/go/mx/note_size.go b/gen/test/go/mx/note_size.go new file mode 100644 index 000000000..b0d1cdabb --- /dev/null +++ b/gen/test/go/mx/note_size.go @@ -0,0 +1,48 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The note-size type indicates the percentage of the regular note size to use for notes with a cue +// and large size as defined in the type element. The grace type is used for notes of cue size that +// that include a grace element. The cue type is used for all other notes with cue size, whether +// defined explicitly or implicitly via a cue element. The large type is used for notes of large +// size. The text content represent the numeric percentage. A value of 100 would be identical to the +// size of a regular note as defined by the music font. +type NoteSize struct { + Type *NoteSizeType // attribute "type" + Value NonNegativeDecimal // text content +} + +func parseNoteSize(el *etree.Element) (*NoteSize, error) { + m := &NoteSize{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseNoteSizeType(a.Value) + m.Type = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseNonNegativeDecimal(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeNoteSize(m *NoteSize, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } +} diff --git a/gen/test/go/mx/note_type.go b/gen/test/go/mx/note_type.go new file mode 100644 index 000000000..2a66215ea --- /dev/null +++ b/gen/test/go/mx/note_type.go @@ -0,0 +1,46 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The note-type type indicates the graphic note type. Values range from 1024th to maxima. The size +// attribute indicates full, cue, grace-cue, or large size. The default is full for regular notes, +// grace-cue for notes that contain both grace and cue elements, and cue for notes that contain +// either a cue or a grace element, but not both. +type NoteType struct { + Size *SymbolSize // attribute "size" + Value NoteTypeValue // text content +} + +func parseNoteType(el *etree.Element) (*NoteType, error) { + m := &NoteType{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "size": + v := ParseSymbolSize(a.Value) + m.Size = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseNoteTypeValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeNoteType(m *NoteType, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Size != nil { + el.CreateAttr("size", (*m.Size).String()) + } +} diff --git a/gen/test/go/mx/notehead.go b/gen/test/go/mx/notehead.go new file mode 100644 index 000000000..369c364c3 --- /dev/null +++ b/gen/test/go/mx/notehead.go @@ -0,0 +1,100 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The notehead type indicates shapes other than the open and closed ovals associated with note +// durations. The smufl attribute can be used to specify a particular notehead, allowing application +// interoperability without requiring every SMuFL glyph to have a MusicXML element equivalent. This +// attribute can be used either with the "other" value, or to refine a specific notehead value such +// as "cluster". Noteheads in the SMuFL "Note name noteheads" range (U+E150–U+E1AF) should not use +// the smufl attribute or the "other" value, but instead use the notehead-text element. For the +// enclosed shapes, the default is to be hollow for half notes and longer, and filled otherwise. The +// filled attribute can be set to change this if needed. If the parentheses attribute is set to yes, +// the notehead is parenthesized. It is no by default. +type Notehead struct { + Filled *YesNo // attribute "filled" + Parentheses *YesNo // attribute "parentheses" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + SMUFL *SMUFLGlyphName // attribute "smufl" + Value NoteheadValue // text content +} + +func parseNotehead(el *etree.Element) (*Notehead, error) { + m := &Notehead{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "filled": + v := ParseYesNo(a.Value) + m.Filled = &v + case "parentheses": + v := ParseYesNo(a.Value) + m.Parentheses = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "smufl": + v := ParseSMUFLGlyphName(a.Value) + m.SMUFL = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseNoteheadValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeNotehead(m *Notehead, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Filled != nil { + el.CreateAttr("filled", (*m.Filled).String()) + } + if m.Parentheses != nil { + el.CreateAttr("parentheses", (*m.Parentheses).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.SMUFL != nil { + el.CreateAttr("smufl", (*m.SMUFL).String()) + } +} diff --git a/gen/test/go/mx/notehead_text.go b/gen/test/go/mx/notehead_text.go new file mode 100644 index 000000000..4d3abddab --- /dev/null +++ b/gen/test/go/mx/notehead_text.go @@ -0,0 +1,69 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The notehead-text type represents text that is displayed inside a notehead, as is done in some +// educational music. It is not needed for the numbers used in tablature or jianpu notation. The +// presence of a TAB or jianpu clefs is sufficient to indicate that numbers are used. The +// display-text and accidental-text elements allow display of fully formatted text and accidentals. +type NoteheadText struct { + Children []NoteheadTextChild // child elements in document order +} + +// NoteheadTextChild is one child element of NoteheadText: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type NoteheadTextChild struct { + DisplayText *FormattedText + AccidentalText *AccidentalText +} + +func parseNoteheadText(el *etree.Element) (*NoteheadText, error) { + m := &NoteheadText{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "display-text": + v, err := parseFormattedText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteheadTextChild{DisplayText: v}) + case "accidental-text": + v, err := parseAccidentalText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, NoteheadTextChild{AccidentalText: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeNoteheadText(m *NoteheadText, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.DisplayText != nil: + serializeFormattedText(ch.DisplayText, el, "display-text") + case ch.AccidentalText != nil: + serializeAccidentalText(ch.AccidentalText, el, "accidental-text") + } + } +} diff --git a/gen/test/go/mx/octave_shift.go b/gen/test/go/mx/octave_shift.go new file mode 100644 index 000000000..10d72d109 --- /dev/null +++ b/gen/test/go/mx/octave_shift.go @@ -0,0 +1,141 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The octave shift type indicates where notes are shifted up or down from their true pitched values +// because of printing difficulty. Thus a treble clef line noted with 8va will be indicated with an +// octave-shift down from the pitch data indicated in the notes. A size of 8 indicates one octave; a +// size of 15 indicates two octaves. +type OctaveShift struct { + Type *UpDownStopContinue // attribute "type" + Number *NumberLevel // attribute "number" + Size *int // attribute "size" + DashLength *Tenths // attribute "dash-length" + SpaceLength *Tenths // attribute "space-length" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + ID *string // attribute "id" +} + +func parseOctaveShift(el *etree.Element) (*OctaveShift, error) { + m := &OctaveShift{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseUpDownStopContinue(a.Value) + m.Type = &v + case "number": + v := ParseNumberLevel(a.Value) + m.Number = &v + case "size": + v := parseInt(a.Value) + m.Size = &v + case "dash-length": + v := ParseTenths(a.Value) + m.DashLength = &v + case "space-length": + v := ParseTenths(a.Value) + m.SpaceLength = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeOctaveShift(m *OctaveShift, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.Size != nil { + el.CreateAttr("size", formatInt((*m.Size))) + } + if m.DashLength != nil { + el.CreateAttr("dash-length", (*m.DashLength).String()) + } + if m.SpaceLength != nil { + el.CreateAttr("space-length", (*m.SpaceLength).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/offset.go b/gen/test/go/mx/offset.go new file mode 100644 index 000000000..a76aab137 --- /dev/null +++ b/gen/test/go/mx/offset.go @@ -0,0 +1,49 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// An offset is represented in terms of divisions, and indicates where the direction will appear +// relative to the current musical location. This affects the visual appearance of the direction. If +// the sound attribute is "yes", then the offset affects playback too. If the sound attribute is +// "no", then any sound associated with the direction takes effect at the current location. The +// sound attribute is "no" by default for compatibility with earlier versions of the MusicXML +// format. If an element within a direction includes a default-x attribute, the offset value will be +// ignored when determining the appearance of that element. +type Offset struct { + Sound *YesNo // attribute "sound" + Value Divisions // text content +} + +func parseOffset(el *etree.Element) (*Offset, error) { + m := &Offset{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "sound": + v := ParseYesNo(a.Value) + m.Sound = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseDivisions(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeOffset(m *Offset, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Sound != nil { + el.CreateAttr("sound", (*m.Sound).String()) + } +} diff --git a/gen/test/go/mx/opus.go b/gen/test/go/mx/opus.go new file mode 100644 index 000000000..323b5199d --- /dev/null +++ b/gen/test/go/mx/opus.go @@ -0,0 +1,76 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The opus type represents a link to a MusicXML opus document that composes multiple MusicXML +// scores into a collection. +type Opus struct { + XlinkHref *string // attribute "xlink:href" + XlinkType *string // attribute "xlink:type" + XlinkRole *string // attribute "xlink:role" + XlinkTitle *string // attribute "xlink:title" + XlinkShow *string // attribute "xlink:show" + XlinkActuate *string // attribute "xlink:actuate" +} + +func parseOpus(el *etree.Element) (*Opus, error) { + m := &Opus{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "xlink:href": + v := a.Value + m.XlinkHref = &v + case "xlink:type": + v := a.Value + m.XlinkType = &v + case "xlink:role": + v := a.Value + m.XlinkRole = &v + case "xlink:title": + v := a.Value + m.XlinkTitle = &v + case "xlink:show": + v := a.Value + m.XlinkShow = &v + case "xlink:actuate": + v := a.Value + m.XlinkActuate = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeOpus(m *Opus, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.XlinkHref != nil { + el.CreateAttr("xlink:href", (*m.XlinkHref)) + } + if m.XlinkType != nil { + el.CreateAttr("xlink:type", (*m.XlinkType)) + } + if m.XlinkRole != nil { + el.CreateAttr("xlink:role", (*m.XlinkRole)) + } + if m.XlinkTitle != nil { + el.CreateAttr("xlink:title", (*m.XlinkTitle)) + } + if m.XlinkShow != nil { + el.CreateAttr("xlink:show", (*m.XlinkShow)) + } + if m.XlinkActuate != nil { + el.CreateAttr("xlink:actuate", (*m.XlinkActuate)) + } +} diff --git a/gen/test/go/mx/ornaments.go b/gen/test/go/mx/ornaments.go new file mode 100644 index 000000000..a29f211ca --- /dev/null +++ b/gen/test/go/mx/ornaments.go @@ -0,0 +1,201 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Ornaments can be any of several types, followed optionally by accidentals. The accidental-mark +// element's content is represented the same as an accidental element, but with a different name to +// reflect the different musical meaning. +type Ornaments struct { + ID *string // attribute "id" + Children []OrnamentsChild // child elements in document order +} + +// OrnamentsChild is one child element of Ornaments: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type OrnamentsChild struct { + TrillMark *EmptyTrillSound + Turn *HorizontalTurn + DelayedTurn *HorizontalTurn + InvertedTurn *HorizontalTurn + DelayedInvertedTurn *HorizontalTurn + VerticalTurn *EmptyTrillSound + InvertedVerticalTurn *EmptyTrillSound + Shake *EmptyTrillSound + WavyLine *WavyLine + Mordent *Mordent + InvertedMordent *Mordent + Schleifer *EmptyPlacement + Tremolo *Tremolo + Haydn *EmptyTrillSound + OtherOrnament *OtherPlacementText + AccidentalMark *AccidentalMark +} + +func parseOrnaments(el *etree.Element) (*Ornaments, error) { + m := &Ornaments{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "trill-mark": + v, err := parseEmptyTrillSound(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, OrnamentsChild{TrillMark: v}) + case "turn": + v, err := parseHorizontalTurn(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, OrnamentsChild{Turn: v}) + case "delayed-turn": + v, err := parseHorizontalTurn(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, OrnamentsChild{DelayedTurn: v}) + case "inverted-turn": + v, err := parseHorizontalTurn(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, OrnamentsChild{InvertedTurn: v}) + case "delayed-inverted-turn": + v, err := parseHorizontalTurn(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, OrnamentsChild{DelayedInvertedTurn: v}) + case "vertical-turn": + v, err := parseEmptyTrillSound(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, OrnamentsChild{VerticalTurn: v}) + case "inverted-vertical-turn": + v, err := parseEmptyTrillSound(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, OrnamentsChild{InvertedVerticalTurn: v}) + case "shake": + v, err := parseEmptyTrillSound(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, OrnamentsChild{Shake: v}) + case "wavy-line": + v, err := parseWavyLine(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, OrnamentsChild{WavyLine: v}) + case "mordent": + v, err := parseMordent(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, OrnamentsChild{Mordent: v}) + case "inverted-mordent": + v, err := parseMordent(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, OrnamentsChild{InvertedMordent: v}) + case "schleifer": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, OrnamentsChild{Schleifer: v}) + case "tremolo": + v, err := parseTremolo(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, OrnamentsChild{Tremolo: v}) + case "haydn": + v, err := parseEmptyTrillSound(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, OrnamentsChild{Haydn: v}) + case "other-ornament": + v, err := parseOtherPlacementText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, OrnamentsChild{OtherOrnament: v}) + case "accidental-mark": + v, err := parseAccidentalMark(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, OrnamentsChild{AccidentalMark: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeOrnaments(m *Ornaments, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.TrillMark != nil: + serializeEmptyTrillSound(ch.TrillMark, el, "trill-mark") + case ch.Turn != nil: + serializeHorizontalTurn(ch.Turn, el, "turn") + case ch.DelayedTurn != nil: + serializeHorizontalTurn(ch.DelayedTurn, el, "delayed-turn") + case ch.InvertedTurn != nil: + serializeHorizontalTurn(ch.InvertedTurn, el, "inverted-turn") + case ch.DelayedInvertedTurn != nil: + serializeHorizontalTurn(ch.DelayedInvertedTurn, el, "delayed-inverted-turn") + case ch.VerticalTurn != nil: + serializeEmptyTrillSound(ch.VerticalTurn, el, "vertical-turn") + case ch.InvertedVerticalTurn != nil: + serializeEmptyTrillSound(ch.InvertedVerticalTurn, el, "inverted-vertical-turn") + case ch.Shake != nil: + serializeEmptyTrillSound(ch.Shake, el, "shake") + case ch.WavyLine != nil: + serializeWavyLine(ch.WavyLine, el, "wavy-line") + case ch.Mordent != nil: + serializeMordent(ch.Mordent, el, "mordent") + case ch.InvertedMordent != nil: + serializeMordent(ch.InvertedMordent, el, "inverted-mordent") + case ch.Schleifer != nil: + serializeEmptyPlacement(ch.Schleifer, el, "schleifer") + case ch.Tremolo != nil: + serializeTremolo(ch.Tremolo, el, "tremolo") + case ch.Haydn != nil: + serializeEmptyTrillSound(ch.Haydn, el, "haydn") + case ch.OtherOrnament != nil: + serializeOtherPlacementText(ch.OtherOrnament, el, "other-ornament") + case ch.AccidentalMark != nil: + serializeAccidentalMark(ch.AccidentalMark, el, "accidental-mark") + } + } +} diff --git a/gen/test/go/mx/other_appearance.go b/gen/test/go/mx/other_appearance.go new file mode 100644 index 000000000..78d23355b --- /dev/null +++ b/gen/test/go/mx/other_appearance.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The other-appearance type is used to define any graphical settings not yet in the current version +// of the MusicXML format. This allows extended representation, though without application +// interoperability. +type OtherAppearance struct { + Type *string // attribute "type" + Value string // text content +} + +func parseOtherAppearance(el *etree.Element) (*OtherAppearance, error) { + m := &OtherAppearance{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := a.Value + m.Type = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeOtherAppearance(m *OtherAppearance, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.Type != nil { + el.CreateAttr("type", (*m.Type)) + } +} diff --git a/gen/test/go/mx/other_direction.go b/gen/test/go/mx/other_direction.go new file mode 100644 index 000000000..4824282f6 --- /dev/null +++ b/gen/test/go/mx/other_direction.go @@ -0,0 +1,138 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The other-direction type is used to define any direction symbols not yet in the MusicXML format. +// The smufl attribute can be used to specify a particular direction symbol, allowing application +// interoperability without requiring every SMuFL glyph to have a MusicXML element equivalent. Using +// the other-direction type without the smufl attribute allows for extended representation, though +// without application interoperability. +type OtherDirection struct { + PrintObject *YesNo // attribute "print-object" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" + SMUFL *SMUFLGlyphName // attribute "smufl" + ID *string // attribute "id" + Value string // text content +} + +func parseOtherDirection(el *etree.Element) (*OtherDirection, error) { + m := &OtherDirection{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "print-object": + v := ParseYesNo(a.Value) + m.PrintObject = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + case "smufl": + v := ParseSMUFLGlyphName(a.Value) + m.SMUFL = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeOtherDirection(m *OtherDirection, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.PrintObject != nil { + el.CreateAttr("print-object", (*m.PrintObject).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } + if m.SMUFL != nil { + el.CreateAttr("smufl", (*m.SMUFL).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/other_notation.go b/gen/test/go/mx/other_notation.go new file mode 100644 index 000000000..5804707be --- /dev/null +++ b/gen/test/go/mx/other_notation.go @@ -0,0 +1,146 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The other-notation type is used to define any notations not yet in the MusicXML format. It +// handles notations where more specific extension elements such as other-dynamics and +// other-technical are not appropriate. The smufl attribute can be used to specify a particular +// notation, allowing application interoperability without requiring every SMuFL glyph to have a +// MusicXML element equivalent. Using the other-notation type without the smufl attribute allows for +// extended representation, though without application interoperability. +type OtherNotation struct { + Type *StartStopSingle // attribute "type" + Number *NumberLevel // attribute "number" + PrintObject *YesNo // attribute "print-object" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" + SMUFL *SMUFLGlyphName // attribute "smufl" + ID *string // attribute "id" + Value string // text content +} + +func parseOtherNotation(el *etree.Element) (*OtherNotation, error) { + m := &OtherNotation{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStartStopSingle(a.Value) + m.Type = &v + case "number": + v := ParseNumberLevel(a.Value) + m.Number = &v + case "print-object": + v := ParseYesNo(a.Value) + m.PrintObject = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "smufl": + v := ParseSMUFLGlyphName(a.Value) + m.SMUFL = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeOtherNotation(m *OtherNotation, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.PrintObject != nil { + el.CreateAttr("print-object", (*m.PrintObject).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.SMUFL != nil { + el.CreateAttr("smufl", (*m.SMUFL).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/other_placement_text.go b/gen/test/go/mx/other_placement_text.go new file mode 100644 index 000000000..677c36869 --- /dev/null +++ b/gen/test/go/mx/other_placement_text.go @@ -0,0 +1,115 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The other-placement-text type represents a text element with print-style, placement, and smufl +// attribute groups. This type is used by MusicXML notation extension elements to allow +// specification of specific SMuFL glyphs without needed to add every glyph as a MusicXML element. +type OtherPlacementText struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" + SMUFL *SMUFLGlyphName // attribute "smufl" + Value string // text content +} + +func parseOtherPlacementText(el *etree.Element) (*OtherPlacementText, error) { + m := &OtherPlacementText{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "smufl": + v := ParseSMUFLGlyphName(a.Value) + m.SMUFL = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeOtherPlacementText(m *OtherPlacementText, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.SMUFL != nil { + el.CreateAttr("smufl", (*m.SMUFL).String()) + } +} diff --git a/gen/test/go/mx/other_play.go b/gen/test/go/mx/other_play.go new file mode 100644 index 000000000..fe7e8e534 --- /dev/null +++ b/gen/test/go/mx/other_play.go @@ -0,0 +1,44 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The other-play element represents other types of playback. The required type attribute indicates +// the type of playback to which the element content applies. +type OtherPlay struct { + Type *string // attribute "type" + Value string // text content +} + +func parseOtherPlay(el *etree.Element) (*OtherPlay, error) { + m := &OtherPlay{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := a.Value + m.Type = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeOtherPlay(m *OtherPlay, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.Type != nil { + el.CreateAttr("type", (*m.Type)) + } +} diff --git a/gen/test/go/mx/other_text.go b/gen/test/go/mx/other_text.go new file mode 100644 index 000000000..613d23253 --- /dev/null +++ b/gen/test/go/mx/other_text.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The other-text type represents a text element with a smufl attribute group. This type is used by +// MusicXML direction extension elements to allow specification of specific SMuFL glyphs without +// needed to add every glyph as a MusicXML element. +type OtherText struct { + SMUFL *SMUFLGlyphName // attribute "smufl" + Value string // text content +} + +func parseOtherText(el *etree.Element) (*OtherText, error) { + m := &OtherText{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "smufl": + v := ParseSMUFLGlyphName(a.Value) + m.SMUFL = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeOtherText(m *OtherText, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.SMUFL != nil { + el.CreateAttr("smufl", (*m.SMUFL).String()) + } +} diff --git a/gen/test/go/mx/page_layout.go b/gen/test/go/mx/page_layout.go new file mode 100644 index 000000000..b0c3ca7a2 --- /dev/null +++ b/gen/test/go/mx/page_layout.go @@ -0,0 +1,72 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Page layout can be defined both in score-wide defaults and in the print element. Page margins are +// specified either for both even and odd pages, or via separate odd and even page number values. +// The type is not needed when used as part of a print element. If omitted when used in the defaults +// element, "both" is the default. +type PageLayout struct { + Children []PageLayoutChild // child elements in document order +} + +// PageLayoutChild is one child element of PageLayout: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type PageLayoutChild struct { + PageHeight *Tenths + PageWidth *Tenths + PageMargins *PageMargins +} + +func parsePageLayout(el *etree.Element) (*PageLayout, error) { + m := &PageLayout{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "page-height": + v := ParseTenths(c.Text()) + m.Children = append(m.Children, PageLayoutChild{PageHeight: &v}) + case "page-width": + v := ParseTenths(c.Text()) + m.Children = append(m.Children, PageLayoutChild{PageWidth: &v}) + case "page-margins": + v, err := parsePageMargins(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PageLayoutChild{PageMargins: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializePageLayout(m *PageLayout, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.PageHeight != nil: + el.CreateElement("page-height").SetText((*ch.PageHeight).String()) + case ch.PageWidth != nil: + el.CreateElement("page-width").SetText((*ch.PageWidth).String()) + case ch.PageMargins != nil: + serializePageMargins(ch.PageMargins, el, "page-margins") + } + } +} diff --git a/gen/test/go/mx/page_margins.go b/gen/test/go/mx/page_margins.go new file mode 100644 index 000000000..11e261a83 --- /dev/null +++ b/gen/test/go/mx/page_margins.go @@ -0,0 +1,81 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Page margins are specified either for both even and odd pages, or via separate odd and even page +// number values. The type attribute is not needed when used as part of a print element. If omitted +// when the page-margins type is used in the defaults element, "both" is the default value. +type PageMargins struct { + Type *MarginType // attribute "type" + Children []PageMarginsChild // child elements in document order +} + +// PageMarginsChild is one child element of PageMargins: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type PageMarginsChild struct { + LeftMargin *Tenths + RightMargin *Tenths + TopMargin *Tenths + BottomMargin *Tenths +} + +func parsePageMargins(el *etree.Element) (*PageMargins, error) { + m := &PageMargins{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseMarginType(a.Value) + m.Type = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "left-margin": + v := ParseTenths(c.Text()) + m.Children = append(m.Children, PageMarginsChild{LeftMargin: &v}) + case "right-margin": + v := ParseTenths(c.Text()) + m.Children = append(m.Children, PageMarginsChild{RightMargin: &v}) + case "top-margin": + v := ParseTenths(c.Text()) + m.Children = append(m.Children, PageMarginsChild{TopMargin: &v}) + case "bottom-margin": + v := ParseTenths(c.Text()) + m.Children = append(m.Children, PageMarginsChild{BottomMargin: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializePageMargins(m *PageMargins, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + for _, ch := range m.Children { + switch { + case ch.LeftMargin != nil: + el.CreateElement("left-margin").SetText((*ch.LeftMargin).String()) + case ch.RightMargin != nil: + el.CreateElement("right-margin").SetText((*ch.RightMargin).String()) + case ch.TopMargin != nil: + el.CreateElement("top-margin").SetText((*ch.TopMargin).String()) + case ch.BottomMargin != nil: + el.CreateElement("bottom-margin").SetText((*ch.BottomMargin).String()) + } + } +} diff --git a/gen/test/go/mx/part_group.go b/gen/test/go/mx/part_group.go new file mode 100644 index 000000000..682725920 --- /dev/null +++ b/gen/test/go/mx/part_group.go @@ -0,0 +1,152 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The part-group element indicates groupings of parts in the score, usually indicated by braces and +// brackets. Braces that are used for multi-staff parts should be defined in the attributes element +// for that part. The part-group start element appears before the first score-part in the group. The +// part-group stop element appears after the last score-part in the group. The number attribute is +// used to distinguish overlapping and nested part-groups, not the sequence of groups. As with +// parts, groups can have a name and abbreviation. Values for the child elements are ignored at the +// stop of a group. A part-group element is not needed for a single multi-staff part. By default, +// multi-staff parts include a brace symbol and (if appropriate given the bar-style) common +// barlines. The symbol formatting for a multi-staff part can be more fully specified using the +// part-symbol element. +type PartGroup struct { + Type *StartStop // attribute "type" + Number *string // attribute "number" + Children []PartGroupChild // child elements in document order +} + +// PartGroupChild is one child element of PartGroup: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type PartGroupChild struct { + GroupName *GroupName + GroupNameDisplay *NameDisplay + GroupAbbreviation *GroupName + GroupAbbreviationDisplay *NameDisplay + GroupSymbol *GroupSymbol + GroupBarline *GroupBarline + GroupTime *Empty + Footnote *FormattedText + Level *Level +} + +func parsePartGroup(el *etree.Element) (*PartGroup, error) { + m := &PartGroup{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStartStop(a.Value) + m.Type = &v + case "number": + v := a.Value + m.Number = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "group-name": + v, err := parseGroupName(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartGroupChild{GroupName: v}) + case "group-name-display": + v, err := parseNameDisplay(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartGroupChild{GroupNameDisplay: v}) + case "group-abbreviation": + v, err := parseGroupName(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartGroupChild{GroupAbbreviation: v}) + case "group-abbreviation-display": + v, err := parseNameDisplay(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartGroupChild{GroupAbbreviationDisplay: v}) + case "group-symbol": + v, err := parseGroupSymbol(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartGroupChild{GroupSymbol: v}) + case "group-barline": + v, err := parseGroupBarline(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartGroupChild{GroupBarline: v}) + case "group-time": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartGroupChild{GroupTime: v}) + case "footnote": + v, err := parseFormattedText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartGroupChild{Footnote: v}) + case "level": + v, err := parseLevel(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartGroupChild{Level: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializePartGroup(m *PartGroup, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Number != nil { + el.CreateAttr("number", (*m.Number)) + } + for _, ch := range m.Children { + switch { + case ch.GroupName != nil: + serializeGroupName(ch.GroupName, el, "group-name") + case ch.GroupNameDisplay != nil: + serializeNameDisplay(ch.GroupNameDisplay, el, "group-name-display") + case ch.GroupAbbreviation != nil: + serializeGroupName(ch.GroupAbbreviation, el, "group-abbreviation") + case ch.GroupAbbreviationDisplay != nil: + serializeNameDisplay(ch.GroupAbbreviationDisplay, el, "group-abbreviation-display") + case ch.GroupSymbol != nil: + serializeGroupSymbol(ch.GroupSymbol, el, "group-symbol") + case ch.GroupBarline != nil: + serializeGroupBarline(ch.GroupBarline, el, "group-barline") + case ch.GroupTime != nil: + serializeEmpty(ch.GroupTime, el, "group-time") + case ch.Footnote != nil: + serializeFormattedText(ch.Footnote, el, "footnote") + case ch.Level != nil: + serializeLevel(ch.Level, el, "level") + } + } +} diff --git a/gen/test/go/mx/part_list.go b/gen/test/go/mx/part_list.go new file mode 100644 index 000000000..083c78411 --- /dev/null +++ b/gen/test/go/mx/part_list.go @@ -0,0 +1,71 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The part-list identifies the different musical parts in this movement. Each part has an ID that +// is used later within the musical data. Since parts may be encoded separately and combined later, +// identification elements are present at both the score and score-part levels. There must be at +// least one score-part, combined as desired with part-group elements that indicate braces and +// brackets. Parts are ordered from top to bottom in a score based on the order in which they appear +// in the part-list. +type PartList struct { + Children []PartListChild // child elements in document order +} + +// PartListChild is one child element of PartList: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type PartListChild struct { + PartGroup *PartGroup + ScorePart *ScorePart +} + +func parsePartList(el *etree.Element) (*PartList, error) { + m := &PartList{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "part-group": + v, err := parsePartGroup(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartListChild{PartGroup: v}) + case "score-part": + v, err := parseScorePart(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartListChild{ScorePart: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializePartList(m *PartList, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.PartGroup != nil: + serializePartGroup(ch.PartGroup, el, "part-group") + case ch.ScorePart != nil: + serializeScorePart(ch.ScorePart, el, "score-part") + } + } +} diff --git a/gen/test/go/mx/part_name.go b/gen/test/go/mx/part_name.go new file mode 100644 index 000000000..1001a67fd --- /dev/null +++ b/gen/test/go/mx/part_name.go @@ -0,0 +1,115 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The part-name type describes the name or abbreviation of a score-part element. Formatting +// attributes for the part-name element are deprecated in Version 2.0 in favor of the new +// part-name-display and part-abbreviation-display elements. +type PartName struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + PrintObject *YesNo // attribute "print-object" + Justify *LeftCenterRight // attribute "justify" + Value string // text content +} + +func parsePartName(el *etree.Element) (*PartName, error) { + m := &PartName{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "print-object": + v := ParseYesNo(a.Value) + m.PrintObject = &v + case "justify": + v := ParseLeftCenterRight(a.Value) + m.Justify = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializePartName(m *PartName, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.PrintObject != nil { + el.CreateAttr("print-object", (*m.PrintObject).String()) + } + if m.Justify != nil { + el.CreateAttr("justify", (*m.Justify).String()) + } +} diff --git a/gen/test/go/mx/part_symbol.go b/gen/test/go/mx/part_symbol.go new file mode 100644 index 000000000..c4d74611d --- /dev/null +++ b/gen/test/go/mx/part_symbol.go @@ -0,0 +1,89 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The part-symbol type indicates how a symbol for a multi-staff part is indicated in the score; +// brace is the default value. The top-staff and bottom-staff elements are used when the brace does +// not extend across the entire part. For example, in a 3-staff organ part, the top-staff will +// typically be 1 for the right hand, while the bottom-staff will typically be 2 for the left hand. +// Staff 3 for the pedals is usually outside the brace. +type PartSymbol struct { + TopStaff *StaffNumber // attribute "top-staff" + BottomStaff *StaffNumber // attribute "bottom-staff" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + Color *Color // attribute "color" + Value GroupSymbolValue // text content +} + +func parsePartSymbol(el *etree.Element) (*PartSymbol, error) { + m := &PartSymbol{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "top-staff": + v := ParseStaffNumber(a.Value) + m.TopStaff = &v + case "bottom-staff": + v := ParseStaffNumber(a.Value) + m.BottomStaff = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseGroupSymbolValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializePartSymbol(m *PartSymbol, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.TopStaff != nil { + el.CreateAttr("top-staff", (*m.TopStaff).String()) + } + if m.BottomStaff != nil { + el.CreateAttr("bottom-staff", (*m.BottomStaff).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/partwise_measure.go b/gen/test/go/mx/partwise_measure.go new file mode 100644 index 000000000..46f0902b1 --- /dev/null +++ b/gen/test/go/mx/partwise_measure.go @@ -0,0 +1,206 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +type PartwiseMeasure struct { + Number *string // attribute "number" + Text *MeasureText // attribute "text" + Implicit *YesNo // attribute "implicit" + NonControlling *YesNo // attribute "non-controlling" + Width *Tenths // attribute "width" + ID *string // attribute "id" + Children []PartwiseMeasureChild // child elements in document order +} + +// PartwiseMeasureChild is one child element of PartwiseMeasure: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type PartwiseMeasureChild struct { + Note *Note + Backup *Backup + Forward *Forward + Direction *Direction + Attributes *Attributes + Harmony *Harmony + FiguredBass *FiguredBass + Print *Print + Sound *Sound + Barline *Barline + Grouping *Grouping + Link *Link + Bookmark *Bookmark +} + +func parsePartwiseMeasure(el *etree.Element) (*PartwiseMeasure, error) { + m := &PartwiseMeasure{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "number": + v := a.Value + m.Number = &v + case "text": + v := ParseMeasureText(a.Value) + m.Text = &v + case "implicit": + v := ParseYesNo(a.Value) + m.Implicit = &v + case "non-controlling": + v := ParseYesNo(a.Value) + m.NonControlling = &v + case "width": + v := ParseTenths(a.Value) + m.Width = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "note": + v, err := parseNote(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartwiseMeasureChild{Note: v}) + case "backup": + v, err := parseBackup(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartwiseMeasureChild{Backup: v}) + case "forward": + v, err := parseForward(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartwiseMeasureChild{Forward: v}) + case "direction": + v, err := parseDirection(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartwiseMeasureChild{Direction: v}) + case "attributes": + v, err := parseAttributes(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartwiseMeasureChild{Attributes: v}) + case "harmony": + v, err := parseHarmony(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartwiseMeasureChild{Harmony: v}) + case "figured-bass": + v, err := parseFiguredBass(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartwiseMeasureChild{FiguredBass: v}) + case "print": + v, err := parsePrint(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartwiseMeasureChild{Print: v}) + case "sound": + v, err := parseSound(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartwiseMeasureChild{Sound: v}) + case "barline": + v, err := parseBarline(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartwiseMeasureChild{Barline: v}) + case "grouping": + v, err := parseGrouping(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartwiseMeasureChild{Grouping: v}) + case "link": + v, err := parseLink(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartwiseMeasureChild{Link: v}) + case "bookmark": + v, err := parseBookmark(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartwiseMeasureChild{Bookmark: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializePartwiseMeasure(m *PartwiseMeasure, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Number != nil { + el.CreateAttr("number", (*m.Number)) + } + if m.Text != nil { + el.CreateAttr("text", (*m.Text).String()) + } + if m.Implicit != nil { + el.CreateAttr("implicit", (*m.Implicit).String()) + } + if m.NonControlling != nil { + el.CreateAttr("non-controlling", (*m.NonControlling).String()) + } + if m.Width != nil { + el.CreateAttr("width", (*m.Width).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.Note != nil: + serializeNote(ch.Note, el, "note") + case ch.Backup != nil: + serializeBackup(ch.Backup, el, "backup") + case ch.Forward != nil: + serializeForward(ch.Forward, el, "forward") + case ch.Direction != nil: + serializeDirection(ch.Direction, el, "direction") + case ch.Attributes != nil: + serializeAttributes(ch.Attributes, el, "attributes") + case ch.Harmony != nil: + serializeHarmony(ch.Harmony, el, "harmony") + case ch.FiguredBass != nil: + serializeFiguredBass(ch.FiguredBass, el, "figured-bass") + case ch.Print != nil: + serializePrint(ch.Print, el, "print") + case ch.Sound != nil: + serializeSound(ch.Sound, el, "sound") + case ch.Barline != nil: + serializeBarline(ch.Barline, el, "barline") + case ch.Grouping != nil: + serializeGrouping(ch.Grouping, el, "grouping") + case ch.Link != nil: + serializeLink(ch.Link, el, "link") + case ch.Bookmark != nil: + serializeBookmark(ch.Bookmark, el, "bookmark") + } + } +} diff --git a/gen/test/go/mx/partwise_part.go b/gen/test/go/mx/partwise_part.go new file mode 100644 index 000000000..3859c0f6c --- /dev/null +++ b/gen/test/go/mx/partwise_part.go @@ -0,0 +1,63 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +type PartwisePart struct { + ID *string // attribute "id" + Children []PartwisePartChild // child elements in document order +} + +// PartwisePartChild is one child element of PartwisePart: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type PartwisePartChild struct { + Measure *PartwiseMeasure +} + +func parsePartwisePart(el *etree.Element) (*PartwisePart, error) { + m := &PartwisePart{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "measure": + v, err := parsePartwiseMeasure(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PartwisePartChild{Measure: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializePartwisePart(m *PartwisePart, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.Measure != nil: + serializePartwiseMeasure(ch.Measure, el, "measure") + } + } +} diff --git a/gen/test/go/mx/pedal.go b/gen/test/go/mx/pedal.go new file mode 100644 index 000000000..b8de24bc0 --- /dev/null +++ b/gen/test/go/mx/pedal.go @@ -0,0 +1,162 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The pedal type represents piano pedal marks. In MusicXML 3.1 this includes sostenuto as well as +// damper pedal marks. The line attribute is yes if pedal lines are used. The sign attribute is yes +// if Ped, Sost, and * signs are used. For MusicXML 2.0 compatibility, the sign attribute is yes by +// default if the line attribute is no, and is no by default if the line attribute is yes. If the +// sign attribute is set to yes and the type is start or sostenuto, the abbreviated attribute is yes +// if the short P and S signs are used, and no if the full Ped and Sost signs are used. It is no by +// default. Otherwise the abbreviated attribute is ignored. The change and continue types are used +// when the line attribute is yes. The change type indicates a pedal lift and retake indicated with +// an inverted V marking. The continue type allows more precise formatting across system breaks and +// for more complex pedaling lines. The alignment attributes are ignored if the line attribute is +// yes. +type Pedal struct { + Type *PedalType // attribute "type" + Number *NumberLevel // attribute "number" + Line *YesNo // attribute "line" + Sign *YesNo // attribute "sign" + Abbreviated *YesNo // attribute "abbreviated" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" + ID *string // attribute "id" +} + +func parsePedal(el *etree.Element) (*Pedal, error) { + m := &Pedal{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParsePedalType(a.Value) + m.Type = &v + case "number": + v := ParseNumberLevel(a.Value) + m.Number = &v + case "line": + v := ParseYesNo(a.Value) + m.Line = &v + case "sign": + v := ParseYesNo(a.Value) + m.Sign = &v + case "abbreviated": + v := ParseYesNo(a.Value) + m.Abbreviated = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializePedal(m *Pedal, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.Line != nil { + el.CreateAttr("line", (*m.Line).String()) + } + if m.Sign != nil { + el.CreateAttr("sign", (*m.Sign).String()) + } + if m.Abbreviated != nil { + el.CreateAttr("abbreviated", (*m.Abbreviated).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/pedal_tuning.go b/gen/test/go/mx/pedal_tuning.go new file mode 100644 index 000000000..6824e3ffd --- /dev/null +++ b/gen/test/go/mx/pedal_tuning.go @@ -0,0 +1,60 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The pedal-tuning type specifies the tuning of a single harp pedal. +type PedalTuning struct { + Children []PedalTuningChild // child elements in document order +} + +// PedalTuningChild is one child element of PedalTuning: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type PedalTuningChild struct { + PedalStep *Step + PedalAlter *Semitones +} + +func parsePedalTuning(el *etree.Element) (*PedalTuning, error) { + m := &PedalTuning{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "pedal-step": + v := ParseStep(c.Text()) + m.Children = append(m.Children, PedalTuningChild{PedalStep: &v}) + case "pedal-alter": + v := ParseSemitones(c.Text()) + m.Children = append(m.Children, PedalTuningChild{PedalAlter: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializePedalTuning(m *PedalTuning, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.PedalStep != nil: + el.CreateElement("pedal-step").SetText((*ch.PedalStep).String()) + case ch.PedalAlter != nil: + el.CreateElement("pedal-alter").SetText((*ch.PedalAlter).String()) + } + } +} diff --git a/gen/test/go/mx/per_minute.go b/gen/test/go/mx/per_minute.go new file mode 100644 index 000000000..8e1fcf43b --- /dev/null +++ b/gen/test/go/mx/per_minute.go @@ -0,0 +1,67 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The per-minute type can be a number, or a text description including numbers. If a font is +// specified, it overrides the font specified for the overall metronome element. This allows +// separate specification of a music font for the beat-unit and a text font for the numeric value, +// in cases where a single metronome font is not used. +type PerMinute struct { + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Value string // text content +} + +func parsePerMinute(el *etree.Element) (*PerMinute, error) { + m := &PerMinute{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializePerMinute(m *PerMinute, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } +} diff --git a/gen/test/go/mx/percussion.go b/gen/test/go/mx/percussion.go new file mode 100644 index 000000000..f579cf917 --- /dev/null +++ b/gen/test/go/mx/percussion.go @@ -0,0 +1,226 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The percussion element is used to define percussion pictogram symbols. Definitions for these +// symbols can be found in Kurt Stone's "Music Notation in the Twentieth Century" on pages 206-212 +// and 223. Some values are added to these based on how usage has evolved in the 30 years since +// Stone's book was published. +type Percussion struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" + Enclosure *EnclosureShape // attribute "enclosure" + ID *string // attribute "id" + Children []PercussionChild // child elements in document order +} + +// PercussionChild is one child element of Percussion: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type PercussionChild struct { + Glass *Glass + Metal *Metal + Wood *Wood + Pitched *Pitched + Membrane *Membrane + Effect *Effect + Timpani *Empty + Beater *Beater + Stick *Stick + StickLocation *StickLocation + OtherPercussion *OtherText +} + +func parsePercussion(el *etree.Element) (*Percussion, error) { + m := &Percussion{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + case "enclosure": + v := ParseEnclosureShape(a.Value) + m.Enclosure = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "glass": + v, err := parseGlass(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PercussionChild{Glass: v}) + case "metal": + v := ParseMetal(c.Text()) + m.Children = append(m.Children, PercussionChild{Metal: &v}) + case "wood": + v := ParseWood(c.Text()) + m.Children = append(m.Children, PercussionChild{Wood: &v}) + case "pitched": + v, err := parsePitched(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PercussionChild{Pitched: v}) + case "membrane": + v := ParseMembrane(c.Text()) + m.Children = append(m.Children, PercussionChild{Membrane: &v}) + case "effect": + v := ParseEffect(c.Text()) + m.Children = append(m.Children, PercussionChild{Effect: &v}) + case "timpani": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PercussionChild{Timpani: v}) + case "beater": + v, err := parseBeater(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PercussionChild{Beater: v}) + case "stick": + v, err := parseStick(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PercussionChild{Stick: v}) + case "stick-location": + v := ParseStickLocation(c.Text()) + m.Children = append(m.Children, PercussionChild{StickLocation: &v}) + case "other-percussion": + v, err := parseOtherText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PercussionChild{OtherPercussion: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializePercussion(m *Percussion, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } + if m.Enclosure != nil { + el.CreateAttr("enclosure", (*m.Enclosure).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.Glass != nil: + serializeGlass(ch.Glass, el, "glass") + case ch.Metal != nil: + el.CreateElement("metal").SetText((*ch.Metal).String()) + case ch.Wood != nil: + el.CreateElement("wood").SetText((*ch.Wood).String()) + case ch.Pitched != nil: + serializePitched(ch.Pitched, el, "pitched") + case ch.Membrane != nil: + el.CreateElement("membrane").SetText((*ch.Membrane).String()) + case ch.Effect != nil: + el.CreateElement("effect").SetText((*ch.Effect).String()) + case ch.Timpani != nil: + serializeEmpty(ch.Timpani, el, "timpani") + case ch.Beater != nil: + serializeBeater(ch.Beater, el, "beater") + case ch.Stick != nil: + serializeStick(ch.Stick, el, "stick") + case ch.StickLocation != nil: + el.CreateElement("stick-location").SetText((*ch.StickLocation).String()) + case ch.OtherPercussion != nil: + serializeOtherText(ch.OtherPercussion, el, "other-percussion") + } + } +} diff --git a/gen/test/go/mx/pitch.go b/gen/test/go/mx/pitch.go new file mode 100644 index 000000000..6689a2b38 --- /dev/null +++ b/gen/test/go/mx/pitch.go @@ -0,0 +1,67 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Pitch is represented as a combination of the step of the diatonic scale, the chromatic +// alteration, and the octave. +type Pitch struct { + Children []PitchChild // child elements in document order +} + +// PitchChild is one child element of Pitch: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type PitchChild struct { + Step *Step + Alter *Semitones + Octave *Octave +} + +func parsePitch(el *etree.Element) (*Pitch, error) { + m := &Pitch{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "step": + v := ParseStep(c.Text()) + m.Children = append(m.Children, PitchChild{Step: &v}) + case "alter": + v := ParseSemitones(c.Text()) + m.Children = append(m.Children, PitchChild{Alter: &v}) + case "octave": + v := ParseOctave(c.Text()) + m.Children = append(m.Children, PitchChild{Octave: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializePitch(m *Pitch, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.Step != nil: + el.CreateElement("step").SetText((*ch.Step).String()) + case ch.Alter != nil: + el.CreateElement("alter").SetText((*ch.Alter).String()) + case ch.Octave != nil: + el.CreateElement("octave").SetText((*ch.Octave).String()) + } + } +} diff --git a/gen/test/go/mx/pitched.go b/gen/test/go/mx/pitched.go new file mode 100644 index 000000000..b90fe6e6e --- /dev/null +++ b/gen/test/go/mx/pitched.go @@ -0,0 +1,45 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The pitched-value type represents pictograms for pitched percussion instruments. The smufl +// attribute is used to distinguish different SMuFL glyphs for a particular pictogram within the +// tuned mallet percussion pictograms range. +type Pitched struct { + SMUFL *SMUFLPictogramGlyphName // attribute "smufl" + Value PitchedValue // text content +} + +func parsePitched(el *etree.Element) (*Pitched, error) { + m := &Pitched{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "smufl": + v := ParseSMUFLPictogramGlyphName(a.Value) + m.SMUFL = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParsePitchedValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializePitched(m *Pitched, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.SMUFL != nil { + el.CreateAttr("smufl", (*m.SMUFL).String()) + } +} diff --git a/gen/test/go/mx/placement_text.go b/gen/test/go/mx/placement_text.go new file mode 100644 index 000000000..8489632b0 --- /dev/null +++ b/gen/test/go/mx/placement_text.go @@ -0,0 +1,107 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The placement-text type represents a text element with print-style and placement attribute +// groups. +type PlacementText struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" + Value string // text content +} + +func parsePlacementText(el *etree.Element) (*PlacementText, error) { + m := &PlacementText{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializePlacementText(m *PlacementText, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } +} diff --git a/gen/test/go/mx/play.go b/gen/test/go/mx/play.go new file mode 100644 index 000000000..102d88173 --- /dev/null +++ b/gen/test/go/mx/play.go @@ -0,0 +1,85 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The play type, new in Version 3.0, specifies playback techniques to be used in conjunction with +// the instrument-sound element. When used as part of a sound element, it applies to all notes going +// forward in score order. In multi-instrument parts, the affected instrument should be specified +// using the id attribute. When used as part of a note element, it applies to the current note only. +type Play struct { + ID *string // attribute "id" + Children []PlayChild // child elements in document order +} + +// PlayChild is one child element of Play: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type PlayChild struct { + Ipa *string + Mute *Mute + SemiPitched *SemiPitched + OtherPlay *OtherPlay +} + +func parsePlay(el *etree.Element) (*Play, error) { + m := &Play{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "ipa": + v := c.Text() + m.Children = append(m.Children, PlayChild{Ipa: &v}) + case "mute": + v := ParseMute(c.Text()) + m.Children = append(m.Children, PlayChild{Mute: &v}) + case "semi-pitched": + v := ParseSemiPitched(c.Text()) + m.Children = append(m.Children, PlayChild{SemiPitched: &v}) + case "other-play": + v, err := parseOtherPlay(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PlayChild{OtherPlay: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializePlay(m *Play, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.Ipa != nil: + el.CreateElement("ipa").SetText((*ch.Ipa)) + case ch.Mute != nil: + el.CreateElement("mute").SetText((*ch.Mute).String()) + case ch.SemiPitched != nil: + el.CreateElement("semi-pitched").SetText((*ch.SemiPitched).String()) + case ch.OtherPlay != nil: + serializeOtherPlay(ch.OtherPlay, el, "other-play") + } + } +} diff --git a/gen/test/go/mx/principal_voice.go b/gen/test/go/mx/principal_voice.go new file mode 100644 index 000000000..422baa160 --- /dev/null +++ b/gen/test/go/mx/principal_voice.go @@ -0,0 +1,138 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The principal-voice element represents principal and secondary voices in a score, either for +// analysis or for square bracket symbols that appear in a score. The symbol attribute indicates the +// type of symbol used at the start of the principal-voice. The content of the principal-voice +// element is used for analysis and may be any text value. When used for analysis separate from any +// printed score markings, the symbol attribute should be set to "none". +type PrincipalVoice struct { + Type *StartStop // attribute "type" + Symbol *PrincipalVoiceSymbol // attribute "symbol" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" + ID *string // attribute "id" + Value string // text content +} + +func parsePrincipalVoice(el *etree.Element) (*PrincipalVoice, error) { + m := &PrincipalVoice{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStartStop(a.Value) + m.Type = &v + case "symbol": + v := ParsePrincipalVoiceSymbol(a.Value) + m.Symbol = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializePrincipalVoice(m *PrincipalVoice, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Symbol != nil { + el.CreateAttr("symbol", (*m.Symbol).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/print.go b/gen/test/go/mx/print.go new file mode 100644 index 000000000..44352f581 --- /dev/null +++ b/gen/test/go/mx/print.go @@ -0,0 +1,159 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The print type contains general printing parameters, including the layout elements defined in the +// layout.mod file. The part-name-display and part-abbreviation-display elements used in the +// score.mod file may also be used here to change how a part name or abbreviation is displayed over +// the course of a piece. They take effect when the current measure or a succeeding measure starts a +// new system. Layout elements in a print statement only apply to the current page, system, staff, +// or measure. Music that follows continues to take the default values from the layout included in +// the defaults element. +type Print struct { + StaffSpacing *Tenths // attribute "staff-spacing" + NewSystem *YesNo // attribute "new-system" + NewPage *YesNo // attribute "new-page" + BlankPage *int // attribute "blank-page" + PageNumber *string // attribute "page-number" + ID *string // attribute "id" + Children []PrintChild // child elements in document order +} + +// PrintChild is one child element of Print: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type PrintChild struct { + PageLayout *PageLayout + SystemLayout *SystemLayout + StaffLayout *StaffLayout + MeasureLayout *MeasureLayout + MeasureNumbering *MeasureNumbering + PartNameDisplay *NameDisplay + PartAbbreviationDisplay *NameDisplay +} + +func parsePrint(el *etree.Element) (*Print, error) { + m := &Print{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "staff-spacing": + v := ParseTenths(a.Value) + m.StaffSpacing = &v + case "new-system": + v := ParseYesNo(a.Value) + m.NewSystem = &v + case "new-page": + v := ParseYesNo(a.Value) + m.NewPage = &v + case "blank-page": + v := parseInt(a.Value) + m.BlankPage = &v + case "page-number": + v := a.Value + m.PageNumber = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "page-layout": + v, err := parsePageLayout(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PrintChild{PageLayout: v}) + case "system-layout": + v, err := parseSystemLayout(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PrintChild{SystemLayout: v}) + case "staff-layout": + v, err := parseStaffLayout(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PrintChild{StaffLayout: v}) + case "measure-layout": + v, err := parseMeasureLayout(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PrintChild{MeasureLayout: v}) + case "measure-numbering": + v, err := parseMeasureNumbering(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PrintChild{MeasureNumbering: v}) + case "part-name-display": + v, err := parseNameDisplay(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PrintChild{PartNameDisplay: v}) + case "part-abbreviation-display": + v, err := parseNameDisplay(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, PrintChild{PartAbbreviationDisplay: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializePrint(m *Print, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.StaffSpacing != nil { + el.CreateAttr("staff-spacing", (*m.StaffSpacing).String()) + } + if m.NewSystem != nil { + el.CreateAttr("new-system", (*m.NewSystem).String()) + } + if m.NewPage != nil { + el.CreateAttr("new-page", (*m.NewPage).String()) + } + if m.BlankPage != nil { + el.CreateAttr("blank-page", formatInt((*m.BlankPage))) + } + if m.PageNumber != nil { + el.CreateAttr("page-number", (*m.PageNumber)) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.PageLayout != nil: + serializePageLayout(ch.PageLayout, el, "page-layout") + case ch.SystemLayout != nil: + serializeSystemLayout(ch.SystemLayout, el, "system-layout") + case ch.StaffLayout != nil: + serializeStaffLayout(ch.StaffLayout, el, "staff-layout") + case ch.MeasureLayout != nil: + serializeMeasureLayout(ch.MeasureLayout, el, "measure-layout") + case ch.MeasureNumbering != nil: + serializeMeasureNumbering(ch.MeasureNumbering, el, "measure-numbering") + case ch.PartNameDisplay != nil: + serializeNameDisplay(ch.PartNameDisplay, el, "part-name-display") + case ch.PartAbbreviationDisplay != nil: + serializeNameDisplay(ch.PartAbbreviationDisplay, el, "part-abbreviation-display") + } + } +} diff --git a/gen/test/go/mx/repeat.go b/gen/test/go/mx/repeat.go new file mode 100644 index 000000000..9c4610556 --- /dev/null +++ b/gen/test/go/mx/repeat.go @@ -0,0 +1,56 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The repeat type represents repeat marks. The start of the repeat has a forward direction while +// the end of the repeat has a backward direction. Backward repeats that are not part of an ending +// can use the times attribute to indicate the number of times the repeated section is played. +type Repeat struct { + Direction *BackwardForward // attribute "direction" + Times *int // attribute "times" + Winged *Winged // attribute "winged" +} + +func parseRepeat(el *etree.Element) (*Repeat, error) { + m := &Repeat{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "direction": + v := ParseBackwardForward(a.Value) + m.Direction = &v + case "times": + v := parseInt(a.Value) + m.Times = &v + case "winged": + v := ParseWinged(a.Value) + m.Winged = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeRepeat(m *Repeat, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Direction != nil { + el.CreateAttr("direction", (*m.Direction).String()) + } + if m.Times != nil { + el.CreateAttr("times", formatInt((*m.Times))) + } + if m.Winged != nil { + el.CreateAttr("winged", (*m.Winged).String()) + } +} diff --git a/gen/test/go/mx/rest.go b/gen/test/go/mx/rest.go new file mode 100644 index 000000000..b679fb5fe --- /dev/null +++ b/gen/test/go/mx/rest.go @@ -0,0 +1,69 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The rest element indicates notated rests or silences. Rest elements are usually empty, but +// placement on the staff can be specified using display-step and display-octave elements. If the +// measure attribute is set to yes, this indicates this is a complete measure rest. +type Rest struct { + Measure *YesNo // attribute "measure" + Children []RestChild // child elements in document order +} + +// RestChild is one child element of Rest: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type RestChild struct { + DisplayStep *Step + DisplayOctave *Octave +} + +func parseRest(el *etree.Element) (*Rest, error) { + m := &Rest{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "measure": + v := ParseYesNo(a.Value) + m.Measure = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "display-step": + v := ParseStep(c.Text()) + m.Children = append(m.Children, RestChild{DisplayStep: &v}) + case "display-octave": + v := ParseOctave(c.Text()) + m.Children = append(m.Children, RestChild{DisplayOctave: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeRest(m *Rest, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Measure != nil { + el.CreateAttr("measure", (*m.Measure).String()) + } + for _, ch := range m.Children { + switch { + case ch.DisplayStep != nil: + el.CreateElement("display-step").SetText((*ch.DisplayStep).String()) + case ch.DisplayOctave != nil: + el.CreateElement("display-octave").SetText((*ch.DisplayOctave).String()) + } + } +} diff --git a/gen/test/go/mx/root.go b/gen/test/go/mx/root.go new file mode 100644 index 000000000..2a04679e2 --- /dev/null +++ b/gen/test/go/mx/root.go @@ -0,0 +1,69 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The root type indicates a pitch like C, D, E vs. a function indication like I, II, III. It is +// used with chord symbols in popular music. The root element has a root-step and optional +// root-alter element similar to the step and alter elements, but renamed to distinguish the +// different musical meanings. +type Root struct { + Children []RootChild // child elements in document order +} + +// RootChild is one child element of Root: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type RootChild struct { + RootStep *RootStep + RootAlter *RootAlter +} + +func parseRoot(el *etree.Element) (*Root, error) { + m := &Root{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "root-step": + v, err := parseRootStep(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, RootChild{RootStep: v}) + case "root-alter": + v, err := parseRootAlter(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, RootChild{RootAlter: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeRoot(m *Root, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.RootStep != nil: + serializeRootStep(ch.RootStep, el, "root-step") + case ch.RootAlter != nil: + serializeRootAlter(ch.RootAlter, el, "root-alter") + } + } +} diff --git a/gen/test/go/mx/root_alter.go b/gen/test/go/mx/root_alter.go new file mode 100644 index 000000000..9283bb1d2 --- /dev/null +++ b/gen/test/go/mx/root_alter.go @@ -0,0 +1,117 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The root-alter type represents the chromatic alteration of the root of the current chord within +// the harmony element. In some chord styles, the text for the root-step element may include +// root-alter information. In that case, the print-object attribute of the root-alter element can be +// set to no. The location attribute indicates whether the alteration should appear to the left or +// the right of the root-step; it is right by default. +type RootAlter struct { + Location *LeftRight // attribute "location" + PrintObject *YesNo // attribute "print-object" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Value Semitones // text content +} + +func parseRootAlter(el *etree.Element) (*RootAlter, error) { + m := &RootAlter{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "location": + v := ParseLeftRight(a.Value) + m.Location = &v + case "print-object": + v := ParseYesNo(a.Value) + m.PrintObject = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseSemitones(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeRootAlter(m *RootAlter, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Location != nil { + el.CreateAttr("location", (*m.Location).String()) + } + if m.PrintObject != nil { + el.CreateAttr("print-object", (*m.PrintObject).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/root_step.go b/gen/test/go/mx/root_step.go new file mode 100644 index 000000000..c0f1a1adc --- /dev/null +++ b/gen/test/go/mx/root_step.go @@ -0,0 +1,108 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The root-step type represents the pitch step of the root of the current chord within the harmony +// element. The text attribute indicates how the root should appear in a score if not using the +// element contents. +type RootStep struct { + Text *string // attribute "text" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Value Step // text content +} + +func parseRootStep(el *etree.Element) (*RootStep, error) { + m := &RootStep{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "text": + v := a.Value + m.Text = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseStep(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeRootStep(m *RootStep, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Text != nil { + el.CreateAttr("text", (*m.Text)) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/scaling.go b/gen/test/go/mx/scaling.go new file mode 100644 index 000000000..9bf6dc319 --- /dev/null +++ b/gen/test/go/mx/scaling.go @@ -0,0 +1,64 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Margins, page sizes, and distances are all measured in tenths to keep MusicXML data in a +// consistent coordinate system as much as possible. The translation to absolute units is done with +// the scaling type, which specifies how many millimeters are equal to how many tenths. For a staff +// height of 7 mm, millimeters would be set to 7 while tenths is set to 40. The ability to set a +// formula rather than a single scaling factor helps avoid roundoff errors. +type Scaling struct { + Children []ScalingChild // child elements in document order +} + +// ScalingChild is one child element of Scaling: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type ScalingChild struct { + Millimeters *Millimeters + Tenths *Tenths +} + +func parseScaling(el *etree.Element) (*Scaling, error) { + m := &Scaling{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "millimeters": + v := ParseMillimeters(c.Text()) + m.Children = append(m.Children, ScalingChild{Millimeters: &v}) + case "tenths": + v := ParseTenths(c.Text()) + m.Children = append(m.Children, ScalingChild{Tenths: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeScaling(m *Scaling, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.Millimeters != nil: + el.CreateElement("millimeters").SetText((*ch.Millimeters).String()) + case ch.Tenths != nil: + el.CreateElement("tenths").SetText((*ch.Tenths).String()) + } + } +} diff --git a/gen/test/go/mx/scordatura.go b/gen/test/go/mx/scordatura.go new file mode 100644 index 000000000..eec22dd40 --- /dev/null +++ b/gen/test/go/mx/scordatura.go @@ -0,0 +1,65 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Scordatura string tunings are represented by a series of accord elements, similar to the +// staff-tuning elements. Strings are numbered from high to low. +type Scordatura struct { + ID *string // attribute "id" + Children []ScordaturaChild // child elements in document order +} + +// ScordaturaChild is one child element of Scordatura: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type ScordaturaChild struct { + Accord *Accord +} + +func parseScordatura(el *etree.Element) (*Scordatura, error) { + m := &Scordatura{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "accord": + v, err := parseAccord(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScordaturaChild{Accord: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeScordatura(m *Scordatura, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.Accord != nil: + serializeAccord(ch.Accord, el, "accord") + } + } +} diff --git a/gen/test/go/mx/score_instrument.go b/gen/test/go/mx/score_instrument.go new file mode 100644 index 000000000..9a29fd739 --- /dev/null +++ b/gen/test/go/mx/score_instrument.go @@ -0,0 +1,102 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The score-instrument type represents a single instrument within a score-part. As with the +// score-part type, each score-instrument has a required ID attribute, a name, and an optional +// abbreviation. A score-instrument type is also required if the score specifies MIDI 1.0 channels, +// banks, or programs. An initial midi-instrument assignment can also be made here. MusicXML +// software should be able to automatically assign reasonable channels and instruments without these +// elements in simple cases, such as where part names match General MIDI instrument names. +type ScoreInstrument struct { + ID *string // attribute "id" + Children []ScoreInstrumentChild // child elements in document order +} + +// ScoreInstrumentChild is one child element of ScoreInstrument: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type ScoreInstrumentChild struct { + InstrumentName *string + InstrumentAbbreviation *string + InstrumentSound *string + Solo *Empty + Ensemble *PositiveIntegerOrEmpty + VirtualInstrument *VirtualInstrument +} + +func parseScoreInstrument(el *etree.Element) (*ScoreInstrument, error) { + m := &ScoreInstrument{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "instrument-name": + v := c.Text() + m.Children = append(m.Children, ScoreInstrumentChild{InstrumentName: &v}) + case "instrument-abbreviation": + v := c.Text() + m.Children = append(m.Children, ScoreInstrumentChild{InstrumentAbbreviation: &v}) + case "instrument-sound": + v := c.Text() + m.Children = append(m.Children, ScoreInstrumentChild{InstrumentSound: &v}) + case "solo": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScoreInstrumentChild{Solo: v}) + case "ensemble": + v := ParsePositiveIntegerOrEmpty(c.Text()) + m.Children = append(m.Children, ScoreInstrumentChild{Ensemble: &v}) + case "virtual-instrument": + v, err := parseVirtualInstrument(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScoreInstrumentChild{VirtualInstrument: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeScoreInstrument(m *ScoreInstrument, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.InstrumentName != nil: + el.CreateElement("instrument-name").SetText((*ch.InstrumentName)) + case ch.InstrumentAbbreviation != nil: + el.CreateElement("instrument-abbreviation").SetText((*ch.InstrumentAbbreviation)) + case ch.InstrumentSound != nil: + el.CreateElement("instrument-sound").SetText((*ch.InstrumentSound)) + case ch.Solo != nil: + serializeEmpty(ch.Solo, el, "solo") + case ch.Ensemble != nil: + el.CreateElement("ensemble").SetText((*ch.Ensemble).String()) + case ch.VirtualInstrument != nil: + serializeVirtualInstrument(ch.VirtualInstrument, el, "virtual-instrument") + } + } +} diff --git a/gen/test/go/mx/score_part.go b/gen/test/go/mx/score_part.go new file mode 100644 index 000000000..d4d1fe3b3 --- /dev/null +++ b/gen/test/go/mx/score_part.go @@ -0,0 +1,136 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Each MusicXML part corresponds to a track in a Standard MIDI Format 1 file. The score-instrument +// elements are used when there are multiple instruments per track. The midi-device element is used +// to make a MIDI device or port assignment for the given track or specific MIDI instruments. +// Initial midi-instrument assignments may be made here as well. +type ScorePart struct { + ID *string // attribute "id" + Children []ScorePartChild // child elements in document order +} + +// ScorePartChild is one child element of ScorePart: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type ScorePartChild struct { + Identification *Identification + PartName *PartName + PartNameDisplay *NameDisplay + PartAbbreviation *PartName + PartAbbreviationDisplay *NameDisplay + Group *string + ScoreInstrument *ScoreInstrument + MIDIDevice *MIDIDevice + MIDIInstrument *MIDIInstrument +} + +func parseScorePart(el *etree.Element) (*ScorePart, error) { + m := &ScorePart{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "identification": + v, err := parseIdentification(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScorePartChild{Identification: v}) + case "part-name": + v, err := parsePartName(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScorePartChild{PartName: v}) + case "part-name-display": + v, err := parseNameDisplay(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScorePartChild{PartNameDisplay: v}) + case "part-abbreviation": + v, err := parsePartName(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScorePartChild{PartAbbreviation: v}) + case "part-abbreviation-display": + v, err := parseNameDisplay(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScorePartChild{PartAbbreviationDisplay: v}) + case "group": + v := c.Text() + m.Children = append(m.Children, ScorePartChild{Group: &v}) + case "score-instrument": + v, err := parseScoreInstrument(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScorePartChild{ScoreInstrument: v}) + case "midi-device": + v, err := parseMIDIDevice(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScorePartChild{MIDIDevice: v}) + case "midi-instrument": + v, err := parseMIDIInstrument(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScorePartChild{MIDIInstrument: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeScorePart(m *ScorePart, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.Identification != nil: + serializeIdentification(ch.Identification, el, "identification") + case ch.PartName != nil: + serializePartName(ch.PartName, el, "part-name") + case ch.PartNameDisplay != nil: + serializeNameDisplay(ch.PartNameDisplay, el, "part-name-display") + case ch.PartAbbreviation != nil: + serializePartName(ch.PartAbbreviation, el, "part-abbreviation") + case ch.PartAbbreviationDisplay != nil: + serializeNameDisplay(ch.PartAbbreviationDisplay, el, "part-abbreviation-display") + case ch.Group != nil: + el.CreateElement("group").SetText((*ch.Group)) + case ch.ScoreInstrument != nil: + serializeScoreInstrument(ch.ScoreInstrument, el, "score-instrument") + case ch.MIDIDevice != nil: + serializeMIDIDevice(ch.MIDIDevice, el, "midi-device") + case ch.MIDIInstrument != nil: + serializeMIDIInstrument(ch.MIDIInstrument, el, "midi-instrument") + } + } +} diff --git a/gen/test/go/mx/score_partwise.go b/gen/test/go/mx/score_partwise.go new file mode 100644 index 000000000..d064cf442 --- /dev/null +++ b/gen/test/go/mx/score_partwise.go @@ -0,0 +1,120 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +type ScorePartwise struct { + Version *string // attribute "version" + Children []ScorePartwiseChild // child elements in document order +} + +// ScorePartwiseChild is one child element of ScorePartwise: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type ScorePartwiseChild struct { + Work *Work + MovementNumber *string + MovementTitle *string + Identification *Identification + Defaults *Defaults + Credit *Credit + PartList *PartList + Part *PartwisePart +} + +func parseScorePartwise(el *etree.Element) (*ScorePartwise, error) { + m := &ScorePartwise{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "version": + v := a.Value + m.Version = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "work": + v, err := parseWork(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScorePartwiseChild{Work: v}) + case "movement-number": + v := c.Text() + m.Children = append(m.Children, ScorePartwiseChild{MovementNumber: &v}) + case "movement-title": + v := c.Text() + m.Children = append(m.Children, ScorePartwiseChild{MovementTitle: &v}) + case "identification": + v, err := parseIdentification(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScorePartwiseChild{Identification: v}) + case "defaults": + v, err := parseDefaults(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScorePartwiseChild{Defaults: v}) + case "credit": + v, err := parseCredit(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScorePartwiseChild{Credit: v}) + case "part-list": + v, err := parsePartList(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScorePartwiseChild{PartList: v}) + case "part": + v, err := parsePartwisePart(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScorePartwiseChild{Part: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeScorePartwise(m *ScorePartwise, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Version != nil { + el.CreateAttr("version", (*m.Version)) + } + for _, ch := range m.Children { + switch { + case ch.Work != nil: + serializeWork(ch.Work, el, "work") + case ch.MovementNumber != nil: + el.CreateElement("movement-number").SetText((*ch.MovementNumber)) + case ch.MovementTitle != nil: + el.CreateElement("movement-title").SetText((*ch.MovementTitle)) + case ch.Identification != nil: + serializeIdentification(ch.Identification, el, "identification") + case ch.Defaults != nil: + serializeDefaults(ch.Defaults, el, "defaults") + case ch.Credit != nil: + serializeCredit(ch.Credit, el, "credit") + case ch.PartList != nil: + serializePartList(ch.PartList, el, "part-list") + case ch.Part != nil: + serializePartwisePart(ch.Part, el, "part") + } + } +} diff --git a/gen/test/go/mx/score_timewise.go b/gen/test/go/mx/score_timewise.go new file mode 100644 index 000000000..561a0ca22 --- /dev/null +++ b/gen/test/go/mx/score_timewise.go @@ -0,0 +1,120 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +type ScoreTimewise struct { + Version *string // attribute "version" + Children []ScoreTimewiseChild // child elements in document order +} + +// ScoreTimewiseChild is one child element of ScoreTimewise: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type ScoreTimewiseChild struct { + Work *Work + MovementNumber *string + MovementTitle *string + Identification *Identification + Defaults *Defaults + Credit *Credit + PartList *PartList + Measure *TimewiseMeasure +} + +func parseScoreTimewise(el *etree.Element) (*ScoreTimewise, error) { + m := &ScoreTimewise{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "version": + v := a.Value + m.Version = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "work": + v, err := parseWork(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScoreTimewiseChild{Work: v}) + case "movement-number": + v := c.Text() + m.Children = append(m.Children, ScoreTimewiseChild{MovementNumber: &v}) + case "movement-title": + v := c.Text() + m.Children = append(m.Children, ScoreTimewiseChild{MovementTitle: &v}) + case "identification": + v, err := parseIdentification(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScoreTimewiseChild{Identification: v}) + case "defaults": + v, err := parseDefaults(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScoreTimewiseChild{Defaults: v}) + case "credit": + v, err := parseCredit(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScoreTimewiseChild{Credit: v}) + case "part-list": + v, err := parsePartList(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScoreTimewiseChild{PartList: v}) + case "measure": + v, err := parseTimewiseMeasure(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, ScoreTimewiseChild{Measure: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeScoreTimewise(m *ScoreTimewise, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Version != nil { + el.CreateAttr("version", (*m.Version)) + } + for _, ch := range m.Children { + switch { + case ch.Work != nil: + serializeWork(ch.Work, el, "work") + case ch.MovementNumber != nil: + el.CreateElement("movement-number").SetText((*ch.MovementNumber)) + case ch.MovementTitle != nil: + el.CreateElement("movement-title").SetText((*ch.MovementTitle)) + case ch.Identification != nil: + serializeIdentification(ch.Identification, el, "identification") + case ch.Defaults != nil: + serializeDefaults(ch.Defaults, el, "defaults") + case ch.Credit != nil: + serializeCredit(ch.Credit, el, "credit") + case ch.PartList != nil: + serializePartList(ch.PartList, el, "part-list") + case ch.Measure != nil: + serializeTimewiseMeasure(ch.Measure, el, "measure") + } + } +} diff --git a/gen/test/go/mx/segno.go b/gen/test/go/mx/segno.go new file mode 100644 index 000000000..3485f3634 --- /dev/null +++ b/gen/test/go/mx/segno.go @@ -0,0 +1,125 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The segno type is the visual indicator of a segno sign. The exact glyph can be specified with the +// smufl attribute. A sound element is also needed to guide playback applications reliably. +type Segno struct { + SMUFL *SMUFLSegnoGlyphName // attribute "smufl" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" + ID *string // attribute "id" +} + +func parseSegno(el *etree.Element) (*Segno, error) { + m := &Segno{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "smufl": + v := ParseSMUFLSegnoGlyphName(a.Value) + m.SMUFL = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeSegno(m *Segno, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.SMUFL != nil { + el.CreateAttr("smufl", (*m.SMUFL).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/slash.go b/gen/test/go/mx/slash.go new file mode 100644 index 000000000..02346e512 --- /dev/null +++ b/gen/test/go/mx/slash.go @@ -0,0 +1,94 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The slash type is used to indicate that slash notation is to be used. If the slash is on every +// beat, use-stems is no (the default). To indicate rhythms but not pitches, use-stems is set to +// yes. The type attribute indicates whether this is the start or stop of a slash notation style. +// The use-dots attribute works as for the beat-repeat element, and only has effect if use-stems is +// no. +type Slash struct { + Type *StartStop // attribute "type" + UseDots *YesNo // attribute "use-dots" + UseStems *YesNo // attribute "use-stems" + Children []SlashChild // child elements in document order +} + +// SlashChild is one child element of Slash: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type SlashChild struct { + SlashType *NoteTypeValue + SlashDot *Empty + ExceptVoice *string +} + +func parseSlash(el *etree.Element) (*Slash, error) { + m := &Slash{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStartStop(a.Value) + m.Type = &v + case "use-dots": + v := ParseYesNo(a.Value) + m.UseDots = &v + case "use-stems": + v := ParseYesNo(a.Value) + m.UseStems = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "slash-type": + v := ParseNoteTypeValue(c.Text()) + m.Children = append(m.Children, SlashChild{SlashType: &v}) + case "slash-dot": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, SlashChild{SlashDot: v}) + case "except-voice": + v := c.Text() + m.Children = append(m.Children, SlashChild{ExceptVoice: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeSlash(m *Slash, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.UseDots != nil { + el.CreateAttr("use-dots", (*m.UseDots).String()) + } + if m.UseStems != nil { + el.CreateAttr("use-stems", (*m.UseStems).String()) + } + for _, ch := range m.Children { + switch { + case ch.SlashType != nil: + el.CreateElement("slash-type").SetText((*ch.SlashType).String()) + case ch.SlashDot != nil: + serializeEmpty(ch.SlashDot, el, "slash-dot") + case ch.ExceptVoice != nil: + el.CreateElement("except-voice").SetText((*ch.ExceptVoice)) + } + } +} diff --git a/gen/test/go/mx/slide.go b/gen/test/go/mx/slide.go new file mode 100644 index 000000000..841698910 --- /dev/null +++ b/gen/test/go/mx/slide.go @@ -0,0 +1,172 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Glissando and slide types both indicate rapidly moving from one pitch to the other so that +// individual notes are not discerned. The distinction is similar to that between NIFF's glissando +// and portamento elements. A slide is continuous between two notes and defaults to a solid line. +// The optional text for a is printed alongside the line. +type Slide struct { + Type *StartStop // attribute "type" + Number *NumberLevel // attribute "number" + LineType *LineType // attribute "line-type" + DashLength *Tenths // attribute "dash-length" + SpaceLength *Tenths // attribute "space-length" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Accelerate *YesNo // attribute "accelerate" + Beats *TrillBeats // attribute "beats" + FirstBeat *Percent // attribute "first-beat" + LastBeat *Percent // attribute "last-beat" + ID *string // attribute "id" + Value string // text content +} + +func parseSlide(el *etree.Element) (*Slide, error) { + m := &Slide{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStartStop(a.Value) + m.Type = &v + case "number": + v := ParseNumberLevel(a.Value) + m.Number = &v + case "line-type": + v := ParseLineType(a.Value) + m.LineType = &v + case "dash-length": + v := ParseTenths(a.Value) + m.DashLength = &v + case "space-length": + v := ParseTenths(a.Value) + m.SpaceLength = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "accelerate": + v := ParseYesNo(a.Value) + m.Accelerate = &v + case "beats": + v := ParseTrillBeats(a.Value) + m.Beats = &v + case "first-beat": + v := ParsePercent(a.Value) + m.FirstBeat = &v + case "last-beat": + v := ParsePercent(a.Value) + m.LastBeat = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeSlide(m *Slide, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.LineType != nil { + el.CreateAttr("line-type", (*m.LineType).String()) + } + if m.DashLength != nil { + el.CreateAttr("dash-length", (*m.DashLength).String()) + } + if m.SpaceLength != nil { + el.CreateAttr("space-length", (*m.SpaceLength).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Accelerate != nil { + el.CreateAttr("accelerate", (*m.Accelerate).String()) + } + if m.Beats != nil { + el.CreateAttr("beats", (*m.Beats).String()) + } + if m.FirstBeat != nil { + el.CreateAttr("first-beat", (*m.FirstBeat).String()) + } + if m.LastBeat != nil { + el.CreateAttr("last-beat", (*m.LastBeat).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/slur.go b/gen/test/go/mx/slur.go new file mode 100644 index 000000000..d91487a9c --- /dev/null +++ b/gen/test/go/mx/slur.go @@ -0,0 +1,168 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Slur types are empty. Most slurs are represented with two elements: one with a start type, and +// one with a stop type. Slurs can add more elements using a continue type. This is typically used +// to specify the formatting of cross-system slurs, or to specify the shape of very complex slurs. +type Slur struct { + Type *StartStopContinue // attribute "type" + Number *NumberLevel // attribute "number" + LineType *LineType // attribute "line-type" + DashLength *Tenths // attribute "dash-length" + SpaceLength *Tenths // attribute "space-length" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + Placement *AboveBelow // attribute "placement" + Orientation *OverUnder // attribute "orientation" + BezierX *Tenths // attribute "bezier-x" + BezierY *Tenths // attribute "bezier-y" + BezierX2 *Tenths // attribute "bezier-x2" + BezierY2 *Tenths // attribute "bezier-y2" + BezierOffset *Divisions // attribute "bezier-offset" + BezierOffset2 *Divisions // attribute "bezier-offset2" + Color *Color // attribute "color" + ID *string // attribute "id" +} + +func parseSlur(el *etree.Element) (*Slur, error) { + m := &Slur{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStartStopContinue(a.Value) + m.Type = &v + case "number": + v := ParseNumberLevel(a.Value) + m.Number = &v + case "line-type": + v := ParseLineType(a.Value) + m.LineType = &v + case "dash-length": + v := ParseTenths(a.Value) + m.DashLength = &v + case "space-length": + v := ParseTenths(a.Value) + m.SpaceLength = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "orientation": + v := ParseOverUnder(a.Value) + m.Orientation = &v + case "bezier-x": + v := ParseTenths(a.Value) + m.BezierX = &v + case "bezier-y": + v := ParseTenths(a.Value) + m.BezierY = &v + case "bezier-x2": + v := ParseTenths(a.Value) + m.BezierX2 = &v + case "bezier-y2": + v := ParseTenths(a.Value) + m.BezierY2 = &v + case "bezier-offset": + v := ParseDivisions(a.Value) + m.BezierOffset = &v + case "bezier-offset2": + v := ParseDivisions(a.Value) + m.BezierOffset2 = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeSlur(m *Slur, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.LineType != nil { + el.CreateAttr("line-type", (*m.LineType).String()) + } + if m.DashLength != nil { + el.CreateAttr("dash-length", (*m.DashLength).String()) + } + if m.SpaceLength != nil { + el.CreateAttr("space-length", (*m.SpaceLength).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.Orientation != nil { + el.CreateAttr("orientation", (*m.Orientation).String()) + } + if m.BezierX != nil { + el.CreateAttr("bezier-x", (*m.BezierX).String()) + } + if m.BezierY != nil { + el.CreateAttr("bezier-y", (*m.BezierY).String()) + } + if m.BezierX2 != nil { + el.CreateAttr("bezier-x2", (*m.BezierX2).String()) + } + if m.BezierY2 != nil { + el.CreateAttr("bezier-y2", (*m.BezierY2).String()) + } + if m.BezierOffset != nil { + el.CreateAttr("bezier-offset", (*m.BezierOffset).String()) + } + if m.BezierOffset2 != nil { + el.CreateAttr("bezier-offset2", (*m.BezierOffset2).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/sound.go b/gen/test/go/mx/sound.go new file mode 100644 index 000000000..ba25edb46 --- /dev/null +++ b/gen/test/go/mx/sound.go @@ -0,0 +1,245 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The sound element contains general playback parameters. They can stand alone within a +// part/measure, or be a component element within a direction. Tempo is expressed in quarter notes +// per minute. If 0, the sound-generating program should prompt the user at the time of compiling a +// sound (MIDI) file. Dynamics (or MIDI velocity) are expressed as a percentage of the default forte +// value (90 for MIDI 1.0). Dacapo indicates to go back to the beginning of the movement. When used +// it always has the value "yes". Segno and dalsegno are used for backwards jumps to a segno sign; +// coda and tocoda are used for forward jumps to a coda sign. If there are multiple jumps, the value +// of these parameters can be used to name and distinguish them. If segno or coda is used, the +// divisions attribute can also be used to indicate the number of divisions per quarter note. +// Otherwise sound and MIDI generating programs may have to recompute this. By default, a dalsegno +// or dacapo attribute indicates that the jump should occur the first time through, while a tocoda +// attribute indicates the jump should occur the second time through. The time that jumps occur can +// be changed by using the time-only attribute. Forward-repeat is used when a forward repeat sign is +// implied, and usually follows a bar line. When used it always has the value of "yes". The fine +// attribute follows the final note or rest in a movement with a da capo or dal segno direction. If +// numeric, the value represents the actual duration of the final note or rest, which can be +// ambiguous in written notation and different among parts and voices. The value may also be "yes" +// to indicate no change to the final duration. If the sound element applies only particular times +// through a repeat, the time-only attribute indicates which times to apply the sound element. +// Pizzicato in a sound element effects all following notes. Yes indicates pizzicato, no indicates +// arco. The pan and elevation attributes are deprecated in Version 2.0. The pan and elevation +// elements in the midi-instrument element should be used instead. The meaning of the pan and +// elevation attributes is the same as for the pan and elevation elements. If both are present, the +// mid-instrument elements take priority. The damper-pedal, soft-pedal, and sostenuto-pedal +// attributes effect playback of the three common piano pedals and their MIDI controller +// equivalents. The yes value indicates the pedal is depressed; no indicates the pedal is released. +// A numeric value from 0 to 100 may also be used for half pedaling. This value is the percentage +// that the pedal is depressed. A value of 0 is equivalent to no, and a value of 100 is equivalent +// to yes. MIDI devices, MIDI instruments, and playback techniques are changed using the +// midi-device, midi-instrument, and play elements. When there are multiple instances of these +// elements, they should be grouped together by instrument using the id attribute values. The offset +// element is used to indicate that the sound takes place offset from the current score position. If +// the sound element is a child of a direction element, the sound offset element overrides the +// direction offset element if both elements are present. Note that the offset reflects the intended +// musical position for the change in sound. It should not be used to compensate for latency issues +// in particular hardware configurations. +type Sound struct { + Tempo *NonNegativeDecimal // attribute "tempo" + Dynamics *NonNegativeDecimal // attribute "dynamics" + Dacapo *YesNo // attribute "dacapo" + Segno *string // attribute "segno" + Dalsegno *string // attribute "dalsegno" + Coda *string // attribute "coda" + Tocoda *string // attribute "tocoda" + Divisions *Divisions // attribute "divisions" + ForwardRepeat *YesNo // attribute "forward-repeat" + Fine *string // attribute "fine" + TimeOnly *TimeOnly // attribute "time-only" + Pizzicato *YesNo // attribute "pizzicato" + Pan *RotationDegrees // attribute "pan" + Elevation *RotationDegrees // attribute "elevation" + DamperPedal *YesNoNumber // attribute "damper-pedal" + SoftPedal *YesNoNumber // attribute "soft-pedal" + SostenutoPedal *YesNoNumber // attribute "sostenuto-pedal" + ID *string // attribute "id" + Children []SoundChild // child elements in document order +} + +// SoundChild is one child element of Sound: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type SoundChild struct { + MIDIDevice *MIDIDevice + MIDIInstrument *MIDIInstrument + Play *Play + Offset *Offset +} + +func parseSound(el *etree.Element) (*Sound, error) { + m := &Sound{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "tempo": + v := ParseNonNegativeDecimal(a.Value) + m.Tempo = &v + case "dynamics": + v := ParseNonNegativeDecimal(a.Value) + m.Dynamics = &v + case "dacapo": + v := ParseYesNo(a.Value) + m.Dacapo = &v + case "segno": + v := a.Value + m.Segno = &v + case "dalsegno": + v := a.Value + m.Dalsegno = &v + case "coda": + v := a.Value + m.Coda = &v + case "tocoda": + v := a.Value + m.Tocoda = &v + case "divisions": + v := ParseDivisions(a.Value) + m.Divisions = &v + case "forward-repeat": + v := ParseYesNo(a.Value) + m.ForwardRepeat = &v + case "fine": + v := a.Value + m.Fine = &v + case "time-only": + v := ParseTimeOnly(a.Value) + m.TimeOnly = &v + case "pizzicato": + v := ParseYesNo(a.Value) + m.Pizzicato = &v + case "pan": + v := ParseRotationDegrees(a.Value) + m.Pan = &v + case "elevation": + v := ParseRotationDegrees(a.Value) + m.Elevation = &v + case "damper-pedal": + v := ParseYesNoNumber(a.Value) + m.DamperPedal = &v + case "soft-pedal": + v := ParseYesNoNumber(a.Value) + m.SoftPedal = &v + case "sostenuto-pedal": + v := ParseYesNoNumber(a.Value) + m.SostenutoPedal = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "midi-device": + v, err := parseMIDIDevice(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, SoundChild{MIDIDevice: v}) + case "midi-instrument": + v, err := parseMIDIInstrument(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, SoundChild{MIDIInstrument: v}) + case "play": + v, err := parsePlay(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, SoundChild{Play: v}) + case "offset": + v, err := parseOffset(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, SoundChild{Offset: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeSound(m *Sound, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Tempo != nil { + el.CreateAttr("tempo", (*m.Tempo).String()) + } + if m.Dynamics != nil { + el.CreateAttr("dynamics", (*m.Dynamics).String()) + } + if m.Dacapo != nil { + el.CreateAttr("dacapo", (*m.Dacapo).String()) + } + if m.Segno != nil { + el.CreateAttr("segno", (*m.Segno)) + } + if m.Dalsegno != nil { + el.CreateAttr("dalsegno", (*m.Dalsegno)) + } + if m.Coda != nil { + el.CreateAttr("coda", (*m.Coda)) + } + if m.Tocoda != nil { + el.CreateAttr("tocoda", (*m.Tocoda)) + } + if m.Divisions != nil { + el.CreateAttr("divisions", (*m.Divisions).String()) + } + if m.ForwardRepeat != nil { + el.CreateAttr("forward-repeat", (*m.ForwardRepeat).String()) + } + if m.Fine != nil { + el.CreateAttr("fine", (*m.Fine)) + } + if m.TimeOnly != nil { + el.CreateAttr("time-only", (*m.TimeOnly).String()) + } + if m.Pizzicato != nil { + el.CreateAttr("pizzicato", (*m.Pizzicato).String()) + } + if m.Pan != nil { + el.CreateAttr("pan", (*m.Pan).String()) + } + if m.Elevation != nil { + el.CreateAttr("elevation", (*m.Elevation).String()) + } + if m.DamperPedal != nil { + el.CreateAttr("damper-pedal", (*m.DamperPedal).String()) + } + if m.SoftPedal != nil { + el.CreateAttr("soft-pedal", (*m.SoftPedal).String()) + } + if m.SostenutoPedal != nil { + el.CreateAttr("sostenuto-pedal", (*m.SostenutoPedal).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.MIDIDevice != nil: + serializeMIDIDevice(ch.MIDIDevice, el, "midi-device") + case ch.MIDIInstrument != nil: + serializeMIDIInstrument(ch.MIDIInstrument, el, "midi-instrument") + case ch.Play != nil: + serializePlay(ch.Play, el, "play") + case ch.Offset != nil: + serializeOffset(ch.Offset, el, "offset") + } + } +} diff --git a/gen/test/go/mx/staff_details.go b/gen/test/go/mx/staff_details.go new file mode 100644 index 000000000..ddc13373d --- /dev/null +++ b/gen/test/go/mx/staff_details.go @@ -0,0 +1,114 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The staff-details element is used to indicate different types of staves. The optional number +// attribute specifies the staff number from top to bottom on the system, as with clef. The +// print-object attribute is used to indicate when a staff is not printed in a part, usually in +// large scores where empty parts are omitted. It is yes by default. If print-spacing is yes while +// print-object is no, the score is printed in cutaway format where vertical space is left for the +// empty part. +type StaffDetails struct { + Number *StaffNumber // attribute "number" + ShowFrets *ShowFrets // attribute "show-frets" + PrintObject *YesNo // attribute "print-object" + PrintSpacing *YesNo // attribute "print-spacing" + Children []StaffDetailsChild // child elements in document order +} + +// StaffDetailsChild is one child element of StaffDetails: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type StaffDetailsChild struct { + StaffType *StaffType + StaffLines *int + StaffTuning *StaffTuning + Capo *int + StaffSize *NonNegativeDecimal +} + +func parseStaffDetails(el *etree.Element) (*StaffDetails, error) { + m := &StaffDetails{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "number": + v := ParseStaffNumber(a.Value) + m.Number = &v + case "show-frets": + v := ParseShowFrets(a.Value) + m.ShowFrets = &v + case "print-object": + v := ParseYesNo(a.Value) + m.PrintObject = &v + case "print-spacing": + v := ParseYesNo(a.Value) + m.PrintSpacing = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "staff-type": + v := ParseStaffType(c.Text()) + m.Children = append(m.Children, StaffDetailsChild{StaffType: &v}) + case "staff-lines": + v := parseInt(c.Text()) + m.Children = append(m.Children, StaffDetailsChild{StaffLines: &v}) + case "staff-tuning": + v, err := parseStaffTuning(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, StaffDetailsChild{StaffTuning: v}) + case "capo": + v := parseInt(c.Text()) + m.Children = append(m.Children, StaffDetailsChild{Capo: &v}) + case "staff-size": + v := ParseNonNegativeDecimal(c.Text()) + m.Children = append(m.Children, StaffDetailsChild{StaffSize: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeStaffDetails(m *StaffDetails, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.ShowFrets != nil { + el.CreateAttr("show-frets", (*m.ShowFrets).String()) + } + if m.PrintObject != nil { + el.CreateAttr("print-object", (*m.PrintObject).String()) + } + if m.PrintSpacing != nil { + el.CreateAttr("print-spacing", (*m.PrintSpacing).String()) + } + for _, ch := range m.Children { + switch { + case ch.StaffType != nil: + el.CreateElement("staff-type").SetText((*ch.StaffType).String()) + case ch.StaffLines != nil: + el.CreateElement("staff-lines").SetText(formatInt((*ch.StaffLines))) + case ch.StaffTuning != nil: + serializeStaffTuning(ch.StaffTuning, el, "staff-tuning") + case ch.Capo != nil: + el.CreateElement("capo").SetText(formatInt((*ch.Capo))) + case ch.StaffSize != nil: + el.CreateElement("staff-size").SetText((*ch.StaffSize).String()) + } + } +} diff --git a/gen/test/go/mx/staff_divide.go b/gen/test/go/mx/staff_divide.go new file mode 100644 index 000000000..e1a667902 --- /dev/null +++ b/gen/test/go/mx/staff_divide.go @@ -0,0 +1,125 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The staff-divide element represents the staff division arrow symbols found at SMuFL code points +// U+E00B, U+E00C, and U+E00D. +type StaffDivide struct { + Type *StaffDivideSymbol // attribute "type" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" + ID *string // attribute "id" +} + +func parseStaffDivide(el *etree.Element) (*StaffDivide, error) { + m := &StaffDivide{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStaffDivideSymbol(a.Value) + m.Type = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeStaffDivide(m *StaffDivide, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/staff_layout.go b/gen/test/go/mx/staff_layout.go new file mode 100644 index 000000000..3d1db8237 --- /dev/null +++ b/gen/test/go/mx/staff_layout.go @@ -0,0 +1,65 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Staff layout includes the vertical distance from the bottom line of the previous staff in this +// system to the top line of the staff specified by the number attribute. The optional number +// attribute refers to staff numbers within the part, from top to bottom on the system. A value of 1 +// is assumed if not present. When used in the defaults element, the values apply to all parts. This +// value is ignored for the first staff in a system. +type StaffLayout struct { + Number *StaffNumber // attribute "number" + Children []StaffLayoutChild // child elements in document order +} + +// StaffLayoutChild is one child element of StaffLayout: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type StaffLayoutChild struct { + StaffDistance *Tenths +} + +func parseStaffLayout(el *etree.Element) (*StaffLayout, error) { + m := &StaffLayout{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "number": + v := ParseStaffNumber(a.Value) + m.Number = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "staff-distance": + v := ParseTenths(c.Text()) + m.Children = append(m.Children, StaffLayoutChild{StaffDistance: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeStaffLayout(m *StaffLayout, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + for _, ch := range m.Children { + switch { + case ch.StaffDistance != nil: + el.CreateElement("staff-distance").SetText((*ch.StaffDistance).String()) + } + } +} diff --git a/gen/test/go/mx/staff_tuning.go b/gen/test/go/mx/staff_tuning.go new file mode 100644 index 000000000..20b7ad002 --- /dev/null +++ b/gen/test/go/mx/staff_tuning.go @@ -0,0 +1,73 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The staff-tuning type specifies the open, non-capo tuning of the lines on a tablature staff. +type StaffTuning struct { + Line *StaffLine // attribute "line" + Children []StaffTuningChild // child elements in document order +} + +// StaffTuningChild is one child element of StaffTuning: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type StaffTuningChild struct { + TuningStep *Step + TuningAlter *Semitones + TuningOctave *Octave +} + +func parseStaffTuning(el *etree.Element) (*StaffTuning, error) { + m := &StaffTuning{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "line": + v := ParseStaffLine(a.Value) + m.Line = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "tuning-step": + v := ParseStep(c.Text()) + m.Children = append(m.Children, StaffTuningChild{TuningStep: &v}) + case "tuning-alter": + v := ParseSemitones(c.Text()) + m.Children = append(m.Children, StaffTuningChild{TuningAlter: &v}) + case "tuning-octave": + v := ParseOctave(c.Text()) + m.Children = append(m.Children, StaffTuningChild{TuningOctave: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeStaffTuning(m *StaffTuning, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Line != nil { + el.CreateAttr("line", (*m.Line).String()) + } + for _, ch := range m.Children { + switch { + case ch.TuningStep != nil: + el.CreateElement("tuning-step").SetText((*ch.TuningStep).String()) + case ch.TuningAlter != nil: + el.CreateElement("tuning-alter").SetText((*ch.TuningAlter).String()) + case ch.TuningOctave != nil: + el.CreateElement("tuning-octave").SetText((*ch.TuningOctave).String()) + } + } +} diff --git a/gen/test/go/mx/stem.go b/gen/test/go/mx/stem.go new file mode 100644 index 000000000..11c2ba03f --- /dev/null +++ b/gen/test/go/mx/stem.go @@ -0,0 +1,75 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Stems can be down, up, none, or double. For down and up stems, the position attributes can be +// used to specify stem length. The relative values specify the end of the stem relative to the +// program default. Default values specify an absolute end stem position. Negative values of +// relative-y that would flip a stem instead of shortening it are ignored. A stem element associated +// with a rest refers to a stemlet. +type Stem struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + Color *Color // attribute "color" + Value StemValue // text content +} + +func parseStem(el *etree.Element) (*Stem, error) { + m := &Stem{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseStemValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeStem(m *Stem, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/stick.go b/gen/test/go/mx/stick.go new file mode 100644 index 000000000..47a273b43 --- /dev/null +++ b/gen/test/go/mx/stick.go @@ -0,0 +1,83 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The stick type represents pictograms where the material of the stick, mallet, or beater is +// included.The parentheses and dashed-circle attributes indicate the presence of these marks around +// the round beater part of a pictogram. Values for these attributes are "no" if not present. +type Stick struct { + Tip *TipDirection // attribute "tip" + Parentheses *YesNo // attribute "parentheses" + DashedCircle *YesNo // attribute "dashed-circle" + Children []StickChild // child elements in document order +} + +// StickChild is one child element of Stick: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type StickChild struct { + StickType *StickType + StickMaterial *StickMaterial +} + +func parseStick(el *etree.Element) (*Stick, error) { + m := &Stick{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "tip": + v := ParseTipDirection(a.Value) + m.Tip = &v + case "parentheses": + v := ParseYesNo(a.Value) + m.Parentheses = &v + case "dashed-circle": + v := ParseYesNo(a.Value) + m.DashedCircle = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "stick-type": + v := ParseStickType(c.Text()) + m.Children = append(m.Children, StickChild{StickType: &v}) + case "stick-material": + v := ParseStickMaterial(c.Text()) + m.Children = append(m.Children, StickChild{StickMaterial: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeStick(m *Stick, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Tip != nil { + el.CreateAttr("tip", (*m.Tip).String()) + } + if m.Parentheses != nil { + el.CreateAttr("parentheses", (*m.Parentheses).String()) + } + if m.DashedCircle != nil { + el.CreateAttr("dashed-circle", (*m.DashedCircle).String()) + } + for _, ch := range m.Children { + switch { + case ch.StickType != nil: + el.CreateElement("stick-type").SetText((*ch.StickType).String()) + case ch.StickMaterial != nil: + el.CreateElement("stick-material").SetText((*ch.StickMaterial).String()) + } + } +} diff --git a/gen/test/go/mx/string.go b/gen/test/go/mx/string.go new file mode 100644 index 000000000..148d40521 --- /dev/null +++ b/gen/test/go/mx/string.go @@ -0,0 +1,107 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The string type is used with tablature notation, regular notation (where it is often circled), +// and chord diagrams. String numbers start with 1 for the highest pitched full-length string. +type String struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" + Value StringNumber // text content +} + +func parseString(el *etree.Element) (*String, error) { + m := &String{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseStringNumber(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeString(m *String, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } +} diff --git a/gen/test/go/mx/string_mute.go b/gen/test/go/mx/string_mute.go new file mode 100644 index 000000000..cb2e89dc8 --- /dev/null +++ b/gen/test/go/mx/string_mute.go @@ -0,0 +1,124 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The string-mute type represents string mute on and mute off symbols. +type StringMute struct { + Type *OnOff // attribute "type" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" + ID *string // attribute "id" +} + +func parseStringMute(el *etree.Element) (*StringMute, error) { + m := &StringMute{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseOnOff(a.Value) + m.Type = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeStringMute(m *StringMute, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/strong_accent.go b/gen/test/go/mx/strong_accent.go new file mode 100644 index 000000000..6b13a4b77 --- /dev/null +++ b/gen/test/go/mx/strong_accent.go @@ -0,0 +1,102 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The strong-accent type indicates a vertical accent mark. The type attribute indicates if the +// point of the accent is down or up. +type StrongAccent struct { + EmptyPlacement + Type *UpDown // attribute "type" +} + +func parseStrongAccent(el *etree.Element) (*StrongAccent, error) { + m := &StrongAccent{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "type": + v := ParseUpDown(a.Value) + m.Type = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeStrongAccent(m *StrongAccent, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } +} diff --git a/gen/test/go/mx/style_text.go b/gen/test/go/mx/style_text.go new file mode 100644 index 000000000..9bc734600 --- /dev/null +++ b/gen/test/go/mx/style_text.go @@ -0,0 +1,99 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The style-text type represents a text element with a print-style attribute group. +type StyleText struct { + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Value string // text content +} + +func parseStyleText(el *etree.Element) (*StyleText, error) { + m := &StyleText{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeStyleText(m *StyleText, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/supports.go b/gen/test/go/mx/supports.go new file mode 100644 index 000000000..d4e506ff8 --- /dev/null +++ b/gen/test/go/mx/supports.go @@ -0,0 +1,66 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The supports type indicates if a MusicXML encoding supports a particular MusicXML element. This +// is recommended for elements like beam, stem, and accidental, where the absence of an element is +// ambiguous if you do not know if the encoding supports that element. For Version 2.0, the supports +// element is expanded to allow programs to indicate support for particular attributes or particular +// values. This lets applications communicate, for example, that all system and/or page breaks are +// contained in the MusicXML file. +type Supports struct { + Type *YesNo // attribute "type" + Element *string // attribute "element" + Attribute *string // attribute "attribute" + Value *string // attribute "value" +} + +func parseSupports(el *etree.Element) (*Supports, error) { + m := &Supports{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseYesNo(a.Value) + m.Type = &v + case "element": + v := a.Value + m.Element = &v + case "attribute": + v := a.Value + m.Attribute = &v + case "value": + v := a.Value + m.Value = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeSupports(m *Supports, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Element != nil { + el.CreateAttr("element", (*m.Element)) + } + if m.Attribute != nil { + el.CreateAttr("attribute", (*m.Attribute)) + } + if m.Value != nil { + el.CreateAttr("value", (*m.Value)) + } +} diff --git a/gen/test/go/mx/system_dividers.go b/gen/test/go/mx/system_dividers.go new file mode 100644 index 000000000..87ebe106d --- /dev/null +++ b/gen/test/go/mx/system_dividers.go @@ -0,0 +1,72 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The system-dividers element indicates the presence or absence of system dividers (also known as +// system separation marks) between systems displayed on the same page. Dividers on the left and +// right side of the page are controlled by the left-divider and right-divider elements +// respectively. The default vertical position is half the system-distance value from the top of the +// system that is below the divider. The default horizontal position is the left and right system +// margin, respectively. When used in the print element, the system-dividers element affects the +// dividers that would appear between the current system and the previous system. +type SystemDividers struct { + Children []SystemDividersChild // child elements in document order +} + +// SystemDividersChild is one child element of SystemDividers: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type SystemDividersChild struct { + LeftDivider *EmptyPrintObjectStyleAlign + RightDivider *EmptyPrintObjectStyleAlign +} + +func parseSystemDividers(el *etree.Element) (*SystemDividers, error) { + m := &SystemDividers{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "left-divider": + v, err := parseEmptyPrintObjectStyleAlign(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, SystemDividersChild{LeftDivider: v}) + case "right-divider": + v, err := parseEmptyPrintObjectStyleAlign(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, SystemDividersChild{RightDivider: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeSystemDividers(m *SystemDividers, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.LeftDivider != nil: + serializeEmptyPrintObjectStyleAlign(ch.LeftDivider, el, "left-divider") + case ch.RightDivider != nil: + serializeEmptyPrintObjectStyleAlign(ch.RightDivider, el, "right-divider") + } + } +} diff --git a/gen/test/go/mx/system_layout.go b/gen/test/go/mx/system_layout.go new file mode 100644 index 000000000..7eb845ecc --- /dev/null +++ b/gen/test/go/mx/system_layout.go @@ -0,0 +1,87 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// A system is a group of staves that are read and played simultaneously. System layout includes +// left and right margins and the vertical distance from the previous system. The system distance is +// measured from the bottom line of the previous system to the top line of the current system. It is +// ignored for the first system on a page. The top system distance is measured from the page's top +// margin to the top line of the first system. It is ignored for all but the first system on a page. +// Sometimes the sum of measure widths in a system may not equal the system width specified by the +// layout elements due to roundoff or other errors. The behavior when reading MusicXML files in +// these cases is application-dependent. For instance, applications may find that the system layout +// data is more reliable than the sum of the measure widths, and adjust the measure widths +// accordingly. +type SystemLayout struct { + Children []SystemLayoutChild // child elements in document order +} + +// SystemLayoutChild is one child element of SystemLayout: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type SystemLayoutChild struct { + SystemMargins *SystemMargins + SystemDistance *Tenths + TopSystemDistance *Tenths + SystemDividers *SystemDividers +} + +func parseSystemLayout(el *etree.Element) (*SystemLayout, error) { + m := &SystemLayout{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "system-margins": + v, err := parseSystemMargins(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, SystemLayoutChild{SystemMargins: v}) + case "system-distance": + v := ParseTenths(c.Text()) + m.Children = append(m.Children, SystemLayoutChild{SystemDistance: &v}) + case "top-system-distance": + v := ParseTenths(c.Text()) + m.Children = append(m.Children, SystemLayoutChild{TopSystemDistance: &v}) + case "system-dividers": + v, err := parseSystemDividers(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, SystemLayoutChild{SystemDividers: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeSystemLayout(m *SystemLayout, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.SystemMargins != nil: + serializeSystemMargins(ch.SystemMargins, el, "system-margins") + case ch.SystemDistance != nil: + el.CreateElement("system-distance").SetText((*ch.SystemDistance).String()) + case ch.TopSystemDistance != nil: + el.CreateElement("top-system-distance").SetText((*ch.TopSystemDistance).String()) + case ch.SystemDividers != nil: + serializeSystemDividers(ch.SystemDividers, el, "system-dividers") + } + } +} diff --git a/gen/test/go/mx/system_margins.go b/gen/test/go/mx/system_margins.go new file mode 100644 index 000000000..445f682f4 --- /dev/null +++ b/gen/test/go/mx/system_margins.go @@ -0,0 +1,61 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// System margins are relative to the page margins. Positive values indent and negative values +// reduce the margin size. +type SystemMargins struct { + Children []SystemMarginsChild // child elements in document order +} + +// SystemMarginsChild is one child element of SystemMargins: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type SystemMarginsChild struct { + LeftMargin *Tenths + RightMargin *Tenths +} + +func parseSystemMargins(el *etree.Element) (*SystemMargins, error) { + m := &SystemMargins{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "left-margin": + v := ParseTenths(c.Text()) + m.Children = append(m.Children, SystemMarginsChild{LeftMargin: &v}) + case "right-margin": + v := ParseTenths(c.Text()) + m.Children = append(m.Children, SystemMarginsChild{RightMargin: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeSystemMargins(m *SystemMargins, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.LeftMargin != nil: + el.CreateElement("left-margin").SetText((*ch.LeftMargin).String()) + case ch.RightMargin != nil: + el.CreateElement("right-margin").SetText((*ch.RightMargin).String()) + } + } +} diff --git a/gen/test/go/mx/tap.go b/gen/test/go/mx/tap.go new file mode 100644 index 000000000..d60d498cb --- /dev/null +++ b/gen/test/go/mx/tap.go @@ -0,0 +1,117 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The tap type indicates a tap on the fretboard. The text content allows specification of the +// notation; + and T are common choices. If the element is empty, the hand attribute is used to +// specify the symbol to use. The hand attribute is ignored if the tap glyph is already specified by +// the text content. If neither text content nor the hand attribute are present, the display is +// application-specific. +type Tap struct { + Hand *TapHand // attribute "hand" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" + Value string // text content +} + +func parseTap(el *etree.Element) (*Tap, error) { + m := &Tap{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "hand": + v := ParseTapHand(a.Value) + m.Hand = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeTap(m *Tap, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.Hand != nil { + el.CreateAttr("hand", (*m.Hand).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } +} diff --git a/gen/test/go/mx/technical.go b/gen/test/go/mx/technical.go new file mode 100644 index 000000000..e8df107a6 --- /dev/null +++ b/gen/test/go/mx/technical.go @@ -0,0 +1,334 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Technical indications give performance information for individual instruments. +type Technical struct { + ID *string // attribute "id" + Children []TechnicalChild // child elements in document order +} + +// TechnicalChild is one child element of Technical: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type TechnicalChild struct { + UpBow *EmptyPlacement + DownBow *EmptyPlacement + Harmonic *Harmonic + OpenString *EmptyPlacement + ThumbPosition *EmptyPlacement + Fingering *Fingering + Pluck *PlacementText + DoubleTongue *EmptyPlacement + TripleTongue *EmptyPlacement + Stopped *EmptyPlacementSMUFL + SnapPizzicato *EmptyPlacement + Fret *Fret + String *String + HammerOn *HammerOnPullOff + PullOff *HammerOnPullOff + Bend *Bend + Tap *Tap + Heel *HeelToe + Toe *HeelToe + Fingernails *EmptyPlacement + Hole *Hole + Arrow *Arrow + Handbell *Handbell + BrassBend *EmptyPlacement + Flip *EmptyPlacement + Smear *EmptyPlacement + Open *EmptyPlacementSMUFL + HalfMuted *EmptyPlacementSMUFL + HarmonMute *HarmonMute + Golpe *EmptyPlacement + OtherTechnical *OtherPlacementText +} + +func parseTechnical(el *etree.Element) (*Technical, error) { + m := &Technical{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "up-bow": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{UpBow: v}) + case "down-bow": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{DownBow: v}) + case "harmonic": + v, err := parseHarmonic(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{Harmonic: v}) + case "open-string": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{OpenString: v}) + case "thumb-position": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{ThumbPosition: v}) + case "fingering": + v, err := parseFingering(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{Fingering: v}) + case "pluck": + v, err := parsePlacementText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{Pluck: v}) + case "double-tongue": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{DoubleTongue: v}) + case "triple-tongue": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{TripleTongue: v}) + case "stopped": + v, err := parseEmptyPlacementSMUFL(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{Stopped: v}) + case "snap-pizzicato": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{SnapPizzicato: v}) + case "fret": + v, err := parseFret(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{Fret: v}) + case "string": + v, err := parseString(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{String: v}) + case "hammer-on": + v, err := parseHammerOnPullOff(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{HammerOn: v}) + case "pull-off": + v, err := parseHammerOnPullOff(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{PullOff: v}) + case "bend": + v, err := parseBend(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{Bend: v}) + case "tap": + v, err := parseTap(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{Tap: v}) + case "heel": + v, err := parseHeelToe(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{Heel: v}) + case "toe": + v, err := parseHeelToe(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{Toe: v}) + case "fingernails": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{Fingernails: v}) + case "hole": + v, err := parseHole(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{Hole: v}) + case "arrow": + v, err := parseArrow(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{Arrow: v}) + case "handbell": + v, err := parseHandbell(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{Handbell: v}) + case "brass-bend": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{BrassBend: v}) + case "flip": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{Flip: v}) + case "smear": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{Smear: v}) + case "open": + v, err := parseEmptyPlacementSMUFL(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{Open: v}) + case "half-muted": + v, err := parseEmptyPlacementSMUFL(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{HalfMuted: v}) + case "harmon-mute": + v, err := parseHarmonMute(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{HarmonMute: v}) + case "golpe": + v, err := parseEmptyPlacement(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{Golpe: v}) + case "other-technical": + v, err := parseOtherPlacementText(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TechnicalChild{OtherTechnical: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeTechnical(m *Technical, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.UpBow != nil: + serializeEmptyPlacement(ch.UpBow, el, "up-bow") + case ch.DownBow != nil: + serializeEmptyPlacement(ch.DownBow, el, "down-bow") + case ch.Harmonic != nil: + serializeHarmonic(ch.Harmonic, el, "harmonic") + case ch.OpenString != nil: + serializeEmptyPlacement(ch.OpenString, el, "open-string") + case ch.ThumbPosition != nil: + serializeEmptyPlacement(ch.ThumbPosition, el, "thumb-position") + case ch.Fingering != nil: + serializeFingering(ch.Fingering, el, "fingering") + case ch.Pluck != nil: + serializePlacementText(ch.Pluck, el, "pluck") + case ch.DoubleTongue != nil: + serializeEmptyPlacement(ch.DoubleTongue, el, "double-tongue") + case ch.TripleTongue != nil: + serializeEmptyPlacement(ch.TripleTongue, el, "triple-tongue") + case ch.Stopped != nil: + serializeEmptyPlacementSMUFL(ch.Stopped, el, "stopped") + case ch.SnapPizzicato != nil: + serializeEmptyPlacement(ch.SnapPizzicato, el, "snap-pizzicato") + case ch.Fret != nil: + serializeFret(ch.Fret, el, "fret") + case ch.String != nil: + serializeString(ch.String, el, "string") + case ch.HammerOn != nil: + serializeHammerOnPullOff(ch.HammerOn, el, "hammer-on") + case ch.PullOff != nil: + serializeHammerOnPullOff(ch.PullOff, el, "pull-off") + case ch.Bend != nil: + serializeBend(ch.Bend, el, "bend") + case ch.Tap != nil: + serializeTap(ch.Tap, el, "tap") + case ch.Heel != nil: + serializeHeelToe(ch.Heel, el, "heel") + case ch.Toe != nil: + serializeHeelToe(ch.Toe, el, "toe") + case ch.Fingernails != nil: + serializeEmptyPlacement(ch.Fingernails, el, "fingernails") + case ch.Hole != nil: + serializeHole(ch.Hole, el, "hole") + case ch.Arrow != nil: + serializeArrow(ch.Arrow, el, "arrow") + case ch.Handbell != nil: + serializeHandbell(ch.Handbell, el, "handbell") + case ch.BrassBend != nil: + serializeEmptyPlacement(ch.BrassBend, el, "brass-bend") + case ch.Flip != nil: + serializeEmptyPlacement(ch.Flip, el, "flip") + case ch.Smear != nil: + serializeEmptyPlacement(ch.Smear, el, "smear") + case ch.Open != nil: + serializeEmptyPlacementSMUFL(ch.Open, el, "open") + case ch.HalfMuted != nil: + serializeEmptyPlacementSMUFL(ch.HalfMuted, el, "half-muted") + case ch.HarmonMute != nil: + serializeHarmonMute(ch.HarmonMute, el, "harmon-mute") + case ch.Golpe != nil: + serializeEmptyPlacement(ch.Golpe, el, "golpe") + case ch.OtherTechnical != nil: + serializeOtherPlacementText(ch.OtherTechnical, el, "other-technical") + } + } +} diff --git a/gen/test/go/mx/text_element_data.go b/gen/test/go/mx/text_element_data.go new file mode 100644 index 000000000..bdbe91e9e --- /dev/null +++ b/gen/test/go/mx/text_element_data.go @@ -0,0 +1,122 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The text-element-data type represents a syllable or portion of a syllable for lyric text +// underlay. A hyphen in the string content should only be used for an actual hyphenated word. +// Language names for text elements come from ISO 639, with optional country subcodes from ISO 3166. +type TextElementData struct { + XMLLang *string // attribute "xml:lang" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Underline *NumberOfLines // attribute "underline" + Overline *NumberOfLines // attribute "overline" + LineThrough *NumberOfLines // attribute "line-through" + Rotation *RotationDegrees // attribute "rotation" + LetterSpacing *NumberOrNormal // attribute "letter-spacing" + Dir *TextDirection // attribute "dir" + Value string // text content +} + +func parseTextElementData(el *etree.Element) (*TextElementData, error) { + m := &TextElementData{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "xml:lang": + v := a.Value + m.XMLLang = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "underline": + v := ParseNumberOfLines(a.Value) + m.Underline = &v + case "overline": + v := ParseNumberOfLines(a.Value) + m.Overline = &v + case "line-through": + v := ParseNumberOfLines(a.Value) + m.LineThrough = &v + case "rotation": + v := ParseRotationDegrees(a.Value) + m.Rotation = &v + case "letter-spacing": + v := ParseNumberOrNormal(a.Value) + m.LetterSpacing = &v + case "dir": + v := ParseTextDirection(a.Value) + m.Dir = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeTextElementData(m *TextElementData, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.XMLLang != nil { + el.CreateAttr("xml:lang", (*m.XMLLang)) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Underline != nil { + el.CreateAttr("underline", (*m.Underline).String()) + } + if m.Overline != nil { + el.CreateAttr("overline", (*m.Overline).String()) + } + if m.LineThrough != nil { + el.CreateAttr("line-through", (*m.LineThrough).String()) + } + if m.Rotation != nil { + el.CreateAttr("rotation", (*m.Rotation).String()) + } + if m.LetterSpacing != nil { + el.CreateAttr("letter-spacing", (*m.LetterSpacing).String()) + } + if m.Dir != nil { + el.CreateAttr("dir", (*m.Dir).String()) + } +} diff --git a/gen/test/go/mx/tie.go b/gen/test/go/mx/tie.go new file mode 100644 index 000000000..cd62864f7 --- /dev/null +++ b/gen/test/go/mx/tie.go @@ -0,0 +1,49 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The tie element indicates that a tie begins or ends with this note. If the tie element applies +// only particular times through a repeat, the time-only attribute indicates which times to apply +// it. The tie element indicates sound; the tied element indicates notation. +type Tie struct { + Type *StartStop // attribute "type" + TimeOnly *TimeOnly // attribute "time-only" +} + +func parseTie(el *etree.Element) (*Tie, error) { + m := &Tie{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStartStop(a.Value) + m.Type = &v + case "time-only": + v := ParseTimeOnly(a.Value) + m.TimeOnly = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeTie(m *Tie, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.TimeOnly != nil { + el.CreateAttr("time-only", (*m.TimeOnly).String()) + } +} diff --git a/gen/test/go/mx/tied.go b/gen/test/go/mx/tied.go new file mode 100644 index 000000000..97b12554e --- /dev/null +++ b/gen/test/go/mx/tied.go @@ -0,0 +1,177 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The tied element represents the notated tie. The tie element represents the tie sound. The number +// attribute is rarely needed to disambiguate ties, since note pitches will usually suffice. The +// attribute is implied rather than defaulting to 1 as with most elements. It is available for use +// in more complex tied notation situations. Ties that join two notes of the same pitch together +// should be represented with a tied element on the first note with type="start" and a tied element +// on the second note with type="stop". This can also be done if the two notes being tied are +// enharmonically equivalent, but have different step values. It is not recommended to use tied +// elements to join two notes with enharmonically inequivalent pitches. Ties that indicate that an +// instrument should be undamped are specified with a single tied element with type="let-ring". Ties +// that are visually attached to only one note, other than undamped ties, should be specified with +// two tied elements on the same note, first type="start" then type="stop". This can be used to +// represent ties into or out of repeated sections or codas. +type Tied struct { + Type *TiedType // attribute "type" + Number *NumberLevel // attribute "number" + LineType *LineType // attribute "line-type" + DashLength *Tenths // attribute "dash-length" + SpaceLength *Tenths // attribute "space-length" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + Placement *AboveBelow // attribute "placement" + Orientation *OverUnder // attribute "orientation" + BezierX *Tenths // attribute "bezier-x" + BezierY *Tenths // attribute "bezier-y" + BezierX2 *Tenths // attribute "bezier-x2" + BezierY2 *Tenths // attribute "bezier-y2" + BezierOffset *Divisions // attribute "bezier-offset" + BezierOffset2 *Divisions // attribute "bezier-offset2" + Color *Color // attribute "color" + ID *string // attribute "id" +} + +func parseTied(el *etree.Element) (*Tied, error) { + m := &Tied{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseTiedType(a.Value) + m.Type = &v + case "number": + v := ParseNumberLevel(a.Value) + m.Number = &v + case "line-type": + v := ParseLineType(a.Value) + m.LineType = &v + case "dash-length": + v := ParseTenths(a.Value) + m.DashLength = &v + case "space-length": + v := ParseTenths(a.Value) + m.SpaceLength = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "orientation": + v := ParseOverUnder(a.Value) + m.Orientation = &v + case "bezier-x": + v := ParseTenths(a.Value) + m.BezierX = &v + case "bezier-y": + v := ParseTenths(a.Value) + m.BezierY = &v + case "bezier-x2": + v := ParseTenths(a.Value) + m.BezierX2 = &v + case "bezier-y2": + v := ParseTenths(a.Value) + m.BezierY2 = &v + case "bezier-offset": + v := ParseDivisions(a.Value) + m.BezierOffset = &v + case "bezier-offset2": + v := ParseDivisions(a.Value) + m.BezierOffset2 = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeTied(m *Tied, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.LineType != nil { + el.CreateAttr("line-type", (*m.LineType).String()) + } + if m.DashLength != nil { + el.CreateAttr("dash-length", (*m.DashLength).String()) + } + if m.SpaceLength != nil { + el.CreateAttr("space-length", (*m.SpaceLength).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.Orientation != nil { + el.CreateAttr("orientation", (*m.Orientation).String()) + } + if m.BezierX != nil { + el.CreateAttr("bezier-x", (*m.BezierX).String()) + } + if m.BezierY != nil { + el.CreateAttr("bezier-y", (*m.BezierY).String()) + } + if m.BezierX2 != nil { + el.CreateAttr("bezier-x2", (*m.BezierX2).String()) + } + if m.BezierY2 != nil { + el.CreateAttr("bezier-y2", (*m.BezierY2).String()) + } + if m.BezierOffset != nil { + el.CreateAttr("bezier-offset", (*m.BezierOffset).String()) + } + if m.BezierOffset2 != nil { + el.CreateAttr("bezier-offset2", (*m.BezierOffset2).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/time.go b/gen/test/go/mx/time.go new file mode 100644 index 000000000..aa9b30053 --- /dev/null +++ b/gen/test/go/mx/time.go @@ -0,0 +1,194 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Time signatures are represented by the beats element for the numerator and the beat-type element +// for the denominator. The symbol attribute is used indicate common and cut time symbols as well as +// a single number display. Multiple pairs of beat and beat-type elements are used for composite +// time signatures with multiple denominators, such as 2/4 + 3/8. A composite such as 3+2/8 requires +// only one beat/beat-type pair. The print-object attribute allows a time signature to be specified +// but not printed, as is the case for excerpts from the middle of a score. The value is "yes" if +// not present. The optional number attribute refers to staff numbers within the part. If absent, +// the time signature applies to all staves in the part. +type Time struct { + Number *StaffNumber // attribute "number" + Symbol *TimeSymbol // attribute "symbol" + Separator *TimeSeparator // attribute "separator" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Halign *LeftCenterRight // attribute "halign" + Valign *Valign // attribute "valign" + PrintObject *YesNo // attribute "print-object" + ID *string // attribute "id" + Children []TimeChild // child elements in document order +} + +// TimeChild is one child element of Time: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type TimeChild struct { + Beats *string + BeatType *string + Interchangeable *Interchangeable + SenzaMisura *string +} + +func parseTime(el *etree.Element) (*Time, error) { + m := &Time{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "number": + v := ParseStaffNumber(a.Value) + m.Number = &v + case "symbol": + v := ParseTimeSymbol(a.Value) + m.Symbol = &v + case "separator": + v := ParseTimeSeparator(a.Value) + m.Separator = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "halign": + v := ParseLeftCenterRight(a.Value) + m.Halign = &v + case "valign": + v := ParseValign(a.Value) + m.Valign = &v + case "print-object": + v := ParseYesNo(a.Value) + m.PrintObject = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "beats": + v := c.Text() + m.Children = append(m.Children, TimeChild{Beats: &v}) + case "beat-type": + v := c.Text() + m.Children = append(m.Children, TimeChild{BeatType: &v}) + case "interchangeable": + v, err := parseInterchangeable(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TimeChild{Interchangeable: v}) + case "senza-misura": + v := c.Text() + m.Children = append(m.Children, TimeChild{SenzaMisura: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeTime(m *Time, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.Symbol != nil { + el.CreateAttr("symbol", (*m.Symbol).String()) + } + if m.Separator != nil { + el.CreateAttr("separator", (*m.Separator).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Halign != nil { + el.CreateAttr("halign", (*m.Halign).String()) + } + if m.Valign != nil { + el.CreateAttr("valign", (*m.Valign).String()) + } + if m.PrintObject != nil { + el.CreateAttr("print-object", (*m.PrintObject).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.Beats != nil: + el.CreateElement("beats").SetText((*ch.Beats)) + case ch.BeatType != nil: + el.CreateElement("beat-type").SetText((*ch.BeatType)) + case ch.Interchangeable != nil: + serializeInterchangeable(ch.Interchangeable, el, "interchangeable") + case ch.SenzaMisura != nil: + el.CreateElement("senza-misura").SetText((*ch.SenzaMisura)) + } + } +} diff --git a/gen/test/go/mx/time_modification.go b/gen/test/go/mx/time_modification.go new file mode 100644 index 000000000..eec53e9aa --- /dev/null +++ b/gen/test/go/mx/time_modification.go @@ -0,0 +1,79 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Time modification indicates tuplets, double-note tremolos, and other durational changes. A +// time-modification element shows how the cumulative, sounding effect of tuplets and double-note +// tremolos compare to the written note type represented by the type and dot elements. Nested +// tuplets and other notations that use more detailed information need both the time-modification +// and tuplet elements to be represented accurately. +type TimeModification struct { + Children []TimeModificationChild // child elements in document order +} + +// TimeModificationChild is one child element of TimeModification: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type TimeModificationChild struct { + ActualNotes *int + NormalNotes *int + NormalType *NoteTypeValue + NormalDot *Empty +} + +func parseTimeModification(el *etree.Element) (*TimeModification, error) { + m := &TimeModification{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "actual-notes": + v := parseInt(c.Text()) + m.Children = append(m.Children, TimeModificationChild{ActualNotes: &v}) + case "normal-notes": + v := parseInt(c.Text()) + m.Children = append(m.Children, TimeModificationChild{NormalNotes: &v}) + case "normal-type": + v := ParseNoteTypeValue(c.Text()) + m.Children = append(m.Children, TimeModificationChild{NormalType: &v}) + case "normal-dot": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TimeModificationChild{NormalDot: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeTimeModification(m *TimeModification, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.ActualNotes != nil: + el.CreateElement("actual-notes").SetText(formatInt((*ch.ActualNotes))) + case ch.NormalNotes != nil: + el.CreateElement("normal-notes").SetText(formatInt((*ch.NormalNotes))) + case ch.NormalType != nil: + el.CreateElement("normal-type").SetText((*ch.NormalType).String()) + case ch.NormalDot != nil: + serializeEmpty(ch.NormalDot, el, "normal-dot") + } + } +} diff --git a/gen/test/go/mx/timewise_measure.go b/gen/test/go/mx/timewise_measure.go new file mode 100644 index 000000000..45722d3d9 --- /dev/null +++ b/gen/test/go/mx/timewise_measure.go @@ -0,0 +1,98 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +type TimewiseMeasure struct { + Number *string // attribute "number" + Text *MeasureText // attribute "text" + Implicit *YesNo // attribute "implicit" + NonControlling *YesNo // attribute "non-controlling" + Width *Tenths // attribute "width" + ID *string // attribute "id" + Children []TimewiseMeasureChild // child elements in document order +} + +// TimewiseMeasureChild is one child element of TimewiseMeasure: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type TimewiseMeasureChild struct { + Part *TimewisePart +} + +func parseTimewiseMeasure(el *etree.Element) (*TimewiseMeasure, error) { + m := &TimewiseMeasure{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "number": + v := a.Value + m.Number = &v + case "text": + v := ParseMeasureText(a.Value) + m.Text = &v + case "implicit": + v := ParseYesNo(a.Value) + m.Implicit = &v + case "non-controlling": + v := ParseYesNo(a.Value) + m.NonControlling = &v + case "width": + v := ParseTenths(a.Value) + m.Width = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "part": + v, err := parseTimewisePart(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TimewiseMeasureChild{Part: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeTimewiseMeasure(m *TimewiseMeasure, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Number != nil { + el.CreateAttr("number", (*m.Number)) + } + if m.Text != nil { + el.CreateAttr("text", (*m.Text).String()) + } + if m.Implicit != nil { + el.CreateAttr("implicit", (*m.Implicit).String()) + } + if m.NonControlling != nil { + el.CreateAttr("non-controlling", (*m.NonControlling).String()) + } + if m.Width != nil { + el.CreateAttr("width", (*m.Width).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.Part != nil: + serializeTimewisePart(ch.Part, el, "part") + } + } +} diff --git a/gen/test/go/mx/timewise_part.go b/gen/test/go/mx/timewise_part.go new file mode 100644 index 000000000..438b71e33 --- /dev/null +++ b/gen/test/go/mx/timewise_part.go @@ -0,0 +1,171 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +type TimewisePart struct { + ID *string // attribute "id" + Children []TimewisePartChild // child elements in document order +} + +// TimewisePartChild is one child element of TimewisePart: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type TimewisePartChild struct { + Note *Note + Backup *Backup + Forward *Forward + Direction *Direction + Attributes *Attributes + Harmony *Harmony + FiguredBass *FiguredBass + Print *Print + Sound *Sound + Barline *Barline + Grouping *Grouping + Link *Link + Bookmark *Bookmark +} + +func parseTimewisePart(el *etree.Element) (*TimewisePart, error) { + m := &TimewisePart{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "note": + v, err := parseNote(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TimewisePartChild{Note: v}) + case "backup": + v, err := parseBackup(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TimewisePartChild{Backup: v}) + case "forward": + v, err := parseForward(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TimewisePartChild{Forward: v}) + case "direction": + v, err := parseDirection(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TimewisePartChild{Direction: v}) + case "attributes": + v, err := parseAttributes(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TimewisePartChild{Attributes: v}) + case "harmony": + v, err := parseHarmony(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TimewisePartChild{Harmony: v}) + case "figured-bass": + v, err := parseFiguredBass(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TimewisePartChild{FiguredBass: v}) + case "print": + v, err := parsePrint(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TimewisePartChild{Print: v}) + case "sound": + v, err := parseSound(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TimewisePartChild{Sound: v}) + case "barline": + v, err := parseBarline(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TimewisePartChild{Barline: v}) + case "grouping": + v, err := parseGrouping(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TimewisePartChild{Grouping: v}) + case "link": + v, err := parseLink(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TimewisePartChild{Link: v}) + case "bookmark": + v, err := parseBookmark(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TimewisePartChild{Bookmark: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeTimewisePart(m *TimewisePart, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.Note != nil: + serializeNote(ch.Note, el, "note") + case ch.Backup != nil: + serializeBackup(ch.Backup, el, "backup") + case ch.Forward != nil: + serializeForward(ch.Forward, el, "forward") + case ch.Direction != nil: + serializeDirection(ch.Direction, el, "direction") + case ch.Attributes != nil: + serializeAttributes(ch.Attributes, el, "attributes") + case ch.Harmony != nil: + serializeHarmony(ch.Harmony, el, "harmony") + case ch.FiguredBass != nil: + serializeFiguredBass(ch.FiguredBass, el, "figured-bass") + case ch.Print != nil: + serializePrint(ch.Print, el, "print") + case ch.Sound != nil: + serializeSound(ch.Sound, el, "sound") + case ch.Barline != nil: + serializeBarline(ch.Barline, el, "barline") + case ch.Grouping != nil: + serializeGrouping(ch.Grouping, el, "grouping") + case ch.Link != nil: + serializeLink(ch.Link, el, "link") + case ch.Bookmark != nil: + serializeBookmark(ch.Bookmark, el, "bookmark") + } + } +} diff --git a/gen/test/go/mx/transpose.go b/gen/test/go/mx/transpose.go new file mode 100644 index 000000000..dff4ea5fb --- /dev/null +++ b/gen/test/go/mx/transpose.go @@ -0,0 +1,92 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The transpose type represents what must be added to a written pitch to get a correct sounding +// pitch. The optional number attribute refers to staff numbers, from top to bottom on the system. +// If absent, the transposition applies to all staves in the part. Per-staff transposition is most +// often used in parts that represent multiple instruments. +type Transpose struct { + Number *StaffNumber // attribute "number" + ID *string // attribute "id" + Children []TransposeChild // child elements in document order +} + +// TransposeChild is one child element of Transpose: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type TransposeChild struct { + Diatonic *int + Chromatic *Semitones + OctaveChange *int + Double *Empty +} + +func parseTranspose(el *etree.Element) (*Transpose, error) { + m := &Transpose{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "number": + v := ParseStaffNumber(a.Value) + m.Number = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "diatonic": + v := parseInt(c.Text()) + m.Children = append(m.Children, TransposeChild{Diatonic: &v}) + case "chromatic": + v := ParseSemitones(c.Text()) + m.Children = append(m.Children, TransposeChild{Chromatic: &v}) + case "octave-change": + v := parseInt(c.Text()) + m.Children = append(m.Children, TransposeChild{OctaveChange: &v}) + case "double": + v, err := parseEmpty(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TransposeChild{Double: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeTranspose(m *Transpose, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.Diatonic != nil: + el.CreateElement("diatonic").SetText(formatInt((*ch.Diatonic))) + case ch.Chromatic != nil: + el.CreateElement("chromatic").SetText((*ch.Chromatic).String()) + case ch.OctaveChange != nil: + el.CreateElement("octave-change").SetText(formatInt((*ch.OctaveChange))) + case ch.Double != nil: + serializeEmpty(ch.Double, el, "double") + } + } +} diff --git a/gen/test/go/mx/tremolo.go b/gen/test/go/mx/tremolo.go new file mode 100644 index 000000000..8df5a5f3d --- /dev/null +++ b/gen/test/go/mx/tremolo.go @@ -0,0 +1,132 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The tremolo ornament can be used to indicate single-note, double-note, or unmeasured tremolos. +// Single-note tremolos use the single type, double-note tremolos use the start and stop types, and +// unmeasured tremolos use the unmeasured type. The default is "single" for compatibility with +// Version 1.1. The text of the element indicates the number of tremolo marks and is an integer from +// 0 to 8. Note that the number of attached beams is not included in this value, but is represented +// separately using the beam element. The value should be 0 for unmeasured tremolos. When using +// double-note tremolos, the duration of each note in the tremolo should correspond to half of the +// notated type value. A time-modification element should also be added with an actual-notes value +// of 2 and a normal-notes value of 1. If used within a tuplet, this 2/1 ratio should be multiplied +// by the existing tuplet ratio. The smufl attribute specifies the glyph to use from the SMuFL +// tremolos range for an unmeasured tremolo. It is ignored for other tremolo types. The SMuFL +// buzzRoll glyph is used by default if the attribute is missing. Using repeater beams for +// indicating tremolos is deprecated as of MusicXML 3.0. +type Tremolo struct { + Type *TremoloType // attribute "type" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Placement *AboveBelow // attribute "placement" + SMUFL *SMUFLGlyphName // attribute "smufl" + Value TremoloMarks // text content +} + +func parseTremolo(el *etree.Element) (*Tremolo, error) { + m := &Tremolo{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseTremoloType(a.Value) + m.Type = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "smufl": + v := ParseSMUFLGlyphName(a.Value) + m.SMUFL = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseTremoloMarks(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeTremolo(m *Tremolo, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.SMUFL != nil { + el.CreateAttr("smufl", (*m.SMUFL).String()) + } +} diff --git a/gen/test/go/mx/tuplet.go b/gen/test/go/mx/tuplet.go new file mode 100644 index 000000000..10a0e535a --- /dev/null +++ b/gen/test/go/mx/tuplet.go @@ -0,0 +1,162 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// A tuplet element is present when a tuplet is to be displayed graphically, in addition to the +// sound data provided by the time-modification elements. The number attribute is used to +// distinguish nested tuplets. The bracket attribute is used to indicate the presence of a bracket. +// If unspecified, the results are implementation-dependent. The line-shape attribute is used to +// specify whether the bracket is straight or in the older curved or slurred style. It is straight +// by default. Whereas a time-modification element shows how the cumulative, sounding effect of +// tuplets and double-note tremolos compare to the written note type, the tuplet element describes +// how this is displayed. The tuplet element also provides more detailed representation information +// than the time-modification element, and is needed to represent nested tuplets and other complex +// tuplets accurately. The show-number attribute is used to display either the number of actual +// notes, the number of both actual and normal notes, or neither. It is actual by default. The +// show-type attribute is used to display either the actual type, both the actual and normal types, +// or neither. It is none by default. +type Tuplet struct { + Type *StartStop // attribute "type" + Number *NumberLevel // attribute "number" + Bracket *YesNo // attribute "bracket" + ShowNumber *ShowTuplet // attribute "show-number" + ShowType *ShowTuplet // attribute "show-type" + LineShape *LineShape // attribute "line-shape" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + Placement *AboveBelow // attribute "placement" + ID *string // attribute "id" + Children []TupletChild // child elements in document order +} + +// TupletChild is one child element of Tuplet: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type TupletChild struct { + TupletActual *TupletPortion + TupletNormal *TupletPortion +} + +func parseTuplet(el *etree.Element) (*Tuplet, error) { + m := &Tuplet{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStartStop(a.Value) + m.Type = &v + case "number": + v := ParseNumberLevel(a.Value) + m.Number = &v + case "bracket": + v := ParseYesNo(a.Value) + m.Bracket = &v + case "show-number": + v := ParseShowTuplet(a.Value) + m.ShowNumber = &v + case "show-type": + v := ParseShowTuplet(a.Value) + m.ShowType = &v + case "line-shape": + v := ParseLineShape(a.Value) + m.LineShape = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "tuplet-actual": + v, err := parseTupletPortion(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TupletChild{TupletActual: v}) + case "tuplet-normal": + v, err := parseTupletPortion(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TupletChild{TupletNormal: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeTuplet(m *Tuplet, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.Bracket != nil { + el.CreateAttr("bracket", (*m.Bracket).String()) + } + if m.ShowNumber != nil { + el.CreateAttr("show-number", (*m.ShowNumber).String()) + } + if m.ShowType != nil { + el.CreateAttr("show-type", (*m.ShowType).String()) + } + if m.LineShape != nil { + el.CreateAttr("line-shape", (*m.LineShape).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } + for _, ch := range m.Children { + switch { + case ch.TupletActual != nil: + serializeTupletPortion(ch.TupletActual, el, "tuplet-actual") + case ch.TupletNormal != nil: + serializeTupletPortion(ch.TupletNormal, el, "tuplet-normal") + } + } +} diff --git a/gen/test/go/mx/tuplet_dot.go b/gen/test/go/mx/tuplet_dot.go new file mode 100644 index 000000000..1710e64eb --- /dev/null +++ b/gen/test/go/mx/tuplet_dot.go @@ -0,0 +1,68 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The tuplet-dot type is used to specify dotted normal tuplet types. +type TupletDot struct { + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" +} + +func parseTupletDot(el *etree.Element) (*TupletDot, error) { + m := &TupletDot{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeTupletDot(m *TupletDot, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/tuplet_number.go b/gen/test/go/mx/tuplet_number.go new file mode 100644 index 000000000..4ef59e562 --- /dev/null +++ b/gen/test/go/mx/tuplet_number.go @@ -0,0 +1,71 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The tuplet-number type indicates the number of notes for this portion of the tuplet. +type TupletNumber struct { + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Value int // text content +} + +func parseTupletNumber(el *etree.Element) (*TupletNumber, error) { + m := &TupletNumber{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = parseInt(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeTupletNumber(m *TupletNumber, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(formatInt(m.Value)) + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/tuplet_portion.go b/gen/test/go/mx/tuplet_portion.go new file mode 100644 index 000000000..0efcc314a --- /dev/null +++ b/gen/test/go/mx/tuplet_portion.go @@ -0,0 +1,78 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The tuplet-portion type provides optional full control over tuplet specifications. It allows the +// number and note type (including dots) to be set for the actual and normal portions of a single +// tuplet. If any of these elements are absent, their values are based on the time-modification +// element. +type TupletPortion struct { + Children []TupletPortionChild // child elements in document order +} + +// TupletPortionChild is one child element of TupletPortion: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type TupletPortionChild struct { + TupletNumber *TupletNumber + TupletType *TupletType + TupletDot *TupletDot +} + +func parseTupletPortion(el *etree.Element) (*TupletPortion, error) { + m := &TupletPortion{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "tuplet-number": + v, err := parseTupletNumber(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TupletPortionChild{TupletNumber: v}) + case "tuplet-type": + v, err := parseTupletType(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TupletPortionChild{TupletType: v}) + case "tuplet-dot": + v, err := parseTupletDot(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, TupletPortionChild{TupletDot: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeTupletPortion(m *TupletPortion, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.TupletNumber != nil: + serializeTupletNumber(ch.TupletNumber, el, "tuplet-number") + case ch.TupletType != nil: + serializeTupletType(ch.TupletType, el, "tuplet-type") + case ch.TupletDot != nil: + serializeTupletDot(ch.TupletDot, el, "tuplet-dot") + } + } +} diff --git a/gen/test/go/mx/tuplet_type.go b/gen/test/go/mx/tuplet_type.go new file mode 100644 index 000000000..3ba974ae7 --- /dev/null +++ b/gen/test/go/mx/tuplet_type.go @@ -0,0 +1,72 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The tuplet-type type indicates the graphical note type of the notes for this portion of the +// tuplet. +type TupletType struct { + FontFamily *CommaSeparatedText // attribute "font-family" + FontStyle *FontStyle // attribute "font-style" + FontSize *FontSize // attribute "font-size" + FontWeight *FontWeight // attribute "font-weight" + Color *Color // attribute "color" + Value NoteTypeValue // text content +} + +func parseTupletType(el *etree.Element) (*TupletType, error) { + m := &TupletType{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "font-family": + v := ParseCommaSeparatedText(a.Value) + m.FontFamily = &v + case "font-style": + v := ParseFontStyle(a.Value) + m.FontStyle = &v + case "font-size": + v := ParseFontSize(a.Value) + m.FontSize = &v + case "font-weight": + v := ParseFontWeight(a.Value) + m.FontWeight = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = ParseNoteTypeValue(el.Text()) + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeTupletType(m *TupletType, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value.String()) + if m.FontFamily != nil { + el.CreateAttr("font-family", (*m.FontFamily).String()) + } + if m.FontStyle != nil { + el.CreateAttr("font-style", (*m.FontStyle).String()) + } + if m.FontSize != nil { + el.CreateAttr("font-size", (*m.FontSize).String()) + } + if m.FontWeight != nil { + el.CreateAttr("font-weight", (*m.FontWeight).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } +} diff --git a/gen/test/go/mx/typed_text.go b/gen/test/go/mx/typed_text.go new file mode 100644 index 000000000..377c968c4 --- /dev/null +++ b/gen/test/go/mx/typed_text.go @@ -0,0 +1,43 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The typed-text type represents a text element with a type attributes. +type TypedText struct { + Type *string // attribute "type" + Value string // text content +} + +func parseTypedText(el *etree.Element) (*TypedText, error) { + m := &TypedText{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := a.Value + m.Type = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + m.Value = el.Text() + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeTypedText(m *TypedText, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + el.SetText(m.Value) + if m.Type != nil { + el.CreateAttr("type", (*m.Type)) + } +} diff --git a/gen/test/go/mx/unpitched.go b/gen/test/go/mx/unpitched.go new file mode 100644 index 000000000..002e2fcf5 --- /dev/null +++ b/gen/test/go/mx/unpitched.go @@ -0,0 +1,61 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The unpitched type represents musical elements that are notated on the staff but lack definite +// pitch, such as unpitched percussion and speaking voice. +type Unpitched struct { + Children []UnpitchedChild // child elements in document order +} + +// UnpitchedChild is one child element of Unpitched: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type UnpitchedChild struct { + DisplayStep *Step + DisplayOctave *Octave +} + +func parseUnpitched(el *etree.Element) (*Unpitched, error) { + m := &Unpitched{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "display-step": + v := ParseStep(c.Text()) + m.Children = append(m.Children, UnpitchedChild{DisplayStep: &v}) + case "display-octave": + v := ParseOctave(c.Text()) + m.Children = append(m.Children, UnpitchedChild{DisplayOctave: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeUnpitched(m *Unpitched, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.DisplayStep != nil: + el.CreateElement("display-step").SetText((*ch.DisplayStep).String()) + case ch.DisplayOctave != nil: + el.CreateElement("display-octave").SetText((*ch.DisplayOctave).String()) + } + } +} diff --git a/gen/test/go/mx/virtual_instrument.go b/gen/test/go/mx/virtual_instrument.go new file mode 100644 index 000000000..df2643ada --- /dev/null +++ b/gen/test/go/mx/virtual_instrument.go @@ -0,0 +1,61 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The virtual-instrument element defines a specific virtual instrument used for an instrument +// sound. +type VirtualInstrument struct { + Children []VirtualInstrumentChild // child elements in document order +} + +// VirtualInstrumentChild is one child element of VirtualInstrument: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type VirtualInstrumentChild struct { + VirtualLibrary *string + VirtualName *string +} + +func parseVirtualInstrument(el *etree.Element) (*VirtualInstrument, error) { + m := &VirtualInstrument{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "virtual-library": + v := c.Text() + m.Children = append(m.Children, VirtualInstrumentChild{VirtualLibrary: &v}) + case "virtual-name": + v := c.Text() + m.Children = append(m.Children, VirtualInstrumentChild{VirtualName: &v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeVirtualInstrument(m *VirtualInstrument, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.VirtualLibrary != nil: + el.CreateElement("virtual-library").SetText((*ch.VirtualLibrary)) + case ch.VirtualName != nil: + el.CreateElement("virtual-name").SetText((*ch.VirtualName)) + } + } +} diff --git a/gen/test/go/mx/wavy_line.go b/gen/test/go/mx/wavy_line.go new file mode 100644 index 000000000..01f2fc53d --- /dev/null +++ b/gen/test/go/mx/wavy_line.go @@ -0,0 +1,139 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Wavy lines are one way to indicate trills. When used with a barline element, they should always +// have type="continue" set. +type WavyLine struct { + Type *StartStopContinue // attribute "type" + Number *NumberLevel // attribute "number" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + Placement *AboveBelow // attribute "placement" + Color *Color // attribute "color" + StartNote *StartNote // attribute "start-note" + TrillStep *TrillStep // attribute "trill-step" + TwoNoteTurn *TwoNoteTurn // attribute "two-note-turn" + Accelerate *YesNo // attribute "accelerate" + Beats *TrillBeats // attribute "beats" + SecondBeat *Percent // attribute "second-beat" + LastBeat *Percent // attribute "last-beat" +} + +func parseWavyLine(el *etree.Element) (*WavyLine, error) { + m := &WavyLine{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseStartStopContinue(a.Value) + m.Type = &v + case "number": + v := ParseNumberLevel(a.Value) + m.Number = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "placement": + v := ParseAboveBelow(a.Value) + m.Placement = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "start-note": + v := ParseStartNote(a.Value) + m.StartNote = &v + case "trill-step": + v := ParseTrillStep(a.Value) + m.TrillStep = &v + case "two-note-turn": + v := ParseTwoNoteTurn(a.Value) + m.TwoNoteTurn = &v + case "accelerate": + v := ParseYesNo(a.Value) + m.Accelerate = &v + case "beats": + v := ParseTrillBeats(a.Value) + m.Beats = &v + case "second-beat": + v := ParsePercent(a.Value) + m.SecondBeat = &v + case "last-beat": + v := ParsePercent(a.Value) + m.LastBeat = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeWavyLine(m *WavyLine, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.Placement != nil { + el.CreateAttr("placement", (*m.Placement).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.StartNote != nil { + el.CreateAttr("start-note", (*m.StartNote).String()) + } + if m.TrillStep != nil { + el.CreateAttr("trill-step", (*m.TrillStep).String()) + } + if m.TwoNoteTurn != nil { + el.CreateAttr("two-note-turn", (*m.TwoNoteTurn).String()) + } + if m.Accelerate != nil { + el.CreateAttr("accelerate", (*m.Accelerate).String()) + } + if m.Beats != nil { + el.CreateAttr("beats", (*m.Beats).String()) + } + if m.SecondBeat != nil { + el.CreateAttr("second-beat", (*m.SecondBeat).String()) + } + if m.LastBeat != nil { + el.CreateAttr("last-beat", (*m.LastBeat).String()) + } +} diff --git a/gen/test/go/mx/wedge.go b/gen/test/go/mx/wedge.go new file mode 100644 index 000000000..e788cb2f8 --- /dev/null +++ b/gen/test/go/mx/wedge.go @@ -0,0 +1,130 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// The wedge type represents crescendo and diminuendo wedge symbols. The type attribute is crescendo +// for the start of a wedge that is closed at the left side, and diminuendo for the start of a wedge +// that is closed on the right side. Spread values are measured in tenths; those at the start of a +// crescendo wedge or end of a diminuendo wedge are ignored. The niente attribute is yes if a circle +// appears at the point of the wedge, indicating a crescendo from nothing or diminuendo to nothing. +// It is no by default, and used only when the type is crescendo, or the type is stop for a wedge +// that began with a diminuendo type. The line-type is solid by default. +type Wedge struct { + Type *WedgeType // attribute "type" + Number *NumberLevel // attribute "number" + Spread *Tenths // attribute "spread" + Niente *YesNo // attribute "niente" + LineType *LineType // attribute "line-type" + DashLength *Tenths // attribute "dash-length" + SpaceLength *Tenths // attribute "space-length" + DefaultX *Tenths // attribute "default-x" + DefaultY *Tenths // attribute "default-y" + RelativeX *Tenths // attribute "relative-x" + RelativeY *Tenths // attribute "relative-y" + Color *Color // attribute "color" + ID *string // attribute "id" +} + +func parseWedge(el *etree.Element) (*Wedge, error) { + m := &Wedge{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + case "type": + v := ParseWedgeType(a.Value) + m.Type = &v + case "number": + v := ParseNumberLevel(a.Value) + m.Number = &v + case "spread": + v := ParseTenths(a.Value) + m.Spread = &v + case "niente": + v := ParseYesNo(a.Value) + m.Niente = &v + case "line-type": + v := ParseLineType(a.Value) + m.LineType = &v + case "dash-length": + v := ParseTenths(a.Value) + m.DashLength = &v + case "space-length": + v := ParseTenths(a.Value) + m.SpaceLength = &v + case "default-x": + v := ParseTenths(a.Value) + m.DefaultX = &v + case "default-y": + v := ParseTenths(a.Value) + m.DefaultY = &v + case "relative-x": + v := ParseTenths(a.Value) + m.RelativeX = &v + case "relative-y": + v := ParseTenths(a.Value) + m.RelativeY = &v + case "color": + v := ParseColor(a.Value) + m.Color = &v + case "id": + v := a.Value + m.ID = &v + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + return m, nil +} + +func serializeWedge(m *Wedge, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + if m.Type != nil { + el.CreateAttr("type", (*m.Type).String()) + } + if m.Number != nil { + el.CreateAttr("number", (*m.Number).String()) + } + if m.Spread != nil { + el.CreateAttr("spread", (*m.Spread).String()) + } + if m.Niente != nil { + el.CreateAttr("niente", (*m.Niente).String()) + } + if m.LineType != nil { + el.CreateAttr("line-type", (*m.LineType).String()) + } + if m.DashLength != nil { + el.CreateAttr("dash-length", (*m.DashLength).String()) + } + if m.SpaceLength != nil { + el.CreateAttr("space-length", (*m.SpaceLength).String()) + } + if m.DefaultX != nil { + el.CreateAttr("default-x", (*m.DefaultX).String()) + } + if m.DefaultY != nil { + el.CreateAttr("default-y", (*m.DefaultY).String()) + } + if m.RelativeX != nil { + el.CreateAttr("relative-x", (*m.RelativeX).String()) + } + if m.RelativeY != nil { + el.CreateAttr("relative-y", (*m.RelativeY).String()) + } + if m.Color != nil { + el.CreateAttr("color", (*m.Color).String()) + } + if m.ID != nil { + el.CreateAttr("id", (*m.ID)) + } +} diff --git a/gen/test/go/mx/work.go b/gen/test/go/mx/work.go new file mode 100644 index 000000000..603043af6 --- /dev/null +++ b/gen/test/go/mx/work.go @@ -0,0 +1,70 @@ +// Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. + +package mx + +import ( + "fmt" + "github.com/beevik/etree" +) + +// Works are optionally identified by number and title. The work type also may indicate a link to +// the opus document that composes multiple scores into a collection. +type Work struct { + Children []WorkChild // child elements in document order +} + +// WorkChild is one child element of Work: exactly one field +// is non-nil, and that pointer says which element this is. (No kind +// discriminator: schema element names like harmony's would +// collide with a synthetic field.) +type WorkChild struct { + WorkNumber *string + WorkTitle *string + Opus *Opus +} + +func parseWork(el *etree.Element) (*Work, error) { + m := &Work{} + for _, a := range el.Attr { + if a.Space == "xmlns" || (a.Space == "" && a.Key == "xmlns") { + continue + } + switch a.FullKey() { + default: + return nil, fmt.Errorf("unknown attribute %q on <%s>", a.FullKey(), el.Tag) + } + } + for _, c := range el.ChildElements() { + switch c.Tag { + case "work-number": + v := c.Text() + m.Children = append(m.Children, WorkChild{WorkNumber: &v}) + case "work-title": + v := c.Text() + m.Children = append(m.Children, WorkChild{WorkTitle: &v}) + case "opus": + v, err := parseOpus(c) + if err != nil { + return nil, err + } + m.Children = append(m.Children, WorkChild{Opus: v}) + default: + return nil, fmt.Errorf("unknown element <%s> in <%s>", c.Tag, el.Tag) + } + } + return m, nil +} + +func serializeWork(m *Work, parent *etree.Element, tag string) { + el := parent.CreateElement(tag) + for _, ch := range m.Children { + switch { + case ch.WorkNumber != nil: + el.CreateElement("work-number").SetText((*ch.WorkNumber)) + case ch.WorkTitle != nil: + el.CreateElement("work-title").SetText((*ch.WorkTitle)) + case ch.Opus != nil: + serializeOpus(ch.Opus, el, "opus") + } + } +} diff --git a/gen/test/go/stub/roundtrip.go b/gen/test/go/stub/roundtrip.go deleted file mode 100644 index 7290c6ade..000000000 --- a/gen/test/go/stub/roundtrip.go +++ /dev/null @@ -1,23 +0,0 @@ -package stub - -import ( - "errors" - - "github.com/beevik/etree" -) - -var ErrNotImplemented = errors.New("generated parser not implemented") - -// FromXDoc parses an etree document into the typed MusicXML model. -// This is a stub that always returns an error until the generator -// emits the Go typed model. -func FromXDoc(doc *etree.Document) (any, error) { - return nil, ErrNotImplemented -} - -// ToXDoc serializes the typed MusicXML model back to an etree document. -// This is a stub that always returns an error until the generator -// emits the Go typed model. -func ToXDoc(model any) (*etree.Document, error) { - return nil, ErrNotImplemented -} From 2f8b71d1fe81a4453426eb9380cebc367dda65dc Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 19:06:38 +0000 Subject: [PATCH 22/45] gen: incorporate emit-stage review feedback (code review + architecture review) Two independent reviews of the emit stage and its Go/C value backends. The accepted findings, by theme: The clamp policy is now data on the plates (the load-bearing change): - NumberPlate carries family (decimal|integer) and resolved ClampStep rules (facets merged with primitive-implied lower bounds, tightest wins, exclusive bounds clamping past by 1 or 1e-6), computed once in gen/plates/build.clamp_steps and unit-tested there (tie-breaks, exclusive max, implied minimums). Both backends' hand-mirrored _clamp_steps copies are deleted; 'one policy, two spellings' is now structural, not a comment. - The policy hole the duplication hid is closed: a primitive numeric union member (positive-integer-or-empty's integer) now carries and applies the implied clamp, so unions enforce the same leniency as named number types. - The int/float clamp mode comes from the IR base, not from string-matching the spelled target type (a [types] override no longer flips clamp mode). - TryParse's contract is pinned: lexically strict, then clamps; generated doc comments only claim clamping when clamp steps exist. Union discriminators are projected, not template-composed: - UnionPlateMember.tag is a Variant scoped and renameable like an enum value; literal variants double as their own tags; the flat-namespace collision gate now covers every constant the backends emit. The 'Kind' infix is gone from generated constants (FontSizeDecimal, MX_INSTRUMENT_SOUND_SOUND_ID). - An open string union member must be last (it matches anything): both backends fail loud instead of emitting unreachable members. C runtime hardening: - mx_format_decimal: sized buffer + snprintf return check (no truncated digit strings for extreme magnitudes); mx_strdup aborts on OOM instead of memcpy through NULL; mx_try_parse_int rejects ERANGE (aligning strictness with Go's ParseInt); generated parse entry points are NULL-safe (NULL means ""); include guards use _H_INCLUDED so they stay out of the constant namespace the gate certifies; dead variant_const helper removed. - Go formatDecimal canonicalizes negative zero to "0" (parity with the C runtime and the corert normalizer). Robustness around the edges: - Backends reject schema types that project onto their reserved support stems (runtime/document/sources) instead of silently overwriting them. - The writer overwrites an unreadable file at a manifest path instead of crashing; the gofmt scratch dir handles subdirectories. - The emit CLI reports config errors, missing files, and a missing gofmt through the error path rather than a traceback. Smoke tests extended in both languages: strict-rejection paths, negative formatting, negative-zero canonicalization, and the union implied-min clamp. Both targets regenerate; the Go corert suite stays green end to end; documented as the second review round in plates.md section 11. --- docs/ai/design/plates.md | 21 +++ gen/__main__.py | 10 +- gen/emit/c/__init__.py | 7 + gen/emit/c/common.py | 15 +- gen/emit/c/runtime.py | 13 +- gen/emit/c/values.py | 137 ++++++---------- gen/emit/go/__init__.py | 12 ++ gen/emit/go/runtime.py | 6 +- gen/emit/go/values.py | 146 +++++++----------- gen/emit/writer.py | 9 +- gen/plates/build.py | 94 ++++++++++- gen/plates/check.py | 12 +- gen/plates/model.py | 30 +++- gen/test/c/mx/mx_above_below.c | 2 + gen/test/c/mx/mx_above_below.h | 6 +- gen/test/c/mx/mx_accidental_value.c | 2 + gen/test/c/mx/mx_accidental_value.h | 6 +- gen/test/c/mx/mx_accordion_middle.h | 10 +- gen/test/c/mx/mx_arrow_direction.c | 2 + gen/test/c/mx/mx_arrow_direction.h | 6 +- gen/test/c/mx/mx_arrow_style.c | 2 + gen/test/c/mx/mx_arrow_style.h | 6 +- gen/test/c/mx/mx_backward_forward.c | 2 + gen/test/c/mx/mx_backward_forward.h | 6 +- gen/test/c/mx/mx_bar_style.c | 2 + gen/test/c/mx/mx_bar_style.h | 6 +- gen/test/c/mx/mx_beam_level.h | 10 +- gen/test/c/mx/mx_beam_value.c | 2 + gen/test/c/mx/mx_beam_value.h | 6 +- gen/test/c/mx/mx_beater_value.c | 2 + gen/test/c/mx/mx_beater_value.h | 6 +- gen/test/c/mx/mx_breath_mark_value.c | 2 + gen/test/c/mx/mx_breath_mark_value.h | 6 +- gen/test/c/mx/mx_caesura_value.c | 2 + gen/test/c/mx/mx_caesura_value.h | 6 +- gen/test/c/mx/mx_cancel_location.c | 2 + gen/test/c/mx/mx_cancel_location.h | 6 +- gen/test/c/mx/mx_circular_arrow.c | 2 + gen/test/c/mx/mx_circular_arrow.h | 6 +- gen/test/c/mx/mx_clef_sign.c | 2 + gen/test/c/mx/mx_clef_sign.h | 6 +- gen/test/c/mx/mx_color.h | 6 +- gen/test/c/mx/mx_comma_separated_text.h | 6 +- gen/test/c/mx/mx_css_font_size.c | 2 + gen/test/c/mx/mx_css_font_size.h | 6 +- gen/test/c/mx/mx_degree_symbol_value.c | 2 + gen/test/c/mx/mx_degree_symbol_value.h | 6 +- gen/test/c/mx/mx_degree_type_value.c | 2 + gen/test/c/mx/mx_degree_type_value.h | 6 +- gen/test/c/mx/mx_distance_type.h | 6 +- gen/test/c/mx/mx_divisions.h | 10 +- gen/test/c/mx/mx_effect.c | 2 + gen/test/c/mx/mx_effect.h | 6 +- gen/test/c/mx/mx_enclosure_shape.c | 2 + gen/test/c/mx/mx_enclosure_shape.h | 6 +- gen/test/c/mx/mx_ending_number.h | 6 +- gen/test/c/mx/mx_fan.c | 2 + gen/test/c/mx/mx_fan.h | 6 +- gen/test/c/mx/mx_fermata_shape.c | 2 + gen/test/c/mx/mx_fermata_shape.h | 6 +- gen/test/c/mx/mx_fifths.h | 10 +- gen/test/c/mx/mx_font_size.c | 10 +- gen/test/c/mx/mx_font_size.h | 10 +- gen/test/c/mx/mx_font_style.c | 2 + gen/test/c/mx/mx_font_style.h | 6 +- gen/test/c/mx/mx_font_weight.c | 2 + gen/test/c/mx/mx_font_weight.h | 6 +- gen/test/c/mx/mx_glass_value.c | 2 + gen/test/c/mx/mx_glass_value.h | 6 +- gen/test/c/mx/mx_glyph_type.h | 6 +- gen/test/c/mx/mx_group_barline_value.c | 2 + gen/test/c/mx/mx_group_barline_value.h | 6 +- gen/test/c/mx/mx_group_symbol_value.c | 2 + gen/test/c/mx/mx_group_symbol_value.h | 6 +- gen/test/c/mx/mx_handbell_value.c | 2 + gen/test/c/mx/mx_handbell_value.h | 6 +- gen/test/c/mx/mx_harmon_closed_location.c | 2 + gen/test/c/mx/mx_harmon_closed_location.h | 6 +- gen/test/c/mx/mx_harmon_closed_value.c | 2 + gen/test/c/mx/mx_harmon_closed_value.h | 6 +- gen/test/c/mx/mx_harmony_type.c | 2 + gen/test/c/mx/mx_harmony_type.h | 6 +- gen/test/c/mx/mx_hole_closed_location.c | 2 + gen/test/c/mx/mx_hole_closed_location.h | 6 +- gen/test/c/mx/mx_hole_closed_value.c | 2 + gen/test/c/mx/mx_hole_closed_value.h | 6 +- gen/test/c/mx/mx_instrument_sound.c | 12 +- gen/test/c/mx/mx_instrument_sound.h | 10 +- gen/test/c/mx/mx_kind_value.c | 2 + gen/test/c/mx/mx_kind_value.h | 6 +- gen/test/c/mx/mx_left_center_right.c | 2 + gen/test/c/mx/mx_left_center_right.h | 6 +- gen/test/c/mx/mx_left_right.c | 2 + gen/test/c/mx/mx_left_right.h | 6 +- gen/test/c/mx/mx_line_end.c | 2 + gen/test/c/mx/mx_line_end.h | 6 +- gen/test/c/mx/mx_line_length.c | 2 + gen/test/c/mx/mx_line_length.h | 6 +- gen/test/c/mx/mx_line_shape.c | 2 + gen/test/c/mx/mx_line_shape.h | 6 +- gen/test/c/mx/mx_line_type.c | 2 + gen/test/c/mx/mx_line_type.h | 6 +- gen/test/c/mx/mx_line_width_type.h | 6 +- gen/test/c/mx/mx_margin_type.c | 2 + gen/test/c/mx/mx_margin_type.h | 6 +- gen/test/c/mx/mx_measure_numbering_value.c | 2 + gen/test/c/mx/mx_measure_numbering_value.h | 6 +- gen/test/c/mx/mx_measure_text.h | 6 +- gen/test/c/mx/mx_membrane.c | 2 + gen/test/c/mx/mx_membrane.h | 6 +- gen/test/c/mx/mx_metal.c | 2 + gen/test/c/mx/mx_metal.h | 6 +- gen/test/c/mx/mx_midi_128.h | 10 +- gen/test/c/mx/mx_midi_16.h | 10 +- gen/test/c/mx/mx_midi_16384.h | 10 +- gen/test/c/mx/mx_millimeters.h | 10 +- gen/test/c/mx/mx_mode.h | 6 +- gen/test/c/mx/mx_mute.c | 2 + gen/test/c/mx/mx_mute.h | 6 +- gen/test/c/mx/mx_non_negative_decimal.h | 10 +- gen/test/c/mx/mx_note_size_type.c | 2 + gen/test/c/mx/mx_note_size_type.h | 6 +- gen/test/c/mx/mx_note_type_value.c | 2 + gen/test/c/mx/mx_note_type_value.h | 6 +- gen/test/c/mx/mx_notehead_value.c | 2 + gen/test/c/mx/mx_notehead_value.h | 6 +- gen/test/c/mx/mx_number_level.h | 10 +- gen/test/c/mx/mx_number_of_lines.h | 10 +- gen/test/c/mx/mx_number_or_normal.c | 10 +- gen/test/c/mx/mx_number_or_normal.h | 10 +- gen/test/c/mx/mx_octave.h | 10 +- gen/test/c/mx/mx_on_off.c | 2 + gen/test/c/mx/mx_on_off.h | 6 +- gen/test/c/mx/mx_over_under.c | 2 + gen/test/c/mx/mx_over_under.h | 6 +- gen/test/c/mx/mx_pedal_type.c | 2 + gen/test/c/mx/mx_pedal_type.h | 6 +- gen/test/c/mx/mx_percent.h | 10 +- gen/test/c/mx/mx_pitched_value.c | 2 + gen/test/c/mx/mx_pitched_value.h | 6 +- gen/test/c/mx/mx_positive_divisions.h | 10 +- gen/test/c/mx/mx_positive_integer_or_empty.c | 14 +- gen/test/c/mx/mx_positive_integer_or_empty.h | 10 +- gen/test/c/mx/mx_principal_voice_symbol.c | 2 + gen/test/c/mx/mx_principal_voice_symbol.h | 6 +- gen/test/c/mx/mx_right_left_middle.c | 2 + gen/test/c/mx/mx_right_left_middle.h | 6 +- gen/test/c/mx/mx_rotation_degrees.h | 10 +- gen/test/c/mx/mx_runtime.c | 13 +- gen/test/c/mx/mx_runtime.h | 6 +- gen/test/c/mx/mx_semi_pitched.c | 2 + gen/test/c/mx/mx_semi_pitched.h | 6 +- gen/test/c/mx/mx_semitones.h | 10 +- gen/test/c/mx/mx_show_frets.c | 2 + gen/test/c/mx/mx_show_frets.h | 6 +- gen/test/c/mx/mx_show_tuplet.c | 2 + gen/test/c/mx/mx_show_tuplet.h | 6 +- .../c/mx/mx_smufl_accidental_glyph_name.h | 6 +- gen/test/c/mx/mx_smufl_coda_glyph_name.h | 6 +- gen/test/c/mx/mx_smufl_glyph_name.h | 6 +- gen/test/c/mx/mx_smufl_lyrics_glyph_name.h | 6 +- gen/test/c/mx/mx_smufl_pictogram_glyph_name.h | 6 +- gen/test/c/mx/mx_smufl_segno_glyph_name.h | 6 +- gen/test/c/mx/mx_sound_id.c | 2 + gen/test/c/mx/mx_sound_id.h | 6 +- gen/test/c/mx/mx_staff_divide_symbol.c | 2 + gen/test/c/mx/mx_staff_divide_symbol.h | 6 +- gen/test/c/mx/mx_staff_line.h | 10 +- gen/test/c/mx/mx_staff_number.h | 10 +- gen/test/c/mx/mx_staff_type.c | 2 + gen/test/c/mx/mx_staff_type.h | 6 +- gen/test/c/mx/mx_start_note.c | 2 + gen/test/c/mx/mx_start_note.h | 6 +- gen/test/c/mx/mx_start_stop.c | 2 + gen/test/c/mx/mx_start_stop.h | 6 +- gen/test/c/mx/mx_start_stop_continue.c | 2 + gen/test/c/mx/mx_start_stop_continue.h | 6 +- gen/test/c/mx/mx_start_stop_discontinue.c | 2 + gen/test/c/mx/mx_start_stop_discontinue.h | 6 +- gen/test/c/mx/mx_start_stop_single.c | 2 + gen/test/c/mx/mx_start_stop_single.h | 6 +- gen/test/c/mx/mx_stem_value.c | 2 + gen/test/c/mx/mx_stem_value.h | 6 +- gen/test/c/mx/mx_step.c | 2 + gen/test/c/mx/mx_step.h | 6 +- gen/test/c/mx/mx_stick_location.c | 2 + gen/test/c/mx/mx_stick_location.h | 6 +- gen/test/c/mx/mx_stick_material.c | 2 + gen/test/c/mx/mx_stick_material.h | 6 +- gen/test/c/mx/mx_stick_type.c | 2 + gen/test/c/mx/mx_stick_type.h | 6 +- gen/test/c/mx/mx_string_number.h | 10 +- gen/test/c/mx/mx_syllabic.c | 2 + gen/test/c/mx/mx_syllabic.h | 6 +- gen/test/c/mx/mx_symbol_size.c | 2 + gen/test/c/mx/mx_symbol_size.h | 6 +- gen/test/c/mx/mx_tap_hand.c | 2 + gen/test/c/mx/mx_tap_hand.h | 6 +- gen/test/c/mx/mx_tenths.h | 10 +- gen/test/c/mx/mx_text_direction.c | 2 + gen/test/c/mx/mx_text_direction.h | 6 +- gen/test/c/mx/mx_tied_type.c | 2 + gen/test/c/mx/mx_tied_type.h | 6 +- gen/test/c/mx/mx_time_only.h | 6 +- gen/test/c/mx/mx_time_relation.c | 2 + gen/test/c/mx/mx_time_relation.h | 6 +- gen/test/c/mx/mx_time_separator.c | 2 + gen/test/c/mx/mx_time_separator.h | 6 +- gen/test/c/mx/mx_time_symbol.c | 2 + gen/test/c/mx/mx_time_symbol.h | 6 +- gen/test/c/mx/mx_tip_direction.c | 2 + gen/test/c/mx/mx_tip_direction.h | 6 +- gen/test/c/mx/mx_top_bottom.c | 2 + gen/test/c/mx/mx_top_bottom.h | 6 +- gen/test/c/mx/mx_tremolo_marks.h | 10 +- gen/test/c/mx/mx_tremolo_type.c | 2 + gen/test/c/mx/mx_tremolo_type.h | 6 +- gen/test/c/mx/mx_trill_beats.h | 10 +- gen/test/c/mx/mx_trill_step.c | 2 + gen/test/c/mx/mx_trill_step.h | 6 +- gen/test/c/mx/mx_two_note_turn.c | 2 + gen/test/c/mx/mx_two_note_turn.h | 6 +- gen/test/c/mx/mx_up_down.c | 2 + gen/test/c/mx/mx_up_down.h | 6 +- gen/test/c/mx/mx_up_down_stop_continue.c | 2 + gen/test/c/mx/mx_up_down_stop_continue.h | 6 +- gen/test/c/mx/mx_upright_inverted.c | 2 + gen/test/c/mx/mx_upright_inverted.h | 6 +- gen/test/c/mx/mx_valign.c | 2 + gen/test/c/mx/mx_valign.h | 6 +- gen/test/c/mx/mx_valign_image.c | 2 + gen/test/c/mx/mx_valign_image.h | 6 +- gen/test/c/mx/mx_wedge_type.c | 2 + gen/test/c/mx/mx_wedge_type.h | 6 +- gen/test/c/mx/mx_winged.c | 2 + gen/test/c/mx/mx_winged.h | 6 +- gen/test/c/mx/mx_wood.c | 2 + gen/test/c/mx/mx_wood.h | 6 +- gen/test/c/mx/mx_yes_no.c | 2 + gen/test/c/mx/mx_yes_no.h | 6 +- gen/test/c/mx/mx_yes_no_number.c | 10 +- gen/test/c/mx/mx_yes_no_number.h | 10 +- gen/test/c/mx/mx_yyyy_mm_dd.h | 6 +- gen/test/c/src/values_smoke.c | 24 ++- gen/test/go/corert/values_smoke_test.go | 5 +- gen/test/go/mx/accordion_middle.go | 4 +- gen/test/go/mx/beam_level.go | 4 +- gen/test/go/mx/divisions.go | 4 +- gen/test/go/mx/fifths.go | 4 +- gen/test/go/mx/font_size.go | 12 +- gen/test/go/mx/midi_128.go | 4 +- gen/test/go/mx/midi_16.go | 4 +- gen/test/go/mx/midi_16384.go | 4 +- gen/test/go/mx/millimeters.go | 4 +- gen/test/go/mx/non_negative_decimal.go | 4 +- gen/test/go/mx/number_level.go | 4 +- gen/test/go/mx/number_of_lines.go | 4 +- gen/test/go/mx/number_or_normal.go | 12 +- gen/test/go/mx/octave.go | 4 +- gen/test/go/mx/percent.go | 4 +- gen/test/go/mx/positive_divisions.go | 4 +- gen/test/go/mx/positive_integer_or_empty.go | 19 ++- gen/test/go/mx/rotation_degrees.go | 4 +- gen/test/go/mx/runtime.go | 6 +- gen/test/go/mx/semitones.go | 4 +- gen/test/go/mx/staff_line.go | 4 +- gen/test/go/mx/staff_number.go | 4 +- gen/test/go/mx/string_number.go | 4 +- gen/test/go/mx/tenths.go | 4 +- gen/test/go/mx/tremolo_marks.go | 4 +- gen/test/go/mx/trill_beats.go | 4 +- gen/test/go/mx/yes_no_number.go | 12 +- gen/tests/test_plates.py | 72 +++++++++ 273 files changed, 1154 insertions(+), 769 deletions(-) diff --git a/docs/ai/design/plates.md b/docs/ai/design/plates.md index 06f5ce934..a039121a6 100644 --- a/docs/ai/design/plates.md +++ b/docs/ai/design/plates.md @@ -715,3 +715,24 @@ Revised after the first implementation review (one code review, one architecture - **`UnionPlateMember.name` added.** A union member referencing a primitive (`decimal`) has no plate to take a field name from; the member carries its own name bundle so templates invent nothing. + +Revised after the second review round (the emit stage and its first two backends): + +- **The clamp policy is data on the plates.** `NumberPlate.clamp` carries resolved + `ClampStep`s -- facet bounds merged with the primitive-implied lower bounds + (`positive_integer` >= 1, `non_negative_integer` >= 0), tightest bound winning, exclusive bounds + clamping to the nearest representable in-range value (next integer; bound +/- 1e-6 for decimals) + -- plus `family` (decimal vs integer). Both backends had hand-mirrored copies of this logic + ("one policy, two spellings" enforced only by a comment); the policy now lives once, is dumpable, + and is tested in `test_plates`. The same steps apply to primitive numeric union members + (`UnionPlateMember.clamp`), closing the hole where `positive-integer-or-empty` accepted 0. +- **Union discriminator constants are projected, not template-composed.** `UnionPlateMember.tag` + is a `Variant` scoped exactly like an enum variant (renameable via + `rename.enum-value..`), and union literal variants double as their own + tags; the flat-namespace collision gate covers them all. The `Kind` infix the templates used to + compose is gone (`FontSizeDecimal`, `MX_INSTRUMENT_SOUND_SOUND_ID`). +- **`TryParse`'s contract is pinned:** lexically strict (the input must be a well-formed value of + the type's family; an enum literal must match exactly), then numbers clamp. Generated doc + comments mention clamping only when clamp steps exist. +- **An open string union member must be last** (it matches anything); both backends fail loud if + a schema ever orders one earlier rather than silently emitting unreachable members. diff --git a/gen/__main__.py b/gen/__main__.py index 9a9266d89..3afd58c71 100644 --- a/gen/__main__.py +++ b/gen/__main__.py @@ -173,15 +173,19 @@ def _plates(args: list[str]) -> int: def _emit(config_path: str) -> int: + from gen.config import ConfigError from gen.emit import EmitError, emit from gen.plates import PlatesError, build_for_config try: plates, cfg = build_for_config(config_path) result = emit(plates, cfg) - except (PlatesError, EmitError) as e: - message = "\n".join(e.errors) if isinstance(e, PlatesError) else str(e) - print(f"error: {message}", file=sys.stderr) + except PlatesError as e: + for line in e.errors: + print(f"error: {line}", file=sys.stderr) + return 1 + except (EmitError, ConfigError, FileNotFoundError, RuntimeError, ValueError) as e: + print(f"error: {e}", file=sys.stderr) return 1 print(result.summary()) return 0 diff --git a/gen/emit/c/__init__.py b/gen/emit/c/__init__.py index cf3206d10..d1d7c19f5 100644 --- a/gen/emit/c/__init__.py +++ b/gen/emit/c/__init__.py @@ -24,6 +24,13 @@ def render(plates: Plates) -> dict[str, str]: rt = runtime_stem(plates) + reserved = (rt, "sources") + for plate in list(plates.value_types) + list(plates.complex_types): + if plate.file in reserved: + raise ValueError( + f"type '{plate.name.wire}' projects to the reserved file stem " + f"'{plate.file}'; rename it in config.toml" + ) includes_of = { spec.file: spec.includes for spec in (plates.files or []) } diff --git a/gen/emit/c/common.py b/gen/emit/c/common.py index 62755c628..915f84108 100644 --- a/gen/emit/c/common.py +++ b/gen/emit/c/common.py @@ -9,7 +9,7 @@ from __future__ import annotations from gen.emit.writer import banner -from gen.plates.model import Name, Plates, Variant +from gen.plates.model import Name, Plates _ESCAPES = { "\\": "\\\\", @@ -67,17 +67,10 @@ def fn_name(plates: Plates, type_name: Name, verb: str) -> str: return f"{fn_prefix(plates)}{type_name.snake}_{verb}" -def variant_const(plates: Plates, type_name: Name, variant: Variant) -> str: - """C enum constants share one global namespace, so they are composed as - PREFIX_TYPE_VARIANT in the variant convention (screaming). Composition - keeps digit-led variants legal (MX_NOTE_TYPE_VALUE_1024TH), so the - standalone sanitized `ident` is not used here.""" - casing = variant.name.cased[plates.target.variant_convention] - return f"{const_prefix(plates)}{type_name.screaming}_{casing}" - - def guard(stem: str) -> str: - return stem.upper() + "_H" + # The _INCLUDED suffix keeps guards out of the constant namespace the + # plates' collision gate certifies (MX_STEP_H could be a real variant). + return stem.upper() + "_H_INCLUDED" def header_file(plates: Plates, stem: str, body: list[str], includes: list[str]) -> str: diff --git a/gen/emit/c/runtime.py b/gen/emit/c/runtime.py index abe72864e..e317f5baf 100644 --- a/gen/emit/c/runtime.py +++ b/gen/emit/c/runtime.py @@ -32,6 +32,7 @@ char *mx_strdup(const char *s);""" _IMPL_BODY = """\ +#include #include #include #include @@ -66,8 +67,9 @@ if (!s || !*s) return false; char *end = NULL; + errno = 0; long v = strtol(s, &end, 10); - if (end == s) + if (errno == ERANGE || end == s) return false; while (*end == ' ' || *end == '\\t' || *end == '\\n' || *end == '\\r') end++; @@ -88,8 +90,11 @@ } char *mx_format_decimal(double v) { - char buf[64]; - snprintf(buf, sizeof(buf), "%.9f", v); + /* Worst-case %.9f for a double: 309 integer digits + sign + dot + 9. */ + char buf[336]; + int n = snprintf(buf, sizeof(buf), "%.9f", v); + if (n < 0 || n >= (int)sizeof(buf)) + return mx_strdup("0"); /* unreachable for finite doubles; never emit garbage */ /* Trim trailing zeros, then a trailing dot; canonicalize "-0" to "0". */ size_t len = strlen(buf); while (len > 0 && buf[len - 1] == '0') @@ -114,6 +119,8 @@ s = ""; size_t n = strlen(s) + 1; char *out = malloc(n); + if (!out) + abort(); /* the generator's runtime has no error channel for OOM */ memcpy(out, s, n); return out; }""" diff --git a/gen/emit/c/values.py b/gen/emit/c/values.py index bf57060a3..3de8e5905 100644 --- a/gen/emit/c/values.py +++ b/gen/emit/c/values.py @@ -4,17 +4,18 @@ type renders as a header/impl pair (the documented one-FileId-to-two-files mapping for C): - bool mx__try_parse(const char *s, MxT *out) strict membership + bool mx__try_parse(const char *s, MxT *out) lexically strict (a + well-formed value of the type's family; numbers then clamp) MxT mx__parse(const char *s) lenient (fixup policies) to_string the wire spelling: enums return static storage (const char *, do not free); numbers and unions return malloc'd strings the caller frees; string types ARE char* values (parse returns a malloc'd copy). -Leniency policies match the Go backend: unknown enum literal -> first -variant; unparseable number -> 0; every number clamps into its declared -range with primitive-implied lower bounds and exclusive bounds clamping to -the nearest representable value (decimal: +/- 1e-6). +The leniency policy itself (clamp bounds, implied minimums, exclusive-bound +epsilon) is DATA on the plates (NumberPlate.clamp / UnionPlateMember.clamp, +documented in data/README.md); this module only spells it in C. Generated +parse entry points are NULL-safe: NULL means "". """ from __future__ import annotations @@ -41,9 +42,6 @@ "date": ("char *", None, None), } -_IMPLIED_MIN = {"positive_integer": 1, "non_negative_integer": 0} - - def value_files(plates: Plates, plate, includes: list[str], rt: str) -> tuple[str, str]: """Render one value plate into its (header, impl) pair. `includes` is the plate's dependency stems (from Plates.files); `rt` the runtime stem.""" @@ -95,6 +93,8 @@ def _enum(plates: Plates, plate: EnumPlate, rt: str): body += ["};", ""] body += [ f"bool {try_parse}(const char *s, {ident} *out) {{", + " if (!s)", + ' s = "";', f" for (size_t i = 0; i < sizeof({values}) / sizeof({values}[0]); i++) {{", f" if (strcmp(s, {values}[i]) == 0) {{", f" *out = ({ident})i;", @@ -128,62 +128,6 @@ def _enum(plates: Plates, plate: EnumPlate, rt: str): # --------------------------------------------------------------------------- # -def _c_int(literal: str) -> str: - return str(int(float(literal))) - - -def _c_float(literal: str) -> str: - return repr(float(literal)) - - -def _clamp_steps(plate: NumberPlate) -> list[tuple[str, str, str]]: - """(comparison op, bound literal, replacement literal) per active bound, - mirroring the Go backend's policy exactly.""" - is_int = plate.target_type != "double" - b = plate.bounds - steps: list[tuple[str, str, str]] = [] - - lows: list[tuple[float, bool, str]] = [] - if b.min_inclusive is not None: - lows.append((float(b.min_inclusive), False, b.min_inclusive)) - if b.min_exclusive is not None: - lows.append((float(b.min_exclusive), True, b.min_exclusive)) - implied = _IMPLIED_MIN.get(plate.base) - if implied is not None: - lows.append((float(implied), False, str(implied))) - if lows: - value, exclusive, literal = max(lows, key=lambda t: (t[0], t[1])) - if is_int: - bound = _c_int(literal) - steps.append(("<=" if exclusive else "<", - bound, - str(int(float(literal)) + 1) if exclusive else bound)) - else: - bound = _c_float(literal) - steps.append(("<=" if exclusive else "<", - bound, - repr(float(literal) + 1e-6) if exclusive else bound)) - - highs: list[tuple[float, bool, str]] = [] - if b.max_inclusive is not None: - highs.append((float(b.max_inclusive), False, b.max_inclusive)) - if b.max_exclusive is not None: - highs.append((float(b.max_exclusive), True, b.max_exclusive)) - if highs: - value, exclusive, literal = min(highs, key=lambda t: (t[0], -t[1])) - if is_int: - bound = _c_int(literal) - steps.append((">=" if exclusive else ">", - bound, - str(int(float(literal)) - 1) if exclusive else bound)) - else: - bound = _c_float(literal) - steps.append((">=" if exclusive else ">", - bound, - repr(float(literal) - 1e-6) if exclusive else bound)) - return steps - - def _number(plates: Plates, plate: NumberPlate, rt: str): ident = plate.ident wrap = plates.target.doc_style.wrap @@ -193,17 +137,18 @@ def _number(plates: Plates, plate: NumberPlate, rt: str): parse = _fn(plates, plate, "parse") to_string = _fn(plates, plate, "to_string") clamp = _fn(plates, plate, "clamp") - steps = _clamp_steps(plate) + steps = plate.clamp runtime_try = prefix + try_helper runtime_fmt = prefix + fmt_helper - runtime_parse = prefix + ("parse_decimal" if c_type == "double" else "parse_int") + runtime_parse = prefix + ("parse_decimal" if plate.family == "decimal" else "parse_int") + clamps = ", then clamps into the declared range" if steps else "" decl = doc_comment(plate.doc, wrap) decl += [f"typedef {c_type} {ident};", ""] decl += [ - "/* Strict parse, then clamps into the declared range. */", + f"/* Lexically strict parse{clamps}. */", f"bool {try_parse}(const char *s, {ident} *out);", - "/* Lenient: unparseable input becomes 0, then clamps. */", + f"/* Lenient: unparseable input becomes 0{clamps}. */", f"{ident} {parse}(const char *s);", "/* Malloc'd; caller frees. */", f"char *{to_string}({ident} v);", @@ -212,8 +157,8 @@ def _number(plates: Plates, plate: NumberPlate, rt: str): body = [] if steps: body += [f"static {ident} {clamp}({c_type} v) {{"] - for op, bound, repl in steps: - body += [f" if (v {op} {bound})", f" v = {repl};"] + for step in steps: + body += [f" if (v {step.op} {step.bound})", f" v = {step.replacement};"] body += [f" return ({ident})v;", "}", ""] convert = f"{clamp}(v)" else: @@ -282,18 +227,16 @@ def _string(plates: Plates, plate: StringPlate, rt: str): def _union_cases(plates: Plates, plate: UnionPlate): - """Flatten union members into kind cases, mirroring the Go backend: - (kind const, field name or None, C type, try spelling or None for - any-string, lenient-parse spelling, to-string spelling, owns-memory).""" + """Flatten union members into kind cases, mirroring the Go backend. The + kind constants come final from the plates (member tags and literal + variants share the enum-variant scoping and collision gate).""" fconv = plates.target.field_convention - prefix = plates.target.prefix.upper() + "_" if plates.target.prefix else "" - kind_base = f"{prefix}{plate.name.screaming}_KIND_" fn_pre = fn_prefix(plates) cases = [] for m in plate.members: if m.ref is not None: field = m.name.cased[fconv] - kind = kind_base + m.name.screaming + kind = m.tag.ident if m.ref.category == "value": member_fns = lambda verb, n=m.name: fn_name(plates, n, verb) target_plate = plates.plate(m.ref.wire) @@ -328,11 +271,12 @@ def _union_cases(plates: Plates, plate: UnionPlate): "parse": f"{fn_pre}{('parse_decimal' if c_type == 'double' else 'parse_int')}(s)", "to_string": f"{fn_pre}{fmt_helper}(v.{field})", "owns": False, + "clamp": m.clamp, }) else: for variant in m.literals or []: cases.append({ - "kind": kind_base + variant.name.screaming, + "kind": variant.ident, "field": None, "c_type": None, "try": f"strcmp(s, {c_string(variant.wire)}) == 0", "parse": None, @@ -374,12 +318,20 @@ def _union(plates: Plates, plate: UnionPlate, includes: list[str], rt: str): body = [ f"bool {try_parse}(const char *s, {ident} *out) {{", + " if (!s)", + ' s = "";', f" {ident} v;", " memset(&v, 0, sizeof(v));", ] open_ended = False - for c in cases: + for i, c in enumerate(cases): if c["try"] is None: # open string member: always matches + # It must be last, or the members after it could never be reached. + if i != len(cases) - 1: + raise ValueError( + f"{plate.ident}: union member after an open string member " + f"is unreachable" + ) body += [ f" v.kind = {c['kind']};", f" v.{c['field']} = {c['parse']};", @@ -388,22 +340,18 @@ def _union(plates: Plates, plate: UnionPlate, includes: list[str], rt: str): ] open_ended = True break - if c["field"] is None: + body += [f" if ({c['try']}) {{"] + for step in c.get("clamp", []): body += [ - f" if ({c['try']}) {{", - f" v.kind = {c['kind']};", - " *out = v;", - " return true;", - " }", - ] - else: - body += [ - f" if ({c['try']}) {{", - f" v.kind = {c['kind']};", - " *out = v;", - " return true;", - " }", + f" if (v.{c['field']} {step.op} {step.bound})", + f" v.{c['field']} = {step.replacement};", ] + body += [ + f" v.kind = {c['kind']};", + " *out = v;", + " return true;", + " }", + ] if not open_ended: body += [" *out = v;", " return false;"] body += ["}", ""] @@ -419,6 +367,11 @@ def _union(plates: Plates, plate: UnionPlate, includes: list[str], rt: str): ] if first["field"] is not None: body += [f" v.{first['field']} = {first['parse']};"] + for step in first.get("clamp", []): + body += [ + f" if (v.{first['field']} {step.op} {step.bound})", + f" v.{first['field']} = {step.replacement};", + ] body += [" return v;", "}", ""] body += [f"char *{to_string}({ident} v) {{", " switch (v.kind) {"] diff --git a/gen/emit/go/__init__.py b/gen/emit/go/__init__.py index a78b9285e..84a7df5f4 100644 --- a/gen/emit/go/__init__.py +++ b/gen/emit/go/__init__.py @@ -23,11 +23,22 @@ from gen.plates.model import Plates +# File stems this backend reserves for its support files; a schema type +# landing on one would silently overwrite it in the manifest. +_RESERVED_STEMS = ("runtime", "document") + + def render(plates: Plates) -> dict[str, str]: files: dict[str, str] = { "runtime.go": runtime_file(plates), "document.go": document_file(plates), } + for plate in list(plates.value_types) + list(plates.complex_types): + if plate.file in _RESERVED_STEMS: + raise ValueError( + f"type '{plate.name.wire}' projects to the reserved file stem " + f"'{plate.file}'; rename it in config.toml" + ) for plate in plates.value_types: files[plate.file + ".go"] = value_file(plates, plate) for plate in plates.complex_types: @@ -46,6 +57,7 @@ def _gofmt(files: dict[str, str]) -> dict[str, str]: with tempfile.TemporaryDirectory() as scratch: root = Path(scratch) for rel, content in files.items(): + (root / rel).parent.mkdir(parents=True, exist_ok=True) (root / rel).write_text(content, encoding="utf-8") subprocess.run(["gofmt", "-w", scratch], check=True) return {rel: (root / rel).read_text(encoding="utf-8") for rel in files} diff --git a/gen/emit/go/runtime.py b/gen/emit/go/runtime.py index dbb52a7b3..a9cec425f 100644 --- a/gen/emit/go/runtime.py +++ b/gen/emit/go/runtime.py @@ -51,8 +51,12 @@ } // formatDecimal prints the shortest decimal that round-trips the value, -// without exponent notation (8.5 -> "8.5", 4 -> "4"). +// without exponent notation (8.5 -> "8.5", 4 -> "4"). Negative zero +// canonicalizes to "0" (matching the C runtime and the corert normalizer). func formatDecimal(v float64) string { + if v == 0 { + return "0" + } return strconv.FormatFloat(v, 'f', -1, 64) } diff --git a/gen/emit/go/values.py b/gen/emit/go/values.py index 639cebdea..d272e7298 100644 --- a/gen/emit/go/values.py +++ b/gen/emit/go/values.py @@ -4,16 +4,17 @@ string-wrapper, tagged-variant. Every type exposes the same surface so the complex-type templates can call them uniformly: - TryParse(s string) (T, bool) strict: does s belong to the type + TryParse(s string) (T, bool) lexically strict: the input must be a + well-formed value of the type's family + (numbers then clamp into range; an + enum literal must match exactly) Parse(s string) T lenient: malformed input degrades - deterministically (see policies below) + deterministically (T) String() string the wire spelling -Leniency policies (the corpus fixup conventions in data/README.md encode -these): an unknown enum literal falls back to the first variant; an -unparseable number becomes 0; every number is clamped into its declared -range, where an exclusive decimal bound clamps to bound +/- 1e-6 and an -exclusive integer bound to the next integer. +The leniency policy itself (clamp bounds, implied minimums, exclusive-bound +epsilon) is DATA on the plates (NumberPlate.clamp / UnionPlateMember.clamp, +documented in data/README.md); this module only spells it in Go. """ from __future__ import annotations @@ -55,10 +56,6 @@ "non_negative_integer": "formatInt({0})", } -# Primitive-implied lower bounds the schema leaves unstated. -_IMPLIED_MIN = {"positive_integer": 1, "non_negative_integer": 0} - - def value_file(plates: Plates, plate) -> str: if isinstance(plate, EnumPlate): body = _enum_body(plates, plate) @@ -131,64 +128,6 @@ def _enum_body(plates: Plates, plate: EnumPlate) -> list[str]: # --------------------------------------------------------------------------- # -def _go_int(literal: str) -> str: - return str(int(float(literal))) - - -def _go_float(literal: str) -> str: - return repr(float(literal)) - - -def _clamp_steps(plate: NumberPlate) -> list[tuple[str, str, str]]: - """(comparison op, bound literal, replacement literal) per active bound. - Exclusive bounds clamp to the nearest representable in-range value.""" - is_int = plate.target_type != "float64" - b = plate.bounds - steps: list[tuple[str, str, str]] = [] - - lows: list[tuple[float, bool, str]] = [] # (value, exclusive, literal) - if b.min_inclusive is not None: - lows.append((float(b.min_inclusive), False, b.min_inclusive)) - if b.min_exclusive is not None: - lows.append((float(b.min_exclusive), True, b.min_exclusive)) - implied = _IMPLIED_MIN.get(plate.base) - if implied is not None: - lows.append((float(implied), False, str(implied))) - if lows: - # The tightest lower bound wins (an exclusive bound at v is tighter - # than an inclusive one at the same v). - value, exclusive, literal = max(lows, key=lambda t: (t[0], t[1])) - if is_int: - bound = _go_int(literal) - steps.append(("<=" if exclusive else "<", - bound, - str(int(float(literal)) + 1) if exclusive else bound)) - else: - bound = _go_float(literal) - steps.append(("<=" if exclusive else "<", - bound, - repr(float(literal) + 1e-6) if exclusive else bound)) - - highs: list[tuple[float, bool, str]] = [] - if b.max_inclusive is not None: - highs.append((float(b.max_inclusive), False, b.max_inclusive)) - if b.max_exclusive is not None: - highs.append((float(b.max_exclusive), True, b.max_exclusive)) - if highs: - value, exclusive, literal = min(highs, key=lambda t: (t[0], -t[1])) - if is_int: - bound = _go_int(literal) - steps.append((">=" if exclusive else ">", - bound, - str(int(float(literal)) - 1) if exclusive else bound)) - else: - bound = _go_float(literal) - steps.append((">=" if exclusive else ">", - bound, - repr(float(literal) - 1e-6) if exclusive else bound)) - return steps - - def _number_body(plates: Plates, plate: NumberPlate) -> list[str]: ident = plate.ident wrap = plates.target.doc_style.wrap @@ -196,18 +135,20 @@ def _number_body(plates: Plates, plate: NumberPlate) -> list[str]: try_fn = _PRIM_TRY[plate.base] parse_fn = _PRIM_PARSE[plate.base] fmt = _PRIM_FORMAT[plate.base].format(f"{go_type}(v)") - steps = _clamp_steps(plate) + steps = plate.clamp lines = doc_comment(plate.doc, wrap) lines += [f"type {ident} {go_type}", ""] if steps: convert = f"clamp{ident}(v)" + clamps = ", then clamps into the declared range" else: convert = f"{ident}(v)" + clamps = "" lines += [ - f"// TryParse{ident} parses s strictly, then clamps into the declared range.", + f"// TryParse{ident} parses s as a lexically well-formed value{clamps}.", f"func TryParse{ident}(s string) ({ident}, bool) {{", f"\tv, ok := {try_fn}(s)", "\tif !ok {", @@ -216,7 +157,7 @@ def _number_body(plates: Plates, plate: NumberPlate) -> list[str]: f"\treturn {convert}, true", "}", "", - f"// Parse{ident} is lenient: unparseable input becomes 0, then clamps.", + f"// Parse{ident} is lenient: unparseable input becomes 0{clamps}.", f"func Parse{ident}(s string) {ident} {{", f"\tv := {parse_fn}(s)", f"\treturn {convert}", @@ -225,8 +166,8 @@ def _number_body(plates: Plates, plate: NumberPlate) -> list[str]: ] if steps: lines += [f"func clamp{ident}(v {go_type}) {ident} {{"] - for op, bound, repl in steps: - lines += [f"\tif v {op} {bound} {{", f"\t\tv = {repl}", "\t}"] + for step in steps: + lines += [f"\tif v {step.op} {step.bound} {{", f"\t\tv = {step.replacement}", "\t}"] lines += [f"\treturn {ident}(v)", "}", ""] lines += [ "// String returns the wire spelling.", @@ -280,20 +221,22 @@ def _string_body(plates: Plates, plate: StringPlate) -> list[str]: def _member_cases(plates: Plates, plate: UnionPlate): """Flatten union members into kind cases: one per ref member, one per - literal. Each case: (kind const, payload field or None, payload Go type, - strict-try spelling, lenient-parse spelling, to-string spelling).""" - ident = plate.ident + literal. The kind constants come final from the plates (member tags and + literal variants share the enum-variant scoping and collision gate). + Each case: (kind const, payload field or None, payload Go type, + strict-try spelling or None for an open string member, lenient-parse + spelling, to-string spelling, clamp steps).""" cases = [] for m in plate.members: if m.ref is not None: field = member_field(plates, m.name) - kind = f"{ident}Kind{field}" + kind = m.tag.ident if m.ref.category == "value": cases.append( (kind, field, m.ref.ident, f"TryParse{m.ref.ident}(s)", f"Parse{m.ref.ident}(s)", - f"v.{field}.String()") + f"v.{field}.String()", []) ) else: # primitive try_fn = _PRIM_TRY.get(m.ref.wire) @@ -301,18 +244,27 @@ def _member_cases(plates: Plates, plate: UnionPlate): (kind, field, m.ref.ident, f"{try_fn}(s)" if try_fn else None, # None: any string matches f"{_PRIM_PARSE[m.ref.wire]}(s)" if m.ref.wire in _PRIM_PARSE else "s", - _PRIM_FORMAT.get(m.ref.wire, "{0}").format(f"v.{field}")) + _PRIM_FORMAT.get(m.ref.wire, "{0}").format(f"v.{field}"), + m.clamp) ) else: for variant in m.literals or []: - kind = f"{ident}Kind{variant.name.cased[plates.target.variant_convention]}" cases.append( - (kind, None, None, f"s == {go_string(variant.wire)}", None, - go_string(variant.wire)) + (variant.ident, None, None, f"s == {go_string(variant.wire)}", None, + go_string(variant.wire), []) ) return cases +def _clamp_lines(steps, indent: str) -> list[str]: + lines = [] + for step in steps: + lines += [f"{indent}if v {step.op} {step.bound} {{", + f"{indent}\tv = {step.replacement}", + f"{indent}}}"] + return lines + + def _union_body(plates: Plates, plate: UnionPlate) -> list[str]: ident = plate.ident wrap = plates.target.doc_style.wrap @@ -334,27 +286,37 @@ def _union_body(plates: Plates, plate: UnionPlate) -> list[str]: f"// TryParse{ident} tries each union member in schema order.", f"func TryParse{ident}(s string) ({ident}, bool) {{", ] - for kind, field, _go_type, try_expr, _parse, _str in cases: + open_ended = False + for i, (kind, field, _go_type, try_expr, _parse, _str, clamp) in enumerate(cases): + if field is not None and try_expr is None: + # An open string member matches anything: it must be last, or the + # members after it could never be reached. + if i != len(cases) - 1: + raise ValueError( + f"{plate.ident}: union member after an open string member " + f"is unreachable" + ) + lines += [f"\treturn {ident}{{Kind: {kind}, {field}: s}}, true"] + open_ended = True + break if field is None: lines += [ f"\tif {try_expr} {{", f"\t\treturn {ident}{{Kind: {kind}}}, true", "\t}", ] - elif try_expr is None: - lines += [f"\treturn {ident}{{Kind: {kind}, {field}: s}}, true"] else: + lines += [f"\tif v, ok := {try_expr}; ok {{"] + lines += _clamp_lines(clamp, "\t\t") lines += [ - f"\tif v, ok := {try_expr}; ok {{", f"\t\treturn {ident}{{Kind: {kind}, {field}: v}}, true", "\t}", ] - open_ended = any(f is not None and t is None for _, f, _g, t, _p, _s in cases) if not open_ended: lines += [f"\treturn {ident}{{}}, false"] lines += ["}", ""] - first_kind, first_field, _gt, _try, first_parse, _s = cases[0] + first_kind, first_field, _gt, _try, first_parse, _s, first_clamp = cases[0] lines += [ f"// Parse{ident} is lenient: when no member matches, the first member", "// absorbs the input under its own leniency rules.", @@ -365,6 +327,10 @@ def _union_body(plates: Plates, plate: UnionPlate) -> list[str]: ] if first_field is None: lines += [f"\treturn {ident}{{Kind: {first_kind}}}"] + elif first_clamp: + lines += [f"\tv := {first_parse}"] + lines += _clamp_lines(first_clamp, "\t") + lines += [f"\treturn {ident}{{Kind: {first_kind}, {first_field}: v}}"] else: lines += [f"\treturn {ident}{{Kind: {first_kind}, {first_field}: {first_parse}}}"] lines += ["}", ""] @@ -374,7 +340,7 @@ def _union_body(plates: Plates, plate: UnionPlate) -> list[str]: f"func (v {ident}) String() string {{", "\tswitch v.Kind {", ] - for kind, _field, _gt, _try, _parse, to_str in cases[1:]: + for kind, _field, _gt, _try, _parse, to_str, _clamp in cases[1:]: lines += [f"\tcase {kind}:", f"\t\treturn {to_str}"] lines += ["\t}", f"\treturn {cases[0][5]}", "}"] return lines diff --git a/gen/emit/writer.py b/gen/emit/writer.py index 21246abce..9698f7ee6 100644 --- a/gen/emit/writer.py +++ b/gen/emit/writer.py @@ -73,9 +73,12 @@ def write_files(out_dir: Path, files: dict[str, str]) -> EmitResult: for rel in sorted(files): target = out_dir / rel content = files[rel] - if target.exists() and target.read_text(encoding="utf-8") == content: - result.unchanged.append(rel) - continue + try: + if target.exists() and target.read_text(encoding="utf-8") == content: + result.unchanged.append(rel) + continue + except (UnicodeDecodeError, OSError): + pass # whatever is there, it is not our content: overwrite it target.parent.mkdir(parents=True, exist_ok=True) target.write_text(content, encoding="utf-8") result.written.append(rel) diff --git a/gen/plates/build.py b/gen/plates/build.py index ce92ef775..a065a20e7 100644 --- a/gen/plates/build.py +++ b/gen/plates/build.py @@ -25,6 +25,7 @@ from gen.plates import languages from gen.plates.check import run_checks from gen.plates.model import ( + ClampStep, ComplexPlate, EnumPlate, FileSpec, @@ -42,6 +43,66 @@ ) +# Primitive-implied lower bounds the schema leaves unstated; part of the +# uniform clamp policy (see model.ClampStep and data/README.md). +_IMPLIED_MIN = {"positive_integer": 1, "non_negative_integer": 0} + +# The epsilon an exclusive DECIMAL bound clamps past (an exclusive integer +# bound clamps to the next integer). Matches the corpus duration fixup. +_EPSILON = 1e-6 + + +def _number_family(base: str) -> str: + return "decimal" if base == "decimal" else "integer" + + +def _spell(value: float, family: str) -> str: + """A numeric literal valid in every current target language.""" + if family == "integer": + return str(int(value)) + return repr(float(value)) + + +def clamp_steps(base: str, bounds: NumberBounds) -> list[ClampStep]: + """Resolve facets plus primitive-implied bounds into the ordered clamp + rules a wrapper applies after parsing. The tightest lower bound wins (an + exclusive bound at v is tighter than an inclusive one at the same v).""" + family = _number_family(base) + steps: list[ClampStep] = [] + + lows: list[tuple[float, bool]] = [] # (value, exclusive) + if bounds.min_inclusive is not None: + lows.append((float(bounds.min_inclusive), False)) + if bounds.min_exclusive is not None: + lows.append((float(bounds.min_exclusive), True)) + if base in _IMPLIED_MIN: + lows.append((float(_IMPLIED_MIN[base]), False)) + if lows: + value, exclusive = max(lows) + if exclusive: + past = value + (1 if family == "integer" else _EPSILON) + steps.append(ClampStep("<=", _spell(value, family), _spell(past, family))) + else: + bound = _spell(value, family) + steps.append(ClampStep("<", bound, bound)) + + highs: list[tuple[float, bool]] = [] + if bounds.max_inclusive is not None: + highs.append((float(bounds.max_inclusive), False)) + if bounds.max_exclusive is not None: + highs.append((float(bounds.max_exclusive), True)) + if highs: + value, exclusive = min((v, not e) for v, e in highs) + exclusive = not exclusive + if exclusive: + past = value - (1 if family == "integer" else _EPSILON) + steps.append(ClampStep(">=", _spell(value, family), _spell(past, family))) + else: + bound = _spell(value, family) + steps.append(ClampStep(">", bound, bound)) + return steps + + class PlatesError(Exception): """One or more projection failures, collected so a run reports every problem at once rather than the first.""" @@ -218,13 +279,16 @@ def _value_plate(self, v: ir.ValueType): doc=v.doc, ) if isinstance(v, ir.NumberType): + bounds = NumberBounds( + v.min_inclusive, v.max_inclusive, v.min_exclusive, v.max_exclusive + ) return NumberPlate( name=name, ident=ident, base=v.base, - bounds=NumberBounds( - v.min_inclusive, v.max_inclusive, v.min_exclusive, v.max_exclusive - ), + bounds=bounds, + family=_number_family(v.base), + clamp=clamp_steps(v.base, bounds), target_type=self.type_map.get(v.base, v.base), doc=v.doc, ) @@ -244,13 +308,25 @@ def _value_plate(self, v: ir.ValueType): for m in v.members: if m.ref is not None: member_name = self.type_names.get(m.ref.name) or self.factory.make(m.ref.name) + clamp = [] + if m.ref.category == "primitive" and m.ref.name in _IMPLIED_MIN: + # The primitive's implied bounds apply inside a union just + # as they would on a named number type. + clamp = clamp_steps(m.ref.name, NumberBounds()) members.append( - UnionPlateMember(ref=self._plate_ref(m.ref), name=member_name) + UnionPlateMember( + ref=self._plate_ref(m.ref), + name=member_name, + # The member's discriminator constant: scoped, renamed, + # and collision-gated exactly like an enum variant. + tag=self._variant(v.name, m.ref.name), + clamp=clamp, + ) ) else: # An inline literal set projects like a tiny anonymous enum; # its variants are addressable for renames under the union's - # own type name. + # own type name and double as the discriminator constants. members.append( UnionPlateMember( literals=[self._variant(v.name, lit) for lit in m.literals or []] @@ -464,10 +540,12 @@ def _validate_config_against_ir(self) -> list[str]: f"rename.enum-value.{enum}.{value!r}: enum has no such value" ) elif isinstance(vt, ir.UnionType): - literals = {lit for m in vt.members for lit in (m.literals or [])} - if value not in literals: + addressable = {lit for m in vt.members for lit in (m.literals or [])} + addressable |= {m.ref.name for m in vt.members if m.ref is not None} + if value not in addressable: errors.append( - f"rename.enum-value.{enum}.{value!r}: union has no such literal" + f"rename.enum-value.{enum}.{value!r}: union has no such " + f"literal or member" ) else: errors.append(f"rename.enum-value.{enum}: no such enum type") diff --git a/gen/plates/check.py b/gen/plates/check.py index 94647a563..3c6620442 100644 --- a/gen/plates/check.py +++ b/gen/plates/check.py @@ -27,16 +27,24 @@ def run_checks(plates: Plates) -> list[str]: def _variant_pairs(plate) -> list[tuple[str, str]]: - """(ident, claimant description) for every constant a value plate emits.""" + """(ident, claimant description) for every constant a value plate emits: + enum variants, union literal variants, and union member tags (the + discriminator constants) alike.""" if isinstance(plate, EnumPlate): return [(v.ident, f"{plate.name.wire}.{v.wire!r}") for v in plate.variants] if isinstance(plate, UnionPlate): - return [ + pairs = [ (v.ident, f"{plate.name.wire}.{v.wire!r}") for m in plate.members if m.literals for v in m.literals ] + pairs += [ + (m.tag.ident, f"{plate.name.wire} member {m.ref.wire!r}") + for m in plate.members + if m.tag is not None + ] + return pairs return [] diff --git a/gen/plates/model.py b/gen/plates/model.py index e55b008f8..620857f35 100644 --- a/gen/plates/model.py +++ b/gen/plates/model.py @@ -106,6 +106,22 @@ class NumberBounds: max_exclusive: str | None = None +@dataclass +class ClampStep: + """One resolved clamping rule: `if v then v = `. + This is the corpus leniency POLICY (the thing the .fixup.xml sidecars + encode), decided once in the projection: facet bounds and the + primitive-implied lower bounds are merged, the tightest wins, and an + exclusive bound's replacement is the nearest representable in-range value + (next integer, or bound +/- 1e-6 for decimals). The literals are spelled + neutrally (valid in every current target language); templates print them + verbatim.""" + + op: str # "<" | "<=" | ">" | ">=" + bound: str + replacement: str + + @dataclass class EnumPlate: name: Name @@ -123,7 +139,9 @@ class NumberPlate: name: Name ident: str base: str # IR primitive: decimal/integer/positive_integer/non_negative_integer - bounds: NumberBounds = field(default_factory=NumberBounds) + bounds: NumberBounds = field(default_factory=NumberBounds) # neutral core: raw facets + family: str = "" # "decimal" | "integer": which parse/format family applies + clamp: list[ClampStep] = field(default_factory=list) # resolved policy (see ClampStep) target_type: str = "" # type_map[base]: what the wrapper wraps doc: str | None = None file: str | None = None @@ -153,12 +171,18 @@ class UnionPlateMember: type, or an inline literal set projected like a tiny anonymous enum (each literal carries its wire form and a variant identifier). A ref member also carries `name`, the referenced type's name bundle, so a template can spell - the member's tag/field without inventing a name (a primitive member like - `positive_integer` has no plate to look it up on).""" + the member's field without inventing a name (a primitive member like + `positive_integer` has no plate to look it up on), and `tag`, the final + discriminator-constant identifier for this member, scoped exactly like an + enum variant and covered by the same collision gate. A primitive numeric + member carries its `clamp` policy (the primitive-implied bounds), so the + union enforces the same leniency as a named number type would.""" ref: PlateRef | None = None name: Name | None = None + tag: Variant | None = None literals: list[Variant] | None = None + clamp: list[ClampStep] = field(default_factory=list) @dataclass diff --git a/gen/test/c/mx/mx_above_below.c b/gen/test/c/mx/mx_above_below.c index 4b6b14e8c..45876246d 100644 --- a/gen/test/c/mx/mx_above_below.c +++ b/gen/test/c/mx/mx_above_below.c @@ -10,6 +10,8 @@ static const char *const mx_above_below_values[] = { }; bool mx_above_below_try_parse(const char *s, MxAboveBelow *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_above_below_values) / sizeof(mx_above_below_values[0]); i++) { if (strcmp(s, mx_above_below_values[i]) == 0) { *out = (MxAboveBelow)i; diff --git a/gen/test/c/mx/mx_above_below.h b/gen/test/c/mx/mx_above_below.h index 105f6d120..229aef957 100644 --- a/gen/test/c/mx/mx_above_below.h +++ b/gen/test/c/mx/mx_above_below.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_ABOVE_BELOW_H -#define MX_ABOVE_BELOW_H +#ifndef MX_ABOVE_BELOW_H_INCLUDED +#define MX_ABOVE_BELOW_H_INCLUDED #include @@ -20,4 +20,4 @@ MxAboveBelow mx_above_below_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_above_below_to_string(MxAboveBelow v); -#endif /* MX_ABOVE_BELOW_H */ +#endif /* MX_ABOVE_BELOW_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_accidental_value.c b/gen/test/c/mx/mx_accidental_value.c index 514132805..696ebcdff 100644 --- a/gen/test/c/mx/mx_accidental_value.c +++ b/gen/test/c/mx/mx_accidental_value.c @@ -49,6 +49,8 @@ static const char *const mx_accidental_value_values[] = { }; bool mx_accidental_value_try_parse(const char *s, MxAccidentalValue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_accidental_value_values) / sizeof(mx_accidental_value_values[0]); i++) { if (strcmp(s, mx_accidental_value_values[i]) == 0) { *out = (MxAccidentalValue)i; diff --git a/gen/test/c/mx/mx_accidental_value.h b/gen/test/c/mx/mx_accidental_value.h index 403a99bd4..19e336358 100644 --- a/gen/test/c/mx/mx_accidental_value.h +++ b/gen/test/c/mx/mx_accidental_value.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_ACCIDENTAL_VALUE_H -#define MX_ACCIDENTAL_VALUE_H +#ifndef MX_ACCIDENTAL_VALUE_H_INCLUDED +#define MX_ACCIDENTAL_VALUE_H_INCLUDED #include @@ -68,4 +68,4 @@ MxAccidentalValue mx_accidental_value_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_accidental_value_to_string(MxAccidentalValue v); -#endif /* MX_ACCIDENTAL_VALUE_H */ +#endif /* MX_ACCIDENTAL_VALUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_accordion_middle.h b/gen/test/c/mx/mx_accordion_middle.h index fc0daf753..0303bc06d 100644 --- a/gen/test/c/mx/mx_accordion_middle.h +++ b/gen/test/c/mx/mx_accordion_middle.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_ACCORDION_MIDDLE_H -#define MX_ACCORDION_MIDDLE_H +#ifndef MX_ACCORDION_MIDDLE_H_INCLUDED +#define MX_ACCORDION_MIDDLE_H_INCLUDED #include @@ -12,11 +12,11 @@ */ typedef long MxAccordionMiddle; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse, then clamps into the declared range. */ bool mx_accordion_middle_try_parse(const char *s, MxAccordionMiddle *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0, then clamps into the declared range. */ MxAccordionMiddle mx_accordion_middle_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_accordion_middle_to_string(MxAccordionMiddle v); -#endif /* MX_ACCORDION_MIDDLE_H */ +#endif /* MX_ACCORDION_MIDDLE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_arrow_direction.c b/gen/test/c/mx/mx_arrow_direction.c index ffe718827..c09726bd6 100644 --- a/gen/test/c/mx/mx_arrow_direction.c +++ b/gen/test/c/mx/mx_arrow_direction.c @@ -21,6 +21,8 @@ static const char *const mx_arrow_direction_values[] = { }; bool mx_arrow_direction_try_parse(const char *s, MxArrowDirection *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_arrow_direction_values) / sizeof(mx_arrow_direction_values[0]); i++) { if (strcmp(s, mx_arrow_direction_values[i]) == 0) { *out = (MxArrowDirection)i; diff --git a/gen/test/c/mx/mx_arrow_direction.h b/gen/test/c/mx/mx_arrow_direction.h index c98215708..ce0384774 100644 --- a/gen/test/c/mx/mx_arrow_direction.h +++ b/gen/test/c/mx/mx_arrow_direction.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_ARROW_DIRECTION_H -#define MX_ARROW_DIRECTION_H +#ifndef MX_ARROW_DIRECTION_H_INCLUDED +#define MX_ARROW_DIRECTION_H_INCLUDED #include @@ -31,4 +31,4 @@ MxArrowDirection mx_arrow_direction_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_arrow_direction_to_string(MxArrowDirection v); -#endif /* MX_ARROW_DIRECTION_H */ +#endif /* MX_ARROW_DIRECTION_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_arrow_style.c b/gen/test/c/mx/mx_arrow_style.c index e80527dc4..7a9a7163e 100644 --- a/gen/test/c/mx/mx_arrow_style.c +++ b/gen/test/c/mx/mx_arrow_style.c @@ -15,6 +15,8 @@ static const char *const mx_arrow_style_values[] = { }; bool mx_arrow_style_try_parse(const char *s, MxArrowStyle *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_arrow_style_values) / sizeof(mx_arrow_style_values[0]); i++) { if (strcmp(s, mx_arrow_style_values[i]) == 0) { *out = (MxArrowStyle)i; diff --git a/gen/test/c/mx/mx_arrow_style.h b/gen/test/c/mx/mx_arrow_style.h index 2be3df8fc..fc2f281ed 100644 --- a/gen/test/c/mx/mx_arrow_style.h +++ b/gen/test/c/mx/mx_arrow_style.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_ARROW_STYLE_H -#define MX_ARROW_STYLE_H +#ifndef MX_ARROW_STYLE_H_INCLUDED +#define MX_ARROW_STYLE_H_INCLUDED #include @@ -27,4 +27,4 @@ MxArrowStyle mx_arrow_style_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_arrow_style_to_string(MxArrowStyle v); -#endif /* MX_ARROW_STYLE_H */ +#endif /* MX_ARROW_STYLE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_backward_forward.c b/gen/test/c/mx/mx_backward_forward.c index 706f370be..84fc98a83 100644 --- a/gen/test/c/mx/mx_backward_forward.c +++ b/gen/test/c/mx/mx_backward_forward.c @@ -10,6 +10,8 @@ static const char *const mx_backward_forward_values[] = { }; bool mx_backward_forward_try_parse(const char *s, MxBackwardForward *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_backward_forward_values) / sizeof(mx_backward_forward_values[0]); i++) { if (strcmp(s, mx_backward_forward_values[i]) == 0) { *out = (MxBackwardForward)i; diff --git a/gen/test/c/mx/mx_backward_forward.h b/gen/test/c/mx/mx_backward_forward.h index 857e53e45..5d0ce88ce 100644 --- a/gen/test/c/mx/mx_backward_forward.h +++ b/gen/test/c/mx/mx_backward_forward.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_BACKWARD_FORWARD_H -#define MX_BACKWARD_FORWARD_H +#ifndef MX_BACKWARD_FORWARD_H_INCLUDED +#define MX_BACKWARD_FORWARD_H_INCLUDED #include @@ -20,4 +20,4 @@ MxBackwardForward mx_backward_forward_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_backward_forward_to_string(MxBackwardForward v); -#endif /* MX_BACKWARD_FORWARD_H */ +#endif /* MX_BACKWARD_FORWARD_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_bar_style.c b/gen/test/c/mx/mx_bar_style.c index 105c3e795..58c103c8f 100644 --- a/gen/test/c/mx/mx_bar_style.c +++ b/gen/test/c/mx/mx_bar_style.c @@ -19,6 +19,8 @@ static const char *const mx_bar_style_values[] = { }; bool mx_bar_style_try_parse(const char *s, MxBarStyle *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_bar_style_values) / sizeof(mx_bar_style_values[0]); i++) { if (strcmp(s, mx_bar_style_values[i]) == 0) { *out = (MxBarStyle)i; diff --git a/gen/test/c/mx/mx_bar_style.h b/gen/test/c/mx/mx_bar_style.h index e3aa582bc..568a3fb27 100644 --- a/gen/test/c/mx/mx_bar_style.h +++ b/gen/test/c/mx/mx_bar_style.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_BAR_STYLE_H -#define MX_BAR_STYLE_H +#ifndef MX_BAR_STYLE_H_INCLUDED +#define MX_BAR_STYLE_H_INCLUDED #include @@ -30,4 +30,4 @@ MxBarStyle mx_bar_style_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_bar_style_to_string(MxBarStyle v); -#endif /* MX_BAR_STYLE_H */ +#endif /* MX_BAR_STYLE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_beam_level.h b/gen/test/c/mx/mx_beam_level.h index 0c5efeab1..33ee47e7f 100644 --- a/gen/test/c/mx/mx_beam_level.h +++ b/gen/test/c/mx/mx_beam_level.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_BEAM_LEVEL_H -#define MX_BEAM_LEVEL_H +#ifndef MX_BEAM_LEVEL_H_INCLUDED +#define MX_BEAM_LEVEL_H_INCLUDED #include @@ -12,11 +12,11 @@ */ typedef long MxBeamLevel; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse, then clamps into the declared range. */ bool mx_beam_level_try_parse(const char *s, MxBeamLevel *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0, then clamps into the declared range. */ MxBeamLevel mx_beam_level_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_beam_level_to_string(MxBeamLevel v); -#endif /* MX_BEAM_LEVEL_H */ +#endif /* MX_BEAM_LEVEL_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_beam_value.c b/gen/test/c/mx/mx_beam_value.c index f7d8f5c1b..dd1876e15 100644 --- a/gen/test/c/mx/mx_beam_value.c +++ b/gen/test/c/mx/mx_beam_value.c @@ -13,6 +13,8 @@ static const char *const mx_beam_value_values[] = { }; bool mx_beam_value_try_parse(const char *s, MxBeamValue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_beam_value_values) / sizeof(mx_beam_value_values[0]); i++) { if (strcmp(s, mx_beam_value_values[i]) == 0) { *out = (MxBeamValue)i; diff --git a/gen/test/c/mx/mx_beam_value.h b/gen/test/c/mx/mx_beam_value.h index a77c14f42..4cfe28188 100644 --- a/gen/test/c/mx/mx_beam_value.h +++ b/gen/test/c/mx/mx_beam_value.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_BEAM_VALUE_H -#define MX_BEAM_VALUE_H +#ifndef MX_BEAM_VALUE_H_INCLUDED +#define MX_BEAM_VALUE_H_INCLUDED #include @@ -23,4 +23,4 @@ MxBeamValue mx_beam_value_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_beam_value_to_string(MxBeamValue v); -#endif /* MX_BEAM_VALUE_H */ +#endif /* MX_BEAM_VALUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_beater_value.c b/gen/test/c/mx/mx_beater_value.c index 6d162f922..3f04911e3 100644 --- a/gen/test/c/mx/mx_beater_value.c +++ b/gen/test/c/mx/mx_beater_value.c @@ -28,6 +28,8 @@ static const char *const mx_beater_value_values[] = { }; bool mx_beater_value_try_parse(const char *s, MxBeaterValue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_beater_value_values) / sizeof(mx_beater_value_values[0]); i++) { if (strcmp(s, mx_beater_value_values[i]) == 0) { *out = (MxBeaterValue)i; diff --git a/gen/test/c/mx/mx_beater_value.h b/gen/test/c/mx/mx_beater_value.h index 1400d5c29..22c9d5536 100644 --- a/gen/test/c/mx/mx_beater_value.h +++ b/gen/test/c/mx/mx_beater_value.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_BEATER_VALUE_H -#define MX_BEATER_VALUE_H +#ifndef MX_BEATER_VALUE_H_INCLUDED +#define MX_BEATER_VALUE_H_INCLUDED #include @@ -39,4 +39,4 @@ MxBeaterValue mx_beater_value_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_beater_value_to_string(MxBeaterValue v); -#endif /* MX_BEATER_VALUE_H */ +#endif /* MX_BEATER_VALUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_breath_mark_value.c b/gen/test/c/mx/mx_breath_mark_value.c index 0c207cd9d..9652d35ac 100644 --- a/gen/test/c/mx/mx_breath_mark_value.c +++ b/gen/test/c/mx/mx_breath_mark_value.c @@ -13,6 +13,8 @@ static const char *const mx_breath_mark_value_values[] = { }; bool mx_breath_mark_value_try_parse(const char *s, MxBreathMarkValue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_breath_mark_value_values) / sizeof(mx_breath_mark_value_values[0]); i++) { if (strcmp(s, mx_breath_mark_value_values[i]) == 0) { *out = (MxBreathMarkValue)i; diff --git a/gen/test/c/mx/mx_breath_mark_value.h b/gen/test/c/mx/mx_breath_mark_value.h index b56c97048..d4188d539 100644 --- a/gen/test/c/mx/mx_breath_mark_value.h +++ b/gen/test/c/mx/mx_breath_mark_value.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_BREATH_MARK_VALUE_H -#define MX_BREATH_MARK_VALUE_H +#ifndef MX_BREATH_MARK_VALUE_H_INCLUDED +#define MX_BREATH_MARK_VALUE_H_INCLUDED #include @@ -22,4 +22,4 @@ MxBreathMarkValue mx_breath_mark_value_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_breath_mark_value_to_string(MxBreathMarkValue v); -#endif /* MX_BREATH_MARK_VALUE_H */ +#endif /* MX_BREATH_MARK_VALUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_caesura_value.c b/gen/test/c/mx/mx_caesura_value.c index 2492910d8..5f00b40ad 100644 --- a/gen/test/c/mx/mx_caesura_value.c +++ b/gen/test/c/mx/mx_caesura_value.c @@ -14,6 +14,8 @@ static const char *const mx_caesura_value_values[] = { }; bool mx_caesura_value_try_parse(const char *s, MxCaesuraValue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_caesura_value_values) / sizeof(mx_caesura_value_values[0]); i++) { if (strcmp(s, mx_caesura_value_values[i]) == 0) { *out = (MxCaesuraValue)i; diff --git a/gen/test/c/mx/mx_caesura_value.h b/gen/test/c/mx/mx_caesura_value.h index 2f1e5e75f..e646ea018 100644 --- a/gen/test/c/mx/mx_caesura_value.h +++ b/gen/test/c/mx/mx_caesura_value.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_CAESURA_VALUE_H -#define MX_CAESURA_VALUE_H +#ifndef MX_CAESURA_VALUE_H_INCLUDED +#define MX_CAESURA_VALUE_H_INCLUDED #include @@ -23,4 +23,4 @@ MxCaesuraValue mx_caesura_value_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_caesura_value_to_string(MxCaesuraValue v); -#endif /* MX_CAESURA_VALUE_H */ +#endif /* MX_CAESURA_VALUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_cancel_location.c b/gen/test/c/mx/mx_cancel_location.c index c561478b9..512081c88 100644 --- a/gen/test/c/mx/mx_cancel_location.c +++ b/gen/test/c/mx/mx_cancel_location.c @@ -11,6 +11,8 @@ static const char *const mx_cancel_location_values[] = { }; bool mx_cancel_location_try_parse(const char *s, MxCancelLocation *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_cancel_location_values) / sizeof(mx_cancel_location_values[0]); i++) { if (strcmp(s, mx_cancel_location_values[i]) == 0) { *out = (MxCancelLocation)i; diff --git a/gen/test/c/mx/mx_cancel_location.h b/gen/test/c/mx/mx_cancel_location.h index a8635f2bd..2d59466d7 100644 --- a/gen/test/c/mx/mx_cancel_location.h +++ b/gen/test/c/mx/mx_cancel_location.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_CANCEL_LOCATION_H -#define MX_CANCEL_LOCATION_H +#ifndef MX_CANCEL_LOCATION_H_INCLUDED +#define MX_CANCEL_LOCATION_H_INCLUDED #include @@ -23,4 +23,4 @@ MxCancelLocation mx_cancel_location_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_cancel_location_to_string(MxCancelLocation v); -#endif /* MX_CANCEL_LOCATION_H */ +#endif /* MX_CANCEL_LOCATION_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_circular_arrow.c b/gen/test/c/mx/mx_circular_arrow.c index 2cd935660..25dccc9a5 100644 --- a/gen/test/c/mx/mx_circular_arrow.c +++ b/gen/test/c/mx/mx_circular_arrow.c @@ -10,6 +10,8 @@ static const char *const mx_circular_arrow_values[] = { }; bool mx_circular_arrow_try_parse(const char *s, MxCircularArrow *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_circular_arrow_values) / sizeof(mx_circular_arrow_values[0]); i++) { if (strcmp(s, mx_circular_arrow_values[i]) == 0) { *out = (MxCircularArrow)i; diff --git a/gen/test/c/mx/mx_circular_arrow.h b/gen/test/c/mx/mx_circular_arrow.h index eb52319f1..bfcbf8e58 100644 --- a/gen/test/c/mx/mx_circular_arrow.h +++ b/gen/test/c/mx/mx_circular_arrow.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_CIRCULAR_ARROW_H -#define MX_CIRCULAR_ARROW_H +#ifndef MX_CIRCULAR_ARROW_H_INCLUDED +#define MX_CIRCULAR_ARROW_H_INCLUDED #include @@ -20,4 +20,4 @@ MxCircularArrow mx_circular_arrow_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_circular_arrow_to_string(MxCircularArrow v); -#endif /* MX_CIRCULAR_ARROW_H */ +#endif /* MX_CIRCULAR_ARROW_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_clef_sign.c b/gen/test/c/mx/mx_clef_sign.c index 46ce00809..e442cb0db 100644 --- a/gen/test/c/mx/mx_clef_sign.c +++ b/gen/test/c/mx/mx_clef_sign.c @@ -15,6 +15,8 @@ static const char *const mx_clef_sign_values[] = { }; bool mx_clef_sign_try_parse(const char *s, MxClefSign *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_clef_sign_values) / sizeof(mx_clef_sign_values[0]); i++) { if (strcmp(s, mx_clef_sign_values[i]) == 0) { *out = (MxClefSign)i; diff --git a/gen/test/c/mx/mx_clef_sign.h b/gen/test/c/mx/mx_clef_sign.h index 9cb7dae10..55d163205 100644 --- a/gen/test/c/mx/mx_clef_sign.h +++ b/gen/test/c/mx/mx_clef_sign.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_CLEF_SIGN_H -#define MX_CLEF_SIGN_H +#ifndef MX_CLEF_SIGN_H_INCLUDED +#define MX_CLEF_SIGN_H_INCLUDED #include @@ -27,4 +27,4 @@ MxClefSign mx_clef_sign_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_clef_sign_to_string(MxClefSign v); -#endif /* MX_CLEF_SIGN_H */ +#endif /* MX_CLEF_SIGN_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_color.h b/gen/test/c/mx/mx_color.h index 8ce5918e9..924046bb7 100644 --- a/gen/test/c/mx/mx_color.h +++ b/gen/test/c/mx/mx_color.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_COLOR_H -#define MX_COLOR_H +#ifndef MX_COLOR_H_INCLUDED +#define MX_COLOR_H_INCLUDED /* * The color type indicates the color of an element. Color may be represented as hexadecimal RGB @@ -16,4 +16,4 @@ typedef char *MxColor; /* Malloc'd copy of the wire string; the value IS its spelling. */ MxColor mx_color_parse(const char *s); -#endif /* MX_COLOR_H */ +#endif /* MX_COLOR_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_comma_separated_text.h b/gen/test/c/mx/mx_comma_separated_text.h index 09a7f2e9e..57a45ae1c 100644 --- a/gen/test/c/mx/mx_comma_separated_text.h +++ b/gen/test/c/mx/mx_comma_separated_text.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_COMMA_SEPARATED_TEXT_H -#define MX_COMMA_SEPARATED_TEXT_H +#ifndef MX_COMMA_SEPARATED_TEXT_H_INCLUDED +#define MX_COMMA_SEPARATED_TEXT_H_INCLUDED /* * The comma-separated-text type is used to specify a comma-separated list of text elements, as is @@ -12,4 +12,4 @@ typedef char *MxCommaSeparatedText; /* Malloc'd copy of the wire string; the value IS its spelling. */ MxCommaSeparatedText mx_comma_separated_text_parse(const char *s); -#endif /* MX_COMMA_SEPARATED_TEXT_H */ +#endif /* MX_COMMA_SEPARATED_TEXT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_css_font_size.c b/gen/test/c/mx/mx_css_font_size.c index 6c697ea28..36de79ec3 100644 --- a/gen/test/c/mx/mx_css_font_size.c +++ b/gen/test/c/mx/mx_css_font_size.c @@ -15,6 +15,8 @@ static const char *const mx_css_font_size_values[] = { }; bool mx_css_font_size_try_parse(const char *s, MxCSSFontSize *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_css_font_size_values) / sizeof(mx_css_font_size_values[0]); i++) { if (strcmp(s, mx_css_font_size_values[i]) == 0) { *out = (MxCSSFontSize)i; diff --git a/gen/test/c/mx/mx_css_font_size.h b/gen/test/c/mx/mx_css_font_size.h index a02318e38..b0805a6d0 100644 --- a/gen/test/c/mx/mx_css_font_size.h +++ b/gen/test/c/mx/mx_css_font_size.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_CSS_FONT_SIZE_H -#define MX_CSS_FONT_SIZE_H +#ifndef MX_CSS_FONT_SIZE_H_INCLUDED +#define MX_CSS_FONT_SIZE_H_INCLUDED #include @@ -25,4 +25,4 @@ MxCSSFontSize mx_css_font_size_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_css_font_size_to_string(MxCSSFontSize v); -#endif /* MX_CSS_FONT_SIZE_H */ +#endif /* MX_CSS_FONT_SIZE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_degree_symbol_value.c b/gen/test/c/mx/mx_degree_symbol_value.c index 1b5071904..cca693cd1 100644 --- a/gen/test/c/mx/mx_degree_symbol_value.c +++ b/gen/test/c/mx/mx_degree_symbol_value.c @@ -13,6 +13,8 @@ static const char *const mx_degree_symbol_value_values[] = { }; bool mx_degree_symbol_value_try_parse(const char *s, MxDegreeSymbolValue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_degree_symbol_value_values) / sizeof(mx_degree_symbol_value_values[0]); i++) { if (strcmp(s, mx_degree_symbol_value_values[i]) == 0) { *out = (MxDegreeSymbolValue)i; diff --git a/gen/test/c/mx/mx_degree_symbol_value.h b/gen/test/c/mx/mx_degree_symbol_value.h index da2341416..f7f8e330b 100644 --- a/gen/test/c/mx/mx_degree_symbol_value.h +++ b/gen/test/c/mx/mx_degree_symbol_value.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_DEGREE_SYMBOL_VALUE_H -#define MX_DEGREE_SYMBOL_VALUE_H +#ifndef MX_DEGREE_SYMBOL_VALUE_H_INCLUDED +#define MX_DEGREE_SYMBOL_VALUE_H_INCLUDED #include @@ -23,4 +23,4 @@ MxDegreeSymbolValue mx_degree_symbol_value_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_degree_symbol_value_to_string(MxDegreeSymbolValue v); -#endif /* MX_DEGREE_SYMBOL_VALUE_H */ +#endif /* MX_DEGREE_SYMBOL_VALUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_degree_type_value.c b/gen/test/c/mx/mx_degree_type_value.c index efa38488a..e9775986f 100644 --- a/gen/test/c/mx/mx_degree_type_value.c +++ b/gen/test/c/mx/mx_degree_type_value.c @@ -11,6 +11,8 @@ static const char *const mx_degree_type_value_values[] = { }; bool mx_degree_type_value_try_parse(const char *s, MxDegreeTypeValue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_degree_type_value_values) / sizeof(mx_degree_type_value_values[0]); i++) { if (strcmp(s, mx_degree_type_value_values[i]) == 0) { *out = (MxDegreeTypeValue)i; diff --git a/gen/test/c/mx/mx_degree_type_value.h b/gen/test/c/mx/mx_degree_type_value.h index 6f5503f65..daaa02da9 100644 --- a/gen/test/c/mx/mx_degree_type_value.h +++ b/gen/test/c/mx/mx_degree_type_value.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_DEGREE_TYPE_VALUE_H -#define MX_DEGREE_TYPE_VALUE_H +#ifndef MX_DEGREE_TYPE_VALUE_H_INCLUDED +#define MX_DEGREE_TYPE_VALUE_H_INCLUDED #include @@ -21,4 +21,4 @@ MxDegreeTypeValue mx_degree_type_value_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_degree_type_value_to_string(MxDegreeTypeValue v); -#endif /* MX_DEGREE_TYPE_VALUE_H */ +#endif /* MX_DEGREE_TYPE_VALUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_distance_type.h b/gen/test/c/mx/mx_distance_type.h index b3fea6499..f56518509 100644 --- a/gen/test/c/mx/mx_distance_type.h +++ b/gen/test/c/mx/mx_distance_type.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_DISTANCE_TYPE_H -#define MX_DISTANCE_TYPE_H +#ifndef MX_DISTANCE_TYPE_H_INCLUDED +#define MX_DISTANCE_TYPE_H_INCLUDED /* * The distance-type defines what type of distance is being defined in a distance element. Values @@ -13,4 +13,4 @@ typedef char *MxDistanceType; /* Malloc'd copy of the wire string; the value IS its spelling. */ MxDistanceType mx_distance_type_parse(const char *s); -#endif /* MX_DISTANCE_TYPE_H */ +#endif /* MX_DISTANCE_TYPE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_divisions.h b/gen/test/c/mx/mx_divisions.h index 8ec718e38..a6406e117 100644 --- a/gen/test/c/mx/mx_divisions.h +++ b/gen/test/c/mx/mx_divisions.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_DIVISIONS_H -#define MX_DIVISIONS_H +#ifndef MX_DIVISIONS_H_INCLUDED +#define MX_DIVISIONS_H_INCLUDED #include @@ -12,11 +12,11 @@ */ typedef double MxDivisions; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse. */ bool mx_divisions_try_parse(const char *s, MxDivisions *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0. */ MxDivisions mx_divisions_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_divisions_to_string(MxDivisions v); -#endif /* MX_DIVISIONS_H */ +#endif /* MX_DIVISIONS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_effect.c b/gen/test/c/mx/mx_effect.c index 3e1d13381..a324bfc87 100644 --- a/gen/test/c/mx/mx_effect.c +++ b/gen/test/c/mx/mx_effect.c @@ -24,6 +24,8 @@ static const char *const mx_effect_values[] = { }; bool mx_effect_try_parse(const char *s, MxEffect *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_effect_values) / sizeof(mx_effect_values[0]); i++) { if (strcmp(s, mx_effect_values[i]) == 0) { *out = (MxEffect)i; diff --git a/gen/test/c/mx/mx_effect.h b/gen/test/c/mx/mx_effect.h index a94f378ff..03840058c 100644 --- a/gen/test/c/mx/mx_effect.h +++ b/gen/test/c/mx/mx_effect.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_EFFECT_H -#define MX_EFFECT_H +#ifndef MX_EFFECT_H_INCLUDED +#define MX_EFFECT_H_INCLUDED #include @@ -34,4 +34,4 @@ MxEffect mx_effect_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_effect_to_string(MxEffect v); -#endif /* MX_EFFECT_H */ +#endif /* MX_EFFECT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_enclosure_shape.c b/gen/test/c/mx/mx_enclosure_shape.c index 3be4e8e43..5101c294f 100644 --- a/gen/test/c/mx/mx_enclosure_shape.c +++ b/gen/test/c/mx/mx_enclosure_shape.c @@ -22,6 +22,8 @@ static const char *const mx_enclosure_shape_values[] = { }; bool mx_enclosure_shape_try_parse(const char *s, MxEnclosureShape *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_enclosure_shape_values) / sizeof(mx_enclosure_shape_values[0]); i++) { if (strcmp(s, mx_enclosure_shape_values[i]) == 0) { *out = (MxEnclosureShape)i; diff --git a/gen/test/c/mx/mx_enclosure_shape.h b/gen/test/c/mx/mx_enclosure_shape.h index 650fb8e47..56b1ed23c 100644 --- a/gen/test/c/mx/mx_enclosure_shape.h +++ b/gen/test/c/mx/mx_enclosure_shape.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_ENCLOSURE_SHAPE_H -#define MX_ENCLOSURE_SHAPE_H +#ifndef MX_ENCLOSURE_SHAPE_H_INCLUDED +#define MX_ENCLOSURE_SHAPE_H_INCLUDED #include @@ -33,4 +33,4 @@ MxEnclosureShape mx_enclosure_shape_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_enclosure_shape_to_string(MxEnclosureShape v); -#endif /* MX_ENCLOSURE_SHAPE_H */ +#endif /* MX_ENCLOSURE_SHAPE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_ending_number.h b/gen/test/c/mx/mx_ending_number.h index 5b0dd1df5..7b61bea67 100644 --- a/gen/test/c/mx/mx_ending_number.h +++ b/gen/test/c/mx/mx_ending_number.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_ENDING_NUMBER_H -#define MX_ENDING_NUMBER_H +#ifndef MX_ENDING_NUMBER_H_INCLUDED +#define MX_ENDING_NUMBER_H_INCLUDED /* * The ending-number type is used to specify either a comma-separated list of positive integers @@ -15,4 +15,4 @@ typedef char *MxEndingNumber; /* Malloc'd copy of the wire string; the value IS its spelling. */ MxEndingNumber mx_ending_number_parse(const char *s); -#endif /* MX_ENDING_NUMBER_H */ +#endif /* MX_ENDING_NUMBER_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_fan.c b/gen/test/c/mx/mx_fan.c index fc518b502..321399680 100644 --- a/gen/test/c/mx/mx_fan.c +++ b/gen/test/c/mx/mx_fan.c @@ -11,6 +11,8 @@ static const char *const mx_fan_values[] = { }; bool mx_fan_try_parse(const char *s, MxFan *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_fan_values) / sizeof(mx_fan_values[0]); i++) { if (strcmp(s, mx_fan_values[i]) == 0) { *out = (MxFan)i; diff --git a/gen/test/c/mx/mx_fan.h b/gen/test/c/mx/mx_fan.h index 216e4db8d..e57beb078 100644 --- a/gen/test/c/mx/mx_fan.h +++ b/gen/test/c/mx/mx_fan.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_FAN_H -#define MX_FAN_H +#ifndef MX_FAN_H_INCLUDED +#define MX_FAN_H_INCLUDED #include @@ -21,4 +21,4 @@ MxFan mx_fan_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_fan_to_string(MxFan v); -#endif /* MX_FAN_H */ +#endif /* MX_FAN_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_fermata_shape.c b/gen/test/c/mx/mx_fermata_shape.c index 3a9881b25..07d4e9a1c 100644 --- a/gen/test/c/mx/mx_fermata_shape.c +++ b/gen/test/c/mx/mx_fermata_shape.c @@ -17,6 +17,8 @@ static const char *const mx_fermata_shape_values[] = { }; bool mx_fermata_shape_try_parse(const char *s, MxFermataShape *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_fermata_shape_values) / sizeof(mx_fermata_shape_values[0]); i++) { if (strcmp(s, mx_fermata_shape_values[i]) == 0) { *out = (MxFermataShape)i; diff --git a/gen/test/c/mx/mx_fermata_shape.h b/gen/test/c/mx/mx_fermata_shape.h index 061087cb9..7c77ab362 100644 --- a/gen/test/c/mx/mx_fermata_shape.h +++ b/gen/test/c/mx/mx_fermata_shape.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_FERMATA_SHAPE_H -#define MX_FERMATA_SHAPE_H +#ifndef MX_FERMATA_SHAPE_H_INCLUDED +#define MX_FERMATA_SHAPE_H_INCLUDED #include @@ -27,4 +27,4 @@ MxFermataShape mx_fermata_shape_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_fermata_shape_to_string(MxFermataShape v); -#endif /* MX_FERMATA_SHAPE_H */ +#endif /* MX_FERMATA_SHAPE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_fifths.h b/gen/test/c/mx/mx_fifths.h index bd18e4754..525638bcc 100644 --- a/gen/test/c/mx/mx_fifths.h +++ b/gen/test/c/mx/mx_fifths.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_FIFTHS_H -#define MX_FIFTHS_H +#ifndef MX_FIFTHS_H_INCLUDED +#define MX_FIFTHS_H_INCLUDED #include @@ -12,11 +12,11 @@ */ typedef long MxFifths; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse. */ bool mx_fifths_try_parse(const char *s, MxFifths *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0. */ MxFifths mx_fifths_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_fifths_to_string(MxFifths v); -#endif /* MX_FIFTHS_H */ +#endif /* MX_FIFTHS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_font_size.c b/gen/test/c/mx/mx_font_size.c index fadbde4c9..9318f00f7 100644 --- a/gen/test/c/mx/mx_font_size.c +++ b/gen/test/c/mx/mx_font_size.c @@ -7,15 +7,17 @@ #include bool mx_font_size_try_parse(const char *s, MxFontSize *out) { + if (!s) + s = ""; MxFontSize v; memset(&v, 0, sizeof(v)); if (mx_try_parse_decimal(s, &v.decimal)) { - v.kind = MX_FONT_SIZE_KIND_DECIMAL; + v.kind = MX_FONT_SIZE_DECIMAL; *out = v; return true; } if (mx_css_font_size_try_parse(s, &v.css_font_size)) { - v.kind = MX_FONT_SIZE_KIND_CSS_FONT_SIZE; + v.kind = MX_FONT_SIZE_CSS_FONT_SIZE; *out = v; return true; } @@ -28,14 +30,14 @@ MxFontSize mx_font_size_parse(const char *s) { if (mx_font_size_try_parse(s, &v)) return v; memset(&v, 0, sizeof(v)); - v.kind = MX_FONT_SIZE_KIND_DECIMAL; + v.kind = MX_FONT_SIZE_DECIMAL; v.decimal = mx_parse_decimal(s); return v; } char *mx_font_size_to_string(MxFontSize v) { switch (v.kind) { - case MX_FONT_SIZE_KIND_CSS_FONT_SIZE: + case MX_FONT_SIZE_CSS_FONT_SIZE: return mx_strdup(mx_css_font_size_to_string(v.css_font_size)); default: return mx_format_decimal(v.decimal); diff --git a/gen/test/c/mx/mx_font_size.h b/gen/test/c/mx/mx_font_size.h index 748ac3e95..48e720781 100644 --- a/gen/test/c/mx/mx_font_size.h +++ b/gen/test/c/mx/mx_font_size.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_FONT_SIZE_H -#define MX_FONT_SIZE_H +#ifndef MX_FONT_SIZE_H_INCLUDED +#define MX_FONT_SIZE_H_INCLUDED #include #include "mx_css_font_size.h" @@ -10,8 +10,8 @@ * The font-size can be one of the CSS font sizes or a numeric point size. */ typedef enum { - MX_FONT_SIZE_KIND_DECIMAL = 0, - MX_FONT_SIZE_KIND_CSS_FONT_SIZE = 1 + MX_FONT_SIZE_DECIMAL = 0, + MX_FONT_SIZE_CSS_FONT_SIZE = 1 } MxFontSizeKind; typedef struct { @@ -28,4 +28,4 @@ MxFontSize mx_font_size_parse(const char *s); char *mx_font_size_to_string(MxFontSize v); void mx_font_size_free(MxFontSize *v); -#endif /* MX_FONT_SIZE_H */ +#endif /* MX_FONT_SIZE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_font_style.c b/gen/test/c/mx/mx_font_style.c index 9ef638b22..5ba4debcf 100644 --- a/gen/test/c/mx/mx_font_style.c +++ b/gen/test/c/mx/mx_font_style.c @@ -10,6 +10,8 @@ static const char *const mx_font_style_values[] = { }; bool mx_font_style_try_parse(const char *s, MxFontStyle *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_font_style_values) / sizeof(mx_font_style_values[0]); i++) { if (strcmp(s, mx_font_style_values[i]) == 0) { *out = (MxFontStyle)i; diff --git a/gen/test/c/mx/mx_font_style.h b/gen/test/c/mx/mx_font_style.h index d33b447a2..4612a5b48 100644 --- a/gen/test/c/mx/mx_font_style.h +++ b/gen/test/c/mx/mx_font_style.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_FONT_STYLE_H -#define MX_FONT_STYLE_H +#ifndef MX_FONT_STYLE_H_INCLUDED +#define MX_FONT_STYLE_H_INCLUDED #include @@ -19,4 +19,4 @@ MxFontStyle mx_font_style_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_font_style_to_string(MxFontStyle v); -#endif /* MX_FONT_STYLE_H */ +#endif /* MX_FONT_STYLE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_font_weight.c b/gen/test/c/mx/mx_font_weight.c index 565d9a842..4339bcdc6 100644 --- a/gen/test/c/mx/mx_font_weight.c +++ b/gen/test/c/mx/mx_font_weight.c @@ -10,6 +10,8 @@ static const char *const mx_font_weight_values[] = { }; bool mx_font_weight_try_parse(const char *s, MxFontWeight *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_font_weight_values) / sizeof(mx_font_weight_values[0]); i++) { if (strcmp(s, mx_font_weight_values[i]) == 0) { *out = (MxFontWeight)i; diff --git a/gen/test/c/mx/mx_font_weight.h b/gen/test/c/mx/mx_font_weight.h index 96c062c79..2d5197b32 100644 --- a/gen/test/c/mx/mx_font_weight.h +++ b/gen/test/c/mx/mx_font_weight.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_FONT_WEIGHT_H -#define MX_FONT_WEIGHT_H +#ifndef MX_FONT_WEIGHT_H_INCLUDED +#define MX_FONT_WEIGHT_H_INCLUDED #include @@ -19,4 +19,4 @@ MxFontWeight mx_font_weight_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_font_weight_to_string(MxFontWeight v); -#endif /* MX_FONT_WEIGHT_H */ +#endif /* MX_FONT_WEIGHT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_glass_value.c b/gen/test/c/mx/mx_glass_value.c index a79290eed..3ee09c651 100644 --- a/gen/test/c/mx/mx_glass_value.c +++ b/gen/test/c/mx/mx_glass_value.c @@ -11,6 +11,8 @@ static const char *const mx_glass_value_values[] = { }; bool mx_glass_value_try_parse(const char *s, MxGlassValue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_glass_value_values) / sizeof(mx_glass_value_values[0]); i++) { if (strcmp(s, mx_glass_value_values[i]) == 0) { *out = (MxGlassValue)i; diff --git a/gen/test/c/mx/mx_glass_value.h b/gen/test/c/mx/mx_glass_value.h index ed62d3df0..153d5401f 100644 --- a/gen/test/c/mx/mx_glass_value.h +++ b/gen/test/c/mx/mx_glass_value.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_GLASS_VALUE_H -#define MX_GLASS_VALUE_H +#ifndef MX_GLASS_VALUE_H_INCLUDED +#define MX_GLASS_VALUE_H_INCLUDED #include @@ -20,4 +20,4 @@ MxGlassValue mx_glass_value_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_glass_value_to_string(MxGlassValue v); -#endif /* MX_GLASS_VALUE_H */ +#endif /* MX_GLASS_VALUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_glyph_type.h b/gen/test/c/mx/mx_glyph_type.h index 69dab3d89..7e7e57b00 100644 --- a/gen/test/c/mx/mx_glyph_type.h +++ b/gen/test/c/mx/mx_glyph_type.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_GLYPH_TYPE_H -#define MX_GLYPH_TYPE_H +#ifndef MX_GLYPH_TYPE_H_INCLUDED +#define MX_GLYPH_TYPE_H_INCLUDED /* * The glyph-type defines what type of glyph is being defined in a glyph element. Values include @@ -22,4 +22,4 @@ typedef char *MxGlyphType; /* Malloc'd copy of the wire string; the value IS its spelling. */ MxGlyphType mx_glyph_type_parse(const char *s); -#endif /* MX_GLYPH_TYPE_H */ +#endif /* MX_GLYPH_TYPE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_group_barline_value.c b/gen/test/c/mx/mx_group_barline_value.c index 904228ab0..4a7f52f0e 100644 --- a/gen/test/c/mx/mx_group_barline_value.c +++ b/gen/test/c/mx/mx_group_barline_value.c @@ -11,6 +11,8 @@ static const char *const mx_group_barline_value_values[] = { }; bool mx_group_barline_value_try_parse(const char *s, MxGroupBarlineValue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_group_barline_value_values) / sizeof(mx_group_barline_value_values[0]); i++) { if (strcmp(s, mx_group_barline_value_values[i]) == 0) { *out = (MxGroupBarlineValue)i; diff --git a/gen/test/c/mx/mx_group_barline_value.h b/gen/test/c/mx/mx_group_barline_value.h index fe2645dc2..f6b0fa3e4 100644 --- a/gen/test/c/mx/mx_group_barline_value.h +++ b/gen/test/c/mx/mx_group_barline_value.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_GROUP_BARLINE_VALUE_H -#define MX_GROUP_BARLINE_VALUE_H +#ifndef MX_GROUP_BARLINE_VALUE_H_INCLUDED +#define MX_GROUP_BARLINE_VALUE_H_INCLUDED #include @@ -20,4 +20,4 @@ MxGroupBarlineValue mx_group_barline_value_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_group_barline_value_to_string(MxGroupBarlineValue v); -#endif /* MX_GROUP_BARLINE_VALUE_H */ +#endif /* MX_GROUP_BARLINE_VALUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_group_symbol_value.c b/gen/test/c/mx/mx_group_symbol_value.c index a66de51c4..abb3f8e53 100644 --- a/gen/test/c/mx/mx_group_symbol_value.c +++ b/gen/test/c/mx/mx_group_symbol_value.c @@ -13,6 +13,8 @@ static const char *const mx_group_symbol_value_values[] = { }; bool mx_group_symbol_value_try_parse(const char *s, MxGroupSymbolValue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_group_symbol_value_values) / sizeof(mx_group_symbol_value_values[0]); i++) { if (strcmp(s, mx_group_symbol_value_values[i]) == 0) { *out = (MxGroupSymbolValue)i; diff --git a/gen/test/c/mx/mx_group_symbol_value.h b/gen/test/c/mx/mx_group_symbol_value.h index ada0599d3..223624d09 100644 --- a/gen/test/c/mx/mx_group_symbol_value.h +++ b/gen/test/c/mx/mx_group_symbol_value.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_GROUP_SYMBOL_VALUE_H -#define MX_GROUP_SYMBOL_VALUE_H +#ifndef MX_GROUP_SYMBOL_VALUE_H_INCLUDED +#define MX_GROUP_SYMBOL_VALUE_H_INCLUDED #include @@ -23,4 +23,4 @@ MxGroupSymbolValue mx_group_symbol_value_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_group_symbol_value_to_string(MxGroupSymbolValue v); -#endif /* MX_GROUP_SYMBOL_VALUE_H */ +#endif /* MX_GROUP_SYMBOL_VALUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_handbell_value.c b/gen/test/c/mx/mx_handbell_value.c index 6b720b552..5a8137378 100644 --- a/gen/test/c/mx/mx_handbell_value.c +++ b/gen/test/c/mx/mx_handbell_value.c @@ -20,6 +20,8 @@ static const char *const mx_handbell_value_values[] = { }; bool mx_handbell_value_try_parse(const char *s, MxHandbellValue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_handbell_value_values) / sizeof(mx_handbell_value_values[0]); i++) { if (strcmp(s, mx_handbell_value_values[i]) == 0) { *out = (MxHandbellValue)i; diff --git a/gen/test/c/mx/mx_handbell_value.h b/gen/test/c/mx/mx_handbell_value.h index 2f581531c..f2971403e 100644 --- a/gen/test/c/mx/mx_handbell_value.h +++ b/gen/test/c/mx/mx_handbell_value.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_HANDBELL_VALUE_H -#define MX_HANDBELL_VALUE_H +#ifndef MX_HANDBELL_VALUE_H_INCLUDED +#define MX_HANDBELL_VALUE_H_INCLUDED #include @@ -29,4 +29,4 @@ MxHandbellValue mx_handbell_value_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_handbell_value_to_string(MxHandbellValue v); -#endif /* MX_HANDBELL_VALUE_H */ +#endif /* MX_HANDBELL_VALUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_harmon_closed_location.c b/gen/test/c/mx/mx_harmon_closed_location.c index 1b1602af2..179a2532f 100644 --- a/gen/test/c/mx/mx_harmon_closed_location.c +++ b/gen/test/c/mx/mx_harmon_closed_location.c @@ -12,6 +12,8 @@ static const char *const mx_harmon_closed_location_values[] = { }; bool mx_harmon_closed_location_try_parse(const char *s, MxHarmonClosedLocation *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_harmon_closed_location_values) / sizeof(mx_harmon_closed_location_values[0]); i++) { if (strcmp(s, mx_harmon_closed_location_values[i]) == 0) { *out = (MxHarmonClosedLocation)i; diff --git a/gen/test/c/mx/mx_harmon_closed_location.h b/gen/test/c/mx/mx_harmon_closed_location.h index 63fa35bce..fd3120f5a 100644 --- a/gen/test/c/mx/mx_harmon_closed_location.h +++ b/gen/test/c/mx/mx_harmon_closed_location.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_HARMON_CLOSED_LOCATION_H -#define MX_HARMON_CLOSED_LOCATION_H +#ifndef MX_HARMON_CLOSED_LOCATION_H_INCLUDED +#define MX_HARMON_CLOSED_LOCATION_H_INCLUDED #include @@ -22,4 +22,4 @@ MxHarmonClosedLocation mx_harmon_closed_location_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_harmon_closed_location_to_string(MxHarmonClosedLocation v); -#endif /* MX_HARMON_CLOSED_LOCATION_H */ +#endif /* MX_HARMON_CLOSED_LOCATION_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_harmon_closed_value.c b/gen/test/c/mx/mx_harmon_closed_value.c index 9e98e7d7d..c89a0b2b0 100644 --- a/gen/test/c/mx/mx_harmon_closed_value.c +++ b/gen/test/c/mx/mx_harmon_closed_value.c @@ -11,6 +11,8 @@ static const char *const mx_harmon_closed_value_values[] = { }; bool mx_harmon_closed_value_try_parse(const char *s, MxHarmonClosedValue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_harmon_closed_value_values) / sizeof(mx_harmon_closed_value_values[0]); i++) { if (strcmp(s, mx_harmon_closed_value_values[i]) == 0) { *out = (MxHarmonClosedValue)i; diff --git a/gen/test/c/mx/mx_harmon_closed_value.h b/gen/test/c/mx/mx_harmon_closed_value.h index 525f759a3..38513ca81 100644 --- a/gen/test/c/mx/mx_harmon_closed_value.h +++ b/gen/test/c/mx/mx_harmon_closed_value.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_HARMON_CLOSED_VALUE_H -#define MX_HARMON_CLOSED_VALUE_H +#ifndef MX_HARMON_CLOSED_VALUE_H_INCLUDED +#define MX_HARMON_CLOSED_VALUE_H_INCLUDED #include @@ -20,4 +20,4 @@ MxHarmonClosedValue mx_harmon_closed_value_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_harmon_closed_value_to_string(MxHarmonClosedValue v); -#endif /* MX_HARMON_CLOSED_VALUE_H */ +#endif /* MX_HARMON_CLOSED_VALUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_harmony_type.c b/gen/test/c/mx/mx_harmony_type.c index d939161bf..f0c61a5d4 100644 --- a/gen/test/c/mx/mx_harmony_type.c +++ b/gen/test/c/mx/mx_harmony_type.c @@ -11,6 +11,8 @@ static const char *const mx_harmony_type_values[] = { }; bool mx_harmony_type_try_parse(const char *s, MxHarmonyType *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_harmony_type_values) / sizeof(mx_harmony_type_values[0]); i++) { if (strcmp(s, mx_harmony_type_values[i]) == 0) { *out = (MxHarmonyType)i; diff --git a/gen/test/c/mx/mx_harmony_type.h b/gen/test/c/mx/mx_harmony_type.h index 45046dab7..127064f00 100644 --- a/gen/test/c/mx/mx_harmony_type.h +++ b/gen/test/c/mx/mx_harmony_type.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_HARMONY_TYPE_H -#define MX_HARMONY_TYPE_H +#ifndef MX_HARMONY_TYPE_H_INCLUDED +#define MX_HARMONY_TYPE_H_INCLUDED #include @@ -22,4 +22,4 @@ MxHarmonyType mx_harmony_type_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_harmony_type_to_string(MxHarmonyType v); -#endif /* MX_HARMONY_TYPE_H */ +#endif /* MX_HARMONY_TYPE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_hole_closed_location.c b/gen/test/c/mx/mx_hole_closed_location.c index bc50629ec..c9dd5c671 100644 --- a/gen/test/c/mx/mx_hole_closed_location.c +++ b/gen/test/c/mx/mx_hole_closed_location.c @@ -12,6 +12,8 @@ static const char *const mx_hole_closed_location_values[] = { }; bool mx_hole_closed_location_try_parse(const char *s, MxHoleClosedLocation *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_hole_closed_location_values) / sizeof(mx_hole_closed_location_values[0]); i++) { if (strcmp(s, mx_hole_closed_location_values[i]) == 0) { *out = (MxHoleClosedLocation)i; diff --git a/gen/test/c/mx/mx_hole_closed_location.h b/gen/test/c/mx/mx_hole_closed_location.h index c7ee5fca9..51a3335f7 100644 --- a/gen/test/c/mx/mx_hole_closed_location.h +++ b/gen/test/c/mx/mx_hole_closed_location.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_HOLE_CLOSED_LOCATION_H -#define MX_HOLE_CLOSED_LOCATION_H +#ifndef MX_HOLE_CLOSED_LOCATION_H_INCLUDED +#define MX_HOLE_CLOSED_LOCATION_H_INCLUDED #include @@ -22,4 +22,4 @@ MxHoleClosedLocation mx_hole_closed_location_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_hole_closed_location_to_string(MxHoleClosedLocation v); -#endif /* MX_HOLE_CLOSED_LOCATION_H */ +#endif /* MX_HOLE_CLOSED_LOCATION_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_hole_closed_value.c b/gen/test/c/mx/mx_hole_closed_value.c index 34734401e..b4aedff56 100644 --- a/gen/test/c/mx/mx_hole_closed_value.c +++ b/gen/test/c/mx/mx_hole_closed_value.c @@ -11,6 +11,8 @@ static const char *const mx_hole_closed_value_values[] = { }; bool mx_hole_closed_value_try_parse(const char *s, MxHoleClosedValue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_hole_closed_value_values) / sizeof(mx_hole_closed_value_values[0]); i++) { if (strcmp(s, mx_hole_closed_value_values[i]) == 0) { *out = (MxHoleClosedValue)i; diff --git a/gen/test/c/mx/mx_hole_closed_value.h b/gen/test/c/mx/mx_hole_closed_value.h index e94c3ebac..8b32e83ff 100644 --- a/gen/test/c/mx/mx_hole_closed_value.h +++ b/gen/test/c/mx/mx_hole_closed_value.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_HOLE_CLOSED_VALUE_H -#define MX_HOLE_CLOSED_VALUE_H +#ifndef MX_HOLE_CLOSED_VALUE_H_INCLUDED +#define MX_HOLE_CLOSED_VALUE_H_INCLUDED #include @@ -20,4 +20,4 @@ MxHoleClosedValue mx_hole_closed_value_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_hole_closed_value_to_string(MxHoleClosedValue v); -#endif /* MX_HOLE_CLOSED_VALUE_H */ +#endif /* MX_HOLE_CLOSED_VALUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_instrument_sound.c b/gen/test/c/mx/mx_instrument_sound.c index 194581201..7f648dfe0 100644 --- a/gen/test/c/mx/mx_instrument_sound.c +++ b/gen/test/c/mx/mx_instrument_sound.c @@ -7,14 +7,16 @@ #include bool mx_instrument_sound_try_parse(const char *s, MxInstrumentSound *out) { + if (!s) + s = ""; MxInstrumentSound v; memset(&v, 0, sizeof(v)); if (mx_sound_id_try_parse(s, &v.sound_id)) { - v.kind = MX_INSTRUMENT_SOUND_KIND_SOUND_ID; + v.kind = MX_INSTRUMENT_SOUND_SOUND_ID; *out = v; return true; } - v.kind = MX_INSTRUMENT_SOUND_KIND_STRING; + v.kind = MX_INSTRUMENT_SOUND_STRING; v.string = mx_strdup(s); *out = v; return true; @@ -25,14 +27,14 @@ MxInstrumentSound mx_instrument_sound_parse(const char *s) { if (mx_instrument_sound_try_parse(s, &v)) return v; memset(&v, 0, sizeof(v)); - v.kind = MX_INSTRUMENT_SOUND_KIND_SOUND_ID; + v.kind = MX_INSTRUMENT_SOUND_SOUND_ID; v.sound_id = mx_sound_id_parse(s); return v; } char *mx_instrument_sound_to_string(MxInstrumentSound v) { switch (v.kind) { - case MX_INSTRUMENT_SOUND_KIND_STRING: + case MX_INSTRUMENT_SOUND_STRING: return mx_strdup(v.string); default: return mx_strdup(mx_sound_id_to_string(v.sound_id)); @@ -42,7 +44,7 @@ char *mx_instrument_sound_to_string(MxInstrumentSound v) { void mx_instrument_sound_free(MxInstrumentSound *v) { if (!v) return; - if (v->kind == MX_INSTRUMENT_SOUND_KIND_STRING) { + if (v->kind == MX_INSTRUMENT_SOUND_STRING) { free(v->string); v->string = NULL; } diff --git a/gen/test/c/mx/mx_instrument_sound.h b/gen/test/c/mx/mx_instrument_sound.h index ce2e612a7..bb778b15f 100644 --- a/gen/test/c/mx/mx_instrument_sound.h +++ b/gen/test/c/mx/mx_instrument_sound.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_INSTRUMENT_SOUND_H -#define MX_INSTRUMENT_SOUND_H +#ifndef MX_INSTRUMENT_SOUND_H_INCLUDED +#define MX_INSTRUMENT_SOUND_H_INCLUDED #include #include "mx_sound_id.h" @@ -11,8 +11,8 @@ * schema leaves the content open (xs:string), so the string member is intrinsic, not a fallback. */ typedef enum { - MX_INSTRUMENT_SOUND_KIND_SOUND_ID = 0, - MX_INSTRUMENT_SOUND_KIND_STRING = 1 + MX_INSTRUMENT_SOUND_SOUND_ID = 0, + MX_INSTRUMENT_SOUND_STRING = 1 } MxInstrumentSoundKind; typedef struct { @@ -29,4 +29,4 @@ MxInstrumentSound mx_instrument_sound_parse(const char *s); char *mx_instrument_sound_to_string(MxInstrumentSound v); void mx_instrument_sound_free(MxInstrumentSound *v); -#endif /* MX_INSTRUMENT_SOUND_H */ +#endif /* MX_INSTRUMENT_SOUND_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_kind_value.c b/gen/test/c/mx/mx_kind_value.c index 57ff1cfca..6c6a9be08 100644 --- a/gen/test/c/mx/mx_kind_value.c +++ b/gen/test/c/mx/mx_kind_value.c @@ -41,6 +41,8 @@ static const char *const mx_kind_value_values[] = { }; bool mx_kind_value_try_parse(const char *s, MxKindValue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_kind_value_values) / sizeof(mx_kind_value_values[0]); i++) { if (strcmp(s, mx_kind_value_values[i]) == 0) { *out = (MxKindValue)i; diff --git a/gen/test/c/mx/mx_kind_value.h b/gen/test/c/mx/mx_kind_value.h index 1c824396c..66dffb23f 100644 --- a/gen/test/c/mx/mx_kind_value.h +++ b/gen/test/c/mx/mx_kind_value.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_KIND_VALUE_H -#define MX_KIND_VALUE_H +#ifndef MX_KIND_VALUE_H_INCLUDED +#define MX_KIND_VALUE_H_INCLUDED #include @@ -66,4 +66,4 @@ MxKindValue mx_kind_value_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_kind_value_to_string(MxKindValue v); -#endif /* MX_KIND_VALUE_H */ +#endif /* MX_KIND_VALUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_left_center_right.c b/gen/test/c/mx/mx_left_center_right.c index 809a14e52..e2a488aec 100644 --- a/gen/test/c/mx/mx_left_center_right.c +++ b/gen/test/c/mx/mx_left_center_right.c @@ -11,6 +11,8 @@ static const char *const mx_left_center_right_values[] = { }; bool mx_left_center_right_try_parse(const char *s, MxLeftCenterRight *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_left_center_right_values) / sizeof(mx_left_center_right_values[0]); i++) { if (strcmp(s, mx_left_center_right_values[i]) == 0) { *out = (MxLeftCenterRight)i; diff --git a/gen/test/c/mx/mx_left_center_right.h b/gen/test/c/mx/mx_left_center_right.h index deb8ec41c..90857fbbb 100644 --- a/gen/test/c/mx/mx_left_center_right.h +++ b/gen/test/c/mx/mx_left_center_right.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_LEFT_CENTER_RIGHT_H -#define MX_LEFT_CENTER_RIGHT_H +#ifndef MX_LEFT_CENTER_RIGHT_H_INCLUDED +#define MX_LEFT_CENTER_RIGHT_H_INCLUDED #include @@ -20,4 +20,4 @@ MxLeftCenterRight mx_left_center_right_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_left_center_right_to_string(MxLeftCenterRight v); -#endif /* MX_LEFT_CENTER_RIGHT_H */ +#endif /* MX_LEFT_CENTER_RIGHT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_left_right.c b/gen/test/c/mx/mx_left_right.c index 632636d84..5fccb5cfd 100644 --- a/gen/test/c/mx/mx_left_right.c +++ b/gen/test/c/mx/mx_left_right.c @@ -10,6 +10,8 @@ static const char *const mx_left_right_values[] = { }; bool mx_left_right_try_parse(const char *s, MxLeftRight *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_left_right_values) / sizeof(mx_left_right_values[0]); i++) { if (strcmp(s, mx_left_right_values[i]) == 0) { *out = (MxLeftRight)i; diff --git a/gen/test/c/mx/mx_left_right.h b/gen/test/c/mx/mx_left_right.h index bf5a3339e..58221daf1 100644 --- a/gen/test/c/mx/mx_left_right.h +++ b/gen/test/c/mx/mx_left_right.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_LEFT_RIGHT_H -#define MX_LEFT_RIGHT_H +#ifndef MX_LEFT_RIGHT_H_INCLUDED +#define MX_LEFT_RIGHT_H_INCLUDED #include @@ -20,4 +20,4 @@ MxLeftRight mx_left_right_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_left_right_to_string(MxLeftRight v); -#endif /* MX_LEFT_RIGHT_H */ +#endif /* MX_LEFT_RIGHT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_line_end.c b/gen/test/c/mx/mx_line_end.c index 1530b8a7f..95b03a346 100644 --- a/gen/test/c/mx/mx_line_end.c +++ b/gen/test/c/mx/mx_line_end.c @@ -13,6 +13,8 @@ static const char *const mx_line_end_values[] = { }; bool mx_line_end_try_parse(const char *s, MxLineEnd *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_line_end_values) / sizeof(mx_line_end_values[0]); i++) { if (strcmp(s, mx_line_end_values[i]) == 0) { *out = (MxLineEnd)i; diff --git a/gen/test/c/mx/mx_line_end.h b/gen/test/c/mx/mx_line_end.h index f2887e216..88126fd55 100644 --- a/gen/test/c/mx/mx_line_end.h +++ b/gen/test/c/mx/mx_line_end.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_LINE_END_H -#define MX_LINE_END_H +#ifndef MX_LINE_END_H_INCLUDED +#define MX_LINE_END_H_INCLUDED #include @@ -23,4 +23,4 @@ MxLineEnd mx_line_end_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_line_end_to_string(MxLineEnd v); -#endif /* MX_LINE_END_H */ +#endif /* MX_LINE_END_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_line_length.c b/gen/test/c/mx/mx_line_length.c index a321a232d..bd999db2d 100644 --- a/gen/test/c/mx/mx_line_length.c +++ b/gen/test/c/mx/mx_line_length.c @@ -11,6 +11,8 @@ static const char *const mx_line_length_values[] = { }; bool mx_line_length_try_parse(const char *s, MxLineLength *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_line_length_values) / sizeof(mx_line_length_values[0]); i++) { if (strcmp(s, mx_line_length_values[i]) == 0) { *out = (MxLineLength)i; diff --git a/gen/test/c/mx/mx_line_length.h b/gen/test/c/mx/mx_line_length.h index 3a3fc81a9..fa086205b 100644 --- a/gen/test/c/mx/mx_line_length.h +++ b/gen/test/c/mx/mx_line_length.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_LINE_LENGTH_H -#define MX_LINE_LENGTH_H +#ifndef MX_LINE_LENGTH_H_INCLUDED +#define MX_LINE_LENGTH_H_INCLUDED #include @@ -21,4 +21,4 @@ MxLineLength mx_line_length_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_line_length_to_string(MxLineLength v); -#endif /* MX_LINE_LENGTH_H */ +#endif /* MX_LINE_LENGTH_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_line_shape.c b/gen/test/c/mx/mx_line_shape.c index bf0c04859..897c01843 100644 --- a/gen/test/c/mx/mx_line_shape.c +++ b/gen/test/c/mx/mx_line_shape.c @@ -10,6 +10,8 @@ static const char *const mx_line_shape_values[] = { }; bool mx_line_shape_try_parse(const char *s, MxLineShape *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_line_shape_values) / sizeof(mx_line_shape_values[0]); i++) { if (strcmp(s, mx_line_shape_values[i]) == 0) { *out = (MxLineShape)i; diff --git a/gen/test/c/mx/mx_line_shape.h b/gen/test/c/mx/mx_line_shape.h index 116fc7880..76b9a1c35 100644 --- a/gen/test/c/mx/mx_line_shape.h +++ b/gen/test/c/mx/mx_line_shape.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_LINE_SHAPE_H -#define MX_LINE_SHAPE_H +#ifndef MX_LINE_SHAPE_H_INCLUDED +#define MX_LINE_SHAPE_H_INCLUDED #include @@ -19,4 +19,4 @@ MxLineShape mx_line_shape_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_line_shape_to_string(MxLineShape v); -#endif /* MX_LINE_SHAPE_H */ +#endif /* MX_LINE_SHAPE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_line_type.c b/gen/test/c/mx/mx_line_type.c index 8fe7a07c6..b3fa2f8c2 100644 --- a/gen/test/c/mx/mx_line_type.c +++ b/gen/test/c/mx/mx_line_type.c @@ -12,6 +12,8 @@ static const char *const mx_line_type_values[] = { }; bool mx_line_type_try_parse(const char *s, MxLineType *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_line_type_values) / sizeof(mx_line_type_values[0]); i++) { if (strcmp(s, mx_line_type_values[i]) == 0) { *out = (MxLineType)i; diff --git a/gen/test/c/mx/mx_line_type.h b/gen/test/c/mx/mx_line_type.h index 266ed2f28..d26bf4b50 100644 --- a/gen/test/c/mx/mx_line_type.h +++ b/gen/test/c/mx/mx_line_type.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_LINE_TYPE_H -#define MX_LINE_TYPE_H +#ifndef MX_LINE_TYPE_H_INCLUDED +#define MX_LINE_TYPE_H_INCLUDED #include @@ -21,4 +21,4 @@ MxLineType mx_line_type_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_line_type_to_string(MxLineType v); -#endif /* MX_LINE_TYPE_H */ +#endif /* MX_LINE_TYPE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_line_width_type.h b/gen/test/c/mx/mx_line_width_type.h index 2e052c202..50c3832c0 100644 --- a/gen/test/c/mx/mx_line_width_type.h +++ b/gen/test/c/mx/mx_line_width_type.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_LINE_WIDTH_TYPE_H -#define MX_LINE_WIDTH_TYPE_H +#ifndef MX_LINE_WIDTH_TYPE_H_INCLUDED +#define MX_LINE_WIDTH_TYPE_H_INCLUDED /* * The line-width-type defines what type of line is being defined in a line-width element. Values @@ -15,4 +15,4 @@ typedef char *MxLineWidthType; /* Malloc'd copy of the wire string; the value IS its spelling. */ MxLineWidthType mx_line_width_type_parse(const char *s); -#endif /* MX_LINE_WIDTH_TYPE_H */ +#endif /* MX_LINE_WIDTH_TYPE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_margin_type.c b/gen/test/c/mx/mx_margin_type.c index ae672ccc5..60c6d8b37 100644 --- a/gen/test/c/mx/mx_margin_type.c +++ b/gen/test/c/mx/mx_margin_type.c @@ -11,6 +11,8 @@ static const char *const mx_margin_type_values[] = { }; bool mx_margin_type_try_parse(const char *s, MxMarginType *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_margin_type_values) / sizeof(mx_margin_type_values[0]); i++) { if (strcmp(s, mx_margin_type_values[i]) == 0) { *out = (MxMarginType)i; diff --git a/gen/test/c/mx/mx_margin_type.h b/gen/test/c/mx/mx_margin_type.h index 970b1c117..5f05b651f 100644 --- a/gen/test/c/mx/mx_margin_type.h +++ b/gen/test/c/mx/mx_margin_type.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_MARGIN_TYPE_H -#define MX_MARGIN_TYPE_H +#ifndef MX_MARGIN_TYPE_H_INCLUDED +#define MX_MARGIN_TYPE_H_INCLUDED #include @@ -20,4 +20,4 @@ MxMarginType mx_margin_type_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_margin_type_to_string(MxMarginType v); -#endif /* MX_MARGIN_TYPE_H */ +#endif /* MX_MARGIN_TYPE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_measure_numbering_value.c b/gen/test/c/mx/mx_measure_numbering_value.c index a38af158b..2a5663429 100644 --- a/gen/test/c/mx/mx_measure_numbering_value.c +++ b/gen/test/c/mx/mx_measure_numbering_value.c @@ -11,6 +11,8 @@ static const char *const mx_measure_numbering_value_values[] = { }; bool mx_measure_numbering_value_try_parse(const char *s, MxMeasureNumberingValue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_measure_numbering_value_values) / sizeof(mx_measure_numbering_value_values[0]); i++) { if (strcmp(s, mx_measure_numbering_value_values[i]) == 0) { *out = (MxMeasureNumberingValue)i; diff --git a/gen/test/c/mx/mx_measure_numbering_value.h b/gen/test/c/mx/mx_measure_numbering_value.h index 911fb6dce..f8080e3b1 100644 --- a/gen/test/c/mx/mx_measure_numbering_value.h +++ b/gen/test/c/mx/mx_measure_numbering_value.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_MEASURE_NUMBERING_VALUE_H -#define MX_MEASURE_NUMBERING_VALUE_H +#ifndef MX_MEASURE_NUMBERING_VALUE_H_INCLUDED +#define MX_MEASURE_NUMBERING_VALUE_H_INCLUDED #include @@ -21,4 +21,4 @@ MxMeasureNumberingValue mx_measure_numbering_value_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_measure_numbering_value_to_string(MxMeasureNumberingValue v); -#endif /* MX_MEASURE_NUMBERING_VALUE_H */ +#endif /* MX_MEASURE_NUMBERING_VALUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_measure_text.h b/gen/test/c/mx/mx_measure_text.h index ff1452ed9..2fd33be01 100644 --- a/gen/test/c/mx/mx_measure_text.h +++ b/gen/test/c/mx/mx_measure_text.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_MEASURE_TEXT_H -#define MX_MEASURE_TEXT_H +#ifndef MX_MEASURE_TEXT_H_INCLUDED +#define MX_MEASURE_TEXT_H_INCLUDED /* * The measure-text type is used for the text attribute of measure elements. It has at least one @@ -13,4 +13,4 @@ typedef char *MxMeasureText; /* Malloc'd copy of the wire string; the value IS its spelling. */ MxMeasureText mx_measure_text_parse(const char *s); -#endif /* MX_MEASURE_TEXT_H */ +#endif /* MX_MEASURE_TEXT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_membrane.c b/gen/test/c/mx/mx_membrane.c index 7c6f78fb6..9d631ebc8 100644 --- a/gen/test/c/mx/mx_membrane.c +++ b/gen/test/c/mx/mx_membrane.c @@ -25,6 +25,8 @@ static const char *const mx_membrane_values[] = { }; bool mx_membrane_try_parse(const char *s, MxMembrane *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_membrane_values) / sizeof(mx_membrane_values[0]); i++) { if (strcmp(s, mx_membrane_values[i]) == 0) { *out = (MxMembrane)i; diff --git a/gen/test/c/mx/mx_membrane.h b/gen/test/c/mx/mx_membrane.h index a9f2c0134..9e7f8f2ea 100644 --- a/gen/test/c/mx/mx_membrane.h +++ b/gen/test/c/mx/mx_membrane.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_MEMBRANE_H -#define MX_MEMBRANE_H +#ifndef MX_MEMBRANE_H_INCLUDED +#define MX_MEMBRANE_H_INCLUDED #include @@ -34,4 +34,4 @@ MxMembrane mx_membrane_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_membrane_to_string(MxMembrane v); -#endif /* MX_MEMBRANE_H */ +#endif /* MX_MEMBRANE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_metal.c b/gen/test/c/mx/mx_metal.c index 29ebb5b05..9c092085b 100644 --- a/gen/test/c/mx/mx_metal.c +++ b/gen/test/c/mx/mx_metal.c @@ -40,6 +40,8 @@ static const char *const mx_metal_values[] = { }; bool mx_metal_try_parse(const char *s, MxMetal *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_metal_values) / sizeof(mx_metal_values[0]); i++) { if (strcmp(s, mx_metal_values[i]) == 0) { *out = (MxMetal)i; diff --git a/gen/test/c/mx/mx_metal.h b/gen/test/c/mx/mx_metal.h index f7d07d157..02a805def 100644 --- a/gen/test/c/mx/mx_metal.h +++ b/gen/test/c/mx/mx_metal.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_METAL_H -#define MX_METAL_H +#ifndef MX_METAL_H_INCLUDED +#define MX_METAL_H_INCLUDED #include @@ -50,4 +50,4 @@ MxMetal mx_metal_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_metal_to_string(MxMetal v); -#endif /* MX_METAL_H */ +#endif /* MX_METAL_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_midi_128.h b/gen/test/c/mx/mx_midi_128.h index 350041b18..ace953733 100644 --- a/gen/test/c/mx/mx_midi_128.h +++ b/gen/test/c/mx/mx_midi_128.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_MIDI_128_H -#define MX_MIDI_128_H +#ifndef MX_MIDI_128_H_INCLUDED +#define MX_MIDI_128_H_INCLUDED #include @@ -10,11 +10,11 @@ */ typedef long MxMIDI128; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse, then clamps into the declared range. */ bool mx_midi_128_try_parse(const char *s, MxMIDI128 *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0, then clamps into the declared range. */ MxMIDI128 mx_midi_128_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_midi_128_to_string(MxMIDI128 v); -#endif /* MX_MIDI_128_H */ +#endif /* MX_MIDI_128_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_midi_16.h b/gen/test/c/mx/mx_midi_16.h index 4303ab564..7d8f74c9f 100644 --- a/gen/test/c/mx/mx_midi_16.h +++ b/gen/test/c/mx/mx_midi_16.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_MIDI_16_H -#define MX_MIDI_16_H +#ifndef MX_MIDI_16_H_INCLUDED +#define MX_MIDI_16_H_INCLUDED #include @@ -10,11 +10,11 @@ */ typedef long MxMIDI16; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse, then clamps into the declared range. */ bool mx_midi_16_try_parse(const char *s, MxMIDI16 *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0, then clamps into the declared range. */ MxMIDI16 mx_midi_16_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_midi_16_to_string(MxMIDI16 v); -#endif /* MX_MIDI_16_H */ +#endif /* MX_MIDI_16_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_midi_16384.h b/gen/test/c/mx/mx_midi_16384.h index e10f8eb9c..42f100600 100644 --- a/gen/test/c/mx/mx_midi_16384.h +++ b/gen/test/c/mx/mx_midi_16384.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_MIDI_16384_H -#define MX_MIDI_16384_H +#ifndef MX_MIDI_16384_H_INCLUDED +#define MX_MIDI_16384_H_INCLUDED #include @@ -10,11 +10,11 @@ */ typedef long MxMIDI16384; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse, then clamps into the declared range. */ bool mx_midi_16384_try_parse(const char *s, MxMIDI16384 *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0, then clamps into the declared range. */ MxMIDI16384 mx_midi_16384_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_midi_16384_to_string(MxMIDI16384 v); -#endif /* MX_MIDI_16384_H */ +#endif /* MX_MIDI_16384_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_millimeters.h b/gen/test/c/mx/mx_millimeters.h index 900b4f3dc..1ec198550 100644 --- a/gen/test/c/mx/mx_millimeters.h +++ b/gen/test/c/mx/mx_millimeters.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_MILLIMETERS_H -#define MX_MILLIMETERS_H +#ifndef MX_MILLIMETERS_H_INCLUDED +#define MX_MILLIMETERS_H_INCLUDED #include @@ -11,11 +11,11 @@ */ typedef double MxMillimeters; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse. */ bool mx_millimeters_try_parse(const char *s, MxMillimeters *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0. */ MxMillimeters mx_millimeters_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_millimeters_to_string(MxMillimeters v); -#endif /* MX_MILLIMETERS_H */ +#endif /* MX_MILLIMETERS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_mode.h b/gen/test/c/mx/mx_mode.h index 4219892e0..f8bb31fcf 100644 --- a/gen/test/c/mx/mx_mode.h +++ b/gen/test/c/mx/mx_mode.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_MODE_H -#define MX_MODE_H +#ifndef MX_MODE_H_INCLUDED +#define MX_MODE_H_INCLUDED /* * The mode type is used to specify major/minor and other mode distinctions. Valid mode values @@ -12,4 +12,4 @@ typedef char *MxMode; /* Malloc'd copy of the wire string; the value IS its spelling. */ MxMode mx_mode_parse(const char *s); -#endif /* MX_MODE_H */ +#endif /* MX_MODE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_mute.c b/gen/test/c/mx/mx_mute.c index 64e653030..f42a18aec 100644 --- a/gen/test/c/mx/mx_mute.c +++ b/gen/test/c/mx/mx_mute.c @@ -23,6 +23,8 @@ static const char *const mx_mute_values[] = { }; bool mx_mute_try_parse(const char *s, MxMute *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_mute_values) / sizeof(mx_mute_values[0]); i++) { if (strcmp(s, mx_mute_values[i]) == 0) { *out = (MxMute)i; diff --git a/gen/test/c/mx/mx_mute.h b/gen/test/c/mx/mx_mute.h index f29a40e27..c13efa5c3 100644 --- a/gen/test/c/mx/mx_mute.h +++ b/gen/test/c/mx/mx_mute.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_MUTE_H -#define MX_MUTE_H +#ifndef MX_MUTE_H_INCLUDED +#define MX_MUTE_H_INCLUDED #include @@ -34,4 +34,4 @@ MxMute mx_mute_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_mute_to_string(MxMute v); -#endif /* MX_MUTE_H */ +#endif /* MX_MUTE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_non_negative_decimal.h b/gen/test/c/mx/mx_non_negative_decimal.h index 6b3b2fcd1..5e1fc4e98 100644 --- a/gen/test/c/mx/mx_non_negative_decimal.h +++ b/gen/test/c/mx/mx_non_negative_decimal.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_NON_NEGATIVE_DECIMAL_H -#define MX_NON_NEGATIVE_DECIMAL_H +#ifndef MX_NON_NEGATIVE_DECIMAL_H_INCLUDED +#define MX_NON_NEGATIVE_DECIMAL_H_INCLUDED #include @@ -10,11 +10,11 @@ */ typedef double MxNonNegativeDecimal; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse, then clamps into the declared range. */ bool mx_non_negative_decimal_try_parse(const char *s, MxNonNegativeDecimal *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0, then clamps into the declared range. */ MxNonNegativeDecimal mx_non_negative_decimal_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_non_negative_decimal_to_string(MxNonNegativeDecimal v); -#endif /* MX_NON_NEGATIVE_DECIMAL_H */ +#endif /* MX_NON_NEGATIVE_DECIMAL_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_note_size_type.c b/gen/test/c/mx/mx_note_size_type.c index 0965a6d8c..97adc2f3e 100644 --- a/gen/test/c/mx/mx_note_size_type.c +++ b/gen/test/c/mx/mx_note_size_type.c @@ -12,6 +12,8 @@ static const char *const mx_note_size_type_values[] = { }; bool mx_note_size_type_try_parse(const char *s, MxNoteSizeType *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_note_size_type_values) / sizeof(mx_note_size_type_values[0]); i++) { if (strcmp(s, mx_note_size_type_values[i]) == 0) { *out = (MxNoteSizeType)i; diff --git a/gen/test/c/mx/mx_note_size_type.h b/gen/test/c/mx/mx_note_size_type.h index 3101c9bef..ef1a0eb16 100644 --- a/gen/test/c/mx/mx_note_size_type.h +++ b/gen/test/c/mx/mx_note_size_type.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_NOTE_SIZE_TYPE_H -#define MX_NOTE_SIZE_TYPE_H +#ifndef MX_NOTE_SIZE_TYPE_H_INCLUDED +#define MX_NOTE_SIZE_TYPE_H_INCLUDED #include @@ -25,4 +25,4 @@ MxNoteSizeType mx_note_size_type_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_note_size_type_to_string(MxNoteSizeType v); -#endif /* MX_NOTE_SIZE_TYPE_H */ +#endif /* MX_NOTE_SIZE_TYPE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_note_type_value.c b/gen/test/c/mx/mx_note_type_value.c index 8f849a19b..1c3833b97 100644 --- a/gen/test/c/mx/mx_note_type_value.c +++ b/gen/test/c/mx/mx_note_type_value.c @@ -22,6 +22,8 @@ static const char *const mx_note_type_value_values[] = { }; bool mx_note_type_value_try_parse(const char *s, MxNoteTypeValue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_note_type_value_values) / sizeof(mx_note_type_value_values[0]); i++) { if (strcmp(s, mx_note_type_value_values[i]) == 0) { *out = (MxNoteTypeValue)i; diff --git a/gen/test/c/mx/mx_note_type_value.h b/gen/test/c/mx/mx_note_type_value.h index 48209fc1e..5f8803949 100644 --- a/gen/test/c/mx/mx_note_type_value.h +++ b/gen/test/c/mx/mx_note_type_value.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_NOTE_TYPE_VALUE_H -#define MX_NOTE_TYPE_VALUE_H +#ifndef MX_NOTE_TYPE_VALUE_H_INCLUDED +#define MX_NOTE_TYPE_VALUE_H_INCLUDED #include @@ -32,4 +32,4 @@ MxNoteTypeValue mx_note_type_value_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_note_type_value_to_string(MxNoteTypeValue v); -#endif /* MX_NOTE_TYPE_VALUE_H */ +#endif /* MX_NOTE_TYPE_VALUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_notehead_value.c b/gen/test/c/mx/mx_notehead_value.c index 96c811a6d..53a23c9be 100644 --- a/gen/test/c/mx/mx_notehead_value.c +++ b/gen/test/c/mx/mx_notehead_value.c @@ -36,6 +36,8 @@ static const char *const mx_notehead_value_values[] = { }; bool mx_notehead_value_try_parse(const char *s, MxNoteheadValue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_notehead_value_values) / sizeof(mx_notehead_value_values[0]); i++) { if (strcmp(s, mx_notehead_value_values[i]) == 0) { *out = (MxNoteheadValue)i; diff --git a/gen/test/c/mx/mx_notehead_value.h b/gen/test/c/mx/mx_notehead_value.h index 86b7ff28c..41dc7dc0d 100644 --- a/gen/test/c/mx/mx_notehead_value.h +++ b/gen/test/c/mx/mx_notehead_value.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_NOTEHEAD_VALUE_H -#define MX_NOTEHEAD_VALUE_H +#ifndef MX_NOTEHEAD_VALUE_H_INCLUDED +#define MX_NOTEHEAD_VALUE_H_INCLUDED #include @@ -57,4 +57,4 @@ MxNoteheadValue mx_notehead_value_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_notehead_value_to_string(MxNoteheadValue v); -#endif /* MX_NOTEHEAD_VALUE_H */ +#endif /* MX_NOTEHEAD_VALUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_number_level.h b/gen/test/c/mx/mx_number_level.h index 92fd5b1e5..b95330d80 100644 --- a/gen/test/c/mx/mx_number_level.h +++ b/gen/test/c/mx/mx_number_level.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_NUMBER_LEVEL_H -#define MX_NUMBER_LEVEL_H +#ifndef MX_NUMBER_LEVEL_H_INCLUDED +#define MX_NUMBER_LEVEL_H_INCLUDED #include @@ -14,11 +14,11 @@ */ typedef long MxNumberLevel; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse, then clamps into the declared range. */ bool mx_number_level_try_parse(const char *s, MxNumberLevel *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0, then clamps into the declared range. */ MxNumberLevel mx_number_level_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_number_level_to_string(MxNumberLevel v); -#endif /* MX_NUMBER_LEVEL_H */ +#endif /* MX_NUMBER_LEVEL_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_number_of_lines.h b/gen/test/c/mx/mx_number_of_lines.h index dca604a46..6c0c86228 100644 --- a/gen/test/c/mx/mx_number_of_lines.h +++ b/gen/test/c/mx/mx_number_of_lines.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_NUMBER_OF_LINES_H -#define MX_NUMBER_OF_LINES_H +#ifndef MX_NUMBER_OF_LINES_H_INCLUDED +#define MX_NUMBER_OF_LINES_H_INCLUDED #include @@ -10,11 +10,11 @@ */ typedef long MxNumberOfLines; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse, then clamps into the declared range. */ bool mx_number_of_lines_try_parse(const char *s, MxNumberOfLines *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0, then clamps into the declared range. */ MxNumberOfLines mx_number_of_lines_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_number_of_lines_to_string(MxNumberOfLines v); -#endif /* MX_NUMBER_OF_LINES_H */ +#endif /* MX_NUMBER_OF_LINES_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_number_or_normal.c b/gen/test/c/mx/mx_number_or_normal.c index 8e246e275..695329e8f 100644 --- a/gen/test/c/mx/mx_number_or_normal.c +++ b/gen/test/c/mx/mx_number_or_normal.c @@ -7,15 +7,17 @@ #include bool mx_number_or_normal_try_parse(const char *s, MxNumberOrNormal *out) { + if (!s) + s = ""; MxNumberOrNormal v; memset(&v, 0, sizeof(v)); if (mx_try_parse_decimal(s, &v.decimal)) { - v.kind = MX_NUMBER_OR_NORMAL_KIND_DECIMAL; + v.kind = MX_NUMBER_OR_NORMAL_DECIMAL; *out = v; return true; } if (strcmp(s, "normal") == 0) { - v.kind = MX_NUMBER_OR_NORMAL_KIND_NORMAL; + v.kind = MX_NUMBER_OR_NORMAL_NORMAL; *out = v; return true; } @@ -28,14 +30,14 @@ MxNumberOrNormal mx_number_or_normal_parse(const char *s) { if (mx_number_or_normal_try_parse(s, &v)) return v; memset(&v, 0, sizeof(v)); - v.kind = MX_NUMBER_OR_NORMAL_KIND_DECIMAL; + v.kind = MX_NUMBER_OR_NORMAL_DECIMAL; v.decimal = mx_parse_decimal(s); return v; } char *mx_number_or_normal_to_string(MxNumberOrNormal v) { switch (v.kind) { - case MX_NUMBER_OR_NORMAL_KIND_NORMAL: + case MX_NUMBER_OR_NORMAL_NORMAL: return mx_strdup("normal"); default: return mx_format_decimal(v.decimal); diff --git a/gen/test/c/mx/mx_number_or_normal.h b/gen/test/c/mx/mx_number_or_normal.h index b9fc6232d..54db51aa4 100644 --- a/gen/test/c/mx/mx_number_or_normal.h +++ b/gen/test/c/mx/mx_number_or_normal.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_NUMBER_OR_NORMAL_H -#define MX_NUMBER_OR_NORMAL_H +#ifndef MX_NUMBER_OR_NORMAL_H_INCLUDED +#define MX_NUMBER_OR_NORMAL_H_INCLUDED #include @@ -10,8 +10,8 @@ * by the line-height and letter-spacing attributes. */ typedef enum { - MX_NUMBER_OR_NORMAL_KIND_DECIMAL = 0, - MX_NUMBER_OR_NORMAL_KIND_NORMAL = 1 + MX_NUMBER_OR_NORMAL_DECIMAL = 0, + MX_NUMBER_OR_NORMAL_NORMAL = 1 } MxNumberOrNormalKind; typedef struct { @@ -27,4 +27,4 @@ MxNumberOrNormal mx_number_or_normal_parse(const char *s); char *mx_number_or_normal_to_string(MxNumberOrNormal v); void mx_number_or_normal_free(MxNumberOrNormal *v); -#endif /* MX_NUMBER_OR_NORMAL_H */ +#endif /* MX_NUMBER_OR_NORMAL_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_octave.h b/gen/test/c/mx/mx_octave.h index 4e4f74d92..2b2399132 100644 --- a/gen/test/c/mx/mx_octave.h +++ b/gen/test/c/mx/mx_octave.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_OCTAVE_H -#define MX_OCTAVE_H +#ifndef MX_OCTAVE_H_INCLUDED +#define MX_OCTAVE_H_INCLUDED #include @@ -10,11 +10,11 @@ */ typedef long MxOctave; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse, then clamps into the declared range. */ bool mx_octave_try_parse(const char *s, MxOctave *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0, then clamps into the declared range. */ MxOctave mx_octave_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_octave_to_string(MxOctave v); -#endif /* MX_OCTAVE_H */ +#endif /* MX_OCTAVE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_on_off.c b/gen/test/c/mx/mx_on_off.c index 52acc54c1..ee4fbd986 100644 --- a/gen/test/c/mx/mx_on_off.c +++ b/gen/test/c/mx/mx_on_off.c @@ -10,6 +10,8 @@ static const char *const mx_on_off_values[] = { }; bool mx_on_off_try_parse(const char *s, MxOnOff *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_on_off_values) / sizeof(mx_on_off_values[0]); i++) { if (strcmp(s, mx_on_off_values[i]) == 0) { *out = (MxOnOff)i; diff --git a/gen/test/c/mx/mx_on_off.h b/gen/test/c/mx/mx_on_off.h index 472d475e5..0c6a12da2 100644 --- a/gen/test/c/mx/mx_on_off.h +++ b/gen/test/c/mx/mx_on_off.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_ON_OFF_H -#define MX_ON_OFF_H +#ifndef MX_ON_OFF_H_INCLUDED +#define MX_ON_OFF_H_INCLUDED #include @@ -19,4 +19,4 @@ MxOnOff mx_on_off_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_on_off_to_string(MxOnOff v); -#endif /* MX_ON_OFF_H */ +#endif /* MX_ON_OFF_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_over_under.c b/gen/test/c/mx/mx_over_under.c index a7dab752f..44b62b8a9 100644 --- a/gen/test/c/mx/mx_over_under.c +++ b/gen/test/c/mx/mx_over_under.c @@ -10,6 +10,8 @@ static const char *const mx_over_under_values[] = { }; bool mx_over_under_try_parse(const char *s, MxOverUnder *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_over_under_values) / sizeof(mx_over_under_values[0]); i++) { if (strcmp(s, mx_over_under_values[i]) == 0) { *out = (MxOverUnder)i; diff --git a/gen/test/c/mx/mx_over_under.h b/gen/test/c/mx/mx_over_under.h index 8840aafff..8350dc6c3 100644 --- a/gen/test/c/mx/mx_over_under.h +++ b/gen/test/c/mx/mx_over_under.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_OVER_UNDER_H -#define MX_OVER_UNDER_H +#ifndef MX_OVER_UNDER_H_INCLUDED +#define MX_OVER_UNDER_H_INCLUDED #include @@ -20,4 +20,4 @@ MxOverUnder mx_over_under_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_over_under_to_string(MxOverUnder v); -#endif /* MX_OVER_UNDER_H */ +#endif /* MX_OVER_UNDER_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_pedal_type.c b/gen/test/c/mx/mx_pedal_type.c index 640f3e660..56ff05367 100644 --- a/gen/test/c/mx/mx_pedal_type.c +++ b/gen/test/c/mx/mx_pedal_type.c @@ -13,6 +13,8 @@ static const char *const mx_pedal_type_values[] = { }; bool mx_pedal_type_try_parse(const char *s, MxPedalType *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_pedal_type_values) / sizeof(mx_pedal_type_values[0]); i++) { if (strcmp(s, mx_pedal_type_values[i]) == 0) { *out = (MxPedalType)i; diff --git a/gen/test/c/mx/mx_pedal_type.h b/gen/test/c/mx/mx_pedal_type.h index 7c0cfc3d5..3ada89e00 100644 --- a/gen/test/c/mx/mx_pedal_type.h +++ b/gen/test/c/mx/mx_pedal_type.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_PEDAL_TYPE_H -#define MX_PEDAL_TYPE_H +#ifndef MX_PEDAL_TYPE_H_INCLUDED +#define MX_PEDAL_TYPE_H_INCLUDED #include @@ -26,4 +26,4 @@ MxPedalType mx_pedal_type_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_pedal_type_to_string(MxPedalType v); -#endif /* MX_PEDAL_TYPE_H */ +#endif /* MX_PEDAL_TYPE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_percent.h b/gen/test/c/mx/mx_percent.h index b2a681924..2a86c8edc 100644 --- a/gen/test/c/mx/mx_percent.h +++ b/gen/test/c/mx/mx_percent.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_PERCENT_H -#define MX_PERCENT_H +#ifndef MX_PERCENT_H_INCLUDED +#define MX_PERCENT_H_INCLUDED #include @@ -10,11 +10,11 @@ */ typedef double MxPercent; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse, then clamps into the declared range. */ bool mx_percent_try_parse(const char *s, MxPercent *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0, then clamps into the declared range. */ MxPercent mx_percent_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_percent_to_string(MxPercent v); -#endif /* MX_PERCENT_H */ +#endif /* MX_PERCENT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_pitched_value.c b/gen/test/c/mx/mx_pitched_value.c index 1d707b1fb..67afb7213 100644 --- a/gen/test/c/mx/mx_pitched_value.c +++ b/gen/test/c/mx/mx_pitched_value.c @@ -19,6 +19,8 @@ static const char *const mx_pitched_value_values[] = { }; bool mx_pitched_value_try_parse(const char *s, MxPitchedValue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_pitched_value_values) / sizeof(mx_pitched_value_values[0]); i++) { if (strcmp(s, mx_pitched_value_values[i]) == 0) { *out = (MxPitchedValue)i; diff --git a/gen/test/c/mx/mx_pitched_value.h b/gen/test/c/mx/mx_pitched_value.h index 1c973be4c..3f51ecdc8 100644 --- a/gen/test/c/mx/mx_pitched_value.h +++ b/gen/test/c/mx/mx_pitched_value.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_PITCHED_VALUE_H -#define MX_PITCHED_VALUE_H +#ifndef MX_PITCHED_VALUE_H_INCLUDED +#define MX_PITCHED_VALUE_H_INCLUDED #include @@ -29,4 +29,4 @@ MxPitchedValue mx_pitched_value_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_pitched_value_to_string(MxPitchedValue v); -#endif /* MX_PITCHED_VALUE_H */ +#endif /* MX_PITCHED_VALUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_positive_divisions.h b/gen/test/c/mx/mx_positive_divisions.h index 193f5ae9b..b2f39e735 100644 --- a/gen/test/c/mx/mx_positive_divisions.h +++ b/gen/test/c/mx/mx_positive_divisions.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_POSITIVE_DIVISIONS_H -#define MX_POSITIVE_DIVISIONS_H +#ifndef MX_POSITIVE_DIVISIONS_H_INCLUDED +#define MX_POSITIVE_DIVISIONS_H_INCLUDED #include @@ -10,11 +10,11 @@ */ typedef double MxPositiveDivisions; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse, then clamps into the declared range. */ bool mx_positive_divisions_try_parse(const char *s, MxPositiveDivisions *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0, then clamps into the declared range. */ MxPositiveDivisions mx_positive_divisions_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_positive_divisions_to_string(MxPositiveDivisions v); -#endif /* MX_POSITIVE_DIVISIONS_H */ +#endif /* MX_POSITIVE_DIVISIONS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_positive_integer_or_empty.c b/gen/test/c/mx/mx_positive_integer_or_empty.c index b4ad3a5d9..383cdae2e 100644 --- a/gen/test/c/mx/mx_positive_integer_or_empty.c +++ b/gen/test/c/mx/mx_positive_integer_or_empty.c @@ -7,15 +7,19 @@ #include bool mx_positive_integer_or_empty_try_parse(const char *s, MxPositiveIntegerOrEmpty *out) { + if (!s) + s = ""; MxPositiveIntegerOrEmpty v; memset(&v, 0, sizeof(v)); if (mx_try_parse_int(s, &v.positive_integer)) { - v.kind = MX_POSITIVE_INTEGER_OR_EMPTY_KIND_POSITIVE_INTEGER; + if (v.positive_integer < 1) + v.positive_integer = 1; + v.kind = MX_POSITIVE_INTEGER_OR_EMPTY_POSITIVE_INTEGER; *out = v; return true; } if (strcmp(s, "") == 0) { - v.kind = MX_POSITIVE_INTEGER_OR_EMPTY_KIND_EMPTY; + v.kind = MX_POSITIVE_INTEGER_OR_EMPTY_EMPTY; *out = v; return true; } @@ -28,14 +32,16 @@ MxPositiveIntegerOrEmpty mx_positive_integer_or_empty_parse(const char *s) { if (mx_positive_integer_or_empty_try_parse(s, &v)) return v; memset(&v, 0, sizeof(v)); - v.kind = MX_POSITIVE_INTEGER_OR_EMPTY_KIND_POSITIVE_INTEGER; + v.kind = MX_POSITIVE_INTEGER_OR_EMPTY_POSITIVE_INTEGER; v.positive_integer = mx_parse_int(s); + if (v.positive_integer < 1) + v.positive_integer = 1; return v; } char *mx_positive_integer_or_empty_to_string(MxPositiveIntegerOrEmpty v) { switch (v.kind) { - case MX_POSITIVE_INTEGER_OR_EMPTY_KIND_EMPTY: + case MX_POSITIVE_INTEGER_OR_EMPTY_EMPTY: return mx_strdup(""); default: return mx_format_int(v.positive_integer); diff --git a/gen/test/c/mx/mx_positive_integer_or_empty.h b/gen/test/c/mx/mx_positive_integer_or_empty.h index 5aa1d08a7..fa25d1537 100644 --- a/gen/test/c/mx/mx_positive_integer_or_empty.h +++ b/gen/test/c/mx/mx_positive_integer_or_empty.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_POSITIVE_INTEGER_OR_EMPTY_H -#define MX_POSITIVE_INTEGER_OR_EMPTY_H +#ifndef MX_POSITIVE_INTEGER_OR_EMPTY_H_INCLUDED +#define MX_POSITIVE_INTEGER_OR_EMPTY_H_INCLUDED #include @@ -9,8 +9,8 @@ * The positive-integer-or-empty values can be either a positive integer or an empty string. */ typedef enum { - MX_POSITIVE_INTEGER_OR_EMPTY_KIND_POSITIVE_INTEGER = 0, - MX_POSITIVE_INTEGER_OR_EMPTY_KIND_EMPTY = 1 + MX_POSITIVE_INTEGER_OR_EMPTY_POSITIVE_INTEGER = 0, + MX_POSITIVE_INTEGER_OR_EMPTY_EMPTY = 1 } MxPositiveIntegerOrEmptyKind; typedef struct { @@ -26,4 +26,4 @@ MxPositiveIntegerOrEmpty mx_positive_integer_or_empty_parse(const char *s); char *mx_positive_integer_or_empty_to_string(MxPositiveIntegerOrEmpty v); void mx_positive_integer_or_empty_free(MxPositiveIntegerOrEmpty *v); -#endif /* MX_POSITIVE_INTEGER_OR_EMPTY_H */ +#endif /* MX_POSITIVE_INTEGER_OR_EMPTY_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_principal_voice_symbol.c b/gen/test/c/mx/mx_principal_voice_symbol.c index 0a4d3780a..c9deaafb0 100644 --- a/gen/test/c/mx/mx_principal_voice_symbol.c +++ b/gen/test/c/mx/mx_principal_voice_symbol.c @@ -12,6 +12,8 @@ static const char *const mx_principal_voice_symbol_values[] = { }; bool mx_principal_voice_symbol_try_parse(const char *s, MxPrincipalVoiceSymbol *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_principal_voice_symbol_values) / sizeof(mx_principal_voice_symbol_values[0]); i++) { if (strcmp(s, mx_principal_voice_symbol_values[i]) == 0) { *out = (MxPrincipalVoiceSymbol)i; diff --git a/gen/test/c/mx/mx_principal_voice_symbol.h b/gen/test/c/mx/mx_principal_voice_symbol.h index 859ab977c..8c60139ce 100644 --- a/gen/test/c/mx/mx_principal_voice_symbol.h +++ b/gen/test/c/mx/mx_principal_voice_symbol.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_PRINCIPAL_VOICE_SYMBOL_H -#define MX_PRINCIPAL_VOICE_SYMBOL_H +#ifndef MX_PRINCIPAL_VOICE_SYMBOL_H_INCLUDED +#define MX_PRINCIPAL_VOICE_SYMBOL_H_INCLUDED #include @@ -24,4 +24,4 @@ MxPrincipalVoiceSymbol mx_principal_voice_symbol_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_principal_voice_symbol_to_string(MxPrincipalVoiceSymbol v); -#endif /* MX_PRINCIPAL_VOICE_SYMBOL_H */ +#endif /* MX_PRINCIPAL_VOICE_SYMBOL_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_right_left_middle.c b/gen/test/c/mx/mx_right_left_middle.c index 56794bfc7..9ef6aaa62 100644 --- a/gen/test/c/mx/mx_right_left_middle.c +++ b/gen/test/c/mx/mx_right_left_middle.c @@ -11,6 +11,8 @@ static const char *const mx_right_left_middle_values[] = { }; bool mx_right_left_middle_try_parse(const char *s, MxRightLeftMiddle *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_right_left_middle_values) / sizeof(mx_right_left_middle_values[0]); i++) { if (strcmp(s, mx_right_left_middle_values[i]) == 0) { *out = (MxRightLeftMiddle)i; diff --git a/gen/test/c/mx/mx_right_left_middle.h b/gen/test/c/mx/mx_right_left_middle.h index ef7ec6b3f..c0a6c8b2f 100644 --- a/gen/test/c/mx/mx_right_left_middle.h +++ b/gen/test/c/mx/mx_right_left_middle.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_RIGHT_LEFT_MIDDLE_H -#define MX_RIGHT_LEFT_MIDDLE_H +#ifndef MX_RIGHT_LEFT_MIDDLE_H_INCLUDED +#define MX_RIGHT_LEFT_MIDDLE_H_INCLUDED #include @@ -20,4 +20,4 @@ MxRightLeftMiddle mx_right_left_middle_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_right_left_middle_to_string(MxRightLeftMiddle v); -#endif /* MX_RIGHT_LEFT_MIDDLE_H */ +#endif /* MX_RIGHT_LEFT_MIDDLE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_rotation_degrees.h b/gen/test/c/mx/mx_rotation_degrees.h index 79c0ee3d8..ab49349b0 100644 --- a/gen/test/c/mx/mx_rotation_degrees.h +++ b/gen/test/c/mx/mx_rotation_degrees.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_ROTATION_DEGREES_H -#define MX_ROTATION_DEGREES_H +#ifndef MX_ROTATION_DEGREES_H_INCLUDED +#define MX_ROTATION_DEGREES_H_INCLUDED #include @@ -11,11 +11,11 @@ */ typedef double MxRotationDegrees; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse, then clamps into the declared range. */ bool mx_rotation_degrees_try_parse(const char *s, MxRotationDegrees *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0, then clamps into the declared range. */ MxRotationDegrees mx_rotation_degrees_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_rotation_degrees_to_string(MxRotationDegrees v); -#endif /* MX_ROTATION_DEGREES_H */ +#endif /* MX_ROTATION_DEGREES_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_runtime.c b/gen/test/c/mx/mx_runtime.c index ea3619b72..1bbc9fc36 100644 --- a/gen/test/c/mx/mx_runtime.c +++ b/gen/test/c/mx/mx_runtime.c @@ -2,6 +2,7 @@ #include "mx_runtime.h" +#include #include #include #include @@ -36,8 +37,9 @@ bool mx_try_parse_int(const char *s, long *out) { if (!s || !*s) return false; char *end = NULL; + errno = 0; long v = strtol(s, &end, 10); - if (end == s) + if (errno == ERANGE || end == s) return false; while (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r') end++; @@ -58,8 +60,11 @@ long mx_parse_int(const char *s) { } char *mx_format_decimal(double v) { - char buf[64]; - snprintf(buf, sizeof(buf), "%.9f", v); + /* Worst-case %.9f for a double: 309 integer digits + sign + dot + 9. */ + char buf[336]; + int n = snprintf(buf, sizeof(buf), "%.9f", v); + if (n < 0 || n >= (int)sizeof(buf)) + return mx_strdup("0"); /* unreachable for finite doubles; never emit garbage */ /* Trim trailing zeros, then a trailing dot; canonicalize "-0" to "0". */ size_t len = strlen(buf); while (len > 0 && buf[len - 1] == '0') @@ -84,6 +89,8 @@ char *mx_strdup(const char *s) { s = ""; size_t n = strlen(s) + 1; char *out = malloc(n); + if (!out) + abort(); /* the generator's runtime has no error channel for OOM */ memcpy(out, s, n); return out; } diff --git a/gen/test/c/mx/mx_runtime.h b/gen/test/c/mx/mx_runtime.h index efcd0107e..110093ef3 100644 --- a/gen/test/c/mx/mx_runtime.h +++ b/gen/test/c/mx/mx_runtime.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_RUNTIME_H -#define MX_RUNTIME_H +#ifndef MX_RUNTIME_H_INCLUDED +#define MX_RUNTIME_H_INCLUDED #include @@ -23,4 +23,4 @@ char *mx_format_int(long v); /* strdup that maps NULL to "". Malloc'd; caller frees. */ char *mx_strdup(const char *s); -#endif /* MX_RUNTIME_H */ +#endif /* MX_RUNTIME_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_semi_pitched.c b/gen/test/c/mx/mx_semi_pitched.c index 91f61720e..eabcb2f6d 100644 --- a/gen/test/c/mx/mx_semi_pitched.c +++ b/gen/test/c/mx/mx_semi_pitched.c @@ -14,6 +14,8 @@ static const char *const mx_semi_pitched_values[] = { }; bool mx_semi_pitched_try_parse(const char *s, MxSemiPitched *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_semi_pitched_values) / sizeof(mx_semi_pitched_values[0]); i++) { if (strcmp(s, mx_semi_pitched_values[i]) == 0) { *out = (MxSemiPitched)i; diff --git a/gen/test/c/mx/mx_semi_pitched.h b/gen/test/c/mx/mx_semi_pitched.h index ce49b711e..2b25962c1 100644 --- a/gen/test/c/mx/mx_semi_pitched.h +++ b/gen/test/c/mx/mx_semi_pitched.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_SEMI_PITCHED_H -#define MX_SEMI_PITCHED_H +#ifndef MX_SEMI_PITCHED_H_INCLUDED +#define MX_SEMI_PITCHED_H_INCLUDED #include @@ -23,4 +23,4 @@ MxSemiPitched mx_semi_pitched_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_semi_pitched_to_string(MxSemiPitched v); -#endif /* MX_SEMI_PITCHED_H */ +#endif /* MX_SEMI_PITCHED_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_semitones.h b/gen/test/c/mx/mx_semitones.h index 4e184fd1a..55380ec74 100644 --- a/gen/test/c/mx/mx_semitones.h +++ b/gen/test/c/mx/mx_semitones.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_SEMITONES_H -#define MX_SEMITONES_H +#ifndef MX_SEMITONES_H_INCLUDED +#define MX_SEMITONES_H_INCLUDED #include @@ -12,11 +12,11 @@ */ typedef double MxSemitones; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse. */ bool mx_semitones_try_parse(const char *s, MxSemitones *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0. */ MxSemitones mx_semitones_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_semitones_to_string(MxSemitones v); -#endif /* MX_SEMITONES_H */ +#endif /* MX_SEMITONES_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_show_frets.c b/gen/test/c/mx/mx_show_frets.c index 706d9b0f6..28446d001 100644 --- a/gen/test/c/mx/mx_show_frets.c +++ b/gen/test/c/mx/mx_show_frets.c @@ -10,6 +10,8 @@ static const char *const mx_show_frets_values[] = { }; bool mx_show_frets_try_parse(const char *s, MxShowFrets *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_show_frets_values) / sizeof(mx_show_frets_values[0]); i++) { if (strcmp(s, mx_show_frets_values[i]) == 0) { *out = (MxShowFrets)i; diff --git a/gen/test/c/mx/mx_show_frets.h b/gen/test/c/mx/mx_show_frets.h index ddd278be2..3ee6416f1 100644 --- a/gen/test/c/mx/mx_show_frets.h +++ b/gen/test/c/mx/mx_show_frets.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_SHOW_FRETS_H -#define MX_SHOW_FRETS_H +#ifndef MX_SHOW_FRETS_H_INCLUDED +#define MX_SHOW_FRETS_H_INCLUDED #include @@ -20,4 +20,4 @@ MxShowFrets mx_show_frets_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_show_frets_to_string(MxShowFrets v); -#endif /* MX_SHOW_FRETS_H */ +#endif /* MX_SHOW_FRETS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_show_tuplet.c b/gen/test/c/mx/mx_show_tuplet.c index 0d6fc307f..8303e9f6c 100644 --- a/gen/test/c/mx/mx_show_tuplet.c +++ b/gen/test/c/mx/mx_show_tuplet.c @@ -11,6 +11,8 @@ static const char *const mx_show_tuplet_values[] = { }; bool mx_show_tuplet_try_parse(const char *s, MxShowTuplet *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_show_tuplet_values) / sizeof(mx_show_tuplet_values[0]); i++) { if (strcmp(s, mx_show_tuplet_values[i]) == 0) { *out = (MxShowTuplet)i; diff --git a/gen/test/c/mx/mx_show_tuplet.h b/gen/test/c/mx/mx_show_tuplet.h index 2a6b5fe6d..e9a83b9c7 100644 --- a/gen/test/c/mx/mx_show_tuplet.h +++ b/gen/test/c/mx/mx_show_tuplet.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_SHOW_TUPLET_H -#define MX_SHOW_TUPLET_H +#ifndef MX_SHOW_TUPLET_H_INCLUDED +#define MX_SHOW_TUPLET_H_INCLUDED #include @@ -21,4 +21,4 @@ MxShowTuplet mx_show_tuplet_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_show_tuplet_to_string(MxShowTuplet v); -#endif /* MX_SHOW_TUPLET_H */ +#endif /* MX_SHOW_TUPLET_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_smufl_accidental_glyph_name.h b/gen/test/c/mx/mx_smufl_accidental_glyph_name.h index ca685e911..0e8b61c27 100644 --- a/gen/test/c/mx/mx_smufl_accidental_glyph_name.h +++ b/gen/test/c/mx/mx_smufl_accidental_glyph_name.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_SMUFL_ACCIDENTAL_GLYPH_NAME_H -#define MX_SMUFL_ACCIDENTAL_GLYPH_NAME_H +#ifndef MX_SMUFL_ACCIDENTAL_GLYPH_NAME_H_INCLUDED +#define MX_SMUFL_ACCIDENTAL_GLYPH_NAME_H_INCLUDED /* * The smufl-accidental-glyph-name type is used to reference a specific Standard Music Font Layout @@ -13,4 +13,4 @@ typedef char *MxSMUFLAccidentalGlyphName; /* Malloc'd copy of the wire string; the value IS its spelling. */ MxSMUFLAccidentalGlyphName mx_smufl_accidental_glyph_name_parse(const char *s); -#endif /* MX_SMUFL_ACCIDENTAL_GLYPH_NAME_H */ +#endif /* MX_SMUFL_ACCIDENTAL_GLYPH_NAME_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_smufl_coda_glyph_name.h b/gen/test/c/mx/mx_smufl_coda_glyph_name.h index 5c5317b57..e05d4bc62 100644 --- a/gen/test/c/mx/mx_smufl_coda_glyph_name.h +++ b/gen/test/c/mx/mx_smufl_coda_glyph_name.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_SMUFL_CODA_GLYPH_NAME_H -#define MX_SMUFL_CODA_GLYPH_NAME_H +#ifndef MX_SMUFL_CODA_GLYPH_NAME_H_INCLUDED +#define MX_SMUFL_CODA_GLYPH_NAME_H_INCLUDED /* * The smufl-coda-glyph-name type is used to reference a specific Standard Music Font Layout (SMuFL) @@ -13,4 +13,4 @@ typedef char *MxSMUFLCodaGlyphName; /* Malloc'd copy of the wire string; the value IS its spelling. */ MxSMUFLCodaGlyphName mx_smufl_coda_glyph_name_parse(const char *s); -#endif /* MX_SMUFL_CODA_GLYPH_NAME_H */ +#endif /* MX_SMUFL_CODA_GLYPH_NAME_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_smufl_glyph_name.h b/gen/test/c/mx/mx_smufl_glyph_name.h index 5f35258bf..7fb87d5e9 100644 --- a/gen/test/c/mx/mx_smufl_glyph_name.h +++ b/gen/test/c/mx/mx_smufl_glyph_name.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_SMUFL_GLYPH_NAME_H -#define MX_SMUFL_GLYPH_NAME_H +#ifndef MX_SMUFL_GLYPH_NAME_H_INCLUDED +#define MX_SMUFL_GLYPH_NAME_H_INCLUDED /* * The smufl-glyph-name type is used for attributes that reference a specific Standard Music Font @@ -13,4 +13,4 @@ typedef char *MxSMUFLGlyphName; /* Malloc'd copy of the wire string; the value IS its spelling. */ MxSMUFLGlyphName mx_smufl_glyph_name_parse(const char *s); -#endif /* MX_SMUFL_GLYPH_NAME_H */ +#endif /* MX_SMUFL_GLYPH_NAME_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_smufl_lyrics_glyph_name.h b/gen/test/c/mx/mx_smufl_lyrics_glyph_name.h index 19af70f30..21f9a712e 100644 --- a/gen/test/c/mx/mx_smufl_lyrics_glyph_name.h +++ b/gen/test/c/mx/mx_smufl_lyrics_glyph_name.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_SMUFL_LYRICS_GLYPH_NAME_H -#define MX_SMUFL_LYRICS_GLYPH_NAME_H +#ifndef MX_SMUFL_LYRICS_GLYPH_NAME_H_INCLUDED +#define MX_SMUFL_LYRICS_GLYPH_NAME_H_INCLUDED /* * The smufl-lyrics-glyph-name type is used to reference a specific Standard Music Font Layout @@ -13,4 +13,4 @@ typedef char *MxSMUFLLyricsGlyphName; /* Malloc'd copy of the wire string; the value IS its spelling. */ MxSMUFLLyricsGlyphName mx_smufl_lyrics_glyph_name_parse(const char *s); -#endif /* MX_SMUFL_LYRICS_GLYPH_NAME_H */ +#endif /* MX_SMUFL_LYRICS_GLYPH_NAME_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_smufl_pictogram_glyph_name.h b/gen/test/c/mx/mx_smufl_pictogram_glyph_name.h index 431b07708..97f4d6abd 100644 --- a/gen/test/c/mx/mx_smufl_pictogram_glyph_name.h +++ b/gen/test/c/mx/mx_smufl_pictogram_glyph_name.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_SMUFL_PICTOGRAM_GLYPH_NAME_H -#define MX_SMUFL_PICTOGRAM_GLYPH_NAME_H +#ifndef MX_SMUFL_PICTOGRAM_GLYPH_NAME_H_INCLUDED +#define MX_SMUFL_PICTOGRAM_GLYPH_NAME_H_INCLUDED /* * The smufl-pictogram-glyph-name type is used to reference a specific Standard Music Font Layout @@ -13,4 +13,4 @@ typedef char *MxSMUFLPictogramGlyphName; /* Malloc'd copy of the wire string; the value IS its spelling. */ MxSMUFLPictogramGlyphName mx_smufl_pictogram_glyph_name_parse(const char *s); -#endif /* MX_SMUFL_PICTOGRAM_GLYPH_NAME_H */ +#endif /* MX_SMUFL_PICTOGRAM_GLYPH_NAME_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_smufl_segno_glyph_name.h b/gen/test/c/mx/mx_smufl_segno_glyph_name.h index bfaf2cb18..9ba2d726d 100644 --- a/gen/test/c/mx/mx_smufl_segno_glyph_name.h +++ b/gen/test/c/mx/mx_smufl_segno_glyph_name.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_SMUFL_SEGNO_GLYPH_NAME_H -#define MX_SMUFL_SEGNO_GLYPH_NAME_H +#ifndef MX_SMUFL_SEGNO_GLYPH_NAME_H_INCLUDED +#define MX_SMUFL_SEGNO_GLYPH_NAME_H_INCLUDED /* * The smufl-segno-glyph-name type is used to reference a specific Standard Music Font Layout @@ -13,4 +13,4 @@ typedef char *MxSMUFLSegnoGlyphName; /* Malloc'd copy of the wire string; the value IS its spelling. */ MxSMUFLSegnoGlyphName mx_smufl_segno_glyph_name_parse(const char *s); -#endif /* MX_SMUFL_SEGNO_GLYPH_NAME_H */ +#endif /* MX_SMUFL_SEGNO_GLYPH_NAME_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_sound_id.c b/gen/test/c/mx/mx_sound_id.c index 066daea4a..a4369bbb6 100644 --- a/gen/test/c/mx/mx_sound_id.c +++ b/gen/test/c/mx/mx_sound_id.c @@ -894,6 +894,8 @@ static const char *const mx_sound_id_values[] = { }; bool mx_sound_id_try_parse(const char *s, MxSoundID *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_sound_id_values) / sizeof(mx_sound_id_values[0]); i++) { if (strcmp(s, mx_sound_id_values[i]) == 0) { *out = (MxSoundID)i; diff --git a/gen/test/c/mx/mx_sound_id.h b/gen/test/c/mx/mx_sound_id.h index 913f84fa8..36ab391dc 100644 --- a/gen/test/c/mx/mx_sound_id.h +++ b/gen/test/c/mx/mx_sound_id.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_SOUND_ID_H -#define MX_SOUND_ID_H +#ifndef MX_SOUND_ID_H_INCLUDED +#define MX_SOUND_ID_H_INCLUDED #include @@ -904,4 +904,4 @@ MxSoundID mx_sound_id_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_sound_id_to_string(MxSoundID v); -#endif /* MX_SOUND_ID_H */ +#endif /* MX_SOUND_ID_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_staff_divide_symbol.c b/gen/test/c/mx/mx_staff_divide_symbol.c index 8659458af..6debe849d 100644 --- a/gen/test/c/mx/mx_staff_divide_symbol.c +++ b/gen/test/c/mx/mx_staff_divide_symbol.c @@ -11,6 +11,8 @@ static const char *const mx_staff_divide_symbol_values[] = { }; bool mx_staff_divide_symbol_try_parse(const char *s, MxStaffDivideSymbol *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_staff_divide_symbol_values) / sizeof(mx_staff_divide_symbol_values[0]); i++) { if (strcmp(s, mx_staff_divide_symbol_values[i]) == 0) { *out = (MxStaffDivideSymbol)i; diff --git a/gen/test/c/mx/mx_staff_divide_symbol.h b/gen/test/c/mx/mx_staff_divide_symbol.h index f564b6ef8..c9cb90741 100644 --- a/gen/test/c/mx/mx_staff_divide_symbol.h +++ b/gen/test/c/mx/mx_staff_divide_symbol.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_STAFF_DIVIDE_SYMBOL_H -#define MX_STAFF_DIVIDE_SYMBOL_H +#ifndef MX_STAFF_DIVIDE_SYMBOL_H_INCLUDED +#define MX_STAFF_DIVIDE_SYMBOL_H_INCLUDED #include @@ -21,4 +21,4 @@ MxStaffDivideSymbol mx_staff_divide_symbol_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_staff_divide_symbol_to_string(MxStaffDivideSymbol v); -#endif /* MX_STAFF_DIVIDE_SYMBOL_H */ +#endif /* MX_STAFF_DIVIDE_SYMBOL_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_staff_line.h b/gen/test/c/mx/mx_staff_line.h index ca3fe4f02..0c4d16a35 100644 --- a/gen/test/c/mx/mx_staff_line.h +++ b/gen/test/c/mx/mx_staff_line.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_STAFF_LINE_H -#define MX_STAFF_LINE_H +#ifndef MX_STAFF_LINE_H_INCLUDED +#define MX_STAFF_LINE_H_INCLUDED #include @@ -12,11 +12,11 @@ */ typedef long MxStaffLine; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse. */ bool mx_staff_line_try_parse(const char *s, MxStaffLine *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0. */ MxStaffLine mx_staff_line_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_staff_line_to_string(MxStaffLine v); -#endif /* MX_STAFF_LINE_H */ +#endif /* MX_STAFF_LINE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_staff_number.h b/gen/test/c/mx/mx_staff_number.h index 4e03e0b08..62efb7faa 100644 --- a/gen/test/c/mx/mx_staff_number.h +++ b/gen/test/c/mx/mx_staff_number.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_STAFF_NUMBER_H -#define MX_STAFF_NUMBER_H +#ifndef MX_STAFF_NUMBER_H_INCLUDED +#define MX_STAFF_NUMBER_H_INCLUDED #include @@ -11,11 +11,11 @@ */ typedef long MxStaffNumber; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse, then clamps into the declared range. */ bool mx_staff_number_try_parse(const char *s, MxStaffNumber *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0, then clamps into the declared range. */ MxStaffNumber mx_staff_number_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_staff_number_to_string(MxStaffNumber v); -#endif /* MX_STAFF_NUMBER_H */ +#endif /* MX_STAFF_NUMBER_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_staff_type.c b/gen/test/c/mx/mx_staff_type.c index 7f1d8a724..53851969d 100644 --- a/gen/test/c/mx/mx_staff_type.c +++ b/gen/test/c/mx/mx_staff_type.c @@ -13,6 +13,8 @@ static const char *const mx_staff_type_values[] = { }; bool mx_staff_type_try_parse(const char *s, MxStaffType *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_staff_type_values) / sizeof(mx_staff_type_values[0]); i++) { if (strcmp(s, mx_staff_type_values[i]) == 0) { *out = (MxStaffType)i; diff --git a/gen/test/c/mx/mx_staff_type.h b/gen/test/c/mx/mx_staff_type.h index cf786150f..bb945a058 100644 --- a/gen/test/c/mx/mx_staff_type.h +++ b/gen/test/c/mx/mx_staff_type.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_STAFF_TYPE_H -#define MX_STAFF_TYPE_H +#ifndef MX_STAFF_TYPE_H_INCLUDED +#define MX_STAFF_TYPE_H_INCLUDED #include @@ -24,4 +24,4 @@ MxStaffType mx_staff_type_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_staff_type_to_string(MxStaffType v); -#endif /* MX_STAFF_TYPE_H */ +#endif /* MX_STAFF_TYPE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_start_note.c b/gen/test/c/mx/mx_start_note.c index 4c1dab4bd..8e5f1cd7d 100644 --- a/gen/test/c/mx/mx_start_note.c +++ b/gen/test/c/mx/mx_start_note.c @@ -11,6 +11,8 @@ static const char *const mx_start_note_values[] = { }; bool mx_start_note_try_parse(const char *s, MxStartNote *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_start_note_values) / sizeof(mx_start_note_values[0]); i++) { if (strcmp(s, mx_start_note_values[i]) == 0) { *out = (MxStartNote)i; diff --git a/gen/test/c/mx/mx_start_note.h b/gen/test/c/mx/mx_start_note.h index 419d0b2d1..bb42194ce 100644 --- a/gen/test/c/mx/mx_start_note.h +++ b/gen/test/c/mx/mx_start_note.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_START_NOTE_H -#define MX_START_NOTE_H +#ifndef MX_START_NOTE_H_INCLUDED +#define MX_START_NOTE_H_INCLUDED #include @@ -21,4 +21,4 @@ MxStartNote mx_start_note_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_start_note_to_string(MxStartNote v); -#endif /* MX_START_NOTE_H */ +#endif /* MX_START_NOTE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_start_stop.c b/gen/test/c/mx/mx_start_stop.c index 782c1fadf..d53b055b8 100644 --- a/gen/test/c/mx/mx_start_stop.c +++ b/gen/test/c/mx/mx_start_stop.c @@ -10,6 +10,8 @@ static const char *const mx_start_stop_values[] = { }; bool mx_start_stop_try_parse(const char *s, MxStartStop *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_start_stop_values) / sizeof(mx_start_stop_values[0]); i++) { if (strcmp(s, mx_start_stop_values[i]) == 0) { *out = (MxStartStop)i; diff --git a/gen/test/c/mx/mx_start_stop.h b/gen/test/c/mx/mx_start_stop.h index 9d2b3adcf..9af49477b 100644 --- a/gen/test/c/mx/mx_start_stop.h +++ b/gen/test/c/mx/mx_start_stop.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_START_STOP_H -#define MX_START_STOP_H +#ifndef MX_START_STOP_H_INCLUDED +#define MX_START_STOP_H_INCLUDED #include @@ -24,4 +24,4 @@ MxStartStop mx_start_stop_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_start_stop_to_string(MxStartStop v); -#endif /* MX_START_STOP_H */ +#endif /* MX_START_STOP_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_start_stop_continue.c b/gen/test/c/mx/mx_start_stop_continue.c index d5805df54..4522443b5 100644 --- a/gen/test/c/mx/mx_start_stop_continue.c +++ b/gen/test/c/mx/mx_start_stop_continue.c @@ -11,6 +11,8 @@ static const char *const mx_start_stop_continue_values[] = { }; bool mx_start_stop_continue_try_parse(const char *s, MxStartStopContinue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_start_stop_continue_values) / sizeof(mx_start_stop_continue_values[0]); i++) { if (strcmp(s, mx_start_stop_continue_values[i]) == 0) { *out = (MxStartStopContinue)i; diff --git a/gen/test/c/mx/mx_start_stop_continue.h b/gen/test/c/mx/mx_start_stop_continue.h index 31da17d12..dda3fdccd 100644 --- a/gen/test/c/mx/mx_start_stop_continue.h +++ b/gen/test/c/mx/mx_start_stop_continue.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_START_STOP_CONTINUE_H -#define MX_START_STOP_CONTINUE_H +#ifndef MX_START_STOP_CONTINUE_H_INCLUDED +#define MX_START_STOP_CONTINUE_H_INCLUDED #include @@ -27,4 +27,4 @@ MxStartStopContinue mx_start_stop_continue_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_start_stop_continue_to_string(MxStartStopContinue v); -#endif /* MX_START_STOP_CONTINUE_H */ +#endif /* MX_START_STOP_CONTINUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_start_stop_discontinue.c b/gen/test/c/mx/mx_start_stop_discontinue.c index 3d1fc98ad..6cde5f5bd 100644 --- a/gen/test/c/mx/mx_start_stop_discontinue.c +++ b/gen/test/c/mx/mx_start_stop_discontinue.c @@ -11,6 +11,8 @@ static const char *const mx_start_stop_discontinue_values[] = { }; bool mx_start_stop_discontinue_try_parse(const char *s, MxStartStopDiscontinue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_start_stop_discontinue_values) / sizeof(mx_start_stop_discontinue_values[0]); i++) { if (strcmp(s, mx_start_stop_discontinue_values[i]) == 0) { *out = (MxStartStopDiscontinue)i; diff --git a/gen/test/c/mx/mx_start_stop_discontinue.h b/gen/test/c/mx/mx_start_stop_discontinue.h index 903975384..fa71c7e76 100644 --- a/gen/test/c/mx/mx_start_stop_discontinue.h +++ b/gen/test/c/mx/mx_start_stop_discontinue.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_START_STOP_DISCONTINUE_H -#define MX_START_STOP_DISCONTINUE_H +#ifndef MX_START_STOP_DISCONTINUE_H_INCLUDED +#define MX_START_STOP_DISCONTINUE_H_INCLUDED #include @@ -25,4 +25,4 @@ MxStartStopDiscontinue mx_start_stop_discontinue_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_start_stop_discontinue_to_string(MxStartStopDiscontinue v); -#endif /* MX_START_STOP_DISCONTINUE_H */ +#endif /* MX_START_STOP_DISCONTINUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_start_stop_single.c b/gen/test/c/mx/mx_start_stop_single.c index b3e9394b5..edda1ea4b 100644 --- a/gen/test/c/mx/mx_start_stop_single.c +++ b/gen/test/c/mx/mx_start_stop_single.c @@ -11,6 +11,8 @@ static const char *const mx_start_stop_single_values[] = { }; bool mx_start_stop_single_try_parse(const char *s, MxStartStopSingle *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_start_stop_single_values) / sizeof(mx_start_stop_single_values[0]); i++) { if (strcmp(s, mx_start_stop_single_values[i]) == 0) { *out = (MxStartStopSingle)i; diff --git a/gen/test/c/mx/mx_start_stop_single.h b/gen/test/c/mx/mx_start_stop_single.h index 38d50a6a5..85c160fe6 100644 --- a/gen/test/c/mx/mx_start_stop_single.h +++ b/gen/test/c/mx/mx_start_stop_single.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_START_STOP_SINGLE_H -#define MX_START_STOP_SINGLE_H +#ifndef MX_START_STOP_SINGLE_H_INCLUDED +#define MX_START_STOP_SINGLE_H_INCLUDED #include @@ -21,4 +21,4 @@ MxStartStopSingle mx_start_stop_single_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_start_stop_single_to_string(MxStartStopSingle v); -#endif /* MX_START_STOP_SINGLE_H */ +#endif /* MX_START_STOP_SINGLE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_stem_value.c b/gen/test/c/mx/mx_stem_value.c index 9306f6cec..f024c0f6d 100644 --- a/gen/test/c/mx/mx_stem_value.c +++ b/gen/test/c/mx/mx_stem_value.c @@ -12,6 +12,8 @@ static const char *const mx_stem_value_values[] = { }; bool mx_stem_value_try_parse(const char *s, MxStemValue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_stem_value_values) / sizeof(mx_stem_value_values[0]); i++) { if (strcmp(s, mx_stem_value_values[i]) == 0) { *out = (MxStemValue)i; diff --git a/gen/test/c/mx/mx_stem_value.h b/gen/test/c/mx/mx_stem_value.h index a6c6e3aba..698075d88 100644 --- a/gen/test/c/mx/mx_stem_value.h +++ b/gen/test/c/mx/mx_stem_value.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_STEM_VALUE_H -#define MX_STEM_VALUE_H +#ifndef MX_STEM_VALUE_H_INCLUDED +#define MX_STEM_VALUE_H_INCLUDED #include @@ -21,4 +21,4 @@ MxStemValue mx_stem_value_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_stem_value_to_string(MxStemValue v); -#endif /* MX_STEM_VALUE_H */ +#endif /* MX_STEM_VALUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_step.c b/gen/test/c/mx/mx_step.c index 620c9fc0d..466ff83a6 100644 --- a/gen/test/c/mx/mx_step.c +++ b/gen/test/c/mx/mx_step.c @@ -15,6 +15,8 @@ static const char *const mx_step_values[] = { }; bool mx_step_try_parse(const char *s, MxStep *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_step_values) / sizeof(mx_step_values[0]); i++) { if (strcmp(s, mx_step_values[i]) == 0) { *out = (MxStep)i; diff --git a/gen/test/c/mx/mx_step.h b/gen/test/c/mx/mx_step.h index fe0b5f330..cb6807f41 100644 --- a/gen/test/c/mx/mx_step.h +++ b/gen/test/c/mx/mx_step.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_STEP_H -#define MX_STEP_H +#ifndef MX_STEP_H_INCLUDED +#define MX_STEP_H_INCLUDED #include @@ -25,4 +25,4 @@ MxStep mx_step_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_step_to_string(MxStep v); -#endif /* MX_STEP_H */ +#endif /* MX_STEP_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_stick_location.c b/gen/test/c/mx/mx_stick_location.c index ab7571ad1..0c3b72832 100644 --- a/gen/test/c/mx/mx_stick_location.c +++ b/gen/test/c/mx/mx_stick_location.c @@ -12,6 +12,8 @@ static const char *const mx_stick_location_values[] = { }; bool mx_stick_location_try_parse(const char *s, MxStickLocation *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_stick_location_values) / sizeof(mx_stick_location_values[0]); i++) { if (strcmp(s, mx_stick_location_values[i]) == 0) { *out = (MxStickLocation)i; diff --git a/gen/test/c/mx/mx_stick_location.h b/gen/test/c/mx/mx_stick_location.h index 0a50fee94..dc1c75845 100644 --- a/gen/test/c/mx/mx_stick_location.h +++ b/gen/test/c/mx/mx_stick_location.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_STICK_LOCATION_H -#define MX_STICK_LOCATION_H +#ifndef MX_STICK_LOCATION_H_INCLUDED +#define MX_STICK_LOCATION_H_INCLUDED #include @@ -22,4 +22,4 @@ MxStickLocation mx_stick_location_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_stick_location_to_string(MxStickLocation v); -#endif /* MX_STICK_LOCATION_H */ +#endif /* MX_STICK_LOCATION_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_stick_material.c b/gen/test/c/mx/mx_stick_material.c index 421eea8e8..a24ba41c1 100644 --- a/gen/test/c/mx/mx_stick_material.c +++ b/gen/test/c/mx/mx_stick_material.c @@ -13,6 +13,8 @@ static const char *const mx_stick_material_values[] = { }; bool mx_stick_material_try_parse(const char *s, MxStickMaterial *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_stick_material_values) / sizeof(mx_stick_material_values[0]); i++) { if (strcmp(s, mx_stick_material_values[i]) == 0) { *out = (MxStickMaterial)i; diff --git a/gen/test/c/mx/mx_stick_material.h b/gen/test/c/mx/mx_stick_material.h index 1606559ff..2c71f91a8 100644 --- a/gen/test/c/mx/mx_stick_material.h +++ b/gen/test/c/mx/mx_stick_material.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_STICK_MATERIAL_H -#define MX_STICK_MATERIAL_H +#ifndef MX_STICK_MATERIAL_H_INCLUDED +#define MX_STICK_MATERIAL_H_INCLUDED #include @@ -22,4 +22,4 @@ MxStickMaterial mx_stick_material_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_stick_material_to_string(MxStickMaterial v); -#endif /* MX_STICK_MATERIAL_H */ +#endif /* MX_STICK_MATERIAL_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_stick_type.c b/gen/test/c/mx/mx_stick_type.c index 642fd54b0..f74d5485e 100644 --- a/gen/test/c/mx/mx_stick_type.c +++ b/gen/test/c/mx/mx_stick_type.c @@ -18,6 +18,8 @@ static const char *const mx_stick_type_values[] = { }; bool mx_stick_type_try_parse(const char *s, MxStickType *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_stick_type_values) / sizeof(mx_stick_type_values[0]); i++) { if (strcmp(s, mx_stick_type_values[i]) == 0) { *out = (MxStickType)i; diff --git a/gen/test/c/mx/mx_stick_type.h b/gen/test/c/mx/mx_stick_type.h index d5a311099..fcddcde19 100644 --- a/gen/test/c/mx/mx_stick_type.h +++ b/gen/test/c/mx/mx_stick_type.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_STICK_TYPE_H -#define MX_STICK_TYPE_H +#ifndef MX_STICK_TYPE_H_INCLUDED +#define MX_STICK_TYPE_H_INCLUDED #include @@ -28,4 +28,4 @@ MxStickType mx_stick_type_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_stick_type_to_string(MxStickType v); -#endif /* MX_STICK_TYPE_H */ +#endif /* MX_STICK_TYPE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_string_number.h b/gen/test/c/mx/mx_string_number.h index b524ca4e2..a94640b49 100644 --- a/gen/test/c/mx/mx_string_number.h +++ b/gen/test/c/mx/mx_string_number.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_STRING_NUMBER_H -#define MX_STRING_NUMBER_H +#ifndef MX_STRING_NUMBER_H_INCLUDED +#define MX_STRING_NUMBER_H_INCLUDED #include @@ -11,11 +11,11 @@ */ typedef long MxStringNumber; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse, then clamps into the declared range. */ bool mx_string_number_try_parse(const char *s, MxStringNumber *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0, then clamps into the declared range. */ MxStringNumber mx_string_number_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_string_number_to_string(MxStringNumber v); -#endif /* MX_STRING_NUMBER_H */ +#endif /* MX_STRING_NUMBER_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_syllabic.c b/gen/test/c/mx/mx_syllabic.c index 9363c44c8..b99e2d160 100644 --- a/gen/test/c/mx/mx_syllabic.c +++ b/gen/test/c/mx/mx_syllabic.c @@ -12,6 +12,8 @@ static const char *const mx_syllabic_values[] = { }; bool mx_syllabic_try_parse(const char *s, MxSyllabic *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_syllabic_values) / sizeof(mx_syllabic_values[0]); i++) { if (strcmp(s, mx_syllabic_values[i]) == 0) { *out = (MxSyllabic)i; diff --git a/gen/test/c/mx/mx_syllabic.h b/gen/test/c/mx/mx_syllabic.h index 65d74e5c8..317c48ce2 100644 --- a/gen/test/c/mx/mx_syllabic.h +++ b/gen/test/c/mx/mx_syllabic.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_SYLLABIC_H -#define MX_SYLLABIC_H +#ifndef MX_SYLLABIC_H_INCLUDED +#define MX_SYLLABIC_H_INCLUDED #include @@ -23,4 +23,4 @@ MxSyllabic mx_syllabic_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_syllabic_to_string(MxSyllabic v); -#endif /* MX_SYLLABIC_H */ +#endif /* MX_SYLLABIC_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_symbol_size.c b/gen/test/c/mx/mx_symbol_size.c index 333eb8ab5..b9488ea41 100644 --- a/gen/test/c/mx/mx_symbol_size.c +++ b/gen/test/c/mx/mx_symbol_size.c @@ -12,6 +12,8 @@ static const char *const mx_symbol_size_values[] = { }; bool mx_symbol_size_try_parse(const char *s, MxSymbolSize *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_symbol_size_values) / sizeof(mx_symbol_size_values[0]); i++) { if (strcmp(s, mx_symbol_size_values[i]) == 0) { *out = (MxSymbolSize)i; diff --git a/gen/test/c/mx/mx_symbol_size.h b/gen/test/c/mx/mx_symbol_size.h index 6f4acdb9f..81bee07e1 100644 --- a/gen/test/c/mx/mx_symbol_size.h +++ b/gen/test/c/mx/mx_symbol_size.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_SYMBOL_SIZE_H -#define MX_SYMBOL_SIZE_H +#ifndef MX_SYMBOL_SIZE_H_INCLUDED +#define MX_SYMBOL_SIZE_H_INCLUDED #include @@ -22,4 +22,4 @@ MxSymbolSize mx_symbol_size_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_symbol_size_to_string(MxSymbolSize v); -#endif /* MX_SYMBOL_SIZE_H */ +#endif /* MX_SYMBOL_SIZE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_tap_hand.c b/gen/test/c/mx/mx_tap_hand.c index 2aaf69dfa..3c512c94a 100644 --- a/gen/test/c/mx/mx_tap_hand.c +++ b/gen/test/c/mx/mx_tap_hand.c @@ -10,6 +10,8 @@ static const char *const mx_tap_hand_values[] = { }; bool mx_tap_hand_try_parse(const char *s, MxTapHand *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_tap_hand_values) / sizeof(mx_tap_hand_values[0]); i++) { if (strcmp(s, mx_tap_hand_values[i]) == 0) { *out = (MxTapHand)i; diff --git a/gen/test/c/mx/mx_tap_hand.h b/gen/test/c/mx/mx_tap_hand.h index edd8ac9e5..0252779f4 100644 --- a/gen/test/c/mx/mx_tap_hand.h +++ b/gen/test/c/mx/mx_tap_hand.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_TAP_HAND_H -#define MX_TAP_HAND_H +#ifndef MX_TAP_HAND_H_INCLUDED +#define MX_TAP_HAND_H_INCLUDED #include @@ -20,4 +20,4 @@ MxTapHand mx_tap_hand_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_tap_hand_to_string(MxTapHand v); -#endif /* MX_TAP_HAND_H */ +#endif /* MX_TAP_HAND_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_tenths.h b/gen/test/c/mx/mx_tenths.h index 8041a9353..3ede8ed52 100644 --- a/gen/test/c/mx/mx_tenths.h +++ b/gen/test/c/mx/mx_tenths.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_TENTHS_H -#define MX_TENTHS_H +#ifndef MX_TENTHS_H_INCLUDED +#define MX_TENTHS_H_INCLUDED #include @@ -17,11 +17,11 @@ */ typedef double MxTenths; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse. */ bool mx_tenths_try_parse(const char *s, MxTenths *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0. */ MxTenths mx_tenths_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_tenths_to_string(MxTenths v); -#endif /* MX_TENTHS_H */ +#endif /* MX_TENTHS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_text_direction.c b/gen/test/c/mx/mx_text_direction.c index 612c86e2b..ce6dd0cb8 100644 --- a/gen/test/c/mx/mx_text_direction.c +++ b/gen/test/c/mx/mx_text_direction.c @@ -12,6 +12,8 @@ static const char *const mx_text_direction_values[] = { }; bool mx_text_direction_try_parse(const char *s, MxTextDirection *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_text_direction_values) / sizeof(mx_text_direction_values[0]); i++) { if (strcmp(s, mx_text_direction_values[i]) == 0) { *out = (MxTextDirection)i; diff --git a/gen/test/c/mx/mx_text_direction.h b/gen/test/c/mx/mx_text_direction.h index 555619f4d..f95ca30cc 100644 --- a/gen/test/c/mx/mx_text_direction.h +++ b/gen/test/c/mx/mx_text_direction.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_TEXT_DIRECTION_H -#define MX_TEXT_DIRECTION_H +#ifndef MX_TEXT_DIRECTION_H_INCLUDED +#define MX_TEXT_DIRECTION_H_INCLUDED #include @@ -26,4 +26,4 @@ MxTextDirection mx_text_direction_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_text_direction_to_string(MxTextDirection v); -#endif /* MX_TEXT_DIRECTION_H */ +#endif /* MX_TEXT_DIRECTION_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_tied_type.c b/gen/test/c/mx/mx_tied_type.c index 31fbaa2aa..c65d4545f 100644 --- a/gen/test/c/mx/mx_tied_type.c +++ b/gen/test/c/mx/mx_tied_type.c @@ -12,6 +12,8 @@ static const char *const mx_tied_type_values[] = { }; bool mx_tied_type_try_parse(const char *s, MxTiedType *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_tied_type_values) / sizeof(mx_tied_type_values[0]); i++) { if (strcmp(s, mx_tied_type_values[i]) == 0) { *out = (MxTiedType)i; diff --git a/gen/test/c/mx/mx_tied_type.h b/gen/test/c/mx/mx_tied_type.h index 1bf51dd34..f923f62a8 100644 --- a/gen/test/c/mx/mx_tied_type.h +++ b/gen/test/c/mx/mx_tied_type.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_TIED_TYPE_H -#define MX_TIED_TYPE_H +#ifndef MX_TIED_TYPE_H_INCLUDED +#define MX_TIED_TYPE_H_INCLUDED #include @@ -28,4 +28,4 @@ MxTiedType mx_tied_type_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_tied_type_to_string(MxTiedType v); -#endif /* MX_TIED_TYPE_H */ +#endif /* MX_TIED_TYPE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_time_only.h b/gen/test/c/mx/mx_time_only.h index 4a62e3328..4bb4c5dae 100644 --- a/gen/test/c/mx/mx_time_only.h +++ b/gen/test/c/mx/mx_time_only.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_TIME_ONLY_H -#define MX_TIME_ONLY_H +#ifndef MX_TIME_ONLY_H_INCLUDED +#define MX_TIME_ONLY_H_INCLUDED /* * The time-only type is used to indicate that a particular playback-related element only applies @@ -14,4 +14,4 @@ typedef char *MxTimeOnly; /* Malloc'd copy of the wire string; the value IS its spelling. */ MxTimeOnly mx_time_only_parse(const char *s); -#endif /* MX_TIME_ONLY_H */ +#endif /* MX_TIME_ONLY_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_time_relation.c b/gen/test/c/mx/mx_time_relation.c index 6eab25e70..5f530ef47 100644 --- a/gen/test/c/mx/mx_time_relation.c +++ b/gen/test/c/mx/mx_time_relation.c @@ -14,6 +14,8 @@ static const char *const mx_time_relation_values[] = { }; bool mx_time_relation_try_parse(const char *s, MxTimeRelation *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_time_relation_values) / sizeof(mx_time_relation_values[0]); i++) { if (strcmp(s, mx_time_relation_values[i]) == 0) { *out = (MxTimeRelation)i; diff --git a/gen/test/c/mx/mx_time_relation.h b/gen/test/c/mx/mx_time_relation.h index d70d2c52a..a349cd119 100644 --- a/gen/test/c/mx/mx_time_relation.h +++ b/gen/test/c/mx/mx_time_relation.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_TIME_RELATION_H -#define MX_TIME_RELATION_H +#ifndef MX_TIME_RELATION_H_INCLUDED +#define MX_TIME_RELATION_H_INCLUDED #include @@ -24,4 +24,4 @@ MxTimeRelation mx_time_relation_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_time_relation_to_string(MxTimeRelation v); -#endif /* MX_TIME_RELATION_H */ +#endif /* MX_TIME_RELATION_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_time_separator.c b/gen/test/c/mx/mx_time_separator.c index 8d43079cd..951d8b21e 100644 --- a/gen/test/c/mx/mx_time_separator.c +++ b/gen/test/c/mx/mx_time_separator.c @@ -13,6 +13,8 @@ static const char *const mx_time_separator_values[] = { }; bool mx_time_separator_try_parse(const char *s, MxTimeSeparator *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_time_separator_values) / sizeof(mx_time_separator_values[0]); i++) { if (strcmp(s, mx_time_separator_values[i]) == 0) { *out = (MxTimeSeparator)i; diff --git a/gen/test/c/mx/mx_time_separator.h b/gen/test/c/mx/mx_time_separator.h index 0c029a1ca..20bd9b96a 100644 --- a/gen/test/c/mx/mx_time_separator.h +++ b/gen/test/c/mx/mx_time_separator.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_TIME_SEPARATOR_H -#define MX_TIME_SEPARATOR_H +#ifndef MX_TIME_SEPARATOR_H_INCLUDED +#define MX_TIME_SEPARATOR_H_INCLUDED #include @@ -27,4 +27,4 @@ MxTimeSeparator mx_time_separator_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_time_separator_to_string(MxTimeSeparator v); -#endif /* MX_TIME_SEPARATOR_H */ +#endif /* MX_TIME_SEPARATOR_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_time_symbol.c b/gen/test/c/mx/mx_time_symbol.c index b87043cf1..2ce817852 100644 --- a/gen/test/c/mx/mx_time_symbol.c +++ b/gen/test/c/mx/mx_time_symbol.c @@ -14,6 +14,8 @@ static const char *const mx_time_symbol_values[] = { }; bool mx_time_symbol_try_parse(const char *s, MxTimeSymbol *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_time_symbol_values) / sizeof(mx_time_symbol_values[0]); i++) { if (strcmp(s, mx_time_symbol_values[i]) == 0) { *out = (MxTimeSymbol)i; diff --git a/gen/test/c/mx/mx_time_symbol.h b/gen/test/c/mx/mx_time_symbol.h index ef3cbb42d..5b38c8194 100644 --- a/gen/test/c/mx/mx_time_symbol.h +++ b/gen/test/c/mx/mx_time_symbol.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_TIME_SYMBOL_H -#define MX_TIME_SYMBOL_H +#ifndef MX_TIME_SYMBOL_H_INCLUDED +#define MX_TIME_SYMBOL_H_INCLUDED #include @@ -29,4 +29,4 @@ MxTimeSymbol mx_time_symbol_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_time_symbol_to_string(MxTimeSymbol v); -#endif /* MX_TIME_SYMBOL_H */ +#endif /* MX_TIME_SYMBOL_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_tip_direction.c b/gen/test/c/mx/mx_tip_direction.c index ea8a6a397..a4697d25a 100644 --- a/gen/test/c/mx/mx_tip_direction.c +++ b/gen/test/c/mx/mx_tip_direction.c @@ -16,6 +16,8 @@ static const char *const mx_tip_direction_values[] = { }; bool mx_tip_direction_try_parse(const char *s, MxTipDirection *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_tip_direction_values) / sizeof(mx_tip_direction_values[0]); i++) { if (strcmp(s, mx_tip_direction_values[i]) == 0) { *out = (MxTipDirection)i; diff --git a/gen/test/c/mx/mx_tip_direction.h b/gen/test/c/mx/mx_tip_direction.h index 497540143..2b95d0b33 100644 --- a/gen/test/c/mx/mx_tip_direction.h +++ b/gen/test/c/mx/mx_tip_direction.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_TIP_DIRECTION_H -#define MX_TIP_DIRECTION_H +#ifndef MX_TIP_DIRECTION_H_INCLUDED +#define MX_TIP_DIRECTION_H_INCLUDED #include @@ -26,4 +26,4 @@ MxTipDirection mx_tip_direction_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_tip_direction_to_string(MxTipDirection v); -#endif /* MX_TIP_DIRECTION_H */ +#endif /* MX_TIP_DIRECTION_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_top_bottom.c b/gen/test/c/mx/mx_top_bottom.c index 9ef213ba9..d55d7d6a3 100644 --- a/gen/test/c/mx/mx_top_bottom.c +++ b/gen/test/c/mx/mx_top_bottom.c @@ -10,6 +10,8 @@ static const char *const mx_top_bottom_values[] = { }; bool mx_top_bottom_try_parse(const char *s, MxTopBottom *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_top_bottom_values) / sizeof(mx_top_bottom_values[0]); i++) { if (strcmp(s, mx_top_bottom_values[i]) == 0) { *out = (MxTopBottom)i; diff --git a/gen/test/c/mx/mx_top_bottom.h b/gen/test/c/mx/mx_top_bottom.h index 53ae3af58..c2c8e84c4 100644 --- a/gen/test/c/mx/mx_top_bottom.h +++ b/gen/test/c/mx/mx_top_bottom.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_TOP_BOTTOM_H -#define MX_TOP_BOTTOM_H +#ifndef MX_TOP_BOTTOM_H_INCLUDED +#define MX_TOP_BOTTOM_H_INCLUDED #include @@ -20,4 +20,4 @@ MxTopBottom mx_top_bottom_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_top_bottom_to_string(MxTopBottom v); -#endif /* MX_TOP_BOTTOM_H */ +#endif /* MX_TOP_BOTTOM_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_tremolo_marks.h b/gen/test/c/mx/mx_tremolo_marks.h index d44c33a70..481d43d63 100644 --- a/gen/test/c/mx/mx_tremolo_marks.h +++ b/gen/test/c/mx/mx_tremolo_marks.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_TREMOLO_MARKS_H -#define MX_TREMOLO_MARKS_H +#ifndef MX_TREMOLO_MARKS_H_INCLUDED +#define MX_TREMOLO_MARKS_H_INCLUDED #include @@ -11,11 +11,11 @@ */ typedef long MxTremoloMarks; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse, then clamps into the declared range. */ bool mx_tremolo_marks_try_parse(const char *s, MxTremoloMarks *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0, then clamps into the declared range. */ MxTremoloMarks mx_tremolo_marks_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_tremolo_marks_to_string(MxTremoloMarks v); -#endif /* MX_TREMOLO_MARKS_H */ +#endif /* MX_TREMOLO_MARKS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_tremolo_type.c b/gen/test/c/mx/mx_tremolo_type.c index 9abf79e14..f19cec1d6 100644 --- a/gen/test/c/mx/mx_tremolo_type.c +++ b/gen/test/c/mx/mx_tremolo_type.c @@ -12,6 +12,8 @@ static const char *const mx_tremolo_type_values[] = { }; bool mx_tremolo_type_try_parse(const char *s, MxTremoloType *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_tremolo_type_values) / sizeof(mx_tremolo_type_values[0]); i++) { if (strcmp(s, mx_tremolo_type_values[i]) == 0) { *out = (MxTremoloType)i; diff --git a/gen/test/c/mx/mx_tremolo_type.h b/gen/test/c/mx/mx_tremolo_type.h index 88a83a9b6..cd4d0e507 100644 --- a/gen/test/c/mx/mx_tremolo_type.h +++ b/gen/test/c/mx/mx_tremolo_type.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_TREMOLO_TYPE_H -#define MX_TREMOLO_TYPE_H +#ifndef MX_TREMOLO_TYPE_H_INCLUDED +#define MX_TREMOLO_TYPE_H_INCLUDED #include @@ -21,4 +21,4 @@ MxTremoloType mx_tremolo_type_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_tremolo_type_to_string(MxTremoloType v); -#endif /* MX_TREMOLO_TYPE_H */ +#endif /* MX_TREMOLO_TYPE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_trill_beats.h b/gen/test/c/mx/mx_trill_beats.h index 944446c61..5e912c488 100644 --- a/gen/test/c/mx/mx_trill_beats.h +++ b/gen/test/c/mx/mx_trill_beats.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_TRILL_BEATS_H -#define MX_TRILL_BEATS_H +#ifndef MX_TRILL_BEATS_H_INCLUDED +#define MX_TRILL_BEATS_H_INCLUDED #include @@ -11,11 +11,11 @@ */ typedef double MxTrillBeats; -/* Strict parse, then clamps into the declared range. */ +/* Lexically strict parse, then clamps into the declared range. */ bool mx_trill_beats_try_parse(const char *s, MxTrillBeats *out); -/* Lenient: unparseable input becomes 0, then clamps. */ +/* Lenient: unparseable input becomes 0, then clamps into the declared range. */ MxTrillBeats mx_trill_beats_parse(const char *s); /* Malloc'd; caller frees. */ char *mx_trill_beats_to_string(MxTrillBeats v); -#endif /* MX_TRILL_BEATS_H */ +#endif /* MX_TRILL_BEATS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_trill_step.c b/gen/test/c/mx/mx_trill_step.c index 8cca04eb8..6867d630e 100644 --- a/gen/test/c/mx/mx_trill_step.c +++ b/gen/test/c/mx/mx_trill_step.c @@ -11,6 +11,8 @@ static const char *const mx_trill_step_values[] = { }; bool mx_trill_step_try_parse(const char *s, MxTrillStep *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_trill_step_values) / sizeof(mx_trill_step_values[0]); i++) { if (strcmp(s, mx_trill_step_values[i]) == 0) { *out = (MxTrillStep)i; diff --git a/gen/test/c/mx/mx_trill_step.h b/gen/test/c/mx/mx_trill_step.h index 183ae9ecf..89579a2b3 100644 --- a/gen/test/c/mx/mx_trill_step.h +++ b/gen/test/c/mx/mx_trill_step.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_TRILL_STEP_H -#define MX_TRILL_STEP_H +#ifndef MX_TRILL_STEP_H_INCLUDED +#define MX_TRILL_STEP_H_INCLUDED #include @@ -21,4 +21,4 @@ MxTrillStep mx_trill_step_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_trill_step_to_string(MxTrillStep v); -#endif /* MX_TRILL_STEP_H */ +#endif /* MX_TRILL_STEP_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_two_note_turn.c b/gen/test/c/mx/mx_two_note_turn.c index da9d959a2..75828d3aa 100644 --- a/gen/test/c/mx/mx_two_note_turn.c +++ b/gen/test/c/mx/mx_two_note_turn.c @@ -11,6 +11,8 @@ static const char *const mx_two_note_turn_values[] = { }; bool mx_two_note_turn_try_parse(const char *s, MxTwoNoteTurn *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_two_note_turn_values) / sizeof(mx_two_note_turn_values[0]); i++) { if (strcmp(s, mx_two_note_turn_values[i]) == 0) { *out = (MxTwoNoteTurn)i; diff --git a/gen/test/c/mx/mx_two_note_turn.h b/gen/test/c/mx/mx_two_note_turn.h index e6f6f8d26..cd7a20e9d 100644 --- a/gen/test/c/mx/mx_two_note_turn.h +++ b/gen/test/c/mx/mx_two_note_turn.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_TWO_NOTE_TURN_H -#define MX_TWO_NOTE_TURN_H +#ifndef MX_TWO_NOTE_TURN_H_INCLUDED +#define MX_TWO_NOTE_TURN_H_INCLUDED #include @@ -21,4 +21,4 @@ MxTwoNoteTurn mx_two_note_turn_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_two_note_turn_to_string(MxTwoNoteTurn v); -#endif /* MX_TWO_NOTE_TURN_H */ +#endif /* MX_TWO_NOTE_TURN_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_up_down.c b/gen/test/c/mx/mx_up_down.c index fc3c5a174..6c104d216 100644 --- a/gen/test/c/mx/mx_up_down.c +++ b/gen/test/c/mx/mx_up_down.c @@ -10,6 +10,8 @@ static const char *const mx_up_down_values[] = { }; bool mx_up_down_try_parse(const char *s, MxUpDown *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_up_down_values) / sizeof(mx_up_down_values[0]); i++) { if (strcmp(s, mx_up_down_values[i]) == 0) { *out = (MxUpDown)i; diff --git a/gen/test/c/mx/mx_up_down.h b/gen/test/c/mx/mx_up_down.h index 78fb8419f..8083159c3 100644 --- a/gen/test/c/mx/mx_up_down.h +++ b/gen/test/c/mx/mx_up_down.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_UP_DOWN_H -#define MX_UP_DOWN_H +#ifndef MX_UP_DOWN_H_INCLUDED +#define MX_UP_DOWN_H_INCLUDED #include @@ -20,4 +20,4 @@ MxUpDown mx_up_down_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_up_down_to_string(MxUpDown v); -#endif /* MX_UP_DOWN_H */ +#endif /* MX_UP_DOWN_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_up_down_stop_continue.c b/gen/test/c/mx/mx_up_down_stop_continue.c index 6bc62961f..dde0d1b76 100644 --- a/gen/test/c/mx/mx_up_down_stop_continue.c +++ b/gen/test/c/mx/mx_up_down_stop_continue.c @@ -12,6 +12,8 @@ static const char *const mx_up_down_stop_continue_values[] = { }; bool mx_up_down_stop_continue_try_parse(const char *s, MxUpDownStopContinue *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_up_down_stop_continue_values) / sizeof(mx_up_down_stop_continue_values[0]); i++) { if (strcmp(s, mx_up_down_stop_continue_values[i]) == 0) { *out = (MxUpDownStopContinue)i; diff --git a/gen/test/c/mx/mx_up_down_stop_continue.h b/gen/test/c/mx/mx_up_down_stop_continue.h index 51d6f0b34..830389ee8 100644 --- a/gen/test/c/mx/mx_up_down_stop_continue.h +++ b/gen/test/c/mx/mx_up_down_stop_continue.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_UP_DOWN_STOP_CONTINUE_H -#define MX_UP_DOWN_STOP_CONTINUE_H +#ifndef MX_UP_DOWN_STOP_CONTINUE_H_INCLUDED +#define MX_UP_DOWN_STOP_CONTINUE_H_INCLUDED #include @@ -22,4 +22,4 @@ MxUpDownStopContinue mx_up_down_stop_continue_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_up_down_stop_continue_to_string(MxUpDownStopContinue v); -#endif /* MX_UP_DOWN_STOP_CONTINUE_H */ +#endif /* MX_UP_DOWN_STOP_CONTINUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_upright_inverted.c b/gen/test/c/mx/mx_upright_inverted.c index bb7974d34..cac5dead4 100644 --- a/gen/test/c/mx/mx_upright_inverted.c +++ b/gen/test/c/mx/mx_upright_inverted.c @@ -10,6 +10,8 @@ static const char *const mx_upright_inverted_values[] = { }; bool mx_upright_inverted_try_parse(const char *s, MxUprightInverted *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_upright_inverted_values) / sizeof(mx_upright_inverted_values[0]); i++) { if (strcmp(s, mx_upright_inverted_values[i]) == 0) { *out = (MxUprightInverted)i; diff --git a/gen/test/c/mx/mx_upright_inverted.h b/gen/test/c/mx/mx_upright_inverted.h index a41529409..a32459d0f 100644 --- a/gen/test/c/mx/mx_upright_inverted.h +++ b/gen/test/c/mx/mx_upright_inverted.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_UPRIGHT_INVERTED_H -#define MX_UPRIGHT_INVERTED_H +#ifndef MX_UPRIGHT_INVERTED_H_INCLUDED +#define MX_UPRIGHT_INVERTED_H_INCLUDED #include @@ -20,4 +20,4 @@ MxUprightInverted mx_upright_inverted_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_upright_inverted_to_string(MxUprightInverted v); -#endif /* MX_UPRIGHT_INVERTED_H */ +#endif /* MX_UPRIGHT_INVERTED_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_valign.c b/gen/test/c/mx/mx_valign.c index 483179f8c..56f94985e 100644 --- a/gen/test/c/mx/mx_valign.c +++ b/gen/test/c/mx/mx_valign.c @@ -12,6 +12,8 @@ static const char *const mx_valign_values[] = { }; bool mx_valign_try_parse(const char *s, MxValign *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_valign_values) / sizeof(mx_valign_values[0]); i++) { if (strcmp(s, mx_valign_values[i]) == 0) { *out = (MxValign)i; diff --git a/gen/test/c/mx/mx_valign.h b/gen/test/c/mx/mx_valign.h index 05ce289d9..5796f05ae 100644 --- a/gen/test/c/mx/mx_valign.h +++ b/gen/test/c/mx/mx_valign.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_VALIGN_H -#define MX_VALIGN_H +#ifndef MX_VALIGN_H_INCLUDED +#define MX_VALIGN_H_INCLUDED #include @@ -22,4 +22,4 @@ MxValign mx_valign_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_valign_to_string(MxValign v); -#endif /* MX_VALIGN_H */ +#endif /* MX_VALIGN_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_valign_image.c b/gen/test/c/mx/mx_valign_image.c index 079e9e754..ce57af88b 100644 --- a/gen/test/c/mx/mx_valign_image.c +++ b/gen/test/c/mx/mx_valign_image.c @@ -11,6 +11,8 @@ static const char *const mx_valign_image_values[] = { }; bool mx_valign_image_try_parse(const char *s, MxValignImage *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_valign_image_values) / sizeof(mx_valign_image_values[0]); i++) { if (strcmp(s, mx_valign_image_values[i]) == 0) { *out = (MxValignImage)i; diff --git a/gen/test/c/mx/mx_valign_image.h b/gen/test/c/mx/mx_valign_image.h index b82bf199f..33dc522e0 100644 --- a/gen/test/c/mx/mx_valign_image.h +++ b/gen/test/c/mx/mx_valign_image.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_VALIGN_IMAGE_H -#define MX_VALIGN_IMAGE_H +#ifndef MX_VALIGN_IMAGE_H_INCLUDED +#define MX_VALIGN_IMAGE_H_INCLUDED #include @@ -21,4 +21,4 @@ MxValignImage mx_valign_image_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_valign_image_to_string(MxValignImage v); -#endif /* MX_VALIGN_IMAGE_H */ +#endif /* MX_VALIGN_IMAGE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_wedge_type.c b/gen/test/c/mx/mx_wedge_type.c index d5b54b898..6c1e0c95e 100644 --- a/gen/test/c/mx/mx_wedge_type.c +++ b/gen/test/c/mx/mx_wedge_type.c @@ -12,6 +12,8 @@ static const char *const mx_wedge_type_values[] = { }; bool mx_wedge_type_try_parse(const char *s, MxWedgeType *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_wedge_type_values) / sizeof(mx_wedge_type_values[0]); i++) { if (strcmp(s, mx_wedge_type_values[i]) == 0) { *out = (MxWedgeType)i; diff --git a/gen/test/c/mx/mx_wedge_type.h b/gen/test/c/mx/mx_wedge_type.h index 3c751c8ab..53cdb5940 100644 --- a/gen/test/c/mx/mx_wedge_type.h +++ b/gen/test/c/mx/mx_wedge_type.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_WEDGE_TYPE_H -#define MX_WEDGE_TYPE_H +#ifndef MX_WEDGE_TYPE_H_INCLUDED +#define MX_WEDGE_TYPE_H_INCLUDED #include @@ -24,4 +24,4 @@ MxWedgeType mx_wedge_type_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_wedge_type_to_string(MxWedgeType v); -#endif /* MX_WEDGE_TYPE_H */ +#endif /* MX_WEDGE_TYPE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_winged.c b/gen/test/c/mx/mx_winged.c index 9e0776c6e..1bd62ec68 100644 --- a/gen/test/c/mx/mx_winged.c +++ b/gen/test/c/mx/mx_winged.c @@ -13,6 +13,8 @@ static const char *const mx_winged_values[] = { }; bool mx_winged_try_parse(const char *s, MxWinged *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_winged_values) / sizeof(mx_winged_values[0]); i++) { if (strcmp(s, mx_winged_values[i]) == 0) { *out = (MxWinged)i; diff --git a/gen/test/c/mx/mx_winged.h b/gen/test/c/mx/mx_winged.h index 7966a88ec..388cdae0f 100644 --- a/gen/test/c/mx/mx_winged.h +++ b/gen/test/c/mx/mx_winged.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_WINGED_H -#define MX_WINGED_H +#ifndef MX_WINGED_H_INCLUDED +#define MX_WINGED_H_INCLUDED #include @@ -25,4 +25,4 @@ MxWinged mx_winged_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_winged_to_string(MxWinged v); -#endif /* MX_WINGED_H */ +#endif /* MX_WINGED_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_wood.c b/gen/test/c/mx/mx_wood.c index ae59f8138..dd8f6dfdc 100644 --- a/gen/test/c/mx/mx_wood.c +++ b/gen/test/c/mx/mx_wood.c @@ -29,6 +29,8 @@ static const char *const mx_wood_values[] = { }; bool mx_wood_try_parse(const char *s, MxWood *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_wood_values) / sizeof(mx_wood_values[0]); i++) { if (strcmp(s, mx_wood_values[i]) == 0) { *out = (MxWood)i; diff --git a/gen/test/c/mx/mx_wood.h b/gen/test/c/mx/mx_wood.h index ea5fc19d4..384cec4a3 100644 --- a/gen/test/c/mx/mx_wood.h +++ b/gen/test/c/mx/mx_wood.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_WOOD_H -#define MX_WOOD_H +#ifndef MX_WOOD_H_INCLUDED +#define MX_WOOD_H_INCLUDED #include @@ -39,4 +39,4 @@ MxWood mx_wood_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_wood_to_string(MxWood v); -#endif /* MX_WOOD_H */ +#endif /* MX_WOOD_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_yes_no.c b/gen/test/c/mx/mx_yes_no.c index 4080ef065..f8ce8c345 100644 --- a/gen/test/c/mx/mx_yes_no.c +++ b/gen/test/c/mx/mx_yes_no.c @@ -10,6 +10,8 @@ static const char *const mx_yes_no_values[] = { }; bool mx_yes_no_try_parse(const char *s, MxYesNo *out) { + if (!s) + s = ""; for (size_t i = 0; i < sizeof(mx_yes_no_values) / sizeof(mx_yes_no_values[0]); i++) { if (strcmp(s, mx_yes_no_values[i]) == 0) { *out = (MxYesNo)i; diff --git a/gen/test/c/mx/mx_yes_no.h b/gen/test/c/mx/mx_yes_no.h index dbf5c4368..7edaf0ac8 100644 --- a/gen/test/c/mx/mx_yes_no.h +++ b/gen/test/c/mx/mx_yes_no.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_YES_NO_H -#define MX_YES_NO_H +#ifndef MX_YES_NO_H_INCLUDED +#define MX_YES_NO_H_INCLUDED #include @@ -20,4 +20,4 @@ MxYesNo mx_yes_no_parse(const char *s); /* Returns the wire literal (static storage; do not free). */ const char *mx_yes_no_to_string(MxYesNo v); -#endif /* MX_YES_NO_H */ +#endif /* MX_YES_NO_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_yes_no_number.c b/gen/test/c/mx/mx_yes_no_number.c index 4b991f110..1f4b3a4f8 100644 --- a/gen/test/c/mx/mx_yes_no_number.c +++ b/gen/test/c/mx/mx_yes_no_number.c @@ -7,15 +7,17 @@ #include bool mx_yes_no_number_try_parse(const char *s, MxYesNoNumber *out) { + if (!s) + s = ""; MxYesNoNumber v; memset(&v, 0, sizeof(v)); if (mx_yes_no_try_parse(s, &v.yes_no)) { - v.kind = MX_YES_NO_NUMBER_KIND_YES_NO; + v.kind = MX_YES_NO_NUMBER_YES_NO; *out = v; return true; } if (mx_try_parse_decimal(s, &v.decimal)) { - v.kind = MX_YES_NO_NUMBER_KIND_DECIMAL; + v.kind = MX_YES_NO_NUMBER_DECIMAL; *out = v; return true; } @@ -28,14 +30,14 @@ MxYesNoNumber mx_yes_no_number_parse(const char *s) { if (mx_yes_no_number_try_parse(s, &v)) return v; memset(&v, 0, sizeof(v)); - v.kind = MX_YES_NO_NUMBER_KIND_YES_NO; + v.kind = MX_YES_NO_NUMBER_YES_NO; v.yes_no = mx_yes_no_parse(s); return v; } char *mx_yes_no_number_to_string(MxYesNoNumber v) { switch (v.kind) { - case MX_YES_NO_NUMBER_KIND_DECIMAL: + case MX_YES_NO_NUMBER_DECIMAL: return mx_format_decimal(v.decimal); default: return mx_strdup(mx_yes_no_to_string(v.yes_no)); diff --git a/gen/test/c/mx/mx_yes_no_number.h b/gen/test/c/mx/mx_yes_no_number.h index e3a89275b..8830ae02a 100644 --- a/gen/test/c/mx/mx_yes_no_number.h +++ b/gen/test/c/mx/mx_yes_no_number.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_YES_NO_NUMBER_H -#define MX_YES_NO_NUMBER_H +#ifndef MX_YES_NO_NUMBER_H_INCLUDED +#define MX_YES_NO_NUMBER_H_INCLUDED #include #include "mx_yes_no.h" @@ -10,8 +10,8 @@ * The yes-no-number type is used for attributes that can be either boolean or numeric values. */ typedef enum { - MX_YES_NO_NUMBER_KIND_YES_NO = 0, - MX_YES_NO_NUMBER_KIND_DECIMAL = 1 + MX_YES_NO_NUMBER_YES_NO = 0, + MX_YES_NO_NUMBER_DECIMAL = 1 } MxYesNoNumberKind; typedef struct { @@ -28,4 +28,4 @@ MxYesNoNumber mx_yes_no_number_parse(const char *s); char *mx_yes_no_number_to_string(MxYesNoNumber v); void mx_yes_no_number_free(MxYesNoNumber *v); -#endif /* MX_YES_NO_NUMBER_H */ +#endif /* MX_YES_NO_NUMBER_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_yyyy_mm_dd.h b/gen/test/c/mx/mx_yyyy_mm_dd.h index 350deed6a..2865886e5 100644 --- a/gen/test/c/mx/mx_yyyy_mm_dd.h +++ b/gen/test/c/mx/mx_yyyy_mm_dd.h @@ -1,7 +1,7 @@ /* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ -#ifndef MX_YYYY_MM_DD_H -#define MX_YYYY_MM_DD_H +#ifndef MX_YYYY_MM_DD_H_INCLUDED +#define MX_YYYY_MM_DD_H_INCLUDED /* * Calendar dates are represented yyyy-mm-dd format, following ISO 8601. This is a W3C XML Schema @@ -12,4 +12,4 @@ typedef char *MxYyyyMmDd; /* Malloc'd copy of the wire string; the value IS its spelling. */ MxYyyyMmDd mx_yyyy_mm_dd_parse(const char *s); -#endif /* MX_YYYY_MM_DD_H */ +#endif /* MX_YYYY_MM_DD_H_INCLUDED */ diff --git a/gen/test/c/src/values_smoke.c b/gen/test/c/src/values_smoke.c index f741dbd73..769900d37 100644 --- a/gen/test/c/src/values_smoke.c +++ b/gen/test/c/src/values_smoke.c @@ -64,13 +64,31 @@ int main(void) { "0.000001"); expect_owned("number implied positive-integer min", mx_string_number_to_string(mx_string_number_parse("")), "1"); + expect_owned("negative decimal formats", + mx_tenths_to_string(mx_tenths_parse("-2.50")), "-2.5"); + expect_owned("negative zero canonicalizes", + mx_tenths_to_string(mx_tenths_parse("-0.0")), "0"); + + MxAboveBelow strict; + if (mx_above_below_try_parse("nope", &strict)) { + printf("FAIL try_parse accepted an unknown literal\n"); + failures++; + } + MxTenths strict_n; + if (mx_tenths_try_parse("12abc", &strict_n)) { + printf("FAIL try_parse accepted a malformed number\n"); + failures++; + } + MxPositiveIntegerOrEmpty pz = mx_positive_integer_or_empty_parse("0"); + expect_owned("union primitive member clamps implied min", + mx_positive_integer_or_empty_to_string(pz), "1"); MxColor color = mx_color_parse("#FF0000"); expect("string passthrough", color, "#FF0000"); free(color); MxFontSize fs = mx_font_size_parse("small"); - if (fs.kind != MX_FONT_SIZE_KIND_CSS_FONT_SIZE) { + if (fs.kind != MX_FONT_SIZE_CSS_FONT_SIZE) { printf("FAIL union kind: got %d, want css-font-size\n", (int)fs.kind); failures++; } @@ -89,7 +107,7 @@ int main(void) { mx_positive_integer_or_empty_to_string(p5), "5"); MxInstrumentSound is = mx_instrument_sound_parse("brass.alphorn"); - if (is.kind != MX_INSTRUMENT_SOUND_KIND_SOUND_ID) { + if (is.kind != MX_INSTRUMENT_SOUND_SOUND_ID) { printf("FAIL open enum known id kind\n"); failures++; } @@ -97,7 +115,7 @@ int main(void) { "brass.alphorn"); mx_instrument_sound_free(&is); MxInstrumentSound is2 = mx_instrument_sound_parse("synth.custom-thing"); - if (is2.kind != MX_INSTRUMENT_SOUND_KIND_STRING) { + if (is2.kind != MX_INSTRUMENT_SOUND_STRING) { printf("FAIL open enum fallback kind\n"); failures++; } diff --git a/gen/test/go/corert/values_smoke_test.go b/gen/test/go/corert/values_smoke_test.go index 04efdad28..c5199cff7 100644 --- a/gen/test/go/corert/values_smoke_test.go +++ b/gen/test/go/corert/values_smoke_test.go @@ -34,6 +34,9 @@ func TestValueSmoke(t *testing.T) { {"union literal member", mx.ParseNumberOrNormal("normal").String(), "normal"}, {"union empty literal member", mx.ParsePositiveIntegerOrEmpty("").String(), ""}, {"union integer member", mx.ParsePositiveIntegerOrEmpty("5").String(), "5"}, + {"union primitive member clamps implied min", mx.ParsePositiveIntegerOrEmpty("0").String(), "1"}, + {"negative decimal formats", mx.ParseTenths("-2.50").String(), "-2.5"}, + {"negative zero canonicalizes", mx.ParseTenths("-0.0").String(), "0"}, } for _, c := range cases { if c.got != c.want { @@ -44,7 +47,7 @@ func TestValueSmoke(t *testing.T) { if v, ok := mx.TryParseAboveBelow("nope"); ok { t.Errorf("TryParse accepted an unknown literal: %v", v) } - if fs := mx.ParseFontSize("small"); fs.Kind != mx.FontSizeKindCSSFontSize { + if fs := mx.ParseFontSize("small"); fs.Kind != mx.FontSizeCSSFontSize { t.Errorf("union kind: got %v, want CSSFontSize", fs.Kind) } } diff --git a/gen/test/go/mx/accordion_middle.go b/gen/test/go/mx/accordion_middle.go index c2abf28b6..69840ce07 100644 --- a/gen/test/go/mx/accordion_middle.go +++ b/gen/test/go/mx/accordion_middle.go @@ -7,7 +7,7 @@ package mx // present. type AccordionMiddle int -// TryParseAccordionMiddle parses s strictly, then clamps into the declared range. +// TryParseAccordionMiddle parses s as a lexically well-formed value, then clamps into the declared range. func TryParseAccordionMiddle(s string) (AccordionMiddle, bool) { v, ok := tryParseInt(s) if !ok { @@ -16,7 +16,7 @@ func TryParseAccordionMiddle(s string) (AccordionMiddle, bool) { return clampAccordionMiddle(v), true } -// ParseAccordionMiddle is lenient: unparseable input becomes 0, then clamps. +// ParseAccordionMiddle is lenient: unparseable input becomes 0, then clamps into the declared range. func ParseAccordionMiddle(s string) AccordionMiddle { v := parseInt(s) return clampAccordionMiddle(v) diff --git a/gen/test/go/mx/beam_level.go b/gen/test/go/mx/beam_level.go index d6db8a380..c54416192 100644 --- a/gen/test/go/mx/beam_level.go +++ b/gen/test/go/mx/beam_level.go @@ -7,7 +7,7 @@ package mx // overlapping beams such as grace notes within regular notes, or beams used in different voices. type BeamLevel int -// TryParseBeamLevel parses s strictly, then clamps into the declared range. +// TryParseBeamLevel parses s as a lexically well-formed value, then clamps into the declared range. func TryParseBeamLevel(s string) (BeamLevel, bool) { v, ok := tryParseInt(s) if !ok { @@ -16,7 +16,7 @@ func TryParseBeamLevel(s string) (BeamLevel, bool) { return clampBeamLevel(v), true } -// ParseBeamLevel is lenient: unparseable input becomes 0, then clamps. +// ParseBeamLevel is lenient: unparseable input becomes 0, then clamps into the declared range. func ParseBeamLevel(s string) BeamLevel { v := parseInt(s) return clampBeamLevel(v) diff --git a/gen/test/go/mx/divisions.go b/gen/test/go/mx/divisions.go index 9dabf64c7..c002d507a 100644 --- a/gen/test/go/mx/divisions.go +++ b/gen/test/go/mx/divisions.go @@ -7,7 +7,7 @@ package mx // and to avoid roundoff errors. type Divisions float64 -// TryParseDivisions parses s strictly, then clamps into the declared range. +// TryParseDivisions parses s as a lexically well-formed value. func TryParseDivisions(s string) (Divisions, bool) { v, ok := tryParseDecimal(s) if !ok { @@ -16,7 +16,7 @@ func TryParseDivisions(s string) (Divisions, bool) { return Divisions(v), true } -// ParseDivisions is lenient: unparseable input becomes 0, then clamps. +// ParseDivisions is lenient: unparseable input becomes 0. func ParseDivisions(s string) Divisions { v := parseDecimal(s) return Divisions(v) diff --git a/gen/test/go/mx/fifths.go b/gen/test/go/mx/fifths.go index 84634b496..4ce8bcc52 100644 --- a/gen/test/go/mx/fifths.go +++ b/gen/test/go/mx/fifths.go @@ -7,7 +7,7 @@ package mx // the circle of fifths (hence the type name). type Fifths int -// TryParseFifths parses s strictly, then clamps into the declared range. +// TryParseFifths parses s as a lexically well-formed value. func TryParseFifths(s string) (Fifths, bool) { v, ok := tryParseInt(s) if !ok { @@ -16,7 +16,7 @@ func TryParseFifths(s string) (Fifths, bool) { return Fifths(v), true } -// ParseFifths is lenient: unparseable input becomes 0, then clamps. +// ParseFifths is lenient: unparseable input becomes 0. func ParseFifths(s string) Fifths { v := parseInt(s) return Fifths(v) diff --git a/gen/test/go/mx/font_size.go b/gen/test/go/mx/font_size.go index 0a1212488..3c7afea20 100644 --- a/gen/test/go/mx/font_size.go +++ b/gen/test/go/mx/font_size.go @@ -12,17 +12,17 @@ type FontSize struct { type FontSizeKind int const ( - FontSizeKindDecimal FontSizeKind = iota - FontSizeKindCSSFontSize + FontSizeDecimal FontSizeKind = iota + FontSizeCSSFontSize ) // TryParseFontSize tries each union member in schema order. func TryParseFontSize(s string) (FontSize, bool) { if v, ok := tryParseDecimal(s); ok { - return FontSize{Kind: FontSizeKindDecimal, Decimal: v}, true + return FontSize{Kind: FontSizeDecimal, Decimal: v}, true } if v, ok := TryParseCSSFontSize(s); ok { - return FontSize{Kind: FontSizeKindCSSFontSize, CSSFontSize: v}, true + return FontSize{Kind: FontSizeCSSFontSize, CSSFontSize: v}, true } return FontSize{}, false } @@ -33,13 +33,13 @@ func ParseFontSize(s string) FontSize { if v, ok := TryParseFontSize(s); ok { return v } - return FontSize{Kind: FontSizeKindDecimal, Decimal: parseDecimal(s)} + return FontSize{Kind: FontSizeDecimal, Decimal: parseDecimal(s)} } // String returns the wire spelling of whichever member is held. func (v FontSize) String() string { switch v.Kind { - case FontSizeKindCSSFontSize: + case FontSizeCSSFontSize: return v.CSSFontSize.String() } return formatDecimal(v.Decimal) diff --git a/gen/test/go/mx/midi_128.go b/gen/test/go/mx/midi_128.go index 62c31b163..2a0741b07 100644 --- a/gen/test/go/mx/midi_128.go +++ b/gen/test/go/mx/midi_128.go @@ -5,7 +5,7 @@ package mx // The midi-16 type is used to express MIDI 1.0 values that range from 1 to 128. type MIDI128 int -// TryParseMIDI128 parses s strictly, then clamps into the declared range. +// TryParseMIDI128 parses s as a lexically well-formed value, then clamps into the declared range. func TryParseMIDI128(s string) (MIDI128, bool) { v, ok := tryParseInt(s) if !ok { @@ -14,7 +14,7 @@ func TryParseMIDI128(s string) (MIDI128, bool) { return clampMIDI128(v), true } -// ParseMIDI128 is lenient: unparseable input becomes 0, then clamps. +// ParseMIDI128 is lenient: unparseable input becomes 0, then clamps into the declared range. func ParseMIDI128(s string) MIDI128 { v := parseInt(s) return clampMIDI128(v) diff --git a/gen/test/go/mx/midi_16.go b/gen/test/go/mx/midi_16.go index cf5ed0d5a..5c5e20f7a 100644 --- a/gen/test/go/mx/midi_16.go +++ b/gen/test/go/mx/midi_16.go @@ -5,7 +5,7 @@ package mx // The midi-16 type is used to express MIDI 1.0 values that range from 1 to 16. type MIDI16 int -// TryParseMIDI16 parses s strictly, then clamps into the declared range. +// TryParseMIDI16 parses s as a lexically well-formed value, then clamps into the declared range. func TryParseMIDI16(s string) (MIDI16, bool) { v, ok := tryParseInt(s) if !ok { @@ -14,7 +14,7 @@ func TryParseMIDI16(s string) (MIDI16, bool) { return clampMIDI16(v), true } -// ParseMIDI16 is lenient: unparseable input becomes 0, then clamps. +// ParseMIDI16 is lenient: unparseable input becomes 0, then clamps into the declared range. func ParseMIDI16(s string) MIDI16 { v := parseInt(s) return clampMIDI16(v) diff --git a/gen/test/go/mx/midi_16384.go b/gen/test/go/mx/midi_16384.go index 16d704738..b110fde8a 100644 --- a/gen/test/go/mx/midi_16384.go +++ b/gen/test/go/mx/midi_16384.go @@ -5,7 +5,7 @@ package mx // The midi-16 type is used to express MIDI 1.0 values that range from 1 to 16,384. type MIDI16384 int -// TryParseMIDI16384 parses s strictly, then clamps into the declared range. +// TryParseMIDI16384 parses s as a lexically well-formed value, then clamps into the declared range. func TryParseMIDI16384(s string) (MIDI16384, bool) { v, ok := tryParseInt(s) if !ok { @@ -14,7 +14,7 @@ func TryParseMIDI16384(s string) (MIDI16384, bool) { return clampMIDI16384(v), true } -// ParseMIDI16384 is lenient: unparseable input becomes 0, then clamps. +// ParseMIDI16384 is lenient: unparseable input becomes 0, then clamps into the declared range. func ParseMIDI16384(s string) MIDI16384 { v := parseInt(s) return clampMIDI16384(v) diff --git a/gen/test/go/mx/millimeters.go b/gen/test/go/mx/millimeters.go index d926df646..fc0ec939c 100644 --- a/gen/test/go/mx/millimeters.go +++ b/gen/test/go/mx/millimeters.go @@ -6,7 +6,7 @@ package mx // provide a default scaling from tenths to physical units. type Millimeters float64 -// TryParseMillimeters parses s strictly, then clamps into the declared range. +// TryParseMillimeters parses s as a lexically well-formed value. func TryParseMillimeters(s string) (Millimeters, bool) { v, ok := tryParseDecimal(s) if !ok { @@ -15,7 +15,7 @@ func TryParseMillimeters(s string) (Millimeters, bool) { return Millimeters(v), true } -// ParseMillimeters is lenient: unparseable input becomes 0, then clamps. +// ParseMillimeters is lenient: unparseable input becomes 0. func ParseMillimeters(s string) Millimeters { v := parseDecimal(s) return Millimeters(v) diff --git a/gen/test/go/mx/non_negative_decimal.go b/gen/test/go/mx/non_negative_decimal.go index 1ebccd45a..5fa33ba00 100644 --- a/gen/test/go/mx/non_negative_decimal.go +++ b/gen/test/go/mx/non_negative_decimal.go @@ -5,7 +5,7 @@ package mx // The non-negative-decimal type specifies a non-negative decimal value. type NonNegativeDecimal float64 -// TryParseNonNegativeDecimal parses s strictly, then clamps into the declared range. +// TryParseNonNegativeDecimal parses s as a lexically well-formed value, then clamps into the declared range. func TryParseNonNegativeDecimal(s string) (NonNegativeDecimal, bool) { v, ok := tryParseDecimal(s) if !ok { @@ -14,7 +14,7 @@ func TryParseNonNegativeDecimal(s string) (NonNegativeDecimal, bool) { return clampNonNegativeDecimal(v), true } -// ParseNonNegativeDecimal is lenient: unparseable input becomes 0, then clamps. +// ParseNonNegativeDecimal is lenient: unparseable input becomes 0, then clamps into the declared range. func ParseNonNegativeDecimal(s string) NonNegativeDecimal { v := parseDecimal(s) return clampNonNegativeDecimal(v) diff --git a/gen/test/go/mx/number_level.go b/gen/test/go/mx/number_level.go index 06e842187..5f9a72e0a 100644 --- a/gen/test/go/mx/number_level.go +++ b/gen/test/go/mx/number_level.go @@ -9,7 +9,7 @@ package mx // number-level value is optional, the value is 1 by default. type NumberLevel int -// TryParseNumberLevel parses s strictly, then clamps into the declared range. +// TryParseNumberLevel parses s as a lexically well-formed value, then clamps into the declared range. func TryParseNumberLevel(s string) (NumberLevel, bool) { v, ok := tryParseInt(s) if !ok { @@ -18,7 +18,7 @@ func TryParseNumberLevel(s string) (NumberLevel, bool) { return clampNumberLevel(v), true } -// ParseNumberLevel is lenient: unparseable input becomes 0, then clamps. +// ParseNumberLevel is lenient: unparseable input becomes 0, then clamps into the declared range. func ParseNumberLevel(s string) NumberLevel { v := parseInt(s) return clampNumberLevel(v) diff --git a/gen/test/go/mx/number_of_lines.go b/gen/test/go/mx/number_of_lines.go index 4b8875de2..905e5f0f1 100644 --- a/gen/test/go/mx/number_of_lines.go +++ b/gen/test/go/mx/number_of_lines.go @@ -5,7 +5,7 @@ package mx // The number-of-lines type is used to specify the number of lines in text decoration attributes. type NumberOfLines int -// TryParseNumberOfLines parses s strictly, then clamps into the declared range. +// TryParseNumberOfLines parses s as a lexically well-formed value, then clamps into the declared range. func TryParseNumberOfLines(s string) (NumberOfLines, bool) { v, ok := tryParseInt(s) if !ok { @@ -14,7 +14,7 @@ func TryParseNumberOfLines(s string) (NumberOfLines, bool) { return clampNumberOfLines(v), true } -// ParseNumberOfLines is lenient: unparseable input becomes 0, then clamps. +// ParseNumberOfLines is lenient: unparseable input becomes 0, then clamps into the declared range. func ParseNumberOfLines(s string) NumberOfLines { v := parseInt(s) return clampNumberOfLines(v) diff --git a/gen/test/go/mx/number_or_normal.go b/gen/test/go/mx/number_or_normal.go index 7d99e66ef..53ef34e37 100644 --- a/gen/test/go/mx/number_or_normal.go +++ b/gen/test/go/mx/number_or_normal.go @@ -12,17 +12,17 @@ type NumberOrNormal struct { type NumberOrNormalKind int const ( - NumberOrNormalKindDecimal NumberOrNormalKind = iota - NumberOrNormalKindNormal + NumberOrNormalDecimal NumberOrNormalKind = iota + NumberOrNormalNormal ) // TryParseNumberOrNormal tries each union member in schema order. func TryParseNumberOrNormal(s string) (NumberOrNormal, bool) { if v, ok := tryParseDecimal(s); ok { - return NumberOrNormal{Kind: NumberOrNormalKindDecimal, Decimal: v}, true + return NumberOrNormal{Kind: NumberOrNormalDecimal, Decimal: v}, true } if s == "normal" { - return NumberOrNormal{Kind: NumberOrNormalKindNormal}, true + return NumberOrNormal{Kind: NumberOrNormalNormal}, true } return NumberOrNormal{}, false } @@ -33,13 +33,13 @@ func ParseNumberOrNormal(s string) NumberOrNormal { if v, ok := TryParseNumberOrNormal(s); ok { return v } - return NumberOrNormal{Kind: NumberOrNormalKindDecimal, Decimal: parseDecimal(s)} + return NumberOrNormal{Kind: NumberOrNormalDecimal, Decimal: parseDecimal(s)} } // String returns the wire spelling of whichever member is held. func (v NumberOrNormal) String() string { switch v.Kind { - case NumberOrNormalKindNormal: + case NumberOrNormalNormal: return "normal" } return formatDecimal(v.Decimal) diff --git a/gen/test/go/mx/octave.go b/gen/test/go/mx/octave.go index 47010d46f..9ad3ed044 100644 --- a/gen/test/go/mx/octave.go +++ b/gen/test/go/mx/octave.go @@ -5,7 +5,7 @@ package mx // Octaves are represented by the numbers 0 to 9, where 4 indicates the octave started by middle C. type Octave int -// TryParseOctave parses s strictly, then clamps into the declared range. +// TryParseOctave parses s as a lexically well-formed value, then clamps into the declared range. func TryParseOctave(s string) (Octave, bool) { v, ok := tryParseInt(s) if !ok { @@ -14,7 +14,7 @@ func TryParseOctave(s string) (Octave, bool) { return clampOctave(v), true } -// ParseOctave is lenient: unparseable input becomes 0, then clamps. +// ParseOctave is lenient: unparseable input becomes 0, then clamps into the declared range. func ParseOctave(s string) Octave { v := parseInt(s) return clampOctave(v) diff --git a/gen/test/go/mx/percent.go b/gen/test/go/mx/percent.go index 7083ffc7e..fccee4253 100644 --- a/gen/test/go/mx/percent.go +++ b/gen/test/go/mx/percent.go @@ -5,7 +5,7 @@ package mx // The percent type specifies a percentage from 0 to 100. type Percent float64 -// TryParsePercent parses s strictly, then clamps into the declared range. +// TryParsePercent parses s as a lexically well-formed value, then clamps into the declared range. func TryParsePercent(s string) (Percent, bool) { v, ok := tryParseDecimal(s) if !ok { @@ -14,7 +14,7 @@ func TryParsePercent(s string) (Percent, bool) { return clampPercent(v), true } -// ParsePercent is lenient: unparseable input becomes 0, then clamps. +// ParsePercent is lenient: unparseable input becomes 0, then clamps into the declared range. func ParsePercent(s string) Percent { v := parseDecimal(s) return clampPercent(v) diff --git a/gen/test/go/mx/positive_divisions.go b/gen/test/go/mx/positive_divisions.go index f259c0ff8..dede987e1 100644 --- a/gen/test/go/mx/positive_divisions.go +++ b/gen/test/go/mx/positive_divisions.go @@ -5,7 +5,7 @@ package mx // The positive-divisions type restricts divisions values to positive numbers. type PositiveDivisions float64 -// TryParsePositiveDivisions parses s strictly, then clamps into the declared range. +// TryParsePositiveDivisions parses s as a lexically well-formed value, then clamps into the declared range. func TryParsePositiveDivisions(s string) (PositiveDivisions, bool) { v, ok := tryParseDecimal(s) if !ok { @@ -14,7 +14,7 @@ func TryParsePositiveDivisions(s string) (PositiveDivisions, bool) { return clampPositiveDivisions(v), true } -// ParsePositiveDivisions is lenient: unparseable input becomes 0, then clamps. +// ParsePositiveDivisions is lenient: unparseable input becomes 0, then clamps into the declared range. func ParsePositiveDivisions(s string) PositiveDivisions { v := parseDecimal(s) return clampPositiveDivisions(v) diff --git a/gen/test/go/mx/positive_integer_or_empty.go b/gen/test/go/mx/positive_integer_or_empty.go index 43abd5128..ebba1c8b7 100644 --- a/gen/test/go/mx/positive_integer_or_empty.go +++ b/gen/test/go/mx/positive_integer_or_empty.go @@ -11,17 +11,20 @@ type PositiveIntegerOrEmpty struct { type PositiveIntegerOrEmptyKind int const ( - PositiveIntegerOrEmptyKindPositiveInteger PositiveIntegerOrEmptyKind = iota - PositiveIntegerOrEmptyKindEmpty + PositiveIntegerOrEmptyPositiveInteger PositiveIntegerOrEmptyKind = iota + PositiveIntegerOrEmptyEmpty ) // TryParsePositiveIntegerOrEmpty tries each union member in schema order. func TryParsePositiveIntegerOrEmpty(s string) (PositiveIntegerOrEmpty, bool) { if v, ok := tryParseInt(s); ok { - return PositiveIntegerOrEmpty{Kind: PositiveIntegerOrEmptyKindPositiveInteger, PositiveInteger: v}, true + if v < 1 { + v = 1 + } + return PositiveIntegerOrEmpty{Kind: PositiveIntegerOrEmptyPositiveInteger, PositiveInteger: v}, true } if s == "" { - return PositiveIntegerOrEmpty{Kind: PositiveIntegerOrEmptyKindEmpty}, true + return PositiveIntegerOrEmpty{Kind: PositiveIntegerOrEmptyEmpty}, true } return PositiveIntegerOrEmpty{}, false } @@ -32,13 +35,17 @@ func ParsePositiveIntegerOrEmpty(s string) PositiveIntegerOrEmpty { if v, ok := TryParsePositiveIntegerOrEmpty(s); ok { return v } - return PositiveIntegerOrEmpty{Kind: PositiveIntegerOrEmptyKindPositiveInteger, PositiveInteger: parseInt(s)} + v := parseInt(s) + if v < 1 { + v = 1 + } + return PositiveIntegerOrEmpty{Kind: PositiveIntegerOrEmptyPositiveInteger, PositiveInteger: v} } // String returns the wire spelling of whichever member is held. func (v PositiveIntegerOrEmpty) String() string { switch v.Kind { - case PositiveIntegerOrEmptyKindEmpty: + case PositiveIntegerOrEmptyEmpty: return "" } return formatInt(v.PositiveInteger) diff --git a/gen/test/go/mx/rotation_degrees.go b/gen/test/go/mx/rotation_degrees.go index 7d3bbb0b6..6e6fc3414 100644 --- a/gen/test/go/mx/rotation_degrees.go +++ b/gen/test/go/mx/rotation_degrees.go @@ -6,7 +6,7 @@ package mx // from -180 to 180. type RotationDegrees float64 -// TryParseRotationDegrees parses s strictly, then clamps into the declared range. +// TryParseRotationDegrees parses s as a lexically well-formed value, then clamps into the declared range. func TryParseRotationDegrees(s string) (RotationDegrees, bool) { v, ok := tryParseDecimal(s) if !ok { @@ -15,7 +15,7 @@ func TryParseRotationDegrees(s string) (RotationDegrees, bool) { return clampRotationDegrees(v), true } -// ParseRotationDegrees is lenient: unparseable input becomes 0, then clamps. +// ParseRotationDegrees is lenient: unparseable input becomes 0, then clamps into the declared range. func ParseRotationDegrees(s string) RotationDegrees { v := parseDecimal(s) return clampRotationDegrees(v) diff --git a/gen/test/go/mx/runtime.go b/gen/test/go/mx/runtime.go index ca866566d..c48378e3c 100644 --- a/gen/test/go/mx/runtime.go +++ b/gen/test/go/mx/runtime.go @@ -47,8 +47,12 @@ func parseInt(s string) int { } // formatDecimal prints the shortest decimal that round-trips the value, -// without exponent notation (8.5 -> "8.5", 4 -> "4"). +// without exponent notation (8.5 -> "8.5", 4 -> "4"). Negative zero +// canonicalizes to "0" (matching the C runtime and the corert normalizer). func formatDecimal(v float64) string { + if v == 0 { + return "0" + } return strconv.FormatFloat(v, 'f', -1, 64) } diff --git a/gen/test/go/mx/semitones.go b/gen/test/go/mx/semitones.go index a1893ea1b..32fbde1f3 100644 --- a/gen/test/go/mx/semitones.go +++ b/gen/test/go/mx/semitones.go @@ -7,7 +7,7 @@ package mx // sharp) are used for microtones. type Semitones float64 -// TryParseSemitones parses s strictly, then clamps into the declared range. +// TryParseSemitones parses s as a lexically well-formed value. func TryParseSemitones(s string) (Semitones, bool) { v, ok := tryParseDecimal(s) if !ok { @@ -16,7 +16,7 @@ func TryParseSemitones(s string) (Semitones, bool) { return Semitones(v), true } -// ParseSemitones is lenient: unparseable input becomes 0, then clamps. +// ParseSemitones is lenient: unparseable input becomes 0. func ParseSemitones(s string) Semitones { v := parseDecimal(s) return Semitones(v) diff --git a/gen/test/go/mx/staff_line.go b/gen/test/go/mx/staff_line.go index 72371a92e..5750a4729 100644 --- a/gen/test/go/mx/staff_line.go +++ b/gen/test/go/mx/staff_line.go @@ -7,7 +7,7 @@ package mx // outside the staff, such as a C clef positioned in the middle of a grand staff. type StaffLine int -// TryParseStaffLine parses s strictly, then clamps into the declared range. +// TryParseStaffLine parses s as a lexically well-formed value. func TryParseStaffLine(s string) (StaffLine, bool) { v, ok := tryParseInt(s) if !ok { @@ -16,7 +16,7 @@ func TryParseStaffLine(s string) (StaffLine, bool) { return StaffLine(v), true } -// ParseStaffLine is lenient: unparseable input becomes 0, then clamps. +// ParseStaffLine is lenient: unparseable input becomes 0. func ParseStaffLine(s string) StaffLine { v := parseInt(s) return StaffLine(v) diff --git a/gen/test/go/mx/staff_number.go b/gen/test/go/mx/staff_number.go index cbd5d87f0..8ef73bdfa 100644 --- a/gen/test/go/mx/staff_number.go +++ b/gen/test/go/mx/staff_number.go @@ -6,7 +6,7 @@ package mx // top to bottom, with 1 being the top staff on a part. type StaffNumber int -// TryParseStaffNumber parses s strictly, then clamps into the declared range. +// TryParseStaffNumber parses s as a lexically well-formed value, then clamps into the declared range. func TryParseStaffNumber(s string) (StaffNumber, bool) { v, ok := tryParseInt(s) if !ok { @@ -15,7 +15,7 @@ func TryParseStaffNumber(s string) (StaffNumber, bool) { return clampStaffNumber(v), true } -// ParseStaffNumber is lenient: unparseable input becomes 0, then clamps. +// ParseStaffNumber is lenient: unparseable input becomes 0, then clamps into the declared range. func ParseStaffNumber(s string) StaffNumber { v := parseInt(s) return clampStaffNumber(v) diff --git a/gen/test/go/mx/string_number.go b/gen/test/go/mx/string_number.go index 83fbf9dd9..9a8069f45 100644 --- a/gen/test/go/mx/string_number.go +++ b/gen/test/go/mx/string_number.go @@ -6,7 +6,7 @@ package mx // being the highest pitched full-length string. type StringNumber int -// TryParseStringNumber parses s strictly, then clamps into the declared range. +// TryParseStringNumber parses s as a lexically well-formed value, then clamps into the declared range. func TryParseStringNumber(s string) (StringNumber, bool) { v, ok := tryParseInt(s) if !ok { @@ -15,7 +15,7 @@ func TryParseStringNumber(s string) (StringNumber, bool) { return clampStringNumber(v), true } -// ParseStringNumber is lenient: unparseable input becomes 0, then clamps. +// ParseStringNumber is lenient: unparseable input becomes 0, then clamps into the declared range. func ParseStringNumber(s string) StringNumber { v := parseInt(s) return clampStringNumber(v) diff --git a/gen/test/go/mx/tenths.go b/gen/test/go/mx/tenths.go index 440cfff2b..b9c314a08 100644 --- a/gen/test/go/mx/tenths.go +++ b/gen/test/go/mx/tenths.go @@ -12,7 +12,7 @@ package mx // staff-size element. type Tenths float64 -// TryParseTenths parses s strictly, then clamps into the declared range. +// TryParseTenths parses s as a lexically well-formed value. func TryParseTenths(s string) (Tenths, bool) { v, ok := tryParseDecimal(s) if !ok { @@ -21,7 +21,7 @@ func TryParseTenths(s string) (Tenths, bool) { return Tenths(v), true } -// ParseTenths is lenient: unparseable input becomes 0, then clamps. +// ParseTenths is lenient: unparseable input becomes 0. func ParseTenths(s string) Tenths { v := parseDecimal(s) return Tenths(v) diff --git a/gen/test/go/mx/tremolo_marks.go b/gen/test/go/mx/tremolo_marks.go index dda601f0c..c1a7c8188 100644 --- a/gen/test/go/mx/tremolo_marks.go +++ b/gen/test/go/mx/tremolo_marks.go @@ -6,7 +6,7 @@ package mx // added. type TremoloMarks int -// TryParseTremoloMarks parses s strictly, then clamps into the declared range. +// TryParseTremoloMarks parses s as a lexically well-formed value, then clamps into the declared range. func TryParseTremoloMarks(s string) (TremoloMarks, bool) { v, ok := tryParseInt(s) if !ok { @@ -15,7 +15,7 @@ func TryParseTremoloMarks(s string) (TremoloMarks, bool) { return clampTremoloMarks(v), true } -// ParseTremoloMarks is lenient: unparseable input becomes 0, then clamps. +// ParseTremoloMarks is lenient: unparseable input becomes 0, then clamps into the declared range. func ParseTremoloMarks(s string) TremoloMarks { v := parseInt(s) return clampTremoloMarks(v) diff --git a/gen/test/go/mx/trill_beats.go b/gen/test/go/mx/trill_beats.go index 227178b39..beda96e14 100644 --- a/gen/test/go/mx/trill_beats.go +++ b/gen/test/go/mx/trill_beats.go @@ -6,7 +6,7 @@ package mx // is a decimal value with a minimum value of 2. type TrillBeats float64 -// TryParseTrillBeats parses s strictly, then clamps into the declared range. +// TryParseTrillBeats parses s as a lexically well-formed value, then clamps into the declared range. func TryParseTrillBeats(s string) (TrillBeats, bool) { v, ok := tryParseDecimal(s) if !ok { @@ -15,7 +15,7 @@ func TryParseTrillBeats(s string) (TrillBeats, bool) { return clampTrillBeats(v), true } -// ParseTrillBeats is lenient: unparseable input becomes 0, then clamps. +// ParseTrillBeats is lenient: unparseable input becomes 0, then clamps into the declared range. func ParseTrillBeats(s string) TrillBeats { v := parseDecimal(s) return clampTrillBeats(v) diff --git a/gen/test/go/mx/yes_no_number.go b/gen/test/go/mx/yes_no_number.go index 85f2d5b72..3025c0774 100644 --- a/gen/test/go/mx/yes_no_number.go +++ b/gen/test/go/mx/yes_no_number.go @@ -12,17 +12,17 @@ type YesNoNumber struct { type YesNoNumberKind int const ( - YesNoNumberKindYesNo YesNoNumberKind = iota - YesNoNumberKindDecimal + YesNoNumberYesNo YesNoNumberKind = iota + YesNoNumberDecimal ) // TryParseYesNoNumber tries each union member in schema order. func TryParseYesNoNumber(s string) (YesNoNumber, bool) { if v, ok := TryParseYesNo(s); ok { - return YesNoNumber{Kind: YesNoNumberKindYesNo, YesNo: v}, true + return YesNoNumber{Kind: YesNoNumberYesNo, YesNo: v}, true } if v, ok := tryParseDecimal(s); ok { - return YesNoNumber{Kind: YesNoNumberKindDecimal, Decimal: v}, true + return YesNoNumber{Kind: YesNoNumberDecimal, Decimal: v}, true } return YesNoNumber{}, false } @@ -33,13 +33,13 @@ func ParseYesNoNumber(s string) YesNoNumber { if v, ok := TryParseYesNoNumber(s); ok { return v } - return YesNoNumber{Kind: YesNoNumberKindYesNo, YesNo: ParseYesNo(s)} + return YesNoNumber{Kind: YesNoNumberYesNo, YesNo: ParseYesNo(s)} } // String returns the wire spelling of whichever member is held. func (v YesNoNumber) String() string { switch v.Kind { - case YesNoNumberKindDecimal: + case YesNoNumberDecimal: return formatDecimal(v.Decimal) } return v.YesNo.String() diff --git a/gen/tests/test_plates.py b/gen/tests/test_plates.py index 974c2535b..4361faa8e 100644 --- a/gen/tests/test_plates.py +++ b/gen/tests/test_plates.py @@ -307,6 +307,78 @@ def test_derived_all_members_built_for_inheriting_targets(self): ) +class ClampPolicy(unittest.TestCase): + """The leniency policy is data on the plates (one decision, two backend + spellings): facet bounds plus primitive-implied bounds, tightest wins, + exclusive bounds clamping to the nearest representable in-range value.""" + + def _steps(self, base, **bounds): + from gen.plates.build import clamp_steps + from gen.plates.model import NumberBounds + + return [ + (s.op, s.bound, s.replacement) + for s in clamp_steps(base, NumberBounds(**bounds)) + ] + + def test_inclusive_bounds(self): + self.assertEqual( + self._steps("integer", min_inclusive="1", max_inclusive="16"), + [("<", "1", "1"), (">", "16", "16")], + ) + + def test_exclusive_decimal_clamps_past_epsilon(self): + self.assertEqual( + self._steps("decimal", min_exclusive="0"), + [("<=", "0.0", "1e-06")], + ) + + def test_exclusive_integer_clamps_to_next(self): + self.assertEqual( + self._steps("integer", min_exclusive="0", max_exclusive="10"), + [("<=", "0", "1"), (">=", "10", "9")], + ) + + def test_exclusive_beats_inclusive_at_same_value(self): + self.assertEqual( + self._steps("decimal", min_inclusive="0", min_exclusive="0"), + [("<=", "0.0", "1e-06")], + ) + self.assertEqual( + self._steps("decimal", max_inclusive="5", max_exclusive="5"), + [(">=", "5.0", "4.999999")], + ) + + def test_implied_minimum_merges_with_facets(self): + # positive_integer implies >= 1 even with a looser explicit min. + self.assertEqual( + self._steps("positive_integer", min_inclusive="0"), + [("<", "1", "1")], + ) + self.assertEqual(self._steps("non_negative_integer"), [("<", "0", "0")]) + self.assertEqual(self._steps("decimal"), []) + + def test_union_primitive_member_carries_implied_clamp(self): + m = tiny_ir() + m.value_types.append( + ir.UnionType( + "positive-or-empty", + [ + ir.UnionMember(ir.Ref("positive_integer", "primitive")), + ir.UnionMember(literals=[""]), + ], + ) + ) + plates = build_plates(m, Config()) + union = plates.plate("positive-or-empty") + member = union.members[0] + self.assertEqual( + [(s.op, s.bound, s.replacement) for s in member.clamp], + [("<", "1", "1")], + ) + self.assertIsNotNone(member.tag) # discriminator constant is final + + class RealTargets(unittest.TestCase): """The shipped configs must project cleanly, deterministically, and with the spot-checkable facts the emitters will lean on.""" From 49d64419e39bb9f458eab34a18c34d903fae1a81 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 19:14:45 +0000 Subject: [PATCH 23/45] gen/emit: C complex-type templates; the C corert suite is green The C backend now renders all four complex shapes and the document entry points; the corert harness drives the generated model instead of the stub. Both secondary targets now round-trip the corpus: 776 pass, 0 fail, 52 version-skipped, in C and Go alike. The C spelling of the same representation Go uses: presence-tracked attributes (bool has_x + value), children as ONE ordered array of structs whose typed pointers discriminate by non-NULL, strict about names, lenient about values. C has no inheritance, so derived types flatten the plates' all_members view into self-contained structs. gen/emit/c/api.py is the value calling convention -- the single place that knows how generated C parses, prints, stores, and frees each plate kind (enum to_string static, number/union malloc'd, string values own themselves) -- consumed at every attribute, text body, and leaf child instead of inline ownership reasoning. Parse errors flow through a runtime message channel (mx_error_set/mx_error) so parse functions return NULL with context instead of threading buffers. Serialization returns the created node (parent NULL -> free node) so the document root and nested elements share one code path; root namespace declarations are preserved through MxDocument (libxml2 keeps them in nsDef, so attribute loops never see them). Harness changes (gen/test/c/): - stub.h/stub.c deleted; roundtrip.c drives mx_document_* and gates documents declaring MusicXML > 3.1 (counted as skipped). - normalize.c strips whitespace-only text nodes everywhere (mirroring Go) and sorts attributes by QUALIFIED name. - compare.c compares each element's DIRECT text only (xmlNodeGetContent's subtree concatenation re-compared every leaf at every ancestor, so one numerically-equivalent reformat failed all its ancestors) and compares attributes by qualified name with entity-resolved values (a parsed xlink:href is (ns, href); a serialized one is the literal name). - mx-c carries the libxml2 include path; corert-c links the model. Corpus: lysuite/ly33d_Spanners_OctaveShifts.xml is marked .invalid -- it begins with stray bytes before the XML declaration, so it is not well-formed XML; strict parsers (libxml2) are entitled to reject it (Go's etree merely happens to tolerate leading garbage). --- .../ly33d_Spanners_OctaveShifts.xml.invalid | 2 + gen/emit/c/__init__.py | 16 +- gen/emit/c/api.py | 73 +++ gen/emit/c/complexes.py | 349 +++++++++++++ gen/emit/c/document.py | 144 ++++++ gen/emit/c/runtime.py | 21 +- gen/test/c/CMakeLists.txt | 6 +- gen/test/c/mx/mx_accidental.c | 171 +++++++ gen/test/c/mx/mx_accidental.h | 66 +++ gen/test/c/mx/mx_accidental_mark.c | 173 +++++++ gen/test/c/mx/mx_accidental_mark.h | 65 +++ gen/test/c/mx/mx_accidental_text.c | 239 +++++++++ gen/test/c/mx/mx_accidental_text.h | 85 ++++ gen/test/c/mx/mx_accord.c | 120 +++++ gen/test/c/mx/mx_accord.h | 40 ++ gen/test/c/mx/mx_accordion_registration.c | 198 ++++++++ gen/test/c/mx/mx_accordion_registration.h | 71 +++ gen/test/c/mx/mx_appearance.c | 125 +++++ gen/test/c/mx/mx_appearance.h | 43 ++ gen/test/c/mx/mx_arpeggiate.c | 124 +++++ gen/test/c/mx/mx_arpeggiate.h | 48 ++ gen/test/c/mx/mx_arrow.c | 202 ++++++++ gen/test/c/mx/mx_arrow.h | 72 +++ gen/test/c/mx/mx_articulations.c | 253 ++++++++++ gen/test/c/mx/mx_articulations.h | 55 ++ gen/test/c/mx/mx_attributes.c | 214 ++++++++ gen/test/c/mx/mx_attributes.h | 56 +++ gen/test/c/mx/mx_backup.c | 108 ++++ gen/test/c/mx/mx_backup.h | 40 ++ gen/test/c/mx/mx_bar_style_color.c | 71 +++ gen/test/c/mx/mx_bar_style_color.h | 27 + gen/test/c/mx/mx_barline.c | 203 ++++++++ gen/test/c/mx/mx_barline.h | 74 +++ gen/test/c/mx/mx_barre.c | 70 +++ gen/test/c/mx/mx_barre.h | 30 ++ gen/test/c/mx/mx_bass.c | 95 ++++ gen/test/c/mx/mx_bass.h | 36 ++ gen/test/c/mx/mx_bass_alter.c | 149 ++++++ gen/test/c/mx/mx_bass_alter.h | 58 +++ gen/test/c/mx/mx_bass_step.c | 141 ++++++ gen/test/c/mx/mx_bass_step.h | 52 ++ gen/test/c/mx/mx_beam.c | 99 ++++ gen/test/c/mx/mx_beam.h | 47 ++ gen/test/c/mx/mx_beat_repeat.c | 124 +++++ gen/test/c/mx/mx_beat_repeat.h | 49 ++ gen/test/c/mx/mx_beat_unit_tied.c | 96 ++++ gen/test/c/mx/mx_beat_unit_tied.h | 37 ++ gen/test/c/mx/mx_beater.c | 69 +++ gen/test/c/mx/mx_beater.h | 28 ++ gen/test/c/mx/mx_bend.c | 218 ++++++++ gen/test/c/mx/mx_bend.h | 77 +++ gen/test/c/mx/mx_bookmark.c | 88 ++++ gen/test/c/mx/mx_bookmark.h | 30 ++ gen/test/c/mx/mx_bracket.c | 154 ++++++ gen/test/c/mx/mx_bracket.h | 57 +++ gen/test/c/mx/mx_breath_mark.c | 139 +++++ gen/test/c/mx/mx_breath_mark.h | 51 ++ gen/test/c/mx/mx_caesura.c | 139 +++++ gen/test/c/mx/mx_caesura.h | 52 ++ gen/test/c/mx/mx_cancel.c | 73 +++ gen/test/c/mx/mx_cancel.h | 31 ++ gen/test/c/mx/mx_clef.c | 222 ++++++++ gen/test/c/mx/mx_clef.h | 84 ++++ gen/test/c/mx/mx_coda.c | 154 ++++++ gen/test/c/mx/mx_coda.h | 58 +++ gen/test/c/mx/mx_credit.c | 149 ++++++ gen/test/c/mx/mx_credit.h | 59 +++ gen/test/c/mx/mx_dashes.c | 134 +++++ gen/test/c/mx/mx_dashes.h | 46 ++ gen/test/c/mx/mx_defaults.c | 165 ++++++ gen/test/c/mx/mx_defaults.h | 47 ++ gen/test/c/mx/mx_degree.c | 111 ++++ gen/test/c/mx/mx_degree.h | 44 ++ gen/test/c/mx/mx_degree_alter.c | 143 ++++++ gen/test/c/mx/mx_degree_alter.h | 56 +++ gen/test/c/mx/mx_degree_type.c | 141 ++++++ gen/test/c/mx/mx_degree_type.h | 53 ++ gen/test/c/mx/mx_degree_value.c | 151 ++++++ gen/test/c/mx/mx_degree_value.h | 56 +++ gen/test/c/mx/mx_direction.c | 166 ++++++ gen/test/c/mx/mx_direction.h | 57 +++ gen/test/c/mx/mx_direction_type.c | 323 ++++++++++++ gen/test/c/mx/mx_direction_type.h | 79 +++ gen/test/c/mx/mx_directive.c | 142 ++++++ gen/test/c/mx/mx_directive.h | 46 ++ gen/test/c/mx/mx_distance.c | 75 +++ gen/test/c/mx/mx_distance.h | 29 ++ gen/test/c/mx/mx_document.c | 83 +++ gen/test/c/mx/mx_document.h | 31 ++ gen/test/c/mx/mx_dynamics.c | 471 +++++++++++++++++ gen/test/c/mx/mx_dynamics.h | 115 +++++ gen/test/c/mx/mx_elision.c | 110 ++++ gen/test/c/mx/mx_elision.h | 47 ++ gen/test/c/mx/mx_empty.c | 57 +++ gen/test/c/mx/mx_empty.h | 23 + gen/test/c/mx/mx_empty_font.c | 86 ++++ gen/test/c/mx/mx_empty_font.h | 34 ++ gen/test/c/mx/mx_empty_line.c | 166 ++++++ gen/test/c/mx/mx_empty_line.h | 63 +++ gen/test/c/mx/mx_empty_placement.c | 132 +++++ gen/test/c/mx/mx_empty_placement.h | 49 ++ gen/test/c/mx/mx_empty_placement_smufl.c | 140 ++++++ gen/test/c/mx/mx_empty_placement_smufl.h | 53 ++ .../c/mx/mx_empty_print_object_style_align.c | 144 ++++++ .../c/mx/mx_empty_print_object_style_align.h | 56 +++ gen/test/c/mx/mx_empty_print_style_align_id.c | 146 ++++++ gen/test/c/mx/mx_empty_print_style_align_id.h | 55 ++ gen/test/c/mx/mx_empty_trill_sound.c | 180 +++++++ gen/test/c/mx/mx_empty_trill_sound.h | 70 +++ gen/test/c/mx/mx_encoding.c | 125 +++++ gen/test/c/mx/mx_encoding.h | 41 ++ gen/test/c/mx/mx_ending.c | 178 +++++++ gen/test/c/mx/mx_ending.h | 74 +++ gen/test/c/mx/mx_extend.c | 102 ++++ gen/test/c/mx/mx_extend.h | 39 ++ gen/test/c/mx/mx_feature.c | 72 +++ gen/test/c/mx/mx_feature.h | 27 + gen/test/c/mx/mx_fermata.c | 147 ++++++ gen/test/c/mx/mx_fermata.h | 54 ++ gen/test/c/mx/mx_figure.c | 135 +++++ gen/test/c/mx/mx_figure.h | 40 ++ gen/test/c/mx/mx_figured_bass.c | 226 +++++++++ gen/test/c/mx/mx_figured_bass.h | 78 +++ gen/test/c/mx/mx_fingering.c | 152 ++++++ gen/test/c/mx/mx_fingering.h | 58 +++ gen/test/c/mx/mx_first_fret.c | 81 +++ gen/test/c/mx/mx_first_fret.h | 31 ++ gen/test/c/mx/mx_formatted_symbol_id.c | 224 +++++++++ gen/test/c/mx/mx_formatted_symbol_id.h | 80 +++ gen/test/c/mx/mx_formatted_text.c | 232 +++++++++ gen/test/c/mx/mx_formatted_text.h | 80 +++ gen/test/c/mx/mx_formatted_text_id.c | 240 +++++++++ gen/test/c/mx/mx_formatted_text_id.h | 82 +++ gen/test/c/mx/mx_forward.c | 129 +++++ gen/test/c/mx/mx_forward.h | 41 ++ gen/test/c/mx/mx_frame.c | 205 ++++++++ gen/test/c/mx/mx_frame.h | 66 +++ gen/test/c/mx/mx_frame_note.c | 115 +++++ gen/test/c/mx/mx_frame_note.h | 39 ++ gen/test/c/mx/mx_fret.c | 105 ++++ gen/test/c/mx/mx_fret.h | 39 ++ gen/test/c/mx/mx_glass.c | 71 +++ gen/test/c/mx/mx_glass.h | 29 ++ gen/test/c/mx/mx_glissando.c | 178 +++++++ gen/test/c/mx/mx_glissando.h | 65 +++ gen/test/c/mx/mx_glyph.c | 72 +++ gen/test/c/mx/mx_glyph.h | 33 ++ gen/test/c/mx/mx_grace.c | 86 ++++ gen/test/c/mx/mx_grace.h | 39 ++ gen/test/c/mx/mx_group_barline.c | 71 +++ gen/test/c/mx/mx_group_barline.h | 27 + gen/test/c/mx/mx_group_name.c | 140 ++++++ gen/test/c/mx/mx_group_name.h | 52 ++ gen/test/c/mx/mx_group_symbol.c | 103 ++++ gen/test/c/mx/mx_group_symbol.h | 36 ++ gen/test/c/mx/mx_grouping.c | 115 +++++ gen/test/c/mx/mx_grouping.h | 47 ++ gen/test/c/mx/mx_hammer_on_pull_off.c | 154 ++++++ gen/test/c/mx/mx_hammer_on_pull_off.h | 60 +++ gen/test/c/mx/mx_handbell.c | 139 +++++ gen/test/c/mx/mx_handbell.h | 52 ++ gen/test/c/mx/mx_harmon_closed.c | 69 +++ gen/test/c/mx/mx_harmon_closed.h | 29 ++ gen/test/c/mx/mx_harmon_mute.c | 161 ++++++ gen/test/c/mx/mx_harmon_mute.h | 59 +++ gen/test/c/mx/mx_harmonic.c | 207 ++++++++ gen/test/c/mx/mx_harmonic.h | 71 +++ gen/test/c/mx/mx_harmony.c | 290 +++++++++++ gen/test/c/mx/mx_harmony.h | 96 ++++ gen/test/c/mx/mx_harp_pedals.c | 175 +++++++ gen/test/c/mx/mx_harp_pedals.h | 67 +++ gen/test/c/mx/mx_heel_toe.c | 138 +++++ gen/test/c/mx/mx_heel_toe.h | 54 ++ gen/test/c/mx/mx_hole.c | 177 +++++++ gen/test/c/mx/mx_hole.h | 62 +++ gen/test/c/mx/mx_hole_closed.c | 69 +++ gen/test/c/mx/mx_hole_closed.h | 29 ++ gen/test/c/mx/mx_horizontal_turn.c | 186 +++++++ gen/test/c/mx/mx_horizontal_turn.h | 73 +++ gen/test/c/mx/mx_identification.c | 133 +++++ gen/test/c/mx/mx_identification.h | 41 ++ gen/test/c/mx/mx_image.c | 140 ++++++ gen/test/c/mx/mx_image.h | 47 ++ gen/test/c/mx/mx_instrument.c | 64 +++ gen/test/c/mx/mx_instrument.h | 26 + gen/test/c/mx/mx_interchangeable.c | 114 +++++ gen/test/c/mx/mx_interchangeable.h | 42 ++ gen/test/c/mx/mx_inversion.c | 137 +++++ gen/test/c/mx/mx_inversion.h | 48 ++ gen/test/c/mx/mx_key.c | 248 +++++++++ gen/test/c/mx/mx_key.h | 79 +++ gen/test/c/mx/mx_key_accidental.c | 71 +++ gen/test/c/mx/mx_key_accidental.h | 29 ++ gen/test/c/mx/mx_key_octave.c | 81 +++ gen/test/c/mx/mx_key_octave.h | 35 ++ gen/test/c/mx/mx_kind.c | 177 +++++++ gen/test/c/mx/mx_kind.h | 80 +++ gen/test/c/mx/mx_level.c | 88 ++++ gen/test/c/mx/mx_level.h | 37 ++ gen/test/c/mx/mx_line_width.c | 75 +++ gen/test/c/mx/mx_line_width.h | 30 ++ gen/test/c/mx/mx_link.c | 160 ++++++ gen/test/c/mx/mx_link.h | 51 ++ gen/test/c/mx/mx_lyric.c | 266 ++++++++++ gen/test/c/mx/mx_lyric.h | 86 ++++ gen/test/c/mx/mx_lyric_font.c | 102 ++++ gen/test/c/mx/mx_lyric_font.h | 38 ++ gen/test/c/mx/mx_lyric_language.c | 80 +++ gen/test/c/mx/mx_lyric_language.h | 28 ++ gen/test/c/mx/mx_measure_layout.c | 88 ++++ gen/test/c/mx/mx_measure_layout.h | 32 ++ gen/test/c/mx/mx_measure_numbering.c | 145 ++++++ gen/test/c/mx/mx_measure_numbering.h | 57 +++ gen/test/c/mx/mx_measure_repeat.c | 82 +++ gen/test/c/mx/mx_measure_repeat.h | 35 ++ gen/test/c/mx/mx_measure_style.c | 169 +++++++ gen/test/c/mx/mx_measure_style.h | 64 +++ gen/test/c/mx/mx_metronome.c | 246 +++++++++ gen/test/c/mx/mx_metronome.h | 85 ++++ gen/test/c/mx/mx_metronome_beam.c | 71 +++ gen/test/c/mx/mx_metronome_beam.h | 28 ++ gen/test/c/mx/mx_metronome_note.c | 126 +++++ gen/test/c/mx/mx_metronome_note.h | 40 ++ gen/test/c/mx/mx_metronome_tied.c | 62 +++ gen/test/c/mx/mx_metronome_tied.h | 27 + gen/test/c/mx/mx_metronome_tuplet.c | 140 ++++++ gen/test/c/mx/mx_metronome_tuplet.h | 47 ++ gen/test/c/mx/mx_midi_device.c | 80 +++ gen/test/c/mx/mx_midi_device.h | 33 ++ gen/test/c/mx/mx_midi_instrument.c | 182 +++++++ gen/test/c/mx/mx_midi_instrument.h | 47 ++ gen/test/c/mx/mx_miscellaneous.c | 85 ++++ gen/test/c/mx/mx_miscellaneous.h | 34 ++ gen/test/c/mx/mx_miscellaneous_field.c | 72 +++ gen/test/c/mx/mx_miscellaneous_field.h | 27 + gen/test/c/mx/mx_mordent.c | 198 ++++++++ gen/test/c/mx/mx_mordent.h | 79 +++ gen/test/c/mx/mx_multiple_rest.c | 74 +++ gen/test/c/mx/mx_multiple_rest.h | 29 ++ gen/test/c/mx/mx_name_display.c | 101 ++++ gen/test/c/mx/mx_name_display.h | 40 ++ gen/test/c/mx/mx_non_arpeggiate.c | 124 +++++ gen/test/c/mx/mx_non_arpeggiate.h | 47 ++ gen/test/c/mx/mx_notations.c | 249 +++++++++ gen/test/c/mx/mx_notations.h | 70 +++ gen/test/c/mx/mx_note.c | 473 ++++++++++++++++++ gen/test/c/mx/mx_note.h | 147 ++++++ gen/test/c/mx/mx_note_size.c | 73 +++ gen/test/c/mx/mx_note_size.h | 32 ++ gen/test/c/mx/mx_note_type.c | 69 +++ gen/test/c/mx/mx_note_type.h | 30 ++ gen/test/c/mx/mx_notehead.c | 121 +++++ gen/test/c/mx/mx_notehead.h | 55 ++ gen/test/c/mx/mx_notehead_text.c | 95 ++++ gen/test/c/mx/mx_notehead_text.h | 37 ++ gen/test/c/mx/mx_octave_shift.c | 172 +++++++ gen/test/c/mx/mx_octave_shift.h | 63 +++ gen/test/c/mx/mx_offset.c | 73 +++ gen/test/c/mx/mx_offset.h | 33 ++ gen/test/c/mx/mx_opus.c | 104 ++++ gen/test/c/mx/mx_opus.h | 35 ++ gen/test/c/mx/mx_ornaments.c | 243 +++++++++ gen/test/c/mx/mx_ornaments.h | 58 +++ gen/test/c/mx/mx_other_appearance.c | 72 +++ gen/test/c/mx/mx_other_appearance.h | 27 + gen/test/c/mx/mx_other_direction.c | 168 +++++++ gen/test/c/mx/mx_other_direction.h | 65 +++ gen/test/c/mx/mx_other_notation.c | 176 +++++++ gen/test/c/mx/mx_other_notation.h | 69 +++ gen/test/c/mx/mx_other_placement_text.c | 148 ++++++ gen/test/c/mx/mx_other_placement_text.h | 55 ++ gen/test/c/mx/mx_other_play.c | 72 +++ gen/test/c/mx/mx_other_play.h | 26 + gen/test/c/mx/mx_other_text.c | 72 +++ gen/test/c/mx/mx_other_text.h | 28 ++ gen/test/c/mx/mx_page_layout.c | 111 ++++ gen/test/c/mx/mx_page_layout.h | 38 ++ gen/test/c/mx/mx_page_margins.c | 133 +++++ gen/test/c/mx/mx_page_margins.h | 40 ++ gen/test/c/mx/mx_part_group.c | 179 +++++++ gen/test/c/mx/mx_part_group.h | 60 +++ gen/test/c/mx/mx_part_list.c | 95 ++++ gen/test/c/mx/mx_part_list.h | 39 ++ gen/test/c/mx/mx_part_name.c | 146 ++++++ gen/test/c/mx/mx_part_name.h | 55 ++ gen/test/c/mx/mx_part_symbol.c | 119 +++++ gen/test/c/mx/mx_part_symbol.h | 45 ++ gen/test/c/mx/mx_partwise_measure.c | 249 +++++++++ gen/test/c/mx/mx_partwise_measure.h | 68 +++ gen/test/c/mx/mx_partwise_part.c | 93 ++++ gen/test/c/mx/mx_partwise_part.h | 31 ++ gen/test/c/mx/mx_pedal.c | 178 +++++++ gen/test/c/mx/mx_pedal.h | 77 +++ gen/test/c/mx/mx_pedal_tuning.c | 99 ++++ gen/test/c/mx/mx_pedal_tuning.h | 34 ++ gen/test/c/mx/mx_per_minute.c | 94 ++++ gen/test/c/mx/mx_per_minute.h | 38 ++ gen/test/c/mx/mx_percussion.c | 286 +++++++++++ gen/test/c/mx/mx_percussion.h | 90 ++++ gen/test/c/mx/mx_pitch.c | 112 +++++ gen/test/c/mx/mx_pitch.h | 37 ++ gen/test/c/mx/mx_pitched.c | 71 +++ gen/test/c/mx/mx_pitched.h | 29 ++ gen/test/c/mx/mx_placement_text.c | 140 ++++++ gen/test/c/mx/mx_placement_text.h | 51 ++ gen/test/c/mx/mx_play.c | 123 +++++ gen/test/c/mx/mx_play.h | 42 ++ gen/test/c/mx/mx_principal_voice.c | 166 ++++++ gen/test/c/mx/mx_principal_voice.h | 65 +++ gen/test/c/mx/mx_print.c | 189 +++++++ gen/test/c/mx/mx_print.h | 63 +++ gen/test/c/mx/mx_repeat.c | 76 +++ gen/test/c/mx/mx_repeat.h | 32 ++ gen/test/c/mx/mx_rest.c | 105 ++++ gen/test/c/mx/mx_rest.h | 39 ++ gen/test/c/mx/mx_root.c | 95 ++++ gen/test/c/mx/mx_root.h | 37 ++ gen/test/c/mx/mx_root_alter.c | 149 ++++++ gen/test/c/mx/mx_root_alter.h | 58 +++ gen/test/c/mx/mx_root_step.c | 141 ++++++ gen/test/c/mx/mx_root_step.h | 52 ++ gen/test/c/mx/mx_runtime.c | 14 + gen/test/c/mx/mx_runtime.h | 5 + gen/test/c/mx/mx_scaling.c | 101 ++++ gen/test/c/mx/mx_scaling.h | 38 ++ gen/test/c/mx/mx_scordatura.c | 93 ++++ gen/test/c/mx/mx_scordatura.h | 35 ++ gen/test/c/mx/mx_score_instrument.c | 151 ++++++ gen/test/c/mx/mx_score_instrument.h | 47 ++ gen/test/c/mx/mx_score_part.c | 171 +++++++ gen/test/c/mx/mx_score_part.h | 50 ++ gen/test/c/mx/mx_score_partwise.c | 159 ++++++ gen/test/c/mx/mx_score_partwise.h | 43 ++ gen/test/c/mx/mx_score_timewise.c | 159 ++++++ gen/test/c/mx/mx_score_timewise.h | 43 ++ gen/test/c/mx/mx_segno.c | 154 ++++++ gen/test/c/mx/mx_segno.h | 58 +++ gen/test/c/mx/mx_slash.c | 122 +++++ gen/test/c/mx/mx_slash.h | 47 ++ gen/test/c/mx/mx_slide.c | 208 ++++++++ gen/test/c/mx/mx_slide.h | 76 +++ gen/test/c/mx/mx_slur.c | 200 ++++++++ gen/test/c/mx/mx_slur.h | 70 +++ gen/test/c/mx/mx_sound.c | 259 ++++++++++ gen/test/c/mx/mx_sound.h | 115 +++++ gen/test/c/mx/mx_staff_details.c | 161 ++++++ gen/test/c/mx/mx_staff_details.h | 54 ++ gen/test/c/mx/mx_staff_divide.c | 152 ++++++ gen/test/c/mx/mx_staff_divide.h | 58 +++ gen/test/c/mx/mx_staff_layout.c | 96 ++++ gen/test/c/mx/mx_staff_layout.h | 39 ++ gen/test/c/mx/mx_staff_tuning.c | 120 +++++ gen/test/c/mx/mx_staff_tuning.h | 39 ++ gen/test/c/mx/mx_stem.c | 103 ++++ gen/test/c/mx/mx_stem.h | 40 ++ gen/test/c/mx/mx_stick.c | 115 +++++ gen/test/c/mx/mx_stick.h | 44 ++ gen/test/c/mx/mx_string.c | 143 ++++++ gen/test/c/mx/mx_string.h | 52 ++ gen/test/c/mx/mx_string_mute.c | 152 ++++++ gen/test/c/mx/mx_string_mute.h | 57 +++ gen/test/c/mx/mx_strong_accent.c | 138 +++++ gen/test/c/mx/mx_strong_accent.h | 54 ++ gen/test/c/mx/mx_style_text.c | 134 +++++ gen/test/c/mx/mx_style_text.h | 47 ++ gen/test/c/mx/mx_supports.c | 86 ++++ gen/test/c/mx/mx_supports.h | 36 ++ gen/test/c/mx/mx_system_dividers.c | 95 ++++ gen/test/c/mx/mx_system_dividers.h | 39 ++ gen/test/c/mx/mx_system_layout.c | 121 +++++ gen/test/c/mx/mx_system_layout.h | 46 ++ gen/test/c/mx/mx_system_margins.c | 101 ++++ gen/test/c/mx/mx_system_margins.h | 34 ++ gen/test/c/mx/mx_tap.c | 146 ++++++ gen/test/c/mx/mx_tap.h | 57 +++ gen/test/c/mx/mx_technical.c | 393 +++++++++++++++ gen/test/c/mx/mx_technical.h | 79 +++ gen/test/c/mx/mx_text_element_data.c | 158 ++++++ gen/test/c/mx/mx_text_element_data.h | 58 +++ gen/test/c/mx/mx_tie.c | 70 +++ gen/test/c/mx/mx_tie.h | 30 ++ gen/test/c/mx/mx_tied.c | 200 ++++++++ gen/test/c/mx/mx_tied.h | 79 +++ gen/test/c/mx/mx_time.c | 225 +++++++++ gen/test/c/mx/mx_time.h | 86 ++++ gen/test/c/mx/mx_time_modification.c | 122 +++++ gen/test/c/mx/mx_time_modification.h | 40 ++ gen/test/c/mx/mx_timewise_measure.c | 129 +++++ gen/test/c/mx/mx_timewise_measure.h | 44 ++ gen/test/c/mx/mx_timewise_part.c | 213 ++++++++ gen/test/c/mx/mx_timewise_part.h | 55 ++ gen/test/c/mx/mx_transpose.c | 140 ++++++ gen/test/c/mx/mx_transpose.h | 44 ++ gen/test/c/mx/mx_tremolo.c | 157 ++++++ gen/test/c/mx/mx_tremolo.h | 69 +++ gen/test/c/mx/mx_tuplet.c | 179 +++++++ gen/test/c/mx/mx_tuplet.h | 76 +++ gen/test/c/mx/mx_tuplet_dot.c | 94 ++++ gen/test/c/mx/mx_tuplet_dot.h | 37 ++ gen/test/c/mx/mx_tuplet_number.c | 105 ++++ gen/test/c/mx/mx_tuplet_number.h | 38 ++ gen/test/c/mx/mx_tuplet_portion.c | 105 ++++ gen/test/c/mx/mx_tuplet_portion.h | 39 ++ gen/test/c/mx/mx_tuplet_type.c | 101 ++++ gen/test/c/mx/mx_tuplet_type.h | 40 ++ gen/test/c/mx/mx_typed_text.c | 72 +++ gen/test/c/mx/mx_typed_text.h | 25 + gen/test/c/mx/mx_unpitched.c | 99 ++++ gen/test/c/mx/mx_unpitched.h | 35 ++ gen/test/c/mx/mx_virtual_instrument.c | 91 ++++ gen/test/c/mx/mx_virtual_instrument.h | 33 ++ gen/test/c/mx/mx_wavy_line.c | 164 ++++++ gen/test/c/mx/mx_wavy_line.h | 64 +++ gen/test/c/mx/mx_wedge.c | 154 ++++++ gen/test/c/mx/mx_wedge.h | 60 +++ gen/test/c/mx/mx_work.c | 101 ++++ gen/test/c/mx/mx_work.h | 35 ++ gen/test/c/mx/sources.cmake | 204 ++++++++ gen/test/c/src/compare.c | 41 +- gen/test/c/src/main.c | 9 +- gen/test/c/src/normalize.c | 51 +- gen/test/c/src/normalize.h | 3 + gen/test/c/src/roundtrip.c | 55 +- gen/test/c/src/roundtrip.h | 1 + gen/test/c/src/stub.c | 17 - gen/test/c/src/stub.h | 12 - 426 files changed, 40220 insertions(+), 58 deletions(-) create mode 100644 data/lysuite/ly33d_Spanners_OctaveShifts.xml.invalid create mode 100644 gen/emit/c/api.py create mode 100644 gen/emit/c/complexes.py create mode 100644 gen/emit/c/document.py create mode 100644 gen/test/c/mx/mx_accidental.c create mode 100644 gen/test/c/mx/mx_accidental.h create mode 100644 gen/test/c/mx/mx_accidental_mark.c create mode 100644 gen/test/c/mx/mx_accidental_mark.h create mode 100644 gen/test/c/mx/mx_accidental_text.c create mode 100644 gen/test/c/mx/mx_accidental_text.h create mode 100644 gen/test/c/mx/mx_accord.c create mode 100644 gen/test/c/mx/mx_accord.h create mode 100644 gen/test/c/mx/mx_accordion_registration.c create mode 100644 gen/test/c/mx/mx_accordion_registration.h create mode 100644 gen/test/c/mx/mx_appearance.c create mode 100644 gen/test/c/mx/mx_appearance.h create mode 100644 gen/test/c/mx/mx_arpeggiate.c create mode 100644 gen/test/c/mx/mx_arpeggiate.h create mode 100644 gen/test/c/mx/mx_arrow.c create mode 100644 gen/test/c/mx/mx_arrow.h create mode 100644 gen/test/c/mx/mx_articulations.c create mode 100644 gen/test/c/mx/mx_articulations.h create mode 100644 gen/test/c/mx/mx_attributes.c create mode 100644 gen/test/c/mx/mx_attributes.h create mode 100644 gen/test/c/mx/mx_backup.c create mode 100644 gen/test/c/mx/mx_backup.h create mode 100644 gen/test/c/mx/mx_bar_style_color.c create mode 100644 gen/test/c/mx/mx_bar_style_color.h create mode 100644 gen/test/c/mx/mx_barline.c create mode 100644 gen/test/c/mx/mx_barline.h create mode 100644 gen/test/c/mx/mx_barre.c create mode 100644 gen/test/c/mx/mx_barre.h create mode 100644 gen/test/c/mx/mx_bass.c create mode 100644 gen/test/c/mx/mx_bass.h create mode 100644 gen/test/c/mx/mx_bass_alter.c create mode 100644 gen/test/c/mx/mx_bass_alter.h create mode 100644 gen/test/c/mx/mx_bass_step.c create mode 100644 gen/test/c/mx/mx_bass_step.h create mode 100644 gen/test/c/mx/mx_beam.c create mode 100644 gen/test/c/mx/mx_beam.h create mode 100644 gen/test/c/mx/mx_beat_repeat.c create mode 100644 gen/test/c/mx/mx_beat_repeat.h create mode 100644 gen/test/c/mx/mx_beat_unit_tied.c create mode 100644 gen/test/c/mx/mx_beat_unit_tied.h create mode 100644 gen/test/c/mx/mx_beater.c create mode 100644 gen/test/c/mx/mx_beater.h create mode 100644 gen/test/c/mx/mx_bend.c create mode 100644 gen/test/c/mx/mx_bend.h create mode 100644 gen/test/c/mx/mx_bookmark.c create mode 100644 gen/test/c/mx/mx_bookmark.h create mode 100644 gen/test/c/mx/mx_bracket.c create mode 100644 gen/test/c/mx/mx_bracket.h create mode 100644 gen/test/c/mx/mx_breath_mark.c create mode 100644 gen/test/c/mx/mx_breath_mark.h create mode 100644 gen/test/c/mx/mx_caesura.c create mode 100644 gen/test/c/mx/mx_caesura.h create mode 100644 gen/test/c/mx/mx_cancel.c create mode 100644 gen/test/c/mx/mx_cancel.h create mode 100644 gen/test/c/mx/mx_clef.c create mode 100644 gen/test/c/mx/mx_clef.h create mode 100644 gen/test/c/mx/mx_coda.c create mode 100644 gen/test/c/mx/mx_coda.h create mode 100644 gen/test/c/mx/mx_credit.c create mode 100644 gen/test/c/mx/mx_credit.h create mode 100644 gen/test/c/mx/mx_dashes.c create mode 100644 gen/test/c/mx/mx_dashes.h create mode 100644 gen/test/c/mx/mx_defaults.c create mode 100644 gen/test/c/mx/mx_defaults.h create mode 100644 gen/test/c/mx/mx_degree.c create mode 100644 gen/test/c/mx/mx_degree.h create mode 100644 gen/test/c/mx/mx_degree_alter.c create mode 100644 gen/test/c/mx/mx_degree_alter.h create mode 100644 gen/test/c/mx/mx_degree_type.c create mode 100644 gen/test/c/mx/mx_degree_type.h create mode 100644 gen/test/c/mx/mx_degree_value.c create mode 100644 gen/test/c/mx/mx_degree_value.h create mode 100644 gen/test/c/mx/mx_direction.c create mode 100644 gen/test/c/mx/mx_direction.h create mode 100644 gen/test/c/mx/mx_direction_type.c create mode 100644 gen/test/c/mx/mx_direction_type.h create mode 100644 gen/test/c/mx/mx_directive.c create mode 100644 gen/test/c/mx/mx_directive.h create mode 100644 gen/test/c/mx/mx_distance.c create mode 100644 gen/test/c/mx/mx_distance.h create mode 100644 gen/test/c/mx/mx_document.c create mode 100644 gen/test/c/mx/mx_document.h create mode 100644 gen/test/c/mx/mx_dynamics.c create mode 100644 gen/test/c/mx/mx_dynamics.h create mode 100644 gen/test/c/mx/mx_elision.c create mode 100644 gen/test/c/mx/mx_elision.h create mode 100644 gen/test/c/mx/mx_empty.c create mode 100644 gen/test/c/mx/mx_empty.h create mode 100644 gen/test/c/mx/mx_empty_font.c create mode 100644 gen/test/c/mx/mx_empty_font.h create mode 100644 gen/test/c/mx/mx_empty_line.c create mode 100644 gen/test/c/mx/mx_empty_line.h create mode 100644 gen/test/c/mx/mx_empty_placement.c create mode 100644 gen/test/c/mx/mx_empty_placement.h create mode 100644 gen/test/c/mx/mx_empty_placement_smufl.c create mode 100644 gen/test/c/mx/mx_empty_placement_smufl.h create mode 100644 gen/test/c/mx/mx_empty_print_object_style_align.c create mode 100644 gen/test/c/mx/mx_empty_print_object_style_align.h create mode 100644 gen/test/c/mx/mx_empty_print_style_align_id.c create mode 100644 gen/test/c/mx/mx_empty_print_style_align_id.h create mode 100644 gen/test/c/mx/mx_empty_trill_sound.c create mode 100644 gen/test/c/mx/mx_empty_trill_sound.h create mode 100644 gen/test/c/mx/mx_encoding.c create mode 100644 gen/test/c/mx/mx_encoding.h create mode 100644 gen/test/c/mx/mx_ending.c create mode 100644 gen/test/c/mx/mx_ending.h create mode 100644 gen/test/c/mx/mx_extend.c create mode 100644 gen/test/c/mx/mx_extend.h create mode 100644 gen/test/c/mx/mx_feature.c create mode 100644 gen/test/c/mx/mx_feature.h create mode 100644 gen/test/c/mx/mx_fermata.c create mode 100644 gen/test/c/mx/mx_fermata.h create mode 100644 gen/test/c/mx/mx_figure.c create mode 100644 gen/test/c/mx/mx_figure.h create mode 100644 gen/test/c/mx/mx_figured_bass.c create mode 100644 gen/test/c/mx/mx_figured_bass.h create mode 100644 gen/test/c/mx/mx_fingering.c create mode 100644 gen/test/c/mx/mx_fingering.h create mode 100644 gen/test/c/mx/mx_first_fret.c create mode 100644 gen/test/c/mx/mx_first_fret.h create mode 100644 gen/test/c/mx/mx_formatted_symbol_id.c create mode 100644 gen/test/c/mx/mx_formatted_symbol_id.h create mode 100644 gen/test/c/mx/mx_formatted_text.c create mode 100644 gen/test/c/mx/mx_formatted_text.h create mode 100644 gen/test/c/mx/mx_formatted_text_id.c create mode 100644 gen/test/c/mx/mx_formatted_text_id.h create mode 100644 gen/test/c/mx/mx_forward.c create mode 100644 gen/test/c/mx/mx_forward.h create mode 100644 gen/test/c/mx/mx_frame.c create mode 100644 gen/test/c/mx/mx_frame.h create mode 100644 gen/test/c/mx/mx_frame_note.c create mode 100644 gen/test/c/mx/mx_frame_note.h create mode 100644 gen/test/c/mx/mx_fret.c create mode 100644 gen/test/c/mx/mx_fret.h create mode 100644 gen/test/c/mx/mx_glass.c create mode 100644 gen/test/c/mx/mx_glass.h create mode 100644 gen/test/c/mx/mx_glissando.c create mode 100644 gen/test/c/mx/mx_glissando.h create mode 100644 gen/test/c/mx/mx_glyph.c create mode 100644 gen/test/c/mx/mx_glyph.h create mode 100644 gen/test/c/mx/mx_grace.c create mode 100644 gen/test/c/mx/mx_grace.h create mode 100644 gen/test/c/mx/mx_group_barline.c create mode 100644 gen/test/c/mx/mx_group_barline.h create mode 100644 gen/test/c/mx/mx_group_name.c create mode 100644 gen/test/c/mx/mx_group_name.h create mode 100644 gen/test/c/mx/mx_group_symbol.c create mode 100644 gen/test/c/mx/mx_group_symbol.h create mode 100644 gen/test/c/mx/mx_grouping.c create mode 100644 gen/test/c/mx/mx_grouping.h create mode 100644 gen/test/c/mx/mx_hammer_on_pull_off.c create mode 100644 gen/test/c/mx/mx_hammer_on_pull_off.h create mode 100644 gen/test/c/mx/mx_handbell.c create mode 100644 gen/test/c/mx/mx_handbell.h create mode 100644 gen/test/c/mx/mx_harmon_closed.c create mode 100644 gen/test/c/mx/mx_harmon_closed.h create mode 100644 gen/test/c/mx/mx_harmon_mute.c create mode 100644 gen/test/c/mx/mx_harmon_mute.h create mode 100644 gen/test/c/mx/mx_harmonic.c create mode 100644 gen/test/c/mx/mx_harmonic.h create mode 100644 gen/test/c/mx/mx_harmony.c create mode 100644 gen/test/c/mx/mx_harmony.h create mode 100644 gen/test/c/mx/mx_harp_pedals.c create mode 100644 gen/test/c/mx/mx_harp_pedals.h create mode 100644 gen/test/c/mx/mx_heel_toe.c create mode 100644 gen/test/c/mx/mx_heel_toe.h create mode 100644 gen/test/c/mx/mx_hole.c create mode 100644 gen/test/c/mx/mx_hole.h create mode 100644 gen/test/c/mx/mx_hole_closed.c create mode 100644 gen/test/c/mx/mx_hole_closed.h create mode 100644 gen/test/c/mx/mx_horizontal_turn.c create mode 100644 gen/test/c/mx/mx_horizontal_turn.h create mode 100644 gen/test/c/mx/mx_identification.c create mode 100644 gen/test/c/mx/mx_identification.h create mode 100644 gen/test/c/mx/mx_image.c create mode 100644 gen/test/c/mx/mx_image.h create mode 100644 gen/test/c/mx/mx_instrument.c create mode 100644 gen/test/c/mx/mx_instrument.h create mode 100644 gen/test/c/mx/mx_interchangeable.c create mode 100644 gen/test/c/mx/mx_interchangeable.h create mode 100644 gen/test/c/mx/mx_inversion.c create mode 100644 gen/test/c/mx/mx_inversion.h create mode 100644 gen/test/c/mx/mx_key.c create mode 100644 gen/test/c/mx/mx_key.h create mode 100644 gen/test/c/mx/mx_key_accidental.c create mode 100644 gen/test/c/mx/mx_key_accidental.h create mode 100644 gen/test/c/mx/mx_key_octave.c create mode 100644 gen/test/c/mx/mx_key_octave.h create mode 100644 gen/test/c/mx/mx_kind.c create mode 100644 gen/test/c/mx/mx_kind.h create mode 100644 gen/test/c/mx/mx_level.c create mode 100644 gen/test/c/mx/mx_level.h create mode 100644 gen/test/c/mx/mx_line_width.c create mode 100644 gen/test/c/mx/mx_line_width.h create mode 100644 gen/test/c/mx/mx_link.c create mode 100644 gen/test/c/mx/mx_link.h create mode 100644 gen/test/c/mx/mx_lyric.c create mode 100644 gen/test/c/mx/mx_lyric.h create mode 100644 gen/test/c/mx/mx_lyric_font.c create mode 100644 gen/test/c/mx/mx_lyric_font.h create mode 100644 gen/test/c/mx/mx_lyric_language.c create mode 100644 gen/test/c/mx/mx_lyric_language.h create mode 100644 gen/test/c/mx/mx_measure_layout.c create mode 100644 gen/test/c/mx/mx_measure_layout.h create mode 100644 gen/test/c/mx/mx_measure_numbering.c create mode 100644 gen/test/c/mx/mx_measure_numbering.h create mode 100644 gen/test/c/mx/mx_measure_repeat.c create mode 100644 gen/test/c/mx/mx_measure_repeat.h create mode 100644 gen/test/c/mx/mx_measure_style.c create mode 100644 gen/test/c/mx/mx_measure_style.h create mode 100644 gen/test/c/mx/mx_metronome.c create mode 100644 gen/test/c/mx/mx_metronome.h create mode 100644 gen/test/c/mx/mx_metronome_beam.c create mode 100644 gen/test/c/mx/mx_metronome_beam.h create mode 100644 gen/test/c/mx/mx_metronome_note.c create mode 100644 gen/test/c/mx/mx_metronome_note.h create mode 100644 gen/test/c/mx/mx_metronome_tied.c create mode 100644 gen/test/c/mx/mx_metronome_tied.h create mode 100644 gen/test/c/mx/mx_metronome_tuplet.c create mode 100644 gen/test/c/mx/mx_metronome_tuplet.h create mode 100644 gen/test/c/mx/mx_midi_device.c create mode 100644 gen/test/c/mx/mx_midi_device.h create mode 100644 gen/test/c/mx/mx_midi_instrument.c create mode 100644 gen/test/c/mx/mx_midi_instrument.h create mode 100644 gen/test/c/mx/mx_miscellaneous.c create mode 100644 gen/test/c/mx/mx_miscellaneous.h create mode 100644 gen/test/c/mx/mx_miscellaneous_field.c create mode 100644 gen/test/c/mx/mx_miscellaneous_field.h create mode 100644 gen/test/c/mx/mx_mordent.c create mode 100644 gen/test/c/mx/mx_mordent.h create mode 100644 gen/test/c/mx/mx_multiple_rest.c create mode 100644 gen/test/c/mx/mx_multiple_rest.h create mode 100644 gen/test/c/mx/mx_name_display.c create mode 100644 gen/test/c/mx/mx_name_display.h create mode 100644 gen/test/c/mx/mx_non_arpeggiate.c create mode 100644 gen/test/c/mx/mx_non_arpeggiate.h create mode 100644 gen/test/c/mx/mx_notations.c create mode 100644 gen/test/c/mx/mx_notations.h create mode 100644 gen/test/c/mx/mx_note.c create mode 100644 gen/test/c/mx/mx_note.h create mode 100644 gen/test/c/mx/mx_note_size.c create mode 100644 gen/test/c/mx/mx_note_size.h create mode 100644 gen/test/c/mx/mx_note_type.c create mode 100644 gen/test/c/mx/mx_note_type.h create mode 100644 gen/test/c/mx/mx_notehead.c create mode 100644 gen/test/c/mx/mx_notehead.h create mode 100644 gen/test/c/mx/mx_notehead_text.c create mode 100644 gen/test/c/mx/mx_notehead_text.h create mode 100644 gen/test/c/mx/mx_octave_shift.c create mode 100644 gen/test/c/mx/mx_octave_shift.h create mode 100644 gen/test/c/mx/mx_offset.c create mode 100644 gen/test/c/mx/mx_offset.h create mode 100644 gen/test/c/mx/mx_opus.c create mode 100644 gen/test/c/mx/mx_opus.h create mode 100644 gen/test/c/mx/mx_ornaments.c create mode 100644 gen/test/c/mx/mx_ornaments.h create mode 100644 gen/test/c/mx/mx_other_appearance.c create mode 100644 gen/test/c/mx/mx_other_appearance.h create mode 100644 gen/test/c/mx/mx_other_direction.c create mode 100644 gen/test/c/mx/mx_other_direction.h create mode 100644 gen/test/c/mx/mx_other_notation.c create mode 100644 gen/test/c/mx/mx_other_notation.h create mode 100644 gen/test/c/mx/mx_other_placement_text.c create mode 100644 gen/test/c/mx/mx_other_placement_text.h create mode 100644 gen/test/c/mx/mx_other_play.c create mode 100644 gen/test/c/mx/mx_other_play.h create mode 100644 gen/test/c/mx/mx_other_text.c create mode 100644 gen/test/c/mx/mx_other_text.h create mode 100644 gen/test/c/mx/mx_page_layout.c create mode 100644 gen/test/c/mx/mx_page_layout.h create mode 100644 gen/test/c/mx/mx_page_margins.c create mode 100644 gen/test/c/mx/mx_page_margins.h create mode 100644 gen/test/c/mx/mx_part_group.c create mode 100644 gen/test/c/mx/mx_part_group.h create mode 100644 gen/test/c/mx/mx_part_list.c create mode 100644 gen/test/c/mx/mx_part_list.h create mode 100644 gen/test/c/mx/mx_part_name.c create mode 100644 gen/test/c/mx/mx_part_name.h create mode 100644 gen/test/c/mx/mx_part_symbol.c create mode 100644 gen/test/c/mx/mx_part_symbol.h create mode 100644 gen/test/c/mx/mx_partwise_measure.c create mode 100644 gen/test/c/mx/mx_partwise_measure.h create mode 100644 gen/test/c/mx/mx_partwise_part.c create mode 100644 gen/test/c/mx/mx_partwise_part.h create mode 100644 gen/test/c/mx/mx_pedal.c create mode 100644 gen/test/c/mx/mx_pedal.h create mode 100644 gen/test/c/mx/mx_pedal_tuning.c create mode 100644 gen/test/c/mx/mx_pedal_tuning.h create mode 100644 gen/test/c/mx/mx_per_minute.c create mode 100644 gen/test/c/mx/mx_per_minute.h create mode 100644 gen/test/c/mx/mx_percussion.c create mode 100644 gen/test/c/mx/mx_percussion.h create mode 100644 gen/test/c/mx/mx_pitch.c create mode 100644 gen/test/c/mx/mx_pitch.h create mode 100644 gen/test/c/mx/mx_pitched.c create mode 100644 gen/test/c/mx/mx_pitched.h create mode 100644 gen/test/c/mx/mx_placement_text.c create mode 100644 gen/test/c/mx/mx_placement_text.h create mode 100644 gen/test/c/mx/mx_play.c create mode 100644 gen/test/c/mx/mx_play.h create mode 100644 gen/test/c/mx/mx_principal_voice.c create mode 100644 gen/test/c/mx/mx_principal_voice.h create mode 100644 gen/test/c/mx/mx_print.c create mode 100644 gen/test/c/mx/mx_print.h create mode 100644 gen/test/c/mx/mx_repeat.c create mode 100644 gen/test/c/mx/mx_repeat.h create mode 100644 gen/test/c/mx/mx_rest.c create mode 100644 gen/test/c/mx/mx_rest.h create mode 100644 gen/test/c/mx/mx_root.c create mode 100644 gen/test/c/mx/mx_root.h create mode 100644 gen/test/c/mx/mx_root_alter.c create mode 100644 gen/test/c/mx/mx_root_alter.h create mode 100644 gen/test/c/mx/mx_root_step.c create mode 100644 gen/test/c/mx/mx_root_step.h create mode 100644 gen/test/c/mx/mx_scaling.c create mode 100644 gen/test/c/mx/mx_scaling.h create mode 100644 gen/test/c/mx/mx_scordatura.c create mode 100644 gen/test/c/mx/mx_scordatura.h create mode 100644 gen/test/c/mx/mx_score_instrument.c create mode 100644 gen/test/c/mx/mx_score_instrument.h create mode 100644 gen/test/c/mx/mx_score_part.c create mode 100644 gen/test/c/mx/mx_score_part.h create mode 100644 gen/test/c/mx/mx_score_partwise.c create mode 100644 gen/test/c/mx/mx_score_partwise.h create mode 100644 gen/test/c/mx/mx_score_timewise.c create mode 100644 gen/test/c/mx/mx_score_timewise.h create mode 100644 gen/test/c/mx/mx_segno.c create mode 100644 gen/test/c/mx/mx_segno.h create mode 100644 gen/test/c/mx/mx_slash.c create mode 100644 gen/test/c/mx/mx_slash.h create mode 100644 gen/test/c/mx/mx_slide.c create mode 100644 gen/test/c/mx/mx_slide.h create mode 100644 gen/test/c/mx/mx_slur.c create mode 100644 gen/test/c/mx/mx_slur.h create mode 100644 gen/test/c/mx/mx_sound.c create mode 100644 gen/test/c/mx/mx_sound.h create mode 100644 gen/test/c/mx/mx_staff_details.c create mode 100644 gen/test/c/mx/mx_staff_details.h create mode 100644 gen/test/c/mx/mx_staff_divide.c create mode 100644 gen/test/c/mx/mx_staff_divide.h create mode 100644 gen/test/c/mx/mx_staff_layout.c create mode 100644 gen/test/c/mx/mx_staff_layout.h create mode 100644 gen/test/c/mx/mx_staff_tuning.c create mode 100644 gen/test/c/mx/mx_staff_tuning.h create mode 100644 gen/test/c/mx/mx_stem.c create mode 100644 gen/test/c/mx/mx_stem.h create mode 100644 gen/test/c/mx/mx_stick.c create mode 100644 gen/test/c/mx/mx_stick.h create mode 100644 gen/test/c/mx/mx_string.c create mode 100644 gen/test/c/mx/mx_string.h create mode 100644 gen/test/c/mx/mx_string_mute.c create mode 100644 gen/test/c/mx/mx_string_mute.h create mode 100644 gen/test/c/mx/mx_strong_accent.c create mode 100644 gen/test/c/mx/mx_strong_accent.h create mode 100644 gen/test/c/mx/mx_style_text.c create mode 100644 gen/test/c/mx/mx_style_text.h create mode 100644 gen/test/c/mx/mx_supports.c create mode 100644 gen/test/c/mx/mx_supports.h create mode 100644 gen/test/c/mx/mx_system_dividers.c create mode 100644 gen/test/c/mx/mx_system_dividers.h create mode 100644 gen/test/c/mx/mx_system_layout.c create mode 100644 gen/test/c/mx/mx_system_layout.h create mode 100644 gen/test/c/mx/mx_system_margins.c create mode 100644 gen/test/c/mx/mx_system_margins.h create mode 100644 gen/test/c/mx/mx_tap.c create mode 100644 gen/test/c/mx/mx_tap.h create mode 100644 gen/test/c/mx/mx_technical.c create mode 100644 gen/test/c/mx/mx_technical.h create mode 100644 gen/test/c/mx/mx_text_element_data.c create mode 100644 gen/test/c/mx/mx_text_element_data.h create mode 100644 gen/test/c/mx/mx_tie.c create mode 100644 gen/test/c/mx/mx_tie.h create mode 100644 gen/test/c/mx/mx_tied.c create mode 100644 gen/test/c/mx/mx_tied.h create mode 100644 gen/test/c/mx/mx_time.c create mode 100644 gen/test/c/mx/mx_time.h create mode 100644 gen/test/c/mx/mx_time_modification.c create mode 100644 gen/test/c/mx/mx_time_modification.h create mode 100644 gen/test/c/mx/mx_timewise_measure.c create mode 100644 gen/test/c/mx/mx_timewise_measure.h create mode 100644 gen/test/c/mx/mx_timewise_part.c create mode 100644 gen/test/c/mx/mx_timewise_part.h create mode 100644 gen/test/c/mx/mx_transpose.c create mode 100644 gen/test/c/mx/mx_transpose.h create mode 100644 gen/test/c/mx/mx_tremolo.c create mode 100644 gen/test/c/mx/mx_tremolo.h create mode 100644 gen/test/c/mx/mx_tuplet.c create mode 100644 gen/test/c/mx/mx_tuplet.h create mode 100644 gen/test/c/mx/mx_tuplet_dot.c create mode 100644 gen/test/c/mx/mx_tuplet_dot.h create mode 100644 gen/test/c/mx/mx_tuplet_number.c create mode 100644 gen/test/c/mx/mx_tuplet_number.h create mode 100644 gen/test/c/mx/mx_tuplet_portion.c create mode 100644 gen/test/c/mx/mx_tuplet_portion.h create mode 100644 gen/test/c/mx/mx_tuplet_type.c create mode 100644 gen/test/c/mx/mx_tuplet_type.h create mode 100644 gen/test/c/mx/mx_typed_text.c create mode 100644 gen/test/c/mx/mx_typed_text.h create mode 100644 gen/test/c/mx/mx_unpitched.c create mode 100644 gen/test/c/mx/mx_unpitched.h create mode 100644 gen/test/c/mx/mx_virtual_instrument.c create mode 100644 gen/test/c/mx/mx_virtual_instrument.h create mode 100644 gen/test/c/mx/mx_wavy_line.c create mode 100644 gen/test/c/mx/mx_wavy_line.h create mode 100644 gen/test/c/mx/mx_wedge.c create mode 100644 gen/test/c/mx/mx_wedge.h create mode 100644 gen/test/c/mx/mx_work.c create mode 100644 gen/test/c/mx/mx_work.h delete mode 100644 gen/test/c/src/stub.c delete mode 100644 gen/test/c/src/stub.h diff --git a/data/lysuite/ly33d_Spanners_OctaveShifts.xml.invalid b/data/lysuite/ly33d_Spanners_OctaveShifts.xml.invalid new file mode 100644 index 000000000..22cb3bd9c --- /dev/null +++ b/data/lysuite/ly33d_Spanners_OctaveShifts.xml.invalid @@ -0,0 +1,2 @@ +Not well-formed XML: the file begins with stray bytes ("Octave.cc") before the +XML declaration. Strict parsers (libxml2) are entitled to reject it. diff --git a/gen/emit/c/__init__.py b/gen/emit/c/__init__.py index d1d7c19f5..81063731a 100644 --- a/gen/emit/c/__init__.py +++ b/gen/emit/c/__init__.py @@ -10,12 +10,14 @@ manifest the test target's CMakeLists includes, so the build never globs and never goes stale. -Currently rendered: the four value shapes (the leaf node types) plus the -runtime. Complex types and the document entry points are later phases. +Rendered: the four value shapes, the four complex shapes, the document +entry points, and the runtime support files. """ from __future__ import annotations +from gen.emit.c.complexes import complex_files +from gen.emit.c.document import document_files, document_stem from gen.emit.c.runtime import runtime_header, runtime_impl, runtime_stem from gen.emit.c.values import value_files from gen.emit.writer import banner @@ -24,7 +26,8 @@ def render(plates: Plates) -> dict[str, str]: rt = runtime_stem(plates) - reserved = (rt, "sources") + doc_stem = document_stem(plates) + reserved = (rt, doc_stem, "sources") for plate in list(plates.value_types) + list(plates.complex_types): if plate.file in reserved: raise ValueError( @@ -43,6 +46,13 @@ def render(plates: Plates) -> dict[str, str]: header, impl = value_files(plates, plate, includes_of.get(plate.file, []), rt) files[plate.file + ".h"] = header files[plate.file + ".c"] = impl + for plate in plates.complex_types: + header, impl = complex_files(plates, plate, includes_of.get(plate.file, []), rt) + files[plate.file + ".h"] = header + files[plate.file + ".c"] = impl + header, impl = document_files(plates, doc_stem, rt) + files[doc_stem + ".h"] = header + files[doc_stem + ".c"] = impl files["sources.cmake"] = _sources_cmake(plates, files) return files diff --git a/gen/emit/c/api.py b/gen/emit/c/api.py new file mode 100644 index 000000000..7ad7bc0d1 --- /dev/null +++ b/gen/emit/c/api.py @@ -0,0 +1,73 @@ +"""The C calling convention for value types and primitives. + +The single place that knows how generated C code parses, prints, stores, and +frees a value of any plate kind or primitive -- the complex-type templates +call this at every attribute, text body, and leaf child instead of +re-deriving the ownership rules inline: + + - enum: value type; to_string returns static storage (not owned) + - number: value type; to_string returns malloc'd storage (owned) + - string: the value IS an owned char*; printing it costs nothing + - union: struct value; to_string malloc'd; the value itself may own + memory (mx__free releases it) + - primitives follow their family (decimal/integer/string) +""" + +from __future__ import annotations + +from dataclasses import dataclass + +from gen.plates.model import PlateRef, Plates +from gen.emit.c.common import fn_name, fn_prefix + + +@dataclass +class CValue: + """How generated C handles one value type: expression templates take the + value (or source string) as `{0}`.""" + + c_type: str # storage type for a by-value field + parse: str # lenient parse from a const char * + to_string: str # the wire spelling of a value + to_string_owned: bool # the to_string result must be freed + free: str | None # statement template releasing a value's OWN memory + + +_PRIM_FAMILY = { + "decimal": "decimal", + "integer": "integer", + "positive_integer": "integer", + "non_negative_integer": "integer", + "string": "string", + "token": "string", + "nmtoken": "string", + "date": "string", +} + + +def value_api(plates: Plates, ref: PlateRef) -> CValue: + p = fn_prefix(plates) + if ref.category == "primitive": + family = _PRIM_FAMILY[ref.wire] + if family == "decimal": + return CValue("double", f"{p}parse_decimal({{0}})", + f"{p}format_decimal({{0}})", True, None) + if family == "integer": + return CValue("long", f"{p}parse_int({{0}})", + f"{p}format_int({{0}})", True, None) + return CValue("char *", f"{p}strdup({{0}})", "{0}", False, "free({0});") + + plate = plates.plate(ref.wire) + parse = fn_name(plates, plate.name, "parse") + "({0})" + to_string = fn_name(plates, plate.name, "to_string") + "({0})" + if plate.kind == "enum": + return CValue(ref.ident, parse, to_string, False, None) + if plate.kind == "number": + return CValue(ref.ident, parse, to_string, True, None) + if plate.kind == "string": + # The typedef is char*: the value is its own spelling and owns itself. + return CValue(ref.ident, parse, "{0}", False, "free({0});") + if plate.kind == "union": + free = fn_name(plates, plate.name, "free") + "(&{0});" + return CValue(ref.ident, parse, to_string, True, free) + raise ValueError(f"not a value reference: {ref.wire} ({plate.kind})") diff --git a/gen/emit/c/complexes.py b/gen/emit/c/complexes.py new file mode 100644 index 000000000..5bfcb3357 --- /dev/null +++ b/gen/emit/c/complexes.py @@ -0,0 +1,349 @@ +"""C templates for the four complex shapes. + +The C spelling of the same representation the Go backend uses (see +gen/emit/go/complexes.py for the rationale): presence-tracked attributes +(`bool has_x` + value), children as ONE ordered array of structs whose +typed pointers discriminate by non-NULL (exactly one set per child), strict +about names, lenient about values. C has no inheritance, so a derived type +flattens its base chain (the plates' all_members view) into a self-contained +struct. + +Each type renders as a header/impl pair against the libxml2 DOM: + + MxT *mx__parse(xmlNodePtr el); NULL on error; the message + is in mx_error() + xmlNodePtr mx__serialize(const MxT *m, xmlNodePtr parent, + const char *tag); parent NULL -> free node + void mx__free(MxT *m); deep free, including m + +Namespace declarations never appear in libxml2's attribute list (they live +in nsDef), so the attribute loops need no xmlns skipping; prefixed +attributes (xml:lang, xlink:href) match their qualified names. +""" + +from __future__ import annotations + +from gen.emit.c.api import CValue, value_api +from gen.emit.c.common import c_string, doc_comment, fn_name, fn_prefix, header_file, impl_file +from gen.plates.model import ComplexPlate, Member, Plates + + +def _attr_members(members: list[Member]) -> list[Member]: + return [m for m in members if m.kind == "attribute"] + + +def _element_members(members: list[Member]) -> list[Member]: + return [m for m in members if m.kind == "element"] + + +def _value_member(members: list[Member]) -> Member | None: + return next((m for m in members if m.kind == "value"), None) + + +def _members_view(plate: ComplexPlate) -> list[Member]: + """C flattens derived types: the merged base-chain view when present.""" + if plate.strategy == "flatten" and plate.all_members is not None: + return plate.all_members + return plate.members + + +def _child_field(plates: Plates, member: Member) -> tuple[str, bool]: + """(field declaration type, value-is-the-pointer). Children discriminate + by non-NULL, so every field is a pointer: complex types and boxed values + point at their structs; string-family values ARE their char* pointer.""" + api = value_api(plates, member.type_ref) if member.type_ref.category != "complex" else None + if member.type_ref.category == "complex": + return f"{member.type_ref.ident} *", False + if api.c_type.endswith("*"): + return api.c_type, True # char* family: the pointer is the value + return f"{api.c_type} *", False + + +def complex_files(plates: Plates, plate: ComplexPlate, includes: list[str], rt: str): + members = _members_view(plate) + attrs = _attr_members(members) + elements = _element_members(members) + value = _value_member(members) + ident = plate.ident + p = fn_prefix(plates) + parse = fn_name(plates, plate.name, "parse") + serialize = fn_name(plates, plate.name, "serialize") + free_fn = fn_name(plates, plate.name, "free") + wrap = plates.target.doc_style.wrap + + # ----- header ---------------------------------------------------------- # + + decl = doc_comment(plate.doc, wrap) + if elements: + decl += [ + f"/* One child element of {ident}: exactly one field is non-NULL,", + " and that pointer says which element this is. Document order is", + " the array order on the owning struct. */", + "typedef struct {", + ] + for m in elements: + ctype, _ = _child_field(plates, m) + sep = "" if ctype.endswith("*") else " " + decl += [f" {ctype}{sep}{m.ident};"] + decl += [f"}} {ident}Child;", ""] + + decl += ["typedef struct {"] + for m in attrs: + api = value_api(plates, m.type_ref) + sep = "" if api.c_type.endswith("*") else " " + decl += [ + f" bool has_{m.ident};", + f" {api.c_type}{sep}{m.ident}; /* attribute {m.name.wire} */", + ] + if value is not None: + api = value_api(plates, value.type_ref) + sep = "" if api.c_type.endswith("*") else " " + decl += [f" {api.c_type}{sep}{value.ident}; /* text content */"] + if elements: + decl += [ + f" {ident}Child *children; /* child elements in document order */", + " size_t children_count;", + ] + if not attrs and value is None and not elements: + decl += [" char unused; /* presence is the only information */"] + decl += [f"}} {ident};", ""] + decl += [ + f"/* NULL on error; the message is in {p}error(). */", + f"{ident} *{parse}(xmlNodePtr el);", + "/* Appends under parent, or creates a free node when parent is NULL. */", + f"xmlNodePtr {serialize}(const {ident} *m, xmlNodePtr parent, const char *tag);", + f"void {free_fn}({ident} *m);", + ] + + # ----- impl: parse ------------------------------------------------------ # + + body = [ + f"{ident} *{parse}(xmlNodePtr el) {{", + f" {ident} *m = calloc(1, sizeof(*m));", + " if (!m)", + " abort();", + " for (xmlAttrPtr a = el->properties; a; a = a->next) {", + " char aname[128];", + " if (a->ns && a->ns->prefix)", + ' snprintf(aname, sizeof(aname), "%s:%s",', + " (const char *)a->ns->prefix, (const char *)a->name);", + " else", + ' snprintf(aname, sizeof(aname), "%s", (const char *)a->name);', + " xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1);", + ' const char *s = avalue ? (const char *)avalue : "";', + " (void)s;", + ] + first = True + for m in attrs: + api = value_api(plates, m.type_ref) + kw = "if" if first else "} else if" + first = False + body += [ + f" {kw} (strcmp(aname, {c_string(m.name.wire)}) == 0) {{", + f" m->has_{m.ident} = true;", + f" m->{m.ident} = {api.parse.format('s')};", + ] + closer = "} else {" if attrs else "{" + body += [ + f" {closer}", + f' {p}error_set("unknown attribute \\"%s\\" on <%s>", aname,', + " (const char *)el->name);", + " xmlFree(avalue);", + f" {free_fn}(m);", + " return NULL;", + " }", + " xmlFree(avalue);", + " }", + ] + + if value is not None: + api = value_api(plates, value.type_ref) + body += [ + " {", + " xmlChar *text = xmlNodeGetContent(el);", + ' const char *s = text ? (const char *)text : "";', + f" m->{value.ident} = {api.parse.format('s')};", + " xmlFree(text);", + " }", + ] + + if elements: + body += [ + " size_t n = 0;", + " for (xmlNodePtr c = el->children; c; c = c->next)", + " if (c->type == XML_ELEMENT_NODE)", + " n++;", + " if (n) {", + " m->children = calloc(n, sizeof(*m->children));", + " if (!m->children)", + " abort();", + " }", + ] + body += [ + " for (xmlNodePtr c = el->children; c; c = c->next) {", + " if (c->type != XML_ELEMENT_NODE)", + " continue;", + " const char *tag = (const char *)c->name;", + ] + if elements: + body += [f" {ident}Child *ch = &m->children[m->children_count];"] + first = True + for m in elements: + kw = "if" if first else "} else if" + first = False + body += [f" {kw} (strcmp(tag, {c_string(m.name.wire)}) == 0) {{"] + if m.type_ref.category == "complex": + child_parse = fn_name(plates, plates.plate(m.type_ref.wire).name, "parse") + body += [ + f" ch->{m.ident} = {child_parse}(c);", + f" if (!ch->{m.ident}) {{", + f" {free_fn}(m);", + " return NULL;", + " }", + ] + else: + api = value_api(plates, m.type_ref) + _, direct = _child_field(plates, m) + body += [ + " xmlChar *text = xmlNodeGetContent(c);", + ' const char *s = text ? (const char *)text : "";', + ] + if direct: + body += [f" ch->{m.ident} = {api.parse.format('s')};"] + else: + body += [ + f" ch->{m.ident} = malloc(sizeof(*ch->{m.ident}));", + f" if (!ch->{m.ident})", + " abort();", + f" *ch->{m.ident} = {api.parse.format('s')};", + ] + body += [" xmlFree(text);"] + closer = "} else {" if elements else "{" + body += [ + f" {closer}", + f' {p}error_set("unknown element <%s> in <%s>", tag,', + " (const char *)el->name);", + f" {free_fn}(m);", + " return NULL;", + " }", + ] + if elements: + body += [" m->children_count++;"] + body += [" }", " return m;", "}", ""] + + # ----- impl: serialize -------------------------------------------------- # + + body += [ + f"xmlNodePtr {serialize}(const {ident} *m, xmlNodePtr parent, const char *tag) {{", + " xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL)", + " : xmlNewNode(NULL, BAD_CAST tag);", + ] + if value is not None: + api = value_api(plates, value.type_ref) + expr = api.to_string.format(f"m->{value.ident}") + if api.to_string_owned: + body += [ + " {", + f" char *s = {expr};", + " xmlAddChild(el, xmlNewText(BAD_CAST s));", + " free(s);", + " }", + ] + else: + body += [f" xmlAddChild(el, xmlNewText(BAD_CAST {expr}));"] + for m in attrs: + api = value_api(plates, m.type_ref) + expr = api.to_string.format(f"m->{m.ident}") + body += [f" if (m->has_{m.ident}) {{"] + if api.to_string_owned: + body += [ + f" char *s = {expr};", + f" xmlSetProp(el, BAD_CAST {c_string(m.name.wire)}, BAD_CAST s);", + " free(s);", + ] + else: + body += [f" xmlSetProp(el, BAD_CAST {c_string(m.name.wire)}, BAD_CAST {expr});"] + body += [" }"] + if elements: + body += [ + " for (size_t i = 0; i < m->children_count; i++) {", + f" const {ident}Child *ch = &m->children[i];", + ] + first = True + for m in elements: + kw = "if" if first else "} else if" + first = False + body += [f" {kw} (ch->{m.ident}) {{"] + if m.type_ref.category == "complex": + child_ser = fn_name(plates, plates.plate(m.type_ref.wire).name, "serialize") + body += [f" {child_ser}(ch->{m.ident}, el, {c_string(m.name.wire)});"] + else: + api = value_api(plates, m.type_ref) + _, direct = _child_field(plates, m) + source = f"ch->{m.ident}" if direct else f"(*ch->{m.ident})" + expr = api.to_string.format(source) + if api.to_string_owned: + body += [ + f" char *s = {expr};", + f" xmlNewTextChild(el, NULL, BAD_CAST {c_string(m.name.wire)}, BAD_CAST s);", + " free(s);", + ] + else: + body += [ + f" xmlNewTextChild(el, NULL, BAD_CAST {c_string(m.name.wire)}, BAD_CAST {expr});", + ] + body += [" }", " }"] + if not attrs and value is None and not elements: + body += [" (void)m;"] + body += [" return el;", "}", ""] + + # ----- impl: free ------------------------------------------------------- # + + body += [f"void {free_fn}({ident} *m) {{", " if (!m)", " return;"] + for m in attrs: + api = value_api(plates, m.type_ref) + if api.free is not None: + body += [ + f" if (m->has_{m.ident})", + f" {api.free.format(f'm->{m.ident}')}", + ] + if value is not None: + api = value_api(plates, value.type_ref) + if api.free is not None: + body += [f" {api.free.format(f'm->{value.ident}')}"] + if elements: + body += [ + " for (size_t i = 0; i < m->children_count; i++) {", + f" {ident}Child *ch = &m->children[i];", + ] + for m in elements: + if m.type_ref.category == "complex": + child_free = fn_name(plates, plates.plate(m.type_ref.wire).name, "free") + body += [ + f" if (ch->{m.ident})", + f" {child_free}(ch->{m.ident});", + ] + else: + api = value_api(plates, m.type_ref) + _, direct = _child_field(plates, m) + if direct: + body += [f" free(ch->{m.ident});"] + elif api.free is not None: + body += [ + f" if (ch->{m.ident}) {{", + f" {api.free.format(f'(*ch->{m.ident})')}", + f" free(ch->{m.ident});", + " }", + ] + else: + body += [f" free(ch->{m.ident});"] + body += [" }", " free(m->children);"] + body += [" free(m);", "}"] + + header_includes = ["", "", ""] + header_includes += [f'"{inc}.h"' for inc in includes] + return ( + header_file(plates, plate.file, decl, header_includes), + impl_file(plates, plate.file, body, + [f'"{rt}.h"', "", ""]), + ) diff --git a/gen/emit/c/document.py b/gen/emit/c/document.py new file mode 100644 index 000000000..48b81f22b --- /dev/null +++ b/gen/emit/c/document.py @@ -0,0 +1,144 @@ +"""The C document entry points: MxDocument, from_xdoc, to_xdoc, free. + +Generated from the plates' roots. The document also preserves the root +element's namespace declarations (libxml2 keeps them in nsDef, never in the +attribute list, so the per-type parsers never see them).""" + +from __future__ import annotations + +from gen.emit.c.common import c_string, fn_name, fn_prefix, header_file, impl_file +from gen.plates.model import Plates + + +def document_stem(plates: Plates) -> str: + return plates.target.file_prefix + "document" + + +def document_files(plates: Plates, stem: str, rt: str): + p = fn_prefix(plates) + type_prefix = plates.target.prefix + ns_type = f"{type_prefix}Namespace" + doc_type = f"{type_prefix}Document" + roots = [(r, plates.plate(r.wire)) for r in plates.roots] + + decl = [ + "/* A namespace declaration preserved from the document root. */", + "typedef struct {", + " char *prefix; /* NULL for the default namespace */", + " char *href;", + f"}} {ns_type};", + "", + "/* A parsed MusicXML document: exactly one root is set. */", + "typedef struct {", + ] + for ref, plate in roots: + decl += [f" {plate.ident} *{plate.name.snake};"] + decl += [ + f" {ns_type} *namespaces;", + " size_t namespaces_count;", + f"}} {doc_type};", + "", + f"/* NULL on error; the message is in {p}error(). */", + f"{doc_type} *{p}document_from_xdoc(xmlDocPtr doc);", + "/* 0 on success; the caller owns *out. */", + f"int {p}document_to_xdoc(const {doc_type} *d, xmlDocPtr *out);", + f"void {p}document_free({doc_type} *d);", + ] + + body = [ + f"{doc_type} *{p}document_from_xdoc(xmlDocPtr doc) {{", + " xmlNodePtr root = xmlDocGetRootElement(doc);", + " if (!root) {", + f' {p}error_set("document has no root element");', + " return NULL;", + " }", + f" {doc_type} *d = calloc(1, sizeof(*d));", + " if (!d)", + " abort();", + " size_t nscount = 0;", + " for (xmlNsPtr ns = root->nsDef; ns; ns = ns->next)", + " nscount++;", + " if (nscount) {", + " d->namespaces = calloc(nscount, sizeof(*d->namespaces));", + " if (!d->namespaces)", + " abort();", + " for (xmlNsPtr ns = root->nsDef; ns; ns = ns->next) {", + f" {ns_type} *slot = &d->namespaces[d->namespaces_count++];", + f" slot->prefix = ns->prefix ? {p}strdup((const char *)ns->prefix) : NULL;", + f" slot->href = {p}strdup((const char *)ns->href);", + " }", + " }", + " const char *tag = (const char *)root->name;", + ] + first = True + for ref, plate in roots: + kw = "if" if first else "} else if" + first = False + parse = fn_name(plates, plate.name, "parse") + body += [ + f" {kw} (strcmp(tag, {c_string(ref.wire)}) == 0) {{", + f" d->{plate.name.snake} = {parse}(root);", + f" if (!d->{plate.name.snake}) {{", + f" {p}document_free(d);", + " return NULL;", + " }", + ] + body += [ + " } else {", + f' {p}error_set("unknown root element <%s>", tag);', + f" {p}document_free(d);", + " return NULL;", + " }", + " return d;", + "}", + "", + f"int {p}document_to_xdoc(const {doc_type} *d, xmlDocPtr *out) {{", + " *out = NULL;", + " xmlNodePtr root = NULL;", + ] + first = True + for ref, plate in roots: + kw = "if" if first else "else if" + first = False + serialize = fn_name(plates, plate.name, "serialize") + body += [ + f" {kw} (d->{plate.name.snake})", + f" root = {serialize}(d->{plate.name.snake}, NULL, {c_string(ref.wire)});", + ] + body += [ + " if (!root) {", + f' {p}error_set("document has no root");', + " return -1;", + " }", + ' xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");', + " xmlDocSetRootElement(doc, root);", + " for (size_t i = 0; i < d->namespaces_count; i++)", + " xmlNewNs(root, BAD_CAST d->namespaces[i].href,", + " BAD_CAST d->namespaces[i].prefix);", + " *out = doc;", + " return 0;", + "}", + "", + f"void {p}document_free({doc_type} *d) {{", + " if (!d)", + " return;", + ] + for ref, plate in roots: + free = fn_name(plates, plate.name, "free") + body += [f" {free}(d->{plate.name.snake});"] + body += [ + " for (size_t i = 0; i < d->namespaces_count; i++) {", + " free(d->namespaces[i].prefix);", + " free(d->namespaces[i].href);", + " }", + " free(d->namespaces);", + " free(d);", + "}", + ] + + includes = ["", ""] + includes += sorted({f'"{plate.file}.h"' for _, plate in roots}) + return ( + header_file(plates, stem, decl, includes), + impl_file(plates, stem, body, [f'"{rt}.h"', "", ""]), + ) diff --git a/gen/emit/c/runtime.py b/gen/emit/c/runtime.py index e317f5baf..7619b3434 100644 --- a/gen/emit/c/runtime.py +++ b/gen/emit/c/runtime.py @@ -29,11 +29,17 @@ char *mx_format_int(long v); /* strdup that maps NULL to "". Malloc'd; caller frees. */ -char *mx_strdup(const char *s);""" +char *mx_strdup(const char *s); + +/* The parse-failure message channel: parse functions returning NULL set it; + the caller reads it before the next parse. Static storage. */ +void mx_error_set(const char *fmt, ...); +const char *mx_error(void);""" _IMPL_BODY = """\ #include #include +#include #include #include #include @@ -123,6 +129,19 @@ abort(); /* the generator's runtime has no error channel for OOM */ memcpy(out, s, n); return out; +} + +static char mx_error_buf[512]; + +void mx_error_set(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + vsnprintf(mx_error_buf, sizeof(mx_error_buf), fmt, args); + va_end(args); +} + +const char *mx_error(void) { + return mx_error_buf; }""" diff --git a/gen/test/c/CMakeLists.txt b/gen/test/c/CMakeLists.txt index b7e399372..ccd4b3bce 100644 --- a/gen/test/c/CMakeLists.txt +++ b/gen/test/c/CMakeLists.txt @@ -15,7 +15,8 @@ endif() # manifest is emitted alongside the sources so the build never globs). include(mx/sources.cmake) add_library(mx-c STATIC ${MX_GENERATED_SOURCES}) -target_include_directories(mx-c PUBLIC mx) +target_include_directories(mx-c PUBLIC mx ${LIBXML2_INCLUDE_DIRS}) +target_link_libraries(mx-c ${LIBXML2_LIBRARIES}) add_executable(corert-c src/main.c @@ -23,8 +24,7 @@ add_executable(corert-c src/fixer.c src/normalize.c src/compare.c - src/roundtrip.c - src/stub.c) + src/roundtrip.c) target_include_directories(corert-c PRIVATE ${LIBXML2_INCLUDE_DIRS} src) target_link_libraries(corert-c mx-c ${LIBXML2_LIBRARIES}) diff --git a/gen/test/c/mx/mx_accidental.c b/gen/test/c/mx/mx_accidental.c new file mode 100644 index 000000000..77e84d406 --- /dev/null +++ b/gen/test/c/mx/mx_accidental.c @@ -0,0 +1,171 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_accidental.h" + +#include "mx_runtime.h" +#include +#include + +MxAccidental *mx_accidental_parse(xmlNodePtr el) { + MxAccidental *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "cautionary") == 0) { + m->has_cautionary = true; + m->cautionary = mx_yes_no_parse(s); + } else if (strcmp(aname, "editorial") == 0) { + m->has_editorial = true; + m->editorial = mx_yes_no_parse(s); + } else if (strcmp(aname, "smufl") == 0) { + m->has_smufl = true; + m->smufl = mx_smufl_accidental_glyph_name_parse(s); + } else if (strcmp(aname, "parentheses") == 0) { + m->has_parentheses = true; + m->parentheses = mx_yes_no_parse(s); + } else if (strcmp(aname, "bracket") == 0) { + m->has_bracket = true; + m->bracket = mx_yes_no_parse(s); + } else if (strcmp(aname, "size") == 0) { + m->has_size = true; + m->size = mx_symbol_size_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_accidental_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_accidental_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_accidental_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_accidental_serialize(const MxAccidental *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_accidental_value_to_string(m->value))); + if (m->has_cautionary) { + xmlSetProp(el, BAD_CAST "cautionary", BAD_CAST mx_yes_no_to_string(m->cautionary)); + } + if (m->has_editorial) { + xmlSetProp(el, BAD_CAST "editorial", BAD_CAST mx_yes_no_to_string(m->editorial)); + } + if (m->has_smufl) { + xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); + } + if (m->has_parentheses) { + xmlSetProp(el, BAD_CAST "parentheses", BAD_CAST mx_yes_no_to_string(m->parentheses)); + } + if (m->has_bracket) { + xmlSetProp(el, BAD_CAST "bracket", BAD_CAST mx_yes_no_to_string(m->bracket)); + } + if (m->has_size) { + xmlSetProp(el, BAD_CAST "size", BAD_CAST mx_symbol_size_to_string(m->size)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_accidental_free(MxAccidental *m) { + if (!m) + return; + if (m->has_smufl) + free(m->smufl); + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_accidental.h b/gen/test/c/mx/mx_accidental.h new file mode 100644 index 000000000..782af7e4d --- /dev/null +++ b/gen/test/c/mx/mx_accidental.h @@ -0,0 +1,66 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ACCIDENTAL_H_INCLUDED +#define MX_ACCIDENTAL_H_INCLUDED + +#include +#include +#include +#include "mx_accidental_value.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_smufl_accidental_glyph_name.h" +#include "mx_symbol_size.h" +#include "mx_tenths.h" +#include "mx_yes_no.h" + +/* + * The accidental type represents actual notated accidentals. Editorial and cautionary indications + * are indicated by attributes. Values for these attributes are "no" if not present. Specific + * graphic display such as parentheses, brackets, and size are controlled by the level-display + * attribute group. + */ +typedef struct { + bool has_cautionary; + MxYesNo cautionary; /* attribute cautionary */ + bool has_editorial; + MxYesNo editorial; /* attribute editorial */ + bool has_smufl; + MxSMUFLAccidentalGlyphName smufl; /* attribute smufl */ + bool has_parentheses; + MxYesNo parentheses; /* attribute parentheses */ + bool has_bracket; + MxYesNo bracket; /* attribute bracket */ + bool has_size; + MxSymbolSize size; /* attribute size */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + MxAccidentalValue value; /* text content */ +} MxAccidental; + +/* NULL on error; the message is in mx_error(). */ +MxAccidental *mx_accidental_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_accidental_serialize(const MxAccidental *m, xmlNodePtr parent, const char *tag); +void mx_accidental_free(MxAccidental *m); + +#endif /* MX_ACCIDENTAL_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_accidental_mark.c b/gen/test/c/mx/mx_accidental_mark.c new file mode 100644 index 000000000..1d15b877a --- /dev/null +++ b/gen/test/c/mx/mx_accidental_mark.c @@ -0,0 +1,173 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_accidental_mark.h" + +#include "mx_runtime.h" +#include +#include + +MxAccidentalMark *mx_accidental_mark_parse(xmlNodePtr el) { + MxAccidentalMark *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "smufl") == 0) { + m->has_smufl = true; + m->smufl = mx_smufl_accidental_glyph_name_parse(s); + } else if (strcmp(aname, "parentheses") == 0) { + m->has_parentheses = true; + m->parentheses = mx_yes_no_parse(s); + } else if (strcmp(aname, "bracket") == 0) { + m->has_bracket = true; + m->bracket = mx_yes_no_parse(s); + } else if (strcmp(aname, "size") == 0) { + m->has_size = true; + m->size = mx_symbol_size_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_accidental_mark_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_accidental_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_accidental_mark_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_accidental_mark_serialize(const MxAccidentalMark *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_accidental_value_to_string(m->value))); + if (m->has_smufl) { + xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); + } + if (m->has_parentheses) { + xmlSetProp(el, BAD_CAST "parentheses", BAD_CAST mx_yes_no_to_string(m->parentheses)); + } + if (m->has_bracket) { + xmlSetProp(el, BAD_CAST "bracket", BAD_CAST mx_yes_no_to_string(m->bracket)); + } + if (m->has_size) { + xmlSetProp(el, BAD_CAST "size", BAD_CAST mx_symbol_size_to_string(m->size)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_accidental_mark_free(MxAccidentalMark *m) { + if (!m) + return; + if (m->has_smufl) + free(m->smufl); + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + free(m); +} diff --git a/gen/test/c/mx/mx_accidental_mark.h b/gen/test/c/mx/mx_accidental_mark.h new file mode 100644 index 000000000..6ac1e29f3 --- /dev/null +++ b/gen/test/c/mx/mx_accidental_mark.h @@ -0,0 +1,65 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ACCIDENTAL_MARK_H_INCLUDED +#define MX_ACCIDENTAL_MARK_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_accidental_value.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_smufl_accidental_glyph_name.h" +#include "mx_symbol_size.h" +#include "mx_tenths.h" +#include "mx_yes_no.h" + +/* + * An accidental-mark can be used as a separate notation or as part of an ornament. When used in an + * ornament, position and placement are relative to the ornament, not relative to the note. + */ +typedef struct { + bool has_smufl; + MxSMUFLAccidentalGlyphName smufl; /* attribute smufl */ + bool has_parentheses; + MxYesNo parentheses; /* attribute parentheses */ + bool has_bracket; + MxYesNo bracket; /* attribute bracket */ + bool has_size; + MxSymbolSize size; /* attribute size */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_id; + char *id; /* attribute id */ + MxAccidentalValue value; /* text content */ +} MxAccidentalMark; + +/* NULL on error; the message is in mx_error(). */ +MxAccidentalMark *mx_accidental_mark_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_accidental_mark_serialize(const MxAccidentalMark *m, xmlNodePtr parent, const char *tag); +void mx_accidental_mark_free(MxAccidentalMark *m); + +#endif /* MX_ACCIDENTAL_MARK_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_accidental_text.c b/gen/test/c/mx/mx_accidental_text.c new file mode 100644 index 000000000..8a41a195e --- /dev/null +++ b/gen/test/c/mx/mx_accidental_text.c @@ -0,0 +1,239 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_accidental_text.h" + +#include "mx_runtime.h" +#include +#include + +MxAccidentalText *mx_accidental_text_parse(xmlNodePtr el) { + MxAccidentalText *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "smufl") == 0) { + m->has_smufl = true; + m->smufl = mx_smufl_accidental_glyph_name_parse(s); + } else if (strcmp(aname, "xml:lang") == 0) { + m->has_xml_lang = true; + m->xml_lang = mx_strdup(s); + } else if (strcmp(aname, "xml:space") == 0) { + m->has_xml_space = true; + m->xml_space = mx_strdup(s); + } else if (strcmp(aname, "justify") == 0) { + m->has_justify = true; + m->justify = mx_left_center_right_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else if (strcmp(aname, "underline") == 0) { + m->has_underline = true; + m->underline = mx_number_of_lines_parse(s); + } else if (strcmp(aname, "overline") == 0) { + m->has_overline = true; + m->overline = mx_number_of_lines_parse(s); + } else if (strcmp(aname, "line-through") == 0) { + m->has_line_through = true; + m->line_through = mx_number_of_lines_parse(s); + } else if (strcmp(aname, "rotation") == 0) { + m->has_rotation = true; + m->rotation = mx_rotation_degrees_parse(s); + } else if (strcmp(aname, "letter-spacing") == 0) { + m->has_letter_spacing = true; + m->letter_spacing = mx_number_or_normal_parse(s); + } else if (strcmp(aname, "line-height") == 0) { + m->has_line_height = true; + m->line_height = mx_number_or_normal_parse(s); + } else if (strcmp(aname, "dir") == 0) { + m->has_dir = true; + m->dir = mx_text_direction_parse(s); + } else if (strcmp(aname, "enclosure") == 0) { + m->has_enclosure = true; + m->enclosure = mx_enclosure_shape_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_accidental_text_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_accidental_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_accidental_text_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_accidental_text_serialize(const MxAccidentalText *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_accidental_value_to_string(m->value))); + if (m->has_smufl) { + xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); + } + if (m->has_xml_lang) { + xmlSetProp(el, BAD_CAST "xml:lang", BAD_CAST m->xml_lang); + } + if (m->has_xml_space) { + xmlSetProp(el, BAD_CAST "xml:space", BAD_CAST m->xml_space); + } + if (m->has_justify) { + xmlSetProp(el, BAD_CAST "justify", BAD_CAST mx_left_center_right_to_string(m->justify)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + if (m->has_underline) { + char *s = mx_number_of_lines_to_string(m->underline); + xmlSetProp(el, BAD_CAST "underline", BAD_CAST s); + free(s); + } + if (m->has_overline) { + char *s = mx_number_of_lines_to_string(m->overline); + xmlSetProp(el, BAD_CAST "overline", BAD_CAST s); + free(s); + } + if (m->has_line_through) { + char *s = mx_number_of_lines_to_string(m->line_through); + xmlSetProp(el, BAD_CAST "line-through", BAD_CAST s); + free(s); + } + if (m->has_rotation) { + char *s = mx_rotation_degrees_to_string(m->rotation); + xmlSetProp(el, BAD_CAST "rotation", BAD_CAST s); + free(s); + } + if (m->has_letter_spacing) { + char *s = mx_number_or_normal_to_string(m->letter_spacing); + xmlSetProp(el, BAD_CAST "letter-spacing", BAD_CAST s); + free(s); + } + if (m->has_line_height) { + char *s = mx_number_or_normal_to_string(m->line_height); + xmlSetProp(el, BAD_CAST "line-height", BAD_CAST s); + free(s); + } + if (m->has_dir) { + xmlSetProp(el, BAD_CAST "dir", BAD_CAST mx_text_direction_to_string(m->dir)); + } + if (m->has_enclosure) { + xmlSetProp(el, BAD_CAST "enclosure", BAD_CAST mx_enclosure_shape_to_string(m->enclosure)); + } + return el; +} + +void mx_accidental_text_free(MxAccidentalText *m) { + if (!m) + return; + if (m->has_smufl) + free(m->smufl); + if (m->has_xml_lang) + free(m->xml_lang); + if (m->has_xml_space) + free(m->xml_space); + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_letter_spacing) + mx_number_or_normal_free(&m->letter_spacing); + if (m->has_line_height) + mx_number_or_normal_free(&m->line_height); + free(m); +} diff --git a/gen/test/c/mx/mx_accidental_text.h b/gen/test/c/mx/mx_accidental_text.h new file mode 100644 index 000000000..5616a5d3d --- /dev/null +++ b/gen/test/c/mx/mx_accidental_text.h @@ -0,0 +1,85 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ACCIDENTAL_TEXT_H_INCLUDED +#define MX_ACCIDENTAL_TEXT_H_INCLUDED + +#include +#include +#include +#include "mx_accidental_value.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_enclosure_shape.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_center_right.h" +#include "mx_number_of_lines.h" +#include "mx_number_or_normal.h" +#include "mx_rotation_degrees.h" +#include "mx_smufl_accidental_glyph_name.h" +#include "mx_tenths.h" +#include "mx_text_direction.h" +#include "mx_valign.h" + +/* + * The accidental-text type represents an element with an accidental value and text-formatting + * attributes. + */ +typedef struct { + bool has_smufl; + MxSMUFLAccidentalGlyphName smufl; /* attribute smufl */ + bool has_xml_lang; + char *xml_lang; /* attribute xml:lang */ + bool has_xml_space; + char *xml_space; /* attribute xml:space */ + bool has_justify; + MxLeftCenterRight justify; /* attribute justify */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ + bool has_underline; + MxNumberOfLines underline; /* attribute underline */ + bool has_overline; + MxNumberOfLines overline; /* attribute overline */ + bool has_line_through; + MxNumberOfLines line_through; /* attribute line-through */ + bool has_rotation; + MxRotationDegrees rotation; /* attribute rotation */ + bool has_letter_spacing; + MxNumberOrNormal letter_spacing; /* attribute letter-spacing */ + bool has_line_height; + MxNumberOrNormal line_height; /* attribute line-height */ + bool has_dir; + MxTextDirection dir; /* attribute dir */ + bool has_enclosure; + MxEnclosureShape enclosure; /* attribute enclosure */ + MxAccidentalValue value; /* text content */ +} MxAccidentalText; + +/* NULL on error; the message is in mx_error(). */ +MxAccidentalText *mx_accidental_text_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_accidental_text_serialize(const MxAccidentalText *m, xmlNodePtr parent, const char *tag); +void mx_accidental_text_free(MxAccidentalText *m); + +#endif /* MX_ACCIDENTAL_TEXT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_accord.c b/gen/test/c/mx/mx_accord.c new file mode 100644 index 000000000..cc14618bc --- /dev/null +++ b/gen/test/c/mx/mx_accord.c @@ -0,0 +1,120 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_accord.h" + +#include "mx_runtime.h" +#include +#include + +MxAccord *mx_accord_parse(xmlNodePtr el) { + MxAccord *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "string") == 0) { + m->has_string = true; + m->string = mx_string_number_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_accord_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxAccordChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "tuning-step") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->tuning_step = malloc(sizeof(*ch->tuning_step)); + if (!ch->tuning_step) + abort(); + *ch->tuning_step = mx_step_parse(s); + xmlFree(text); + } else if (strcmp(tag, "tuning-alter") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->tuning_alter = malloc(sizeof(*ch->tuning_alter)); + if (!ch->tuning_alter) + abort(); + *ch->tuning_alter = mx_semitones_parse(s); + xmlFree(text); + } else if (strcmp(tag, "tuning-octave") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->tuning_octave = malloc(sizeof(*ch->tuning_octave)); + if (!ch->tuning_octave) + abort(); + *ch->tuning_octave = mx_octave_parse(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_accord_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_accord_serialize(const MxAccord *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_string) { + char *s = mx_string_number_to_string(m->string); + xmlSetProp(el, BAD_CAST "string", BAD_CAST s); + free(s); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxAccordChild *ch = &m->children[i]; + if (ch->tuning_step) { + xmlNewTextChild(el, NULL, BAD_CAST "tuning-step", BAD_CAST mx_step_to_string((*ch->tuning_step))); + } else if (ch->tuning_alter) { + char *s = mx_semitones_to_string((*ch->tuning_alter)); + xmlNewTextChild(el, NULL, BAD_CAST "tuning-alter", BAD_CAST s); + free(s); + } else if (ch->tuning_octave) { + char *s = mx_octave_to_string((*ch->tuning_octave)); + xmlNewTextChild(el, NULL, BAD_CAST "tuning-octave", BAD_CAST s); + free(s); + } + } + return el; +} + +void mx_accord_free(MxAccord *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxAccordChild *ch = &m->children[i]; + free(ch->tuning_step); + free(ch->tuning_alter); + free(ch->tuning_octave); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_accord.h b/gen/test/c/mx/mx_accord.h new file mode 100644 index 000000000..fc22e47fc --- /dev/null +++ b/gen/test/c/mx/mx_accord.h @@ -0,0 +1,40 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ACCORD_H_INCLUDED +#define MX_ACCORD_H_INCLUDED + +#include +#include +#include +#include "mx_octave.h" +#include "mx_semitones.h" +#include "mx_step.h" +#include "mx_string_number.h" + +/* + * The accord type represents the tuning of a single string in the scordatura element. It uses the + * same group of elements as the staff-tuning element. Strings are numbered from high to low. + */ +/* One child element of MxAccord: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxStep *tuning_step; + MxSemitones *tuning_alter; + MxOctave *tuning_octave; +} MxAccordChild; + +typedef struct { + bool has_string; + MxStringNumber string; /* attribute string */ + MxAccordChild *children; /* child elements in document order */ + size_t children_count; +} MxAccord; + +/* NULL on error; the message is in mx_error(). */ +MxAccord *mx_accord_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_accord_serialize(const MxAccord *m, xmlNodePtr parent, const char *tag); +void mx_accord_free(MxAccord *m); + +#endif /* MX_ACCORD_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_accordion_registration.c b/gen/test/c/mx/mx_accordion_registration.c new file mode 100644 index 000000000..4e4eb075b --- /dev/null +++ b/gen/test/c/mx/mx_accordion_registration.c @@ -0,0 +1,198 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_accordion_registration.h" + +#include "mx_runtime.h" +#include +#include + +MxAccordionRegistration *mx_accordion_registration_parse(xmlNodePtr el) { + MxAccordionRegistration *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_accordion_registration_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxAccordionRegistrationChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "accordion-high") == 0) { + ch->accordion_high = mx_empty_parse(c); + if (!ch->accordion_high) { + mx_accordion_registration_free(m); + return NULL; + } + } else if (strcmp(tag, "accordion-middle") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->accordion_middle = malloc(sizeof(*ch->accordion_middle)); + if (!ch->accordion_middle) + abort(); + *ch->accordion_middle = mx_accordion_middle_parse(s); + xmlFree(text); + } else if (strcmp(tag, "accordion-low") == 0) { + ch->accordion_low = mx_empty_parse(c); + if (!ch->accordion_low) { + mx_accordion_registration_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_accordion_registration_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_accordion_registration_serialize(const MxAccordionRegistration *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxAccordionRegistrationChild *ch = &m->children[i]; + if (ch->accordion_high) { + mx_empty_serialize(ch->accordion_high, el, "accordion-high"); + } else if (ch->accordion_middle) { + char *s = mx_accordion_middle_to_string((*ch->accordion_middle)); + xmlNewTextChild(el, NULL, BAD_CAST "accordion-middle", BAD_CAST s); + free(s); + } else if (ch->accordion_low) { + mx_empty_serialize(ch->accordion_low, el, "accordion-low"); + } + } + return el; +} + +void mx_accordion_registration_free(MxAccordionRegistration *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxAccordionRegistrationChild *ch = &m->children[i]; + if (ch->accordion_high) + mx_empty_free(ch->accordion_high); + free(ch->accordion_middle); + if (ch->accordion_low) + mx_empty_free(ch->accordion_low); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_accordion_registration.h b/gen/test/c/mx/mx_accordion_registration.h new file mode 100644 index 000000000..66d3bebca --- /dev/null +++ b/gen/test/c/mx/mx_accordion_registration.h @@ -0,0 +1,71 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ACCORDION_REGISTRATION_H_INCLUDED +#define MX_ACCORDION_REGISTRATION_H_INCLUDED + +#include +#include +#include +#include "mx_accordion_middle.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_empty.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_center_right.h" +#include "mx_tenths.h" +#include "mx_valign.h" + +/* + * The accordion-registration type is use for accordion registration symbols. These are circular + * symbols divided horizontally into high, middle, and low sections that correspond to 4', 8', and + * 16' pipes. Each accordion-high, accordion-middle, and accordion-low element represents the + * presence of one or more dots in the registration diagram. An accordion-registration element needs + * to have at least one of the child elements present. + */ +/* One child element of MxAccordionRegistration: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxEmpty *accordion_high; + MxAccordionMiddle *accordion_middle; + MxEmpty *accordion_low; +} MxAccordionRegistrationChild; + +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ + bool has_id; + char *id; /* attribute id */ + MxAccordionRegistrationChild *children; /* child elements in document order */ + size_t children_count; +} MxAccordionRegistration; + +/* NULL on error; the message is in mx_error(). */ +MxAccordionRegistration *mx_accordion_registration_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_accordion_registration_serialize(const MxAccordionRegistration *m, xmlNodePtr parent, const char *tag); +void mx_accordion_registration_free(MxAccordionRegistration *m); + +#endif /* MX_ACCORDION_REGISTRATION_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_appearance.c b/gen/test/c/mx/mx_appearance.c new file mode 100644 index 000000000..5205cfa62 --- /dev/null +++ b/gen/test/c/mx/mx_appearance.c @@ -0,0 +1,125 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_appearance.h" + +#include "mx_runtime.h" +#include +#include + +MxAppearance *mx_appearance_parse(xmlNodePtr el) { + MxAppearance *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_appearance_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxAppearanceChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "line-width") == 0) { + ch->line_width = mx_line_width_parse(c); + if (!ch->line_width) { + mx_appearance_free(m); + return NULL; + } + } else if (strcmp(tag, "note-size") == 0) { + ch->note_size = mx_note_size_parse(c); + if (!ch->note_size) { + mx_appearance_free(m); + return NULL; + } + } else if (strcmp(tag, "distance") == 0) { + ch->distance = mx_distance_parse(c); + if (!ch->distance) { + mx_appearance_free(m); + return NULL; + } + } else if (strcmp(tag, "glyph") == 0) { + ch->glyph = mx_glyph_parse(c); + if (!ch->glyph) { + mx_appearance_free(m); + return NULL; + } + } else if (strcmp(tag, "other-appearance") == 0) { + ch->other_appearance = mx_other_appearance_parse(c); + if (!ch->other_appearance) { + mx_appearance_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_appearance_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_appearance_serialize(const MxAppearance *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxAppearanceChild *ch = &m->children[i]; + if (ch->line_width) { + mx_line_width_serialize(ch->line_width, el, "line-width"); + } else if (ch->note_size) { + mx_note_size_serialize(ch->note_size, el, "note-size"); + } else if (ch->distance) { + mx_distance_serialize(ch->distance, el, "distance"); + } else if (ch->glyph) { + mx_glyph_serialize(ch->glyph, el, "glyph"); + } else if (ch->other_appearance) { + mx_other_appearance_serialize(ch->other_appearance, el, "other-appearance"); + } + } + return el; +} + +void mx_appearance_free(MxAppearance *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxAppearanceChild *ch = &m->children[i]; + if (ch->line_width) + mx_line_width_free(ch->line_width); + if (ch->note_size) + mx_note_size_free(ch->note_size); + if (ch->distance) + mx_distance_free(ch->distance); + if (ch->glyph) + mx_glyph_free(ch->glyph); + if (ch->other_appearance) + mx_other_appearance_free(ch->other_appearance); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_appearance.h b/gen/test/c/mx/mx_appearance.h new file mode 100644 index 000000000..7cf8d0fbd --- /dev/null +++ b/gen/test/c/mx/mx_appearance.h @@ -0,0 +1,43 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_APPEARANCE_H_INCLUDED +#define MX_APPEARANCE_H_INCLUDED + +#include +#include +#include +#include "mx_distance.h" +#include "mx_glyph.h" +#include "mx_line_width.h" +#include "mx_note_size.h" +#include "mx_other_appearance.h" + +/* + * The appearance type controls general graphical settings for the music's final form appearance on + * a printed page of display. This includes support for line widths, definitions for note sizes, and + * standard distances between notation elements, plus an extension element for other aspects of + * appearance. + */ +/* One child element of MxAppearance: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxLineWidth *line_width; + MxNoteSize *note_size; + MxDistance *distance; + MxGlyph *glyph; + MxOtherAppearance *other_appearance; +} MxAppearanceChild; + +typedef struct { + MxAppearanceChild *children; /* child elements in document order */ + size_t children_count; +} MxAppearance; + +/* NULL on error; the message is in mx_error(). */ +MxAppearance *mx_appearance_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_appearance_serialize(const MxAppearance *m, xmlNodePtr parent, const char *tag); +void mx_appearance_free(MxAppearance *m); + +#endif /* MX_APPEARANCE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_arpeggiate.c b/gen/test/c/mx/mx_arpeggiate.c new file mode 100644 index 000000000..5f4c2f5c2 --- /dev/null +++ b/gen/test/c/mx/mx_arpeggiate.c @@ -0,0 +1,124 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_arpeggiate.h" + +#include "mx_runtime.h" +#include +#include + +MxArpeggiate *mx_arpeggiate_parse(xmlNodePtr el) { + MxArpeggiate *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_number_level_parse(s); + } else if (strcmp(aname, "direction") == 0) { + m->has_direction = true; + m->direction = mx_up_down_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_arpeggiate_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_arpeggiate_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_arpeggiate_serialize(const MxArpeggiate *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_number) { + char *s = mx_number_level_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_direction) { + xmlSetProp(el, BAD_CAST "direction", BAD_CAST mx_up_down_to_string(m->direction)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_arpeggiate_free(MxArpeggiate *m) { + if (!m) + return; + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + free(m); +} diff --git a/gen/test/c/mx/mx_arpeggiate.h b/gen/test/c/mx/mx_arpeggiate.h new file mode 100644 index 000000000..8abff7e7e --- /dev/null +++ b/gen/test/c/mx/mx_arpeggiate.h @@ -0,0 +1,48 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ARPEGGIATE_H_INCLUDED +#define MX_ARPEGGIATE_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_number_level.h" +#include "mx_tenths.h" +#include "mx_up_down.h" + +/* + * The arpeggiate type indicates that this note is part of an arpeggiated chord. The number + * attribute can be used to distinguish between two simultaneous chords arpeggiated separately + * (different numbers) or together (same number). The up-down attribute is used if there is an arrow + * on the arpeggio sign. By default, arpeggios go from the lowest to highest note. + */ +typedef struct { + bool has_number; + MxNumberLevel number; /* attribute number */ + bool has_direction; + MxUpDown direction; /* attribute direction */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_color; + MxColor color; /* attribute color */ + bool has_id; + char *id; /* attribute id */ +} MxArpeggiate; + +/* NULL on error; the message is in mx_error(). */ +MxArpeggiate *mx_arpeggiate_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_arpeggiate_serialize(const MxArpeggiate *m, xmlNodePtr parent, const char *tag); +void mx_arpeggiate_free(MxArpeggiate *m); + +#endif /* MX_ARPEGGIATE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_arrow.c b/gen/test/c/mx/mx_arrow.c new file mode 100644 index 000000000..7956bb6ec --- /dev/null +++ b/gen/test/c/mx/mx_arrow.c @@ -0,0 +1,202 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_arrow.h" + +#include "mx_runtime.h" +#include +#include + +MxArrow *mx_arrow_parse(xmlNodePtr el) { + MxArrow *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "smufl") == 0) { + m->has_smufl = true; + m->smufl = mx_smufl_glyph_name_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_arrow_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxArrowChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "arrow-direction") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->arrow_direction = malloc(sizeof(*ch->arrow_direction)); + if (!ch->arrow_direction) + abort(); + *ch->arrow_direction = mx_arrow_direction_parse(s); + xmlFree(text); + } else if (strcmp(tag, "arrow-style") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->arrow_style = malloc(sizeof(*ch->arrow_style)); + if (!ch->arrow_style) + abort(); + *ch->arrow_style = mx_arrow_style_parse(s); + xmlFree(text); + } else if (strcmp(tag, "arrowhead") == 0) { + ch->arrowhead = mx_empty_parse(c); + if (!ch->arrowhead) { + mx_arrow_free(m); + return NULL; + } + } else if (strcmp(tag, "circular-arrow") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->circular_arrow = malloc(sizeof(*ch->circular_arrow)); + if (!ch->circular_arrow) + abort(); + *ch->circular_arrow = mx_circular_arrow_parse(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_arrow_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_arrow_serialize(const MxArrow *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_smufl) { + xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxArrowChild *ch = &m->children[i]; + if (ch->arrow_direction) { + xmlNewTextChild(el, NULL, BAD_CAST "arrow-direction", BAD_CAST mx_arrow_direction_to_string((*ch->arrow_direction))); + } else if (ch->arrow_style) { + xmlNewTextChild(el, NULL, BAD_CAST "arrow-style", BAD_CAST mx_arrow_style_to_string((*ch->arrow_style))); + } else if (ch->arrowhead) { + mx_empty_serialize(ch->arrowhead, el, "arrowhead"); + } else if (ch->circular_arrow) { + xmlNewTextChild(el, NULL, BAD_CAST "circular-arrow", BAD_CAST mx_circular_arrow_to_string((*ch->circular_arrow))); + } + } + return el; +} + +void mx_arrow_free(MxArrow *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_smufl) + free(m->smufl); + for (size_t i = 0; i < m->children_count; i++) { + MxArrowChild *ch = &m->children[i]; + free(ch->arrow_direction); + free(ch->arrow_style); + if (ch->arrowhead) + mx_empty_free(ch->arrowhead); + free(ch->circular_arrow); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_arrow.h b/gen/test/c/mx/mx_arrow.h new file mode 100644 index 000000000..e77387ca4 --- /dev/null +++ b/gen/test/c/mx/mx_arrow.h @@ -0,0 +1,72 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ARROW_H_INCLUDED +#define MX_ARROW_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_arrow_direction.h" +#include "mx_arrow_style.h" +#include "mx_circular_arrow.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_empty.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_smufl_glyph_name.h" +#include "mx_tenths.h" + +/* + * The arrow element represents an arrow used for a musical technical indication. It can represent + * both Unicode and SMuFL arrows. The presence of an arrowhead element indicates that only the + * arrowhead is displayed, not the arrow stem. The smufl attribute distinguishes different SMuFL + * glyphs that have an arrow appearance such as arrowBlackUp, guitarStrumUp, or handbellsSwingUp. + * The specified glyph should match the descriptive representation. + */ +/* One child element of MxArrow: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxArrowDirection *arrow_direction; + MxArrowStyle *arrow_style; + MxEmpty *arrowhead; + MxCircularArrow *circular_arrow; +} MxArrowChild; + +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_smufl; + MxSMUFLGlyphName smufl; /* attribute smufl */ + MxArrowChild *children; /* child elements in document order */ + size_t children_count; +} MxArrow; + +/* NULL on error; the message is in mx_error(). */ +MxArrow *mx_arrow_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_arrow_serialize(const MxArrow *m, xmlNodePtr parent, const char *tag); +void mx_arrow_free(MxArrow *m); + +#endif /* MX_ARROW_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_articulations.c b/gen/test/c/mx/mx_articulations.c new file mode 100644 index 000000000..dca35a98e --- /dev/null +++ b/gen/test/c/mx/mx_articulations.c @@ -0,0 +1,253 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_articulations.h" + +#include "mx_runtime.h" +#include +#include + +MxArticulations *mx_articulations_parse(xmlNodePtr el) { + MxArticulations *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_articulations_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxArticulationsChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "accent") == 0) { + ch->accent = mx_empty_placement_parse(c); + if (!ch->accent) { + mx_articulations_free(m); + return NULL; + } + } else if (strcmp(tag, "strong-accent") == 0) { + ch->strong_accent = mx_strong_accent_parse(c); + if (!ch->strong_accent) { + mx_articulations_free(m); + return NULL; + } + } else if (strcmp(tag, "staccato") == 0) { + ch->staccato = mx_empty_placement_parse(c); + if (!ch->staccato) { + mx_articulations_free(m); + return NULL; + } + } else if (strcmp(tag, "tenuto") == 0) { + ch->tenuto = mx_empty_placement_parse(c); + if (!ch->tenuto) { + mx_articulations_free(m); + return NULL; + } + } else if (strcmp(tag, "detached-legato") == 0) { + ch->detached_legato = mx_empty_placement_parse(c); + if (!ch->detached_legato) { + mx_articulations_free(m); + return NULL; + } + } else if (strcmp(tag, "staccatissimo") == 0) { + ch->staccatissimo = mx_empty_placement_parse(c); + if (!ch->staccatissimo) { + mx_articulations_free(m); + return NULL; + } + } else if (strcmp(tag, "spiccato") == 0) { + ch->spiccato = mx_empty_placement_parse(c); + if (!ch->spiccato) { + mx_articulations_free(m); + return NULL; + } + } else if (strcmp(tag, "scoop") == 0) { + ch->scoop = mx_empty_line_parse(c); + if (!ch->scoop) { + mx_articulations_free(m); + return NULL; + } + } else if (strcmp(tag, "plop") == 0) { + ch->plop = mx_empty_line_parse(c); + if (!ch->plop) { + mx_articulations_free(m); + return NULL; + } + } else if (strcmp(tag, "doit") == 0) { + ch->doit = mx_empty_line_parse(c); + if (!ch->doit) { + mx_articulations_free(m); + return NULL; + } + } else if (strcmp(tag, "falloff") == 0) { + ch->falloff = mx_empty_line_parse(c); + if (!ch->falloff) { + mx_articulations_free(m); + return NULL; + } + } else if (strcmp(tag, "breath-mark") == 0) { + ch->breath_mark = mx_breath_mark_parse(c); + if (!ch->breath_mark) { + mx_articulations_free(m); + return NULL; + } + } else if (strcmp(tag, "caesura") == 0) { + ch->caesura = mx_caesura_parse(c); + if (!ch->caesura) { + mx_articulations_free(m); + return NULL; + } + } else if (strcmp(tag, "stress") == 0) { + ch->stress = mx_empty_placement_parse(c); + if (!ch->stress) { + mx_articulations_free(m); + return NULL; + } + } else if (strcmp(tag, "unstress") == 0) { + ch->unstress = mx_empty_placement_parse(c); + if (!ch->unstress) { + mx_articulations_free(m); + return NULL; + } + } else if (strcmp(tag, "soft-accent") == 0) { + ch->soft_accent = mx_empty_placement_parse(c); + if (!ch->soft_accent) { + mx_articulations_free(m); + return NULL; + } + } else if (strcmp(tag, "other-articulation") == 0) { + ch->other_articulation = mx_other_placement_text_parse(c); + if (!ch->other_articulation) { + mx_articulations_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_articulations_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_articulations_serialize(const MxArticulations *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxArticulationsChild *ch = &m->children[i]; + if (ch->accent) { + mx_empty_placement_serialize(ch->accent, el, "accent"); + } else if (ch->strong_accent) { + mx_strong_accent_serialize(ch->strong_accent, el, "strong-accent"); + } else if (ch->staccato) { + mx_empty_placement_serialize(ch->staccato, el, "staccato"); + } else if (ch->tenuto) { + mx_empty_placement_serialize(ch->tenuto, el, "tenuto"); + } else if (ch->detached_legato) { + mx_empty_placement_serialize(ch->detached_legato, el, "detached-legato"); + } else if (ch->staccatissimo) { + mx_empty_placement_serialize(ch->staccatissimo, el, "staccatissimo"); + } else if (ch->spiccato) { + mx_empty_placement_serialize(ch->spiccato, el, "spiccato"); + } else if (ch->scoop) { + mx_empty_line_serialize(ch->scoop, el, "scoop"); + } else if (ch->plop) { + mx_empty_line_serialize(ch->plop, el, "plop"); + } else if (ch->doit) { + mx_empty_line_serialize(ch->doit, el, "doit"); + } else if (ch->falloff) { + mx_empty_line_serialize(ch->falloff, el, "falloff"); + } else if (ch->breath_mark) { + mx_breath_mark_serialize(ch->breath_mark, el, "breath-mark"); + } else if (ch->caesura) { + mx_caesura_serialize(ch->caesura, el, "caesura"); + } else if (ch->stress) { + mx_empty_placement_serialize(ch->stress, el, "stress"); + } else if (ch->unstress) { + mx_empty_placement_serialize(ch->unstress, el, "unstress"); + } else if (ch->soft_accent) { + mx_empty_placement_serialize(ch->soft_accent, el, "soft-accent"); + } else if (ch->other_articulation) { + mx_other_placement_text_serialize(ch->other_articulation, el, "other-articulation"); + } + } + return el; +} + +void mx_articulations_free(MxArticulations *m) { + if (!m) + return; + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxArticulationsChild *ch = &m->children[i]; + if (ch->accent) + mx_empty_placement_free(ch->accent); + if (ch->strong_accent) + mx_strong_accent_free(ch->strong_accent); + if (ch->staccato) + mx_empty_placement_free(ch->staccato); + if (ch->tenuto) + mx_empty_placement_free(ch->tenuto); + if (ch->detached_legato) + mx_empty_placement_free(ch->detached_legato); + if (ch->staccatissimo) + mx_empty_placement_free(ch->staccatissimo); + if (ch->spiccato) + mx_empty_placement_free(ch->spiccato); + if (ch->scoop) + mx_empty_line_free(ch->scoop); + if (ch->plop) + mx_empty_line_free(ch->plop); + if (ch->doit) + mx_empty_line_free(ch->doit); + if (ch->falloff) + mx_empty_line_free(ch->falloff); + if (ch->breath_mark) + mx_breath_mark_free(ch->breath_mark); + if (ch->caesura) + mx_caesura_free(ch->caesura); + if (ch->stress) + mx_empty_placement_free(ch->stress); + if (ch->unstress) + mx_empty_placement_free(ch->unstress); + if (ch->soft_accent) + mx_empty_placement_free(ch->soft_accent); + if (ch->other_articulation) + mx_other_placement_text_free(ch->other_articulation); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_articulations.h b/gen/test/c/mx/mx_articulations.h new file mode 100644 index 000000000..e49e14934 --- /dev/null +++ b/gen/test/c/mx/mx_articulations.h @@ -0,0 +1,55 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ARTICULATIONS_H_INCLUDED +#define MX_ARTICULATIONS_H_INCLUDED + +#include +#include +#include +#include "mx_breath_mark.h" +#include "mx_caesura.h" +#include "mx_empty_line.h" +#include "mx_empty_placement.h" +#include "mx_other_placement_text.h" +#include "mx_strong_accent.h" + +/* + * Articulations and accents are grouped together here. + */ +/* One child element of MxArticulations: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxEmptyPlacement *accent; + MxStrongAccent *strong_accent; + MxEmptyPlacement *staccato; + MxEmptyPlacement *tenuto; + MxEmptyPlacement *detached_legato; + MxEmptyPlacement *staccatissimo; + MxEmptyPlacement *spiccato; + MxEmptyLine *scoop; + MxEmptyLine *plop; + MxEmptyLine *doit; + MxEmptyLine *falloff; + MxBreathMark *breath_mark; + MxCaesura *caesura; + MxEmptyPlacement *stress; + MxEmptyPlacement *unstress; + MxEmptyPlacement *soft_accent; + MxOtherPlacementText *other_articulation; +} MxArticulationsChild; + +typedef struct { + bool has_id; + char *id; /* attribute id */ + MxArticulationsChild *children; /* child elements in document order */ + size_t children_count; +} MxArticulations; + +/* NULL on error; the message is in mx_error(). */ +MxArticulations *mx_articulations_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_articulations_serialize(const MxArticulations *m, xmlNodePtr parent, const char *tag); +void mx_articulations_free(MxArticulations *m); + +#endif /* MX_ARTICULATIONS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_attributes.c b/gen/test/c/mx/mx_attributes.c new file mode 100644 index 000000000..d2cc364f5 --- /dev/null +++ b/gen/test/c/mx/mx_attributes.c @@ -0,0 +1,214 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_attributes.h" + +#include "mx_runtime.h" +#include +#include + +MxAttributes *mx_attributes_parse(xmlNodePtr el) { + MxAttributes *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_attributes_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxAttributesChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "footnote") == 0) { + ch->footnote = mx_formatted_text_parse(c); + if (!ch->footnote) { + mx_attributes_free(m); + return NULL; + } + } else if (strcmp(tag, "level") == 0) { + ch->level = mx_level_parse(c); + if (!ch->level) { + mx_attributes_free(m); + return NULL; + } + } else if (strcmp(tag, "divisions") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->divisions = malloc(sizeof(*ch->divisions)); + if (!ch->divisions) + abort(); + *ch->divisions = mx_positive_divisions_parse(s); + xmlFree(text); + } else if (strcmp(tag, "key") == 0) { + ch->key = mx_key_parse(c); + if (!ch->key) { + mx_attributes_free(m); + return NULL; + } + } else if (strcmp(tag, "time") == 0) { + ch->time = mx_time_parse(c); + if (!ch->time) { + mx_attributes_free(m); + return NULL; + } + } else if (strcmp(tag, "staves") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->staves = malloc(sizeof(*ch->staves)); + if (!ch->staves) + abort(); + *ch->staves = mx_parse_int(s); + xmlFree(text); + } else if (strcmp(tag, "part-symbol") == 0) { + ch->part_symbol = mx_part_symbol_parse(c); + if (!ch->part_symbol) { + mx_attributes_free(m); + return NULL; + } + } else if (strcmp(tag, "instruments") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->instruments = malloc(sizeof(*ch->instruments)); + if (!ch->instruments) + abort(); + *ch->instruments = mx_parse_int(s); + xmlFree(text); + } else if (strcmp(tag, "clef") == 0) { + ch->clef = mx_clef_parse(c); + if (!ch->clef) { + mx_attributes_free(m); + return NULL; + } + } else if (strcmp(tag, "staff-details") == 0) { + ch->staff_details = mx_staff_details_parse(c); + if (!ch->staff_details) { + mx_attributes_free(m); + return NULL; + } + } else if (strcmp(tag, "transpose") == 0) { + ch->transpose = mx_transpose_parse(c); + if (!ch->transpose) { + mx_attributes_free(m); + return NULL; + } + } else if (strcmp(tag, "directive") == 0) { + ch->directive = mx_directive_parse(c); + if (!ch->directive) { + mx_attributes_free(m); + return NULL; + } + } else if (strcmp(tag, "measure-style") == 0) { + ch->measure_style = mx_measure_style_parse(c); + if (!ch->measure_style) { + mx_attributes_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_attributes_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_attributes_serialize(const MxAttributes *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxAttributesChild *ch = &m->children[i]; + if (ch->footnote) { + mx_formatted_text_serialize(ch->footnote, el, "footnote"); + } else if (ch->level) { + mx_level_serialize(ch->level, el, "level"); + } else if (ch->divisions) { + char *s = mx_positive_divisions_to_string((*ch->divisions)); + xmlNewTextChild(el, NULL, BAD_CAST "divisions", BAD_CAST s); + free(s); + } else if (ch->key) { + mx_key_serialize(ch->key, el, "key"); + } else if (ch->time) { + mx_time_serialize(ch->time, el, "time"); + } else if (ch->staves) { + char *s = mx_format_int((*ch->staves)); + xmlNewTextChild(el, NULL, BAD_CAST "staves", BAD_CAST s); + free(s); + } else if (ch->part_symbol) { + mx_part_symbol_serialize(ch->part_symbol, el, "part-symbol"); + } else if (ch->instruments) { + char *s = mx_format_int((*ch->instruments)); + xmlNewTextChild(el, NULL, BAD_CAST "instruments", BAD_CAST s); + free(s); + } else if (ch->clef) { + mx_clef_serialize(ch->clef, el, "clef"); + } else if (ch->staff_details) { + mx_staff_details_serialize(ch->staff_details, el, "staff-details"); + } else if (ch->transpose) { + mx_transpose_serialize(ch->transpose, el, "transpose"); + } else if (ch->directive) { + mx_directive_serialize(ch->directive, el, "directive"); + } else if (ch->measure_style) { + mx_measure_style_serialize(ch->measure_style, el, "measure-style"); + } + } + return el; +} + +void mx_attributes_free(MxAttributes *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxAttributesChild *ch = &m->children[i]; + if (ch->footnote) + mx_formatted_text_free(ch->footnote); + if (ch->level) + mx_level_free(ch->level); + free(ch->divisions); + if (ch->key) + mx_key_free(ch->key); + if (ch->time) + mx_time_free(ch->time); + free(ch->staves); + if (ch->part_symbol) + mx_part_symbol_free(ch->part_symbol); + free(ch->instruments); + if (ch->clef) + mx_clef_free(ch->clef); + if (ch->staff_details) + mx_staff_details_free(ch->staff_details); + if (ch->transpose) + mx_transpose_free(ch->transpose); + if (ch->directive) + mx_directive_free(ch->directive); + if (ch->measure_style) + mx_measure_style_free(ch->measure_style); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_attributes.h b/gen/test/c/mx/mx_attributes.h new file mode 100644 index 000000000..ef13977a6 --- /dev/null +++ b/gen/test/c/mx/mx_attributes.h @@ -0,0 +1,56 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ATTRIBUTES_H_INCLUDED +#define MX_ATTRIBUTES_H_INCLUDED + +#include +#include +#include +#include "mx_clef.h" +#include "mx_directive.h" +#include "mx_formatted_text.h" +#include "mx_key.h" +#include "mx_level.h" +#include "mx_measure_style.h" +#include "mx_part_symbol.h" +#include "mx_positive_divisions.h" +#include "mx_staff_details.h" +#include "mx_time.h" +#include "mx_transpose.h" + +/* + * The attributes element contains musical information that typically changes on measure boundaries. + * This includes key and time signatures, clefs, transpositions, and staving. When attributes are + * changed mid-measure, it affects the music in score order, not in MusicXML document order. + */ +/* One child element of MxAttributes: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxFormattedText *footnote; + MxLevel *level; + MxPositiveDivisions *divisions; + MxKey *key; + MxTime *time; + long *staves; + MxPartSymbol *part_symbol; + long *instruments; + MxClef *clef; + MxStaffDetails *staff_details; + MxTranspose *transpose; + MxDirective *directive; + MxMeasureStyle *measure_style; +} MxAttributesChild; + +typedef struct { + MxAttributesChild *children; /* child elements in document order */ + size_t children_count; +} MxAttributes; + +/* NULL on error; the message is in mx_error(). */ +MxAttributes *mx_attributes_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_attributes_serialize(const MxAttributes *m, xmlNodePtr parent, const char *tag); +void mx_attributes_free(MxAttributes *m); + +#endif /* MX_ATTRIBUTES_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_backup.c b/gen/test/c/mx/mx_backup.c new file mode 100644 index 000000000..48bf0b7e6 --- /dev/null +++ b/gen/test/c/mx/mx_backup.c @@ -0,0 +1,108 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_backup.h" + +#include "mx_runtime.h" +#include +#include + +MxBackup *mx_backup_parse(xmlNodePtr el) { + MxBackup *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_backup_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxBackupChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "duration") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->duration = malloc(sizeof(*ch->duration)); + if (!ch->duration) + abort(); + *ch->duration = mx_positive_divisions_parse(s); + xmlFree(text); + } else if (strcmp(tag, "footnote") == 0) { + ch->footnote = mx_formatted_text_parse(c); + if (!ch->footnote) { + mx_backup_free(m); + return NULL; + } + } else if (strcmp(tag, "level") == 0) { + ch->level = mx_level_parse(c); + if (!ch->level) { + mx_backup_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_backup_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_backup_serialize(const MxBackup *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxBackupChild *ch = &m->children[i]; + if (ch->duration) { + char *s = mx_positive_divisions_to_string((*ch->duration)); + xmlNewTextChild(el, NULL, BAD_CAST "duration", BAD_CAST s); + free(s); + } else if (ch->footnote) { + mx_formatted_text_serialize(ch->footnote, el, "footnote"); + } else if (ch->level) { + mx_level_serialize(ch->level, el, "level"); + } + } + return el; +} + +void mx_backup_free(MxBackup *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxBackupChild *ch = &m->children[i]; + free(ch->duration); + if (ch->footnote) + mx_formatted_text_free(ch->footnote); + if (ch->level) + mx_level_free(ch->level); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_backup.h b/gen/test/c/mx/mx_backup.h new file mode 100644 index 000000000..e578043e8 --- /dev/null +++ b/gen/test/c/mx/mx_backup.h @@ -0,0 +1,40 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BACKUP_H_INCLUDED +#define MX_BACKUP_H_INCLUDED + +#include +#include +#include +#include "mx_formatted_text.h" +#include "mx_level.h" +#include "mx_positive_divisions.h" + +/* + * The backup and forward elements are required to coordinate multiple voices in one part, including + * music on multiple staves. The backup type is generally used to move between voices and staves. + * Thus the backup element does not include voice or staff elements. Duration values should always + * be positive, and should not cross measure boundaries or mid-measure changes in the divisions + * value. + */ +/* One child element of MxBackup: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxPositiveDivisions *duration; + MxFormattedText *footnote; + MxLevel *level; +} MxBackupChild; + +typedef struct { + MxBackupChild *children; /* child elements in document order */ + size_t children_count; +} MxBackup; + +/* NULL on error; the message is in mx_error(). */ +MxBackup *mx_backup_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_backup_serialize(const MxBackup *m, xmlNodePtr parent, const char *tag); +void mx_backup_free(MxBackup *m); + +#endif /* MX_BACKUP_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_bar_style_color.c b/gen/test/c/mx/mx_bar_style_color.c new file mode 100644 index 000000000..fa89f89a7 --- /dev/null +++ b/gen/test/c/mx/mx_bar_style_color.c @@ -0,0 +1,71 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_bar_style_color.h" + +#include "mx_runtime.h" +#include +#include + +MxBarStyleColor *mx_bar_style_color_parse(xmlNodePtr el) { + MxBarStyleColor *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_bar_style_color_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_bar_style_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_bar_style_color_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_bar_style_color_serialize(const MxBarStyleColor *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_bar_style_to_string(m->value))); + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_bar_style_color_free(MxBarStyleColor *m) { + if (!m) + return; + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_bar_style_color.h b/gen/test/c/mx/mx_bar_style_color.h new file mode 100644 index 000000000..1fc4b96d4 --- /dev/null +++ b/gen/test/c/mx/mx_bar_style_color.h @@ -0,0 +1,27 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BAR_STYLE_COLOR_H_INCLUDED +#define MX_BAR_STYLE_COLOR_H_INCLUDED + +#include +#include +#include +#include "mx_bar_style.h" +#include "mx_color.h" + +/* + * The bar-style-color type contains barline style and color information. + */ +typedef struct { + bool has_color; + MxColor color; /* attribute color */ + MxBarStyle value; /* text content */ +} MxBarStyleColor; + +/* NULL on error; the message is in mx_error(). */ +MxBarStyleColor *mx_bar_style_color_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_bar_style_color_serialize(const MxBarStyleColor *m, xmlNodePtr parent, const char *tag); +void mx_bar_style_color_free(MxBarStyleColor *m); + +#endif /* MX_BAR_STYLE_COLOR_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_barline.c b/gen/test/c/mx/mx_barline.c new file mode 100644 index 000000000..4726f819f --- /dev/null +++ b/gen/test/c/mx/mx_barline.c @@ -0,0 +1,203 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_barline.h" + +#include "mx_runtime.h" +#include +#include + +MxBarline *mx_barline_parse(xmlNodePtr el) { + MxBarline *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "location") == 0) { + m->has_location = true; + m->location = mx_right_left_middle_parse(s); + } else if (strcmp(aname, "segno") == 0) { + m->has_segno_sound = true; + m->segno_sound = mx_strdup(s); + } else if (strcmp(aname, "coda") == 0) { + m->has_coda_sound = true; + m->coda_sound = mx_strdup(s); + } else if (strcmp(aname, "divisions") == 0) { + m->has_divisions = true; + m->divisions = mx_divisions_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_barline_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxBarlineChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "bar-style") == 0) { + ch->bar_style = mx_bar_style_color_parse(c); + if (!ch->bar_style) { + mx_barline_free(m); + return NULL; + } + } else if (strcmp(tag, "footnote") == 0) { + ch->footnote = mx_formatted_text_parse(c); + if (!ch->footnote) { + mx_barline_free(m); + return NULL; + } + } else if (strcmp(tag, "level") == 0) { + ch->level = mx_level_parse(c); + if (!ch->level) { + mx_barline_free(m); + return NULL; + } + } else if (strcmp(tag, "wavy-line") == 0) { + ch->wavy_line = mx_wavy_line_parse(c); + if (!ch->wavy_line) { + mx_barline_free(m); + return NULL; + } + } else if (strcmp(tag, "segno") == 0) { + ch->segno = mx_segno_parse(c); + if (!ch->segno) { + mx_barline_free(m); + return NULL; + } + } else if (strcmp(tag, "coda") == 0) { + ch->coda = mx_coda_parse(c); + if (!ch->coda) { + mx_barline_free(m); + return NULL; + } + } else if (strcmp(tag, "fermata") == 0) { + ch->fermata = mx_fermata_parse(c); + if (!ch->fermata) { + mx_barline_free(m); + return NULL; + } + } else if (strcmp(tag, "ending") == 0) { + ch->ending = mx_ending_parse(c); + if (!ch->ending) { + mx_barline_free(m); + return NULL; + } + } else if (strcmp(tag, "repeat") == 0) { + ch->repeat = mx_repeat_parse(c); + if (!ch->repeat) { + mx_barline_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_barline_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_barline_serialize(const MxBarline *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_location) { + xmlSetProp(el, BAD_CAST "location", BAD_CAST mx_right_left_middle_to_string(m->location)); + } + if (m->has_segno_sound) { + xmlSetProp(el, BAD_CAST "segno", BAD_CAST m->segno_sound); + } + if (m->has_coda_sound) { + xmlSetProp(el, BAD_CAST "coda", BAD_CAST m->coda_sound); + } + if (m->has_divisions) { + char *s = mx_divisions_to_string(m->divisions); + xmlSetProp(el, BAD_CAST "divisions", BAD_CAST s); + free(s); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxBarlineChild *ch = &m->children[i]; + if (ch->bar_style) { + mx_bar_style_color_serialize(ch->bar_style, el, "bar-style"); + } else if (ch->footnote) { + mx_formatted_text_serialize(ch->footnote, el, "footnote"); + } else if (ch->level) { + mx_level_serialize(ch->level, el, "level"); + } else if (ch->wavy_line) { + mx_wavy_line_serialize(ch->wavy_line, el, "wavy-line"); + } else if (ch->segno) { + mx_segno_serialize(ch->segno, el, "segno"); + } else if (ch->coda) { + mx_coda_serialize(ch->coda, el, "coda"); + } else if (ch->fermata) { + mx_fermata_serialize(ch->fermata, el, "fermata"); + } else if (ch->ending) { + mx_ending_serialize(ch->ending, el, "ending"); + } else if (ch->repeat) { + mx_repeat_serialize(ch->repeat, el, "repeat"); + } + } + return el; +} + +void mx_barline_free(MxBarline *m) { + if (!m) + return; + if (m->has_segno_sound) + free(m->segno_sound); + if (m->has_coda_sound) + free(m->coda_sound); + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxBarlineChild *ch = &m->children[i]; + if (ch->bar_style) + mx_bar_style_color_free(ch->bar_style); + if (ch->footnote) + mx_formatted_text_free(ch->footnote); + if (ch->level) + mx_level_free(ch->level); + if (ch->wavy_line) + mx_wavy_line_free(ch->wavy_line); + if (ch->segno) + mx_segno_free(ch->segno); + if (ch->coda) + mx_coda_free(ch->coda); + if (ch->fermata) + mx_fermata_free(ch->fermata); + if (ch->ending) + mx_ending_free(ch->ending); + if (ch->repeat) + mx_repeat_free(ch->repeat); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_barline.h b/gen/test/c/mx/mx_barline.h new file mode 100644 index 000000000..781788122 --- /dev/null +++ b/gen/test/c/mx/mx_barline.h @@ -0,0 +1,74 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BARLINE_H_INCLUDED +#define MX_BARLINE_H_INCLUDED + +#include +#include +#include +#include "mx_bar_style_color.h" +#include "mx_coda.h" +#include "mx_divisions.h" +#include "mx_ending.h" +#include "mx_fermata.h" +#include "mx_formatted_text.h" +#include "mx_level.h" +#include "mx_repeat.h" +#include "mx_right_left_middle.h" +#include "mx_segno.h" +#include "mx_wavy_line.h" + +/* + * If a barline is other than a normal single barline, it should be represented by a barline type + * that describes it. This includes information about repeats and multiple endings, as well as line + * style. Barline data is on the same level as the other musical data in a score - a child of a + * measure in a partwise score, or a part in a timewise score. This allows for barlines within + * measures, as in dotted barlines that subdivide measures in complex meters. The two fermata + * elements allow for fermatas on both sides of the barline (the lower one inverted). Barlines have + * a location attribute to make it easier to process barlines independently of the other musical + * data in a score. It is often easier to set up measures separately from entering notes. The + * location attribute must match where the barline element occurs within the rest of the musical + * data in the score. If location is left, it should be the first element in the measure, aside from + * the print, bookmark, and link elements. If location is right, it should be the last element, + * again with the possible exception of the print, bookmark, and link elements. If no location is + * specified, the right barline is the default. The segno, coda, and divisions attributes work the + * same way as in the sound element. They are used for playback when barline elements contain segno + * or coda child elements. + */ +/* One child element of MxBarline: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxBarStyleColor *bar_style; + MxFormattedText *footnote; + MxLevel *level; + MxWavyLine *wavy_line; + MxSegno *segno; + MxCoda *coda; + MxFermata *fermata; + MxEnding *ending; + MxRepeat *repeat; +} MxBarlineChild; + +typedef struct { + bool has_location; + MxRightLeftMiddle location; /* attribute location */ + bool has_segno_sound; + char *segno_sound; /* attribute segno */ + bool has_coda_sound; + char *coda_sound; /* attribute coda */ + bool has_divisions; + MxDivisions divisions; /* attribute divisions */ + bool has_id; + char *id; /* attribute id */ + MxBarlineChild *children; /* child elements in document order */ + size_t children_count; +} MxBarline; + +/* NULL on error; the message is in mx_error(). */ +MxBarline *mx_barline_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_barline_serialize(const MxBarline *m, xmlNodePtr parent, const char *tag); +void mx_barline_free(MxBarline *m); + +#endif /* MX_BARLINE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_barre.c b/gen/test/c/mx/mx_barre.c new file mode 100644 index 000000000..a3d4962d4 --- /dev/null +++ b/gen/test/c/mx/mx_barre.c @@ -0,0 +1,70 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_barre.h" + +#include "mx_runtime.h" +#include +#include + +MxBarre *mx_barre_parse(xmlNodePtr el) { + MxBarre *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_barre_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_barre_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_barre_serialize(const MxBarre *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_barre_free(MxBarre *m) { + if (!m) + return; + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_barre.h b/gen/test/c/mx/mx_barre.h new file mode 100644 index 000000000..e83abe88f --- /dev/null +++ b/gen/test/c/mx/mx_barre.h @@ -0,0 +1,30 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BARRE_H_INCLUDED +#define MX_BARRE_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_start_stop.h" + +/* + * The barre element indicates placing a finger over multiple strings on a single fret. The type is + * "start" for the lowest pitched string (e.g., the string with the highest MusicXML number) and is + * "stop" for the highest pitched string. + */ +typedef struct { + bool has_type; + MxStartStop type; /* attribute type */ + bool has_color; + MxColor color; /* attribute color */ +} MxBarre; + +/* NULL on error; the message is in mx_error(). */ +MxBarre *mx_barre_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_barre_serialize(const MxBarre *m, xmlNodePtr parent, const char *tag); +void mx_barre_free(MxBarre *m); + +#endif /* MX_BARRE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_bass.c b/gen/test/c/mx/mx_bass.c new file mode 100644 index 000000000..a63daf4ce --- /dev/null +++ b/gen/test/c/mx/mx_bass.c @@ -0,0 +1,95 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_bass.h" + +#include "mx_runtime.h" +#include +#include + +MxBass *mx_bass_parse(xmlNodePtr el) { + MxBass *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_bass_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxBassChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "bass-step") == 0) { + ch->bass_step = mx_bass_step_parse(c); + if (!ch->bass_step) { + mx_bass_free(m); + return NULL; + } + } else if (strcmp(tag, "bass-alter") == 0) { + ch->bass_alter = mx_bass_alter_parse(c); + if (!ch->bass_alter) { + mx_bass_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_bass_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_bass_serialize(const MxBass *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxBassChild *ch = &m->children[i]; + if (ch->bass_step) { + mx_bass_step_serialize(ch->bass_step, el, "bass-step"); + } else if (ch->bass_alter) { + mx_bass_alter_serialize(ch->bass_alter, el, "bass-alter"); + } + } + return el; +} + +void mx_bass_free(MxBass *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxBassChild *ch = &m->children[i]; + if (ch->bass_step) + mx_bass_step_free(ch->bass_step); + if (ch->bass_alter) + mx_bass_alter_free(ch->bass_alter); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_bass.h b/gen/test/c/mx/mx_bass.h new file mode 100644 index 000000000..b656fafd6 --- /dev/null +++ b/gen/test/c/mx/mx_bass.h @@ -0,0 +1,36 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BASS_H_INCLUDED +#define MX_BASS_H_INCLUDED + +#include +#include +#include +#include "mx_bass_alter.h" +#include "mx_bass_step.h" + +/* + * The bass type is used to indicate a bass note in popular music chord symbols, e.g. G/C. It is + * generally not used in functional harmony, as inversion is generally not used in pop chord + * symbols. As with root, it is divided into step and alter elements, similar to pitches. + */ +/* One child element of MxBass: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxBassStep *bass_step; + MxBassAlter *bass_alter; +} MxBassChild; + +typedef struct { + MxBassChild *children; /* child elements in document order */ + size_t children_count; +} MxBass; + +/* NULL on error; the message is in mx_error(). */ +MxBass *mx_bass_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_bass_serialize(const MxBass *m, xmlNodePtr parent, const char *tag); +void mx_bass_free(MxBass *m); + +#endif /* MX_BASS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_bass_alter.c b/gen/test/c/mx/mx_bass_alter.c new file mode 100644 index 000000000..9002414a1 --- /dev/null +++ b/gen/test/c/mx/mx_bass_alter.c @@ -0,0 +1,149 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_bass_alter.h" + +#include "mx_runtime.h" +#include +#include + +MxBassAlter *mx_bass_alter_parse(xmlNodePtr el) { + MxBassAlter *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "location") == 0) { + m->has_location = true; + m->location = mx_left_right_parse(s); + } else if (strcmp(aname, "print-object") == 0) { + m->has_print_object = true; + m->print_object = mx_yes_no_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_bass_alter_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_semitones_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_bass_alter_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_bass_alter_serialize(const MxBassAlter *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + { + char *s = mx_semitones_to_string(m->value); + xmlAddChild(el, xmlNewText(BAD_CAST s)); + free(s); + } + if (m->has_location) { + xmlSetProp(el, BAD_CAST "location", BAD_CAST mx_left_right_to_string(m->location)); + } + if (m->has_print_object) { + xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_bass_alter_free(MxBassAlter *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_bass_alter.h b/gen/test/c/mx/mx_bass_alter.h new file mode 100644 index 000000000..5f9009d9f --- /dev/null +++ b/gen/test/c/mx/mx_bass_alter.h @@ -0,0 +1,58 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BASS_ALTER_H_INCLUDED +#define MX_BASS_ALTER_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_right.h" +#include "mx_semitones.h" +#include "mx_tenths.h" +#include "mx_yes_no.h" + +/* + * The bass-alter type represents the chromatic alteration of the bass of the current chord within + * the harmony element. In some chord styles, the text for the bass-step element may include + * bass-alter information. In that case, the print-object attribute of the bass-alter element can be + * set to no. The location attribute indicates whether the alteration should appear to the left or + * the right of the bass-step; it is right by default. + */ +typedef struct { + bool has_location; + MxLeftRight location; /* attribute location */ + bool has_print_object; + MxYesNo print_object; /* attribute print-object */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + MxSemitones value; /* text content */ +} MxBassAlter; + +/* NULL on error; the message is in mx_error(). */ +MxBassAlter *mx_bass_alter_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_bass_alter_serialize(const MxBassAlter *m, xmlNodePtr parent, const char *tag); +void mx_bass_alter_free(MxBassAlter *m); + +#endif /* MX_BASS_ALTER_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_bass_step.c b/gen/test/c/mx/mx_bass_step.c new file mode 100644 index 000000000..b9d4aae1e --- /dev/null +++ b/gen/test/c/mx/mx_bass_step.c @@ -0,0 +1,141 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_bass_step.h" + +#include "mx_runtime.h" +#include +#include + +MxBassStep *mx_bass_step_parse(xmlNodePtr el) { + MxBassStep *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "text") == 0) { + m->has_text = true; + m->text = mx_strdup(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_bass_step_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_step_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_bass_step_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_bass_step_serialize(const MxBassStep *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_step_to_string(m->value))); + if (m->has_text) { + xmlSetProp(el, BAD_CAST "text", BAD_CAST m->text); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_bass_step_free(MxBassStep *m) { + if (!m) + return; + if (m->has_text) + free(m->text); + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_bass_step.h b/gen/test/c/mx/mx_bass_step.h new file mode 100644 index 000000000..7ac86f331 --- /dev/null +++ b/gen/test/c/mx/mx_bass_step.h @@ -0,0 +1,52 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BASS_STEP_H_INCLUDED +#define MX_BASS_STEP_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_step.h" +#include "mx_tenths.h" + +/* + * The bass-step type represents the pitch step of the bass of the current chord within the harmony + * element. The text attribute indicates how the bass should appear in a score if not using the + * element contents. + */ +typedef struct { + bool has_text; + char *text; /* attribute text */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + MxStep value; /* text content */ +} MxBassStep; + +/* NULL on error; the message is in mx_error(). */ +MxBassStep *mx_bass_step_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_bass_step_serialize(const MxBassStep *m, xmlNodePtr parent, const char *tag); +void mx_bass_step_free(MxBassStep *m); + +#endif /* MX_BASS_STEP_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_beam.c b/gen/test/c/mx/mx_beam.c new file mode 100644 index 000000000..be5df692b --- /dev/null +++ b/gen/test/c/mx/mx_beam.c @@ -0,0 +1,99 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_beam.h" + +#include "mx_runtime.h" +#include +#include + +MxBeam *mx_beam_parse(xmlNodePtr el) { + MxBeam *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_beam_level_parse(s); + } else if (strcmp(aname, "repeater") == 0) { + m->has_repeater = true; + m->repeater = mx_yes_no_parse(s); + } else if (strcmp(aname, "fan") == 0) { + m->has_fan = true; + m->fan = mx_fan_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_beam_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_beam_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_beam_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_beam_serialize(const MxBeam *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_beam_value_to_string(m->value))); + if (m->has_number) { + char *s = mx_beam_level_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_repeater) { + xmlSetProp(el, BAD_CAST "repeater", BAD_CAST mx_yes_no_to_string(m->repeater)); + } + if (m->has_fan) { + xmlSetProp(el, BAD_CAST "fan", BAD_CAST mx_fan_to_string(m->fan)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_beam_free(MxBeam *m) { + if (!m) + return; + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + free(m); +} diff --git a/gen/test/c/mx/mx_beam.h b/gen/test/c/mx/mx_beam.h new file mode 100644 index 000000000..df8d0851d --- /dev/null +++ b/gen/test/c/mx/mx_beam.h @@ -0,0 +1,47 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BEAM_H_INCLUDED +#define MX_BEAM_H_INCLUDED + +#include +#include +#include +#include "mx_beam_level.h" +#include "mx_beam_value.h" +#include "mx_color.h" +#include "mx_fan.h" +#include "mx_yes_no.h" + +/* + * Beam values include begin, continue, end, forward hook, and backward hook. Up to eight concurrent + * beams are available to cover up to 1024th notes. Each beam in a note is represented with a + * separate beam element, starting with the eighth note beam using a number attribute of 1. Note + * that the beam number does not distinguish sets of beams that overlap, as it does for slur and + * other elements. Beaming groups are distinguished by being in different voices and/or the presence + * or absence of grace and cue elements. Beams that have a begin value can also have a fan attribute + * to indicate accelerandos and ritardandos using fanned beams. The fan attribute may also be used + * with a continue value if the fanning direction changes on that note. The value is "none" if not + * specified. The repeater attribute has been deprecated in MusicXML 3.0. Formerly used for + * tremolos, it needs to be specified with a "yes" value for each beam using it. + */ +typedef struct { + bool has_number; + MxBeamLevel number; /* attribute number */ + bool has_repeater; + MxYesNo repeater; /* attribute repeater */ + bool has_fan; + MxFan fan; /* attribute fan */ + bool has_color; + MxColor color; /* attribute color */ + bool has_id; + char *id; /* attribute id */ + MxBeamValue value; /* text content */ +} MxBeam; + +/* NULL on error; the message is in mx_error(). */ +MxBeam *mx_beam_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_beam_serialize(const MxBeam *m, xmlNodePtr parent, const char *tag); +void mx_beam_free(MxBeam *m); + +#endif /* MX_BEAM_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_beat_repeat.c b/gen/test/c/mx/mx_beat_repeat.c new file mode 100644 index 000000000..38f6a8910 --- /dev/null +++ b/gen/test/c/mx/mx_beat_repeat.c @@ -0,0 +1,124 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_beat_repeat.h" + +#include "mx_runtime.h" +#include +#include + +MxBeatRepeat *mx_beat_repeat_parse(xmlNodePtr el) { + MxBeatRepeat *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_parse(s); + } else if (strcmp(aname, "slashes") == 0) { + m->has_slashes = true; + m->slashes = mx_parse_int(s); + } else if (strcmp(aname, "use-dots") == 0) { + m->has_use_dots = true; + m->use_dots = mx_yes_no_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_beat_repeat_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxBeatRepeatChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "slash-type") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->slash_type = malloc(sizeof(*ch->slash_type)); + if (!ch->slash_type) + abort(); + *ch->slash_type = mx_note_type_value_parse(s); + xmlFree(text); + } else if (strcmp(tag, "slash-dot") == 0) { + ch->slash_dot = mx_empty_parse(c); + if (!ch->slash_dot) { + mx_beat_repeat_free(m); + return NULL; + } + } else if (strcmp(tag, "except-voice") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->except_voice = mx_strdup(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_beat_repeat_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_beat_repeat_serialize(const MxBeatRepeat *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); + } + if (m->has_slashes) { + char *s = mx_format_int(m->slashes); + xmlSetProp(el, BAD_CAST "slashes", BAD_CAST s); + free(s); + } + if (m->has_use_dots) { + xmlSetProp(el, BAD_CAST "use-dots", BAD_CAST mx_yes_no_to_string(m->use_dots)); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxBeatRepeatChild *ch = &m->children[i]; + if (ch->slash_type) { + xmlNewTextChild(el, NULL, BAD_CAST "slash-type", BAD_CAST mx_note_type_value_to_string((*ch->slash_type))); + } else if (ch->slash_dot) { + mx_empty_serialize(ch->slash_dot, el, "slash-dot"); + } else if (ch->except_voice) { + xmlNewTextChild(el, NULL, BAD_CAST "except-voice", BAD_CAST ch->except_voice); + } + } + return el; +} + +void mx_beat_repeat_free(MxBeatRepeat *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxBeatRepeatChild *ch = &m->children[i]; + free(ch->slash_type); + if (ch->slash_dot) + mx_empty_free(ch->slash_dot); + free(ch->except_voice); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_beat_repeat.h b/gen/test/c/mx/mx_beat_repeat.h new file mode 100644 index 000000000..c402f7f49 --- /dev/null +++ b/gen/test/c/mx/mx_beat_repeat.h @@ -0,0 +1,49 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BEAT_REPEAT_H_INCLUDED +#define MX_BEAT_REPEAT_H_INCLUDED + +#include +#include +#include +#include "mx_empty.h" +#include "mx_note_type_value.h" +#include "mx_start_stop.h" +#include "mx_yes_no.h" + +/* + * The beat-repeat type is used to indicate that a single beat (but possibly many notes) is + * repeated. Both the start and stop of the beat being repeated should be specified. The slashes + * attribute specifies the number of slashes to use in the symbol. The use-dots attribute indicates + * whether or not to use dots as well (for instance, with mixed rhythm patterns). By default, the + * value for slashes is 1 and the value for use-dots is no. The beat-repeat element specifies a + * notation style for repetitions. The actual music being repeated needs to be repeated within the + * MusicXML file. This element specifies the notation that indicates the repeat. + */ +/* One child element of MxBeatRepeat: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxNoteTypeValue *slash_type; + MxEmpty *slash_dot; + char *except_voice; +} MxBeatRepeatChild; + +typedef struct { + bool has_type; + MxStartStop type; /* attribute type */ + bool has_slashes; + long slashes; /* attribute slashes */ + bool has_use_dots; + MxYesNo use_dots; /* attribute use-dots */ + MxBeatRepeatChild *children; /* child elements in document order */ + size_t children_count; +} MxBeatRepeat; + +/* NULL on error; the message is in mx_error(). */ +MxBeatRepeat *mx_beat_repeat_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_beat_repeat_serialize(const MxBeatRepeat *m, xmlNodePtr parent, const char *tag); +void mx_beat_repeat_free(MxBeatRepeat *m); + +#endif /* MX_BEAT_REPEAT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_beat_unit_tied.c b/gen/test/c/mx/mx_beat_unit_tied.c new file mode 100644 index 000000000..9454c8ce5 --- /dev/null +++ b/gen/test/c/mx/mx_beat_unit_tied.c @@ -0,0 +1,96 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_beat_unit_tied.h" + +#include "mx_runtime.h" +#include +#include + +MxBeatUnitTied *mx_beat_unit_tied_parse(xmlNodePtr el) { + MxBeatUnitTied *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_beat_unit_tied_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxBeatUnitTiedChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "beat-unit") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->beat_unit = malloc(sizeof(*ch->beat_unit)); + if (!ch->beat_unit) + abort(); + *ch->beat_unit = mx_note_type_value_parse(s); + xmlFree(text); + } else if (strcmp(tag, "beat-unit-dot") == 0) { + ch->beat_unit_dot = mx_empty_parse(c); + if (!ch->beat_unit_dot) { + mx_beat_unit_tied_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_beat_unit_tied_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_beat_unit_tied_serialize(const MxBeatUnitTied *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxBeatUnitTiedChild *ch = &m->children[i]; + if (ch->beat_unit) { + xmlNewTextChild(el, NULL, BAD_CAST "beat-unit", BAD_CAST mx_note_type_value_to_string((*ch->beat_unit))); + } else if (ch->beat_unit_dot) { + mx_empty_serialize(ch->beat_unit_dot, el, "beat-unit-dot"); + } + } + return el; +} + +void mx_beat_unit_tied_free(MxBeatUnitTied *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxBeatUnitTiedChild *ch = &m->children[i]; + free(ch->beat_unit); + if (ch->beat_unit_dot) + mx_empty_free(ch->beat_unit_dot); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_beat_unit_tied.h b/gen/test/c/mx/mx_beat_unit_tied.h new file mode 100644 index 000000000..320bd628f --- /dev/null +++ b/gen/test/c/mx/mx_beat_unit_tied.h @@ -0,0 +1,37 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BEAT_UNIT_TIED_H_INCLUDED +#define MX_BEAT_UNIT_TIED_H_INCLUDED + +#include +#include +#include +#include "mx_empty.h" +#include "mx_note_type_value.h" + +/* + * The beat-unit-tied type indicates a beat-unit within a metronome mark that is tied to the + * preceding beat-unit. This allows or two or more tied notes to be associated with a per-minute + * value in a metronome mark, whereas the metronome-tied element is restricted to metric + * relationship marks. + */ +/* One child element of MxBeatUnitTied: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxNoteTypeValue *beat_unit; + MxEmpty *beat_unit_dot; +} MxBeatUnitTiedChild; + +typedef struct { + MxBeatUnitTiedChild *children; /* child elements in document order */ + size_t children_count; +} MxBeatUnitTied; + +/* NULL on error; the message is in mx_error(). */ +MxBeatUnitTied *mx_beat_unit_tied_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_beat_unit_tied_serialize(const MxBeatUnitTied *m, xmlNodePtr parent, const char *tag); +void mx_beat_unit_tied_free(MxBeatUnitTied *m); + +#endif /* MX_BEAT_UNIT_TIED_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_beater.c b/gen/test/c/mx/mx_beater.c new file mode 100644 index 000000000..fcb4a9024 --- /dev/null +++ b/gen/test/c/mx/mx_beater.c @@ -0,0 +1,69 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_beater.h" + +#include "mx_runtime.h" +#include +#include + +MxBeater *mx_beater_parse(xmlNodePtr el) { + MxBeater *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "tip") == 0) { + m->has_tip = true; + m->tip = mx_tip_direction_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_beater_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_beater_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_beater_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_beater_serialize(const MxBeater *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_beater_value_to_string(m->value))); + if (m->has_tip) { + xmlSetProp(el, BAD_CAST "tip", BAD_CAST mx_tip_direction_to_string(m->tip)); + } + return el; +} + +void mx_beater_free(MxBeater *m) { + if (!m) + return; + free(m); +} diff --git a/gen/test/c/mx/mx_beater.h b/gen/test/c/mx/mx_beater.h new file mode 100644 index 000000000..b79f764a9 --- /dev/null +++ b/gen/test/c/mx/mx_beater.h @@ -0,0 +1,28 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BEATER_H_INCLUDED +#define MX_BEATER_H_INCLUDED + +#include +#include +#include +#include "mx_beater_value.h" +#include "mx_tip_direction.h" + +/* + * The beater type represents pictograms for beaters, mallets, and sticks that do not have different + * materials represented in the pictogram. + */ +typedef struct { + bool has_tip; + MxTipDirection tip; /* attribute tip */ + MxBeaterValue value; /* text content */ +} MxBeater; + +/* NULL on error; the message is in mx_error(). */ +MxBeater *mx_beater_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_beater_serialize(const MxBeater *m, xmlNodePtr parent, const char *tag); +void mx_beater_free(MxBeater *m); + +#endif /* MX_BEATER_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_bend.c b/gen/test/c/mx/mx_bend.c new file mode 100644 index 000000000..29a202705 --- /dev/null +++ b/gen/test/c/mx/mx_bend.c @@ -0,0 +1,218 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_bend.h" + +#include "mx_runtime.h" +#include +#include + +MxBend *mx_bend_parse(xmlNodePtr el) { + MxBend *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "accelerate") == 0) { + m->has_accelerate = true; + m->accelerate = mx_yes_no_parse(s); + } else if (strcmp(aname, "beats") == 0) { + m->has_beats = true; + m->beats = mx_trill_beats_parse(s); + } else if (strcmp(aname, "first-beat") == 0) { + m->has_first_beat = true; + m->first_beat = mx_percent_parse(s); + } else if (strcmp(aname, "last-beat") == 0) { + m->has_last_beat = true; + m->last_beat = mx_percent_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_bend_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxBendChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "bend-alter") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->bend_alter = malloc(sizeof(*ch->bend_alter)); + if (!ch->bend_alter) + abort(); + *ch->bend_alter = mx_semitones_parse(s); + xmlFree(text); + } else if (strcmp(tag, "pre-bend") == 0) { + ch->pre_bend = mx_empty_parse(c); + if (!ch->pre_bend) { + mx_bend_free(m); + return NULL; + } + } else if (strcmp(tag, "release") == 0) { + ch->release = mx_empty_parse(c); + if (!ch->release) { + mx_bend_free(m); + return NULL; + } + } else if (strcmp(tag, "with-bar") == 0) { + ch->with_bar = mx_placement_text_parse(c); + if (!ch->with_bar) { + mx_bend_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_bend_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_bend_serialize(const MxBend *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_accelerate) { + xmlSetProp(el, BAD_CAST "accelerate", BAD_CAST mx_yes_no_to_string(m->accelerate)); + } + if (m->has_beats) { + char *s = mx_trill_beats_to_string(m->beats); + xmlSetProp(el, BAD_CAST "beats", BAD_CAST s); + free(s); + } + if (m->has_first_beat) { + char *s = mx_percent_to_string(m->first_beat); + xmlSetProp(el, BAD_CAST "first-beat", BAD_CAST s); + free(s); + } + if (m->has_last_beat) { + char *s = mx_percent_to_string(m->last_beat); + xmlSetProp(el, BAD_CAST "last-beat", BAD_CAST s); + free(s); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxBendChild *ch = &m->children[i]; + if (ch->bend_alter) { + char *s = mx_semitones_to_string((*ch->bend_alter)); + xmlNewTextChild(el, NULL, BAD_CAST "bend-alter", BAD_CAST s); + free(s); + } else if (ch->pre_bend) { + mx_empty_serialize(ch->pre_bend, el, "pre-bend"); + } else if (ch->release) { + mx_empty_serialize(ch->release, el, "release"); + } else if (ch->with_bar) { + mx_placement_text_serialize(ch->with_bar, el, "with-bar"); + } + } + return el; +} + +void mx_bend_free(MxBend *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + for (size_t i = 0; i < m->children_count; i++) { + MxBendChild *ch = &m->children[i]; + free(ch->bend_alter); + if (ch->pre_bend) + mx_empty_free(ch->pre_bend); + if (ch->release) + mx_empty_free(ch->release); + if (ch->with_bar) + mx_placement_text_free(ch->with_bar); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_bend.h b/gen/test/c/mx/mx_bend.h new file mode 100644 index 000000000..5e3bb67a2 --- /dev/null +++ b/gen/test/c/mx/mx_bend.h @@ -0,0 +1,77 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BEND_H_INCLUDED +#define MX_BEND_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_empty.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_percent.h" +#include "mx_placement_text.h" +#include "mx_semitones.h" +#include "mx_tenths.h" +#include "mx_trill_beats.h" +#include "mx_yes_no.h" + +/* + * The bend type is used in guitar and tablature. The bend-alter element indicates the number of + * steps in the bend, similar to the alter element. As with the alter element, numbers like 0.5 can + * be used to indicate microtones. Negative numbers indicate pre-bends or releases; the pre-bend and + * release elements are used to distinguish what is intended. A with-bar element indicates that the + * bend is to be done at the bridge with a whammy or vibrato bar. The content of the element + * indicates how this should be notated. + */ +/* One child element of MxBend: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxSemitones *bend_alter; + MxEmpty *pre_bend; + MxEmpty *release; + MxPlacementText *with_bar; +} MxBendChild; + +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_accelerate; + MxYesNo accelerate; /* attribute accelerate */ + bool has_beats; + MxTrillBeats beats; /* attribute beats */ + bool has_first_beat; + MxPercent first_beat; /* attribute first-beat */ + bool has_last_beat; + MxPercent last_beat; /* attribute last-beat */ + MxBendChild *children; /* child elements in document order */ + size_t children_count; +} MxBend; + +/* NULL on error; the message is in mx_error(). */ +MxBend *mx_bend_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_bend_serialize(const MxBend *m, xmlNodePtr parent, const char *tag); +void mx_bend_free(MxBend *m); + +#endif /* MX_BEND_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_bookmark.c b/gen/test/c/mx/mx_bookmark.c new file mode 100644 index 000000000..cd095aab0 --- /dev/null +++ b/gen/test/c/mx/mx_bookmark.c @@ -0,0 +1,88 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_bookmark.h" + +#include "mx_runtime.h" +#include +#include + +MxBookmark *mx_bookmark_parse(xmlNodePtr el) { + MxBookmark *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else if (strcmp(aname, "name") == 0) { + m->has_name = true; + m->name = mx_strdup(s); + } else if (strcmp(aname, "element") == 0) { + m->has_element = true; + m->element = mx_strdup(s); + } else if (strcmp(aname, "position") == 0) { + m->has_position = true; + m->position = mx_parse_int(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_bookmark_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_bookmark_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_bookmark_serialize(const MxBookmark *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + if (m->has_name) { + xmlSetProp(el, BAD_CAST "name", BAD_CAST m->name); + } + if (m->has_element) { + xmlSetProp(el, BAD_CAST "element", BAD_CAST m->element); + } + if (m->has_position) { + char *s = mx_format_int(m->position); + xmlSetProp(el, BAD_CAST "position", BAD_CAST s); + free(s); + } + return el; +} + +void mx_bookmark_free(MxBookmark *m) { + if (!m) + return; + if (m->has_id) + free(m->id); + if (m->has_name) + free(m->name); + if (m->has_element) + free(m->element); + free(m); +} diff --git a/gen/test/c/mx/mx_bookmark.h b/gen/test/c/mx/mx_bookmark.h new file mode 100644 index 000000000..b4a38e1d2 --- /dev/null +++ b/gen/test/c/mx/mx_bookmark.h @@ -0,0 +1,30 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BOOKMARK_H_INCLUDED +#define MX_BOOKMARK_H_INCLUDED + +#include +#include +#include + +/* + * The bookmark type serves as a well-defined target for an incoming simple XLink. + */ +typedef struct { + bool has_id; + char *id; /* attribute id */ + bool has_name; + char *name; /* attribute name */ + bool has_element; + char *element; /* attribute element */ + bool has_position; + long position; /* attribute position */ +} MxBookmark; + +/* NULL on error; the message is in mx_error(). */ +MxBookmark *mx_bookmark_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_bookmark_serialize(const MxBookmark *m, xmlNodePtr parent, const char *tag); +void mx_bookmark_free(MxBookmark *m); + +#endif /* MX_BOOKMARK_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_bracket.c b/gen/test/c/mx/mx_bracket.c new file mode 100644 index 000000000..9cb5f7d5a --- /dev/null +++ b/gen/test/c/mx/mx_bracket.c @@ -0,0 +1,154 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_bracket.h" + +#include "mx_runtime.h" +#include +#include + +MxBracket *mx_bracket_parse(xmlNodePtr el) { + MxBracket *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_continue_parse(s); + } else if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_number_level_parse(s); + } else if (strcmp(aname, "line-end") == 0) { + m->has_line_end = true; + m->line_end = mx_line_end_parse(s); + } else if (strcmp(aname, "end-length") == 0) { + m->has_end_length = true; + m->end_length = mx_tenths_parse(s); + } else if (strcmp(aname, "line-type") == 0) { + m->has_line_type = true; + m->line_type = mx_line_type_parse(s); + } else if (strcmp(aname, "dash-length") == 0) { + m->has_dash_length = true; + m->dash_length = mx_tenths_parse(s); + } else if (strcmp(aname, "space-length") == 0) { + m->has_space_length = true; + m->space_length = mx_tenths_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_bracket_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_bracket_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_bracket_serialize(const MxBracket *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_continue_to_string(m->type)); + } + if (m->has_number) { + char *s = mx_number_level_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_line_end) { + xmlSetProp(el, BAD_CAST "line-end", BAD_CAST mx_line_end_to_string(m->line_end)); + } + if (m->has_end_length) { + char *s = mx_tenths_to_string(m->end_length); + xmlSetProp(el, BAD_CAST "end-length", BAD_CAST s); + free(s); + } + if (m->has_line_type) { + xmlSetProp(el, BAD_CAST "line-type", BAD_CAST mx_line_type_to_string(m->line_type)); + } + if (m->has_dash_length) { + char *s = mx_tenths_to_string(m->dash_length); + xmlSetProp(el, BAD_CAST "dash-length", BAD_CAST s); + free(s); + } + if (m->has_space_length) { + char *s = mx_tenths_to_string(m->space_length); + xmlSetProp(el, BAD_CAST "space-length", BAD_CAST s); + free(s); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_bracket_free(MxBracket *m) { + if (!m) + return; + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + free(m); +} diff --git a/gen/test/c/mx/mx_bracket.h b/gen/test/c/mx/mx_bracket.h new file mode 100644 index 000000000..3cb4b3f37 --- /dev/null +++ b/gen/test/c/mx/mx_bracket.h @@ -0,0 +1,57 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BRACKET_H_INCLUDED +#define MX_BRACKET_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_line_end.h" +#include "mx_line_type.h" +#include "mx_number_level.h" +#include "mx_start_stop_continue.h" +#include "mx_tenths.h" + +/* + * Brackets are combined with words in a variety of modern directions. The line-end attribute + * specifies if there is a jog up or down (or both), an arrow, or nothing at the start or end of the + * bracket. If the line-end is up or down, the length of the jog can be specified using the + * end-length attribute. The line-type is solid by default. + */ +typedef struct { + bool has_type; + MxStartStopContinue type; /* attribute type */ + bool has_number; + MxNumberLevel number; /* attribute number */ + bool has_line_end; + MxLineEnd line_end; /* attribute line-end */ + bool has_end_length; + MxTenths end_length; /* attribute end-length */ + bool has_line_type; + MxLineType line_type; /* attribute line-type */ + bool has_dash_length; + MxTenths dash_length; /* attribute dash-length */ + bool has_space_length; + MxTenths space_length; /* attribute space-length */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_color; + MxColor color; /* attribute color */ + bool has_id; + char *id; /* attribute id */ +} MxBracket; + +/* NULL on error; the message is in mx_error(). */ +MxBracket *mx_bracket_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_bracket_serialize(const MxBracket *m, xmlNodePtr parent, const char *tag); +void mx_bracket_free(MxBracket *m); + +#endif /* MX_BRACKET_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_breath_mark.c b/gen/test/c/mx/mx_breath_mark.c new file mode 100644 index 000000000..a44892bae --- /dev/null +++ b/gen/test/c/mx/mx_breath_mark.c @@ -0,0 +1,139 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_breath_mark.h" + +#include "mx_runtime.h" +#include +#include + +MxBreathMark *mx_breath_mark_parse(xmlNodePtr el) { + MxBreathMark *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_breath_mark_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_breath_mark_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_breath_mark_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_breath_mark_serialize(const MxBreathMark *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_breath_mark_value_to_string(m->value))); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + return el; +} + +void mx_breath_mark_free(MxBreathMark *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_breath_mark.h b/gen/test/c/mx/mx_breath_mark.h new file mode 100644 index 000000000..8e5b18df4 --- /dev/null +++ b/gen/test/c/mx/mx_breath_mark.h @@ -0,0 +1,51 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_BREATH_MARK_H_INCLUDED +#define MX_BREATH_MARK_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_breath_mark_value.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_tenths.h" + +/* + * The breath-mark element indicates a place to take a breath. + */ +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + MxBreathMarkValue value; /* text content */ +} MxBreathMark; + +/* NULL on error; the message is in mx_error(). */ +MxBreathMark *mx_breath_mark_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_breath_mark_serialize(const MxBreathMark *m, xmlNodePtr parent, const char *tag); +void mx_breath_mark_free(MxBreathMark *m); + +#endif /* MX_BREATH_MARK_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_caesura.c b/gen/test/c/mx/mx_caesura.c new file mode 100644 index 000000000..2f82ea4f2 --- /dev/null +++ b/gen/test/c/mx/mx_caesura.c @@ -0,0 +1,139 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_caesura.h" + +#include "mx_runtime.h" +#include +#include + +MxCaesura *mx_caesura_parse(xmlNodePtr el) { + MxCaesura *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_caesura_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_caesura_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_caesura_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_caesura_serialize(const MxCaesura *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_caesura_value_to_string(m->value))); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + return el; +} + +void mx_caesura_free(MxCaesura *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_caesura.h b/gen/test/c/mx/mx_caesura.h new file mode 100644 index 000000000..8ba932f3c --- /dev/null +++ b/gen/test/c/mx/mx_caesura.h @@ -0,0 +1,52 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_CAESURA_H_INCLUDED +#define MX_CAESURA_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_caesura_value.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_tenths.h" + +/* + * The caesura element indicates a slight pause. It is notated using a "railroad tracks" symbol or + * other variations specified in the element content. + */ +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + MxCaesuraValue value; /* text content */ +} MxCaesura; + +/* NULL on error; the message is in mx_error(). */ +MxCaesura *mx_caesura_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_caesura_serialize(const MxCaesura *m, xmlNodePtr parent, const char *tag); +void mx_caesura_free(MxCaesura *m); + +#endif /* MX_CAESURA_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_cancel.c b/gen/test/c/mx/mx_cancel.c new file mode 100644 index 000000000..3134dad58 --- /dev/null +++ b/gen/test/c/mx/mx_cancel.c @@ -0,0 +1,73 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_cancel.h" + +#include "mx_runtime.h" +#include +#include + +MxCancel *mx_cancel_parse(xmlNodePtr el) { + MxCancel *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "location") == 0) { + m->has_location = true; + m->location = mx_cancel_location_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_cancel_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_fifths_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_cancel_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_cancel_serialize(const MxCancel *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + { + char *s = mx_fifths_to_string(m->value); + xmlAddChild(el, xmlNewText(BAD_CAST s)); + free(s); + } + if (m->has_location) { + xmlSetProp(el, BAD_CAST "location", BAD_CAST mx_cancel_location_to_string(m->location)); + } + return el; +} + +void mx_cancel_free(MxCancel *m) { + if (!m) + return; + free(m); +} diff --git a/gen/test/c/mx/mx_cancel.h b/gen/test/c/mx/mx_cancel.h new file mode 100644 index 000000000..1a39aa94d --- /dev/null +++ b/gen/test/c/mx/mx_cancel.h @@ -0,0 +1,31 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_CANCEL_H_INCLUDED +#define MX_CANCEL_H_INCLUDED + +#include +#include +#include +#include "mx_cancel_location.h" +#include "mx_fifths.h" + +/* + * A cancel element indicates that the old key signature should be cancelled before the new one + * appears. This will always happen when changing to C major or A minor and need not be specified + * then. The cancel value matches the fifths value of the cancelled key signature (e.g., a cancel of + * -2 will provide an explicit cancellation for changing from B flat major to F major). The optional + * location attribute indicates where the cancellation appears relative to the new key signature. + */ +typedef struct { + bool has_location; + MxCancelLocation location; /* attribute location */ + MxFifths value; /* text content */ +} MxCancel; + +/* NULL on error; the message is in mx_error(). */ +MxCancel *mx_cancel_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_cancel_serialize(const MxCancel *m, xmlNodePtr parent, const char *tag); +void mx_cancel_free(MxCancel *m); + +#endif /* MX_CANCEL_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_clef.c b/gen/test/c/mx/mx_clef.c new file mode 100644 index 000000000..f002cc7a7 --- /dev/null +++ b/gen/test/c/mx/mx_clef.c @@ -0,0 +1,222 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_clef.h" + +#include "mx_runtime.h" +#include +#include + +MxClef *mx_clef_parse(xmlNodePtr el) { + MxClef *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_staff_number_parse(s); + } else if (strcmp(aname, "additional") == 0) { + m->has_additional = true; + m->additional = mx_yes_no_parse(s); + } else if (strcmp(aname, "size") == 0) { + m->has_size = true; + m->size = mx_symbol_size_parse(s); + } else if (strcmp(aname, "after-barline") == 0) { + m->has_after_barline = true; + m->after_barline = mx_yes_no_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "print-object") == 0) { + m->has_print_object = true; + m->print_object = mx_yes_no_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_clef_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxClefChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "sign") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->sign = malloc(sizeof(*ch->sign)); + if (!ch->sign) + abort(); + *ch->sign = mx_clef_sign_parse(s); + xmlFree(text); + } else if (strcmp(tag, "line") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->line = malloc(sizeof(*ch->line)); + if (!ch->line) + abort(); + *ch->line = mx_staff_line_parse(s); + xmlFree(text); + } else if (strcmp(tag, "clef-octave-change") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->clef_octave_change = malloc(sizeof(*ch->clef_octave_change)); + if (!ch->clef_octave_change) + abort(); + *ch->clef_octave_change = mx_parse_int(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_clef_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_clef_serialize(const MxClef *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_number) { + char *s = mx_staff_number_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_additional) { + xmlSetProp(el, BAD_CAST "additional", BAD_CAST mx_yes_no_to_string(m->additional)); + } + if (m->has_size) { + xmlSetProp(el, BAD_CAST "size", BAD_CAST mx_symbol_size_to_string(m->size)); + } + if (m->has_after_barline) { + xmlSetProp(el, BAD_CAST "after-barline", BAD_CAST mx_yes_no_to_string(m->after_barline)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_print_object) { + xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxClefChild *ch = &m->children[i]; + if (ch->sign) { + xmlNewTextChild(el, NULL, BAD_CAST "sign", BAD_CAST mx_clef_sign_to_string((*ch->sign))); + } else if (ch->line) { + char *s = mx_staff_line_to_string((*ch->line)); + xmlNewTextChild(el, NULL, BAD_CAST "line", BAD_CAST s); + free(s); + } else if (ch->clef_octave_change) { + char *s = mx_format_int((*ch->clef_octave_change)); + xmlNewTextChild(el, NULL, BAD_CAST "clef-octave-change", BAD_CAST s); + free(s); + } + } + return el; +} + +void mx_clef_free(MxClef *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxClefChild *ch = &m->children[i]; + free(ch->sign); + free(ch->line); + free(ch->clef_octave_change); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_clef.h b/gen/test/c/mx/mx_clef.h new file mode 100644 index 000000000..97dbca3b5 --- /dev/null +++ b/gen/test/c/mx/mx_clef.h @@ -0,0 +1,84 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_CLEF_H_INCLUDED +#define MX_CLEF_H_INCLUDED + +#include +#include +#include +#include "mx_clef_sign.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_staff_line.h" +#include "mx_staff_number.h" +#include "mx_symbol_size.h" +#include "mx_tenths.h" +#include "mx_yes_no.h" + +/* + * Clefs are represented by a combination of sign, line, and clef-octave-change elements. The + * optional number attribute refers to staff numbers within the part. A value of 1 is assumed if not + * present. Sometimes clefs are added to the staff in non-standard line positions, either to + * indicate cue passages, or when there are multiple clefs present simultaneously on one staff. In + * this situation, the additional attribute is set to "yes" and the line value is ignored. The size + * attribute is used for clefs where the additional attribute is "yes". It is typically used to + * indicate cue clefs. Sometimes clefs at the start of a measure need to appear after the barline + * rather than before, as for cues or for use after a repeated section. The after-barline attribute + * is set to "yes" in this situation. The attribute is ignored for mid-measure clefs. Clefs appear + * at the start of each system unless the print-object attribute has been set to "no" or the + * additional attribute has been set to "yes". + */ +/* One child element of MxClef: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxClefSign *sign; + MxStaffLine *line; + long *clef_octave_change; +} MxClefChild; + +typedef struct { + bool has_number; + MxStaffNumber number; /* attribute number */ + bool has_additional; + MxYesNo additional; /* attribute additional */ + bool has_size; + MxSymbolSize size; /* attribute size */ + bool has_after_barline; + MxYesNo after_barline; /* attribute after-barline */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_print_object; + MxYesNo print_object; /* attribute print-object */ + bool has_id; + char *id; /* attribute id */ + MxClefChild *children; /* child elements in document order */ + size_t children_count; +} MxClef; + +/* NULL on error; the message is in mx_error(). */ +MxClef *mx_clef_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_clef_serialize(const MxClef *m, xmlNodePtr parent, const char *tag); +void mx_clef_free(MxClef *m); + +#endif /* MX_CLEF_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_coda.c b/gen/test/c/mx/mx_coda.c new file mode 100644 index 000000000..aab88e110 --- /dev/null +++ b/gen/test/c/mx/mx_coda.c @@ -0,0 +1,154 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_coda.h" + +#include "mx_runtime.h" +#include +#include + +MxCoda *mx_coda_parse(xmlNodePtr el) { + MxCoda *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "smufl") == 0) { + m->has_smufl = true; + m->smufl = mx_smufl_coda_glyph_name_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_coda_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_coda_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_coda_serialize(const MxCoda *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_smufl) { + xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_coda_free(MxCoda *m) { + if (!m) + return; + if (m->has_smufl) + free(m->smufl); + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + free(m); +} diff --git a/gen/test/c/mx/mx_coda.h b/gen/test/c/mx/mx_coda.h new file mode 100644 index 000000000..07ee81029 --- /dev/null +++ b/gen/test/c/mx/mx_coda.h @@ -0,0 +1,58 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_CODA_H_INCLUDED +#define MX_CODA_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_center_right.h" +#include "mx_smufl_coda_glyph_name.h" +#include "mx_tenths.h" +#include "mx_valign.h" + +/* + * The coda type is the visual indicator of a coda sign. The exact glyph can be specified with the + * smufl attribute. A sound element is also needed to guide playback applications reliably. + */ +typedef struct { + bool has_smufl; + MxSMUFLCodaGlyphName smufl; /* attribute smufl */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ + bool has_id; + char *id; /* attribute id */ +} MxCoda; + +/* NULL on error; the message is in mx_error(). */ +MxCoda *mx_coda_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_coda_serialize(const MxCoda *m, xmlNodePtr parent, const char *tag); +void mx_coda_free(MxCoda *m); + +#endif /* MX_CODA_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_credit.c b/gen/test/c/mx/mx_credit.c new file mode 100644 index 000000000..d5ea571e9 --- /dev/null +++ b/gen/test/c/mx/mx_credit.c @@ -0,0 +1,149 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_credit.h" + +#include "mx_runtime.h" +#include +#include + +MxCredit *mx_credit_parse(xmlNodePtr el) { + MxCredit *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "page") == 0) { + m->has_page = true; + m->page = mx_parse_int(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_credit_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxCreditChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "credit-type") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->credit_type = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "link") == 0) { + ch->link = mx_link_parse(c); + if (!ch->link) { + mx_credit_free(m); + return NULL; + } + } else if (strcmp(tag, "bookmark") == 0) { + ch->bookmark = mx_bookmark_parse(c); + if (!ch->bookmark) { + mx_credit_free(m); + return NULL; + } + } else if (strcmp(tag, "credit-image") == 0) { + ch->credit_image = mx_image_parse(c); + if (!ch->credit_image) { + mx_credit_free(m); + return NULL; + } + } else if (strcmp(tag, "credit-words") == 0) { + ch->credit_words = mx_formatted_text_id_parse(c); + if (!ch->credit_words) { + mx_credit_free(m); + return NULL; + } + } else if (strcmp(tag, "credit-symbol") == 0) { + ch->credit_symbol = mx_formatted_symbol_id_parse(c); + if (!ch->credit_symbol) { + mx_credit_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_credit_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_credit_serialize(const MxCredit *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_page) { + char *s = mx_format_int(m->page); + xmlSetProp(el, BAD_CAST "page", BAD_CAST s); + free(s); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxCreditChild *ch = &m->children[i]; + if (ch->credit_type) { + xmlNewTextChild(el, NULL, BAD_CAST "credit-type", BAD_CAST ch->credit_type); + } else if (ch->link) { + mx_link_serialize(ch->link, el, "link"); + } else if (ch->bookmark) { + mx_bookmark_serialize(ch->bookmark, el, "bookmark"); + } else if (ch->credit_image) { + mx_image_serialize(ch->credit_image, el, "credit-image"); + } else if (ch->credit_words) { + mx_formatted_text_id_serialize(ch->credit_words, el, "credit-words"); + } else if (ch->credit_symbol) { + mx_formatted_symbol_id_serialize(ch->credit_symbol, el, "credit-symbol"); + } + } + return el; +} + +void mx_credit_free(MxCredit *m) { + if (!m) + return; + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxCreditChild *ch = &m->children[i]; + free(ch->credit_type); + if (ch->link) + mx_link_free(ch->link); + if (ch->bookmark) + mx_bookmark_free(ch->bookmark); + if (ch->credit_image) + mx_image_free(ch->credit_image); + if (ch->credit_words) + mx_formatted_text_id_free(ch->credit_words); + if (ch->credit_symbol) + mx_formatted_symbol_id_free(ch->credit_symbol); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_credit.h b/gen/test/c/mx/mx_credit.h new file mode 100644 index 000000000..06e7e3ae9 --- /dev/null +++ b/gen/test/c/mx/mx_credit.h @@ -0,0 +1,59 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_CREDIT_H_INCLUDED +#define MX_CREDIT_H_INCLUDED + +#include +#include +#include +#include "mx_bookmark.h" +#include "mx_formatted_symbol_id.h" +#include "mx_formatted_text_id.h" +#include "mx_image.h" +#include "mx_link.h" + +/* + * The credit type represents the appearance of the title, composer, arranger, lyricist, copyright, + * dedication, and other text, symbols, and graphics that commonly appear on the first page of a + * score. The credit-words, credit-symbol, and credit-image elements are similar to the words, + * symbol, and image elements for directions. However, since the credit is not part of a measure, + * the default-x and default-y attributes adjust the origin relative to the bottom left-hand corner + * of the page. The enclosure for credit-words and credit-symbol is none by default. By default, a + * series of credit-words and credit-symbol elements within a single credit element follow one + * another in sequence visually. Non-positional formatting attributes are carried over from the + * previous element by default. The page attribute for the credit element specifies the page number + * where the credit should appear. This is an integer value that starts with 1 for the first page. + * Its value is 1 by default. Since credits occur before the music, these page numbers do not refer + * to the page numbering specified by the print element's page-number attribute. The credit-type + * element indicates the purpose behind a credit. Multiple types of data may be combined in a single + * credit, so multiple elements may be used. Standard values include page number, title, subtitle, + * composer, arranger, lyricist, and rights. + */ +/* One child element of MxCredit: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + char *credit_type; + MxLink *link; + MxBookmark *bookmark; + MxImage *credit_image; + MxFormattedTextID *credit_words; + MxFormattedSymbolID *credit_symbol; +} MxCreditChild; + +typedef struct { + bool has_page; + long page; /* attribute page */ + bool has_id; + char *id; /* attribute id */ + MxCreditChild *children; /* child elements in document order */ + size_t children_count; +} MxCredit; + +/* NULL on error; the message is in mx_error(). */ +MxCredit *mx_credit_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_credit_serialize(const MxCredit *m, xmlNodePtr parent, const char *tag); +void mx_credit_free(MxCredit *m); + +#endif /* MX_CREDIT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_dashes.c b/gen/test/c/mx/mx_dashes.c new file mode 100644 index 000000000..ad6a834ef --- /dev/null +++ b/gen/test/c/mx/mx_dashes.c @@ -0,0 +1,134 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_dashes.h" + +#include "mx_runtime.h" +#include +#include + +MxDashes *mx_dashes_parse(xmlNodePtr el) { + MxDashes *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_continue_parse(s); + } else if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_number_level_parse(s); + } else if (strcmp(aname, "dash-length") == 0) { + m->has_dash_length = true; + m->dash_length = mx_tenths_parse(s); + } else if (strcmp(aname, "space-length") == 0) { + m->has_space_length = true; + m->space_length = mx_tenths_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_dashes_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_dashes_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_dashes_serialize(const MxDashes *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_continue_to_string(m->type)); + } + if (m->has_number) { + char *s = mx_number_level_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_dash_length) { + char *s = mx_tenths_to_string(m->dash_length); + xmlSetProp(el, BAD_CAST "dash-length", BAD_CAST s); + free(s); + } + if (m->has_space_length) { + char *s = mx_tenths_to_string(m->space_length); + xmlSetProp(el, BAD_CAST "space-length", BAD_CAST s); + free(s); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_dashes_free(MxDashes *m) { + if (!m) + return; + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + free(m); +} diff --git a/gen/test/c/mx/mx_dashes.h b/gen/test/c/mx/mx_dashes.h new file mode 100644 index 000000000..4cf351069 --- /dev/null +++ b/gen/test/c/mx/mx_dashes.h @@ -0,0 +1,46 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_DASHES_H_INCLUDED +#define MX_DASHES_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_number_level.h" +#include "mx_start_stop_continue.h" +#include "mx_tenths.h" + +/* + * The dashes type represents dashes, used for instance with cresc. and dim. marks. + */ +typedef struct { + bool has_type; + MxStartStopContinue type; /* attribute type */ + bool has_number; + MxNumberLevel number; /* attribute number */ + bool has_dash_length; + MxTenths dash_length; /* attribute dash-length */ + bool has_space_length; + MxTenths space_length; /* attribute space-length */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_color; + MxColor color; /* attribute color */ + bool has_id; + char *id; /* attribute id */ +} MxDashes; + +/* NULL on error; the message is in mx_error(). */ +MxDashes *mx_dashes_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_dashes_serialize(const MxDashes *m, xmlNodePtr parent, const char *tag); +void mx_dashes_free(MxDashes *m); + +#endif /* MX_DASHES_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_defaults.c b/gen/test/c/mx/mx_defaults.c new file mode 100644 index 000000000..188cdce00 --- /dev/null +++ b/gen/test/c/mx/mx_defaults.c @@ -0,0 +1,165 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_defaults.h" + +#include "mx_runtime.h" +#include +#include + +MxDefaults *mx_defaults_parse(xmlNodePtr el) { + MxDefaults *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_defaults_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxDefaultsChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "scaling") == 0) { + ch->scaling = mx_scaling_parse(c); + if (!ch->scaling) { + mx_defaults_free(m); + return NULL; + } + } else if (strcmp(tag, "page-layout") == 0) { + ch->page_layout = mx_page_layout_parse(c); + if (!ch->page_layout) { + mx_defaults_free(m); + return NULL; + } + } else if (strcmp(tag, "system-layout") == 0) { + ch->system_layout = mx_system_layout_parse(c); + if (!ch->system_layout) { + mx_defaults_free(m); + return NULL; + } + } else if (strcmp(tag, "staff-layout") == 0) { + ch->staff_layout = mx_staff_layout_parse(c); + if (!ch->staff_layout) { + mx_defaults_free(m); + return NULL; + } + } else if (strcmp(tag, "appearance") == 0) { + ch->appearance = mx_appearance_parse(c); + if (!ch->appearance) { + mx_defaults_free(m); + return NULL; + } + } else if (strcmp(tag, "music-font") == 0) { + ch->music_font = mx_empty_font_parse(c); + if (!ch->music_font) { + mx_defaults_free(m); + return NULL; + } + } else if (strcmp(tag, "word-font") == 0) { + ch->word_font = mx_empty_font_parse(c); + if (!ch->word_font) { + mx_defaults_free(m); + return NULL; + } + } else if (strcmp(tag, "lyric-font") == 0) { + ch->lyric_font = mx_lyric_font_parse(c); + if (!ch->lyric_font) { + mx_defaults_free(m); + return NULL; + } + } else if (strcmp(tag, "lyric-language") == 0) { + ch->lyric_language = mx_lyric_language_parse(c); + if (!ch->lyric_language) { + mx_defaults_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_defaults_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_defaults_serialize(const MxDefaults *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxDefaultsChild *ch = &m->children[i]; + if (ch->scaling) { + mx_scaling_serialize(ch->scaling, el, "scaling"); + } else if (ch->page_layout) { + mx_page_layout_serialize(ch->page_layout, el, "page-layout"); + } else if (ch->system_layout) { + mx_system_layout_serialize(ch->system_layout, el, "system-layout"); + } else if (ch->staff_layout) { + mx_staff_layout_serialize(ch->staff_layout, el, "staff-layout"); + } else if (ch->appearance) { + mx_appearance_serialize(ch->appearance, el, "appearance"); + } else if (ch->music_font) { + mx_empty_font_serialize(ch->music_font, el, "music-font"); + } else if (ch->word_font) { + mx_empty_font_serialize(ch->word_font, el, "word-font"); + } else if (ch->lyric_font) { + mx_lyric_font_serialize(ch->lyric_font, el, "lyric-font"); + } else if (ch->lyric_language) { + mx_lyric_language_serialize(ch->lyric_language, el, "lyric-language"); + } + } + return el; +} + +void mx_defaults_free(MxDefaults *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxDefaultsChild *ch = &m->children[i]; + if (ch->scaling) + mx_scaling_free(ch->scaling); + if (ch->page_layout) + mx_page_layout_free(ch->page_layout); + if (ch->system_layout) + mx_system_layout_free(ch->system_layout); + if (ch->staff_layout) + mx_staff_layout_free(ch->staff_layout); + if (ch->appearance) + mx_appearance_free(ch->appearance); + if (ch->music_font) + mx_empty_font_free(ch->music_font); + if (ch->word_font) + mx_empty_font_free(ch->word_font); + if (ch->lyric_font) + mx_lyric_font_free(ch->lyric_font); + if (ch->lyric_language) + mx_lyric_language_free(ch->lyric_language); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_defaults.h b/gen/test/c/mx/mx_defaults.h new file mode 100644 index 000000000..ef8eb1601 --- /dev/null +++ b/gen/test/c/mx/mx_defaults.h @@ -0,0 +1,47 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_DEFAULTS_H_INCLUDED +#define MX_DEFAULTS_H_INCLUDED + +#include +#include +#include +#include "mx_appearance.h" +#include "mx_empty_font.h" +#include "mx_lyric_font.h" +#include "mx_lyric_language.h" +#include "mx_page_layout.h" +#include "mx_scaling.h" +#include "mx_staff_layout.h" +#include "mx_system_layout.h" + +/* + * The defaults type specifies score-wide defaults for scaling, layout, and appearance. + */ +/* One child element of MxDefaults: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxScaling *scaling; + MxPageLayout *page_layout; + MxSystemLayout *system_layout; + MxStaffLayout *staff_layout; + MxAppearance *appearance; + MxEmptyFont *music_font; + MxEmptyFont *word_font; + MxLyricFont *lyric_font; + MxLyricLanguage *lyric_language; +} MxDefaultsChild; + +typedef struct { + MxDefaultsChild *children; /* child elements in document order */ + size_t children_count; +} MxDefaults; + +/* NULL on error; the message is in mx_error(). */ +MxDefaults *mx_defaults_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_defaults_serialize(const MxDefaults *m, xmlNodePtr parent, const char *tag); +void mx_defaults_free(MxDefaults *m); + +#endif /* MX_DEFAULTS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_degree.c b/gen/test/c/mx/mx_degree.c new file mode 100644 index 000000000..1c464791e --- /dev/null +++ b/gen/test/c/mx/mx_degree.c @@ -0,0 +1,111 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_degree.h" + +#include "mx_runtime.h" +#include +#include + +MxDegree *mx_degree_parse(xmlNodePtr el) { + MxDegree *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "print-object") == 0) { + m->has_print_object = true; + m->print_object = mx_yes_no_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_degree_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxDegreeChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "degree-value") == 0) { + ch->degree_value = mx_degree_value_parse(c); + if (!ch->degree_value) { + mx_degree_free(m); + return NULL; + } + } else if (strcmp(tag, "degree-alter") == 0) { + ch->degree_alter = mx_degree_alter_parse(c); + if (!ch->degree_alter) { + mx_degree_free(m); + return NULL; + } + } else if (strcmp(tag, "degree-type") == 0) { + ch->degree_type = mx_degree_type_parse(c); + if (!ch->degree_type) { + mx_degree_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_degree_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_degree_serialize(const MxDegree *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_print_object) { + xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxDegreeChild *ch = &m->children[i]; + if (ch->degree_value) { + mx_degree_value_serialize(ch->degree_value, el, "degree-value"); + } else if (ch->degree_alter) { + mx_degree_alter_serialize(ch->degree_alter, el, "degree-alter"); + } else if (ch->degree_type) { + mx_degree_type_serialize(ch->degree_type, el, "degree-type"); + } + } + return el; +} + +void mx_degree_free(MxDegree *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxDegreeChild *ch = &m->children[i]; + if (ch->degree_value) + mx_degree_value_free(ch->degree_value); + if (ch->degree_alter) + mx_degree_alter_free(ch->degree_alter); + if (ch->degree_type) + mx_degree_type_free(ch->degree_type); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_degree.h b/gen/test/c/mx/mx_degree.h new file mode 100644 index 000000000..b7fe85341 --- /dev/null +++ b/gen/test/c/mx/mx_degree.h @@ -0,0 +1,44 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_DEGREE_H_INCLUDED +#define MX_DEGREE_H_INCLUDED + +#include +#include +#include +#include "mx_degree_alter.h" +#include "mx_degree_type.h" +#include "mx_degree_value.h" +#include "mx_yes_no.h" + +/* + * The degree type is used to add, alter, or subtract individual notes in the chord. The + * print-object attribute can be used to keep the degree from printing separately when it has + * already taken into account in the text attribute of the kind element. The degree-value and + * degree-type text attributes specify how the value and type of the degree should be displayed. A + * harmony of kind "other" can be spelled explicitly by using a series of degree elements together + * with a root. + */ +/* One child element of MxDegree: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxDegreeValue *degree_value; + MxDegreeAlter *degree_alter; + MxDegreeType *degree_type; +} MxDegreeChild; + +typedef struct { + bool has_print_object; + MxYesNo print_object; /* attribute print-object */ + MxDegreeChild *children; /* child elements in document order */ + size_t children_count; +} MxDegree; + +/* NULL on error; the message is in mx_error(). */ +MxDegree *mx_degree_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_degree_serialize(const MxDegree *m, xmlNodePtr parent, const char *tag); +void mx_degree_free(MxDegree *m); + +#endif /* MX_DEGREE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_degree_alter.c b/gen/test/c/mx/mx_degree_alter.c new file mode 100644 index 000000000..7e922885c --- /dev/null +++ b/gen/test/c/mx/mx_degree_alter.c @@ -0,0 +1,143 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_degree_alter.h" + +#include "mx_runtime.h" +#include +#include + +MxDegreeAlter *mx_degree_alter_parse(xmlNodePtr el) { + MxDegreeAlter *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "plus-minus") == 0) { + m->has_plus_minus = true; + m->plus_minus = mx_yes_no_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_degree_alter_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_semitones_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_degree_alter_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_degree_alter_serialize(const MxDegreeAlter *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + { + char *s = mx_semitones_to_string(m->value); + xmlAddChild(el, xmlNewText(BAD_CAST s)); + free(s); + } + if (m->has_plus_minus) { + xmlSetProp(el, BAD_CAST "plus-minus", BAD_CAST mx_yes_no_to_string(m->plus_minus)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_degree_alter_free(MxDegreeAlter *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_degree_alter.h b/gen/test/c/mx/mx_degree_alter.h new file mode 100644 index 000000000..4c5d0c3ee --- /dev/null +++ b/gen/test/c/mx/mx_degree_alter.h @@ -0,0 +1,56 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_DEGREE_ALTER_H_INCLUDED +#define MX_DEGREE_ALTER_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_semitones.h" +#include "mx_tenths.h" +#include "mx_yes_no.h" + +/* + * The degree-alter type represents the chromatic alteration for the current degree. If the + * degree-type value is alter or subtract, the degree-alter value is relative to the degree already + * in the chord based on its kind element. If the degree-type value is add, the degree-alter is + * relative to a dominant chord (major and perfect intervals except for a minor seventh). The + * plus-minus attribute is used to indicate if plus and minus symbols should be used instead of + * sharp and flat symbols to display the degree alteration; it is no by default. + */ +typedef struct { + bool has_plus_minus; + MxYesNo plus_minus; /* attribute plus-minus */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + MxSemitones value; /* text content */ +} MxDegreeAlter; + +/* NULL on error; the message is in mx_error(). */ +MxDegreeAlter *mx_degree_alter_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_degree_alter_serialize(const MxDegreeAlter *m, xmlNodePtr parent, const char *tag); +void mx_degree_alter_free(MxDegreeAlter *m); + +#endif /* MX_DEGREE_ALTER_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_degree_type.c b/gen/test/c/mx/mx_degree_type.c new file mode 100644 index 000000000..8829dc47f --- /dev/null +++ b/gen/test/c/mx/mx_degree_type.c @@ -0,0 +1,141 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_degree_type.h" + +#include "mx_runtime.h" +#include +#include + +MxDegreeType *mx_degree_type_parse(xmlNodePtr el) { + MxDegreeType *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "text") == 0) { + m->has_text = true; + m->text = mx_strdup(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_degree_type_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_degree_type_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_degree_type_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_degree_type_serialize(const MxDegreeType *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_degree_type_value_to_string(m->value))); + if (m->has_text) { + xmlSetProp(el, BAD_CAST "text", BAD_CAST m->text); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_degree_type_free(MxDegreeType *m) { + if (!m) + return; + if (m->has_text) + free(m->text); + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_degree_type.h b/gen/test/c/mx/mx_degree_type.h new file mode 100644 index 000000000..5e14ae2c1 --- /dev/null +++ b/gen/test/c/mx/mx_degree_type.h @@ -0,0 +1,53 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_DEGREE_TYPE_H_INCLUDED +#define MX_DEGREE_TYPE_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_degree_type_value.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_tenths.h" + +/* + * The degree-type type indicates if this degree is an addition, alteration, or subtraction relative + * to the kind of the current chord. The value of the degree-type element affects the interpretation + * of the value of the degree-alter element. The text attribute specifies how the type of the degree + * should be displayed in a score. + */ +typedef struct { + bool has_text; + char *text; /* attribute text */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + MxDegreeTypeValue value; /* text content */ +} MxDegreeType; + +/* NULL on error; the message is in mx_error(). */ +MxDegreeType *mx_degree_type_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_degree_type_serialize(const MxDegreeType *m, xmlNodePtr parent, const char *tag); +void mx_degree_type_free(MxDegreeType *m); + +#endif /* MX_DEGREE_TYPE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_degree_value.c b/gen/test/c/mx/mx_degree_value.c new file mode 100644 index 000000000..ae00b85dd --- /dev/null +++ b/gen/test/c/mx/mx_degree_value.c @@ -0,0 +1,151 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_degree_value.h" + +#include "mx_runtime.h" +#include +#include + +MxDegreeValue *mx_degree_value_parse(xmlNodePtr el) { + MxDegreeValue *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "symbol") == 0) { + m->has_symbol = true; + m->symbol = mx_degree_symbol_value_parse(s); + } else if (strcmp(aname, "text") == 0) { + m->has_text = true; + m->text = mx_strdup(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_degree_value_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_parse_int(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_degree_value_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_degree_value_serialize(const MxDegreeValue *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + { + char *s = mx_format_int(m->value); + xmlAddChild(el, xmlNewText(BAD_CAST s)); + free(s); + } + if (m->has_symbol) { + xmlSetProp(el, BAD_CAST "symbol", BAD_CAST mx_degree_symbol_value_to_string(m->symbol)); + } + if (m->has_text) { + xmlSetProp(el, BAD_CAST "text", BAD_CAST m->text); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_degree_value_free(MxDegreeValue *m) { + if (!m) + return; + if (m->has_text) + free(m->text); + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_degree_value.h b/gen/test/c/mx/mx_degree_value.h new file mode 100644 index 000000000..071d48227 --- /dev/null +++ b/gen/test/c/mx/mx_degree_value.h @@ -0,0 +1,56 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_DEGREE_VALUE_H_INCLUDED +#define MX_DEGREE_VALUE_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_degree_symbol_value.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_tenths.h" + +/* + * The content of the degree-value type is a number indicating the degree of the chord (1 for the + * root, 3 for third, etc). The text attribute specifies how the type of the degree should be + * displayed in a score. The degree-value symbol attribute indicates that a symbol should be used in + * specifying the degree. If the symbol attribute is present, the value of the text attribute + * follows the symbol. + */ +typedef struct { + bool has_symbol; + MxDegreeSymbolValue symbol; /* attribute symbol */ + bool has_text; + char *text; /* attribute text */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + long value; /* text content */ +} MxDegreeValue; + +/* NULL on error; the message is in mx_error(). */ +MxDegreeValue *mx_degree_value_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_degree_value_serialize(const MxDegreeValue *m, xmlNodePtr parent, const char *tag); +void mx_degree_value_free(MxDegreeValue *m); + +#endif /* MX_DEGREE_VALUE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_direction.c b/gen/test/c/mx/mx_direction.c new file mode 100644 index 000000000..c8ee751b7 --- /dev/null +++ b/gen/test/c/mx/mx_direction.c @@ -0,0 +1,166 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_direction.h" + +#include "mx_runtime.h" +#include +#include + +MxDirection *mx_direction_parse(xmlNodePtr el) { + MxDirection *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "directive") == 0) { + m->has_directive = true; + m->directive = mx_yes_no_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_direction_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxDirectionChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "direction-type") == 0) { + ch->direction_type = mx_direction_type_parse(c); + if (!ch->direction_type) { + mx_direction_free(m); + return NULL; + } + } else if (strcmp(tag, "offset") == 0) { + ch->offset = mx_offset_parse(c); + if (!ch->offset) { + mx_direction_free(m); + return NULL; + } + } else if (strcmp(tag, "footnote") == 0) { + ch->footnote = mx_formatted_text_parse(c); + if (!ch->footnote) { + mx_direction_free(m); + return NULL; + } + } else if (strcmp(tag, "level") == 0) { + ch->level = mx_level_parse(c); + if (!ch->level) { + mx_direction_free(m); + return NULL; + } + } else if (strcmp(tag, "voice") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->voice = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "staff") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->staff = malloc(sizeof(*ch->staff)); + if (!ch->staff) + abort(); + *ch->staff = mx_parse_int(s); + xmlFree(text); + } else if (strcmp(tag, "sound") == 0) { + ch->sound = mx_sound_parse(c); + if (!ch->sound) { + mx_direction_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_direction_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_direction_serialize(const MxDirection *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_directive) { + xmlSetProp(el, BAD_CAST "directive", BAD_CAST mx_yes_no_to_string(m->directive)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxDirectionChild *ch = &m->children[i]; + if (ch->direction_type) { + mx_direction_type_serialize(ch->direction_type, el, "direction-type"); + } else if (ch->offset) { + mx_offset_serialize(ch->offset, el, "offset"); + } else if (ch->footnote) { + mx_formatted_text_serialize(ch->footnote, el, "footnote"); + } else if (ch->level) { + mx_level_serialize(ch->level, el, "level"); + } else if (ch->voice) { + xmlNewTextChild(el, NULL, BAD_CAST "voice", BAD_CAST ch->voice); + } else if (ch->staff) { + char *s = mx_format_int((*ch->staff)); + xmlNewTextChild(el, NULL, BAD_CAST "staff", BAD_CAST s); + free(s); + } else if (ch->sound) { + mx_sound_serialize(ch->sound, el, "sound"); + } + } + return el; +} + +void mx_direction_free(MxDirection *m) { + if (!m) + return; + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxDirectionChild *ch = &m->children[i]; + if (ch->direction_type) + mx_direction_type_free(ch->direction_type); + if (ch->offset) + mx_offset_free(ch->offset); + if (ch->footnote) + mx_formatted_text_free(ch->footnote); + if (ch->level) + mx_level_free(ch->level); + free(ch->voice); + free(ch->staff); + if (ch->sound) + mx_sound_free(ch->sound); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_direction.h b/gen/test/c/mx/mx_direction.h new file mode 100644 index 000000000..0c0531cbf --- /dev/null +++ b/gen/test/c/mx/mx_direction.h @@ -0,0 +1,57 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_DIRECTION_H_INCLUDED +#define MX_DIRECTION_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_direction_type.h" +#include "mx_formatted_text.h" +#include "mx_level.h" +#include "mx_offset.h" +#include "mx_sound.h" +#include "mx_yes_no.h" + +/* + * A direction is a musical indication that is not necessarily attached to a specific note. Two or + * more may be combined to indicate starts and stops of wedges, dashes, etc. For applications where + * a specific direction is indeed attached to a specific note, the direction element can be + * associated with the note element that follows it in score order that is not in a different voice. + * By default, a series of direction-type elements and a series of child elements of a + * direction-type within a single direction element follow one another in sequence visually. For a + * series of direction-type children, non-positional formatting attributes are carried over from the + * previous element by default. + */ +/* One child element of MxDirection: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxDirectionType *direction_type; + MxOffset *offset; + MxFormattedText *footnote; + MxLevel *level; + char *voice; + long *staff; + MxSound *sound; +} MxDirectionChild; + +typedef struct { + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_directive; + MxYesNo directive; /* attribute directive */ + bool has_id; + char *id; /* attribute id */ + MxDirectionChild *children; /* child elements in document order */ + size_t children_count; +} MxDirection; + +/* NULL on error; the message is in mx_error(). */ +MxDirection *mx_direction_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_direction_serialize(const MxDirection *m, xmlNodePtr parent, const char *tag); +void mx_direction_free(MxDirection *m); + +#endif /* MX_DIRECTION_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_direction_type.c b/gen/test/c/mx/mx_direction_type.c new file mode 100644 index 000000000..eabaf6c20 --- /dev/null +++ b/gen/test/c/mx/mx_direction_type.c @@ -0,0 +1,323 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_direction_type.h" + +#include "mx_runtime.h" +#include +#include + +MxDirectionType *mx_direction_type_parse(xmlNodePtr el) { + MxDirectionType *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_direction_type_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxDirectionTypeChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "rehearsal") == 0) { + ch->rehearsal = mx_formatted_text_id_parse(c); + if (!ch->rehearsal) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "segno") == 0) { + ch->segno = mx_segno_parse(c); + if (!ch->segno) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "coda") == 0) { + ch->coda = mx_coda_parse(c); + if (!ch->coda) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "words") == 0) { + ch->words = mx_formatted_text_id_parse(c); + if (!ch->words) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "symbol") == 0) { + ch->symbol = mx_formatted_symbol_id_parse(c); + if (!ch->symbol) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "wedge") == 0) { + ch->wedge = mx_wedge_parse(c); + if (!ch->wedge) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "dynamics") == 0) { + ch->dynamics = mx_dynamics_parse(c); + if (!ch->dynamics) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "dashes") == 0) { + ch->dashes = mx_dashes_parse(c); + if (!ch->dashes) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "bracket") == 0) { + ch->bracket = mx_bracket_parse(c); + if (!ch->bracket) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "pedal") == 0) { + ch->pedal = mx_pedal_parse(c); + if (!ch->pedal) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "metronome") == 0) { + ch->metronome = mx_metronome_parse(c); + if (!ch->metronome) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "octave-shift") == 0) { + ch->octave_shift = mx_octave_shift_parse(c); + if (!ch->octave_shift) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "harp-pedals") == 0) { + ch->harp_pedals = mx_harp_pedals_parse(c); + if (!ch->harp_pedals) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "damp") == 0) { + ch->damp = mx_empty_print_style_align_id_parse(c); + if (!ch->damp) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "damp-all") == 0) { + ch->damp_all = mx_empty_print_style_align_id_parse(c); + if (!ch->damp_all) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "eyeglasses") == 0) { + ch->eyeglasses = mx_empty_print_style_align_id_parse(c); + if (!ch->eyeglasses) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "string-mute") == 0) { + ch->string_mute = mx_string_mute_parse(c); + if (!ch->string_mute) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "scordatura") == 0) { + ch->scordatura = mx_scordatura_parse(c); + if (!ch->scordatura) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "image") == 0) { + ch->image = mx_image_parse(c); + if (!ch->image) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "principal-voice") == 0) { + ch->principal_voice = mx_principal_voice_parse(c); + if (!ch->principal_voice) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "percussion") == 0) { + ch->percussion = mx_percussion_parse(c); + if (!ch->percussion) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "accordion-registration") == 0) { + ch->accordion_registration = mx_accordion_registration_parse(c); + if (!ch->accordion_registration) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "staff-divide") == 0) { + ch->staff_divide = mx_staff_divide_parse(c); + if (!ch->staff_divide) { + mx_direction_type_free(m); + return NULL; + } + } else if (strcmp(tag, "other-direction") == 0) { + ch->other_direction = mx_other_direction_parse(c); + if (!ch->other_direction) { + mx_direction_type_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_direction_type_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_direction_type_serialize(const MxDirectionType *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxDirectionTypeChild *ch = &m->children[i]; + if (ch->rehearsal) { + mx_formatted_text_id_serialize(ch->rehearsal, el, "rehearsal"); + } else if (ch->segno) { + mx_segno_serialize(ch->segno, el, "segno"); + } else if (ch->coda) { + mx_coda_serialize(ch->coda, el, "coda"); + } else if (ch->words) { + mx_formatted_text_id_serialize(ch->words, el, "words"); + } else if (ch->symbol) { + mx_formatted_symbol_id_serialize(ch->symbol, el, "symbol"); + } else if (ch->wedge) { + mx_wedge_serialize(ch->wedge, el, "wedge"); + } else if (ch->dynamics) { + mx_dynamics_serialize(ch->dynamics, el, "dynamics"); + } else if (ch->dashes) { + mx_dashes_serialize(ch->dashes, el, "dashes"); + } else if (ch->bracket) { + mx_bracket_serialize(ch->bracket, el, "bracket"); + } else if (ch->pedal) { + mx_pedal_serialize(ch->pedal, el, "pedal"); + } else if (ch->metronome) { + mx_metronome_serialize(ch->metronome, el, "metronome"); + } else if (ch->octave_shift) { + mx_octave_shift_serialize(ch->octave_shift, el, "octave-shift"); + } else if (ch->harp_pedals) { + mx_harp_pedals_serialize(ch->harp_pedals, el, "harp-pedals"); + } else if (ch->damp) { + mx_empty_print_style_align_id_serialize(ch->damp, el, "damp"); + } else if (ch->damp_all) { + mx_empty_print_style_align_id_serialize(ch->damp_all, el, "damp-all"); + } else if (ch->eyeglasses) { + mx_empty_print_style_align_id_serialize(ch->eyeglasses, el, "eyeglasses"); + } else if (ch->string_mute) { + mx_string_mute_serialize(ch->string_mute, el, "string-mute"); + } else if (ch->scordatura) { + mx_scordatura_serialize(ch->scordatura, el, "scordatura"); + } else if (ch->image) { + mx_image_serialize(ch->image, el, "image"); + } else if (ch->principal_voice) { + mx_principal_voice_serialize(ch->principal_voice, el, "principal-voice"); + } else if (ch->percussion) { + mx_percussion_serialize(ch->percussion, el, "percussion"); + } else if (ch->accordion_registration) { + mx_accordion_registration_serialize(ch->accordion_registration, el, "accordion-registration"); + } else if (ch->staff_divide) { + mx_staff_divide_serialize(ch->staff_divide, el, "staff-divide"); + } else if (ch->other_direction) { + mx_other_direction_serialize(ch->other_direction, el, "other-direction"); + } + } + return el; +} + +void mx_direction_type_free(MxDirectionType *m) { + if (!m) + return; + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxDirectionTypeChild *ch = &m->children[i]; + if (ch->rehearsal) + mx_formatted_text_id_free(ch->rehearsal); + if (ch->segno) + mx_segno_free(ch->segno); + if (ch->coda) + mx_coda_free(ch->coda); + if (ch->words) + mx_formatted_text_id_free(ch->words); + if (ch->symbol) + mx_formatted_symbol_id_free(ch->symbol); + if (ch->wedge) + mx_wedge_free(ch->wedge); + if (ch->dynamics) + mx_dynamics_free(ch->dynamics); + if (ch->dashes) + mx_dashes_free(ch->dashes); + if (ch->bracket) + mx_bracket_free(ch->bracket); + if (ch->pedal) + mx_pedal_free(ch->pedal); + if (ch->metronome) + mx_metronome_free(ch->metronome); + if (ch->octave_shift) + mx_octave_shift_free(ch->octave_shift); + if (ch->harp_pedals) + mx_harp_pedals_free(ch->harp_pedals); + if (ch->damp) + mx_empty_print_style_align_id_free(ch->damp); + if (ch->damp_all) + mx_empty_print_style_align_id_free(ch->damp_all); + if (ch->eyeglasses) + mx_empty_print_style_align_id_free(ch->eyeglasses); + if (ch->string_mute) + mx_string_mute_free(ch->string_mute); + if (ch->scordatura) + mx_scordatura_free(ch->scordatura); + if (ch->image) + mx_image_free(ch->image); + if (ch->principal_voice) + mx_principal_voice_free(ch->principal_voice); + if (ch->percussion) + mx_percussion_free(ch->percussion); + if (ch->accordion_registration) + mx_accordion_registration_free(ch->accordion_registration); + if (ch->staff_divide) + mx_staff_divide_free(ch->staff_divide); + if (ch->other_direction) + mx_other_direction_free(ch->other_direction); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_direction_type.h b/gen/test/c/mx/mx_direction_type.h new file mode 100644 index 000000000..d50d5d2a1 --- /dev/null +++ b/gen/test/c/mx/mx_direction_type.h @@ -0,0 +1,79 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_DIRECTION_TYPE_H_INCLUDED +#define MX_DIRECTION_TYPE_H_INCLUDED + +#include +#include +#include +#include "mx_accordion_registration.h" +#include "mx_bracket.h" +#include "mx_coda.h" +#include "mx_dashes.h" +#include "mx_dynamics.h" +#include "mx_empty_print_style_align_id.h" +#include "mx_formatted_symbol_id.h" +#include "mx_formatted_text_id.h" +#include "mx_harp_pedals.h" +#include "mx_image.h" +#include "mx_metronome.h" +#include "mx_octave_shift.h" +#include "mx_other_direction.h" +#include "mx_pedal.h" +#include "mx_percussion.h" +#include "mx_principal_voice.h" +#include "mx_scordatura.h" +#include "mx_segno.h" +#include "mx_staff_divide.h" +#include "mx_string_mute.h" +#include "mx_wedge.h" + +/* + * Textual direction types may have more than 1 component due to multiple fonts. The dynamics + * element may also be used in the notations element. Attribute groups related to print suggestions + * apply to the individual direction-type, not to the overall direction. + */ +/* One child element of MxDirectionType: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxFormattedTextID *rehearsal; + MxSegno *segno; + MxCoda *coda; + MxFormattedTextID *words; + MxFormattedSymbolID *symbol; + MxWedge *wedge; + MxDynamics *dynamics; + MxDashes *dashes; + MxBracket *bracket; + MxPedal *pedal; + MxMetronome *metronome; + MxOctaveShift *octave_shift; + MxHarpPedals *harp_pedals; + MxEmptyPrintStyleAlignID *damp; + MxEmptyPrintStyleAlignID *damp_all; + MxEmptyPrintStyleAlignID *eyeglasses; + MxStringMute *string_mute; + MxScordatura *scordatura; + MxImage *image; + MxPrincipalVoice *principal_voice; + MxPercussion *percussion; + MxAccordionRegistration *accordion_registration; + MxStaffDivide *staff_divide; + MxOtherDirection *other_direction; +} MxDirectionTypeChild; + +typedef struct { + bool has_id; + char *id; /* attribute id */ + MxDirectionTypeChild *children; /* child elements in document order */ + size_t children_count; +} MxDirectionType; + +/* NULL on error; the message is in mx_error(). */ +MxDirectionType *mx_direction_type_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_direction_type_serialize(const MxDirectionType *m, xmlNodePtr parent, const char *tag); +void mx_direction_type_free(MxDirectionType *m); + +#endif /* MX_DIRECTION_TYPE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_directive.c b/gen/test/c/mx/mx_directive.c new file mode 100644 index 000000000..e0cec75c4 --- /dev/null +++ b/gen/test/c/mx/mx_directive.c @@ -0,0 +1,142 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_directive.h" + +#include "mx_runtime.h" +#include +#include + +MxDirective *mx_directive_parse(xmlNodePtr el) { + MxDirective *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "xml:lang") == 0) { + m->has_xml_lang = true; + m->xml_lang = mx_strdup(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_directive_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_directive_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_directive_serialize(const MxDirective *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_xml_lang) { + xmlSetProp(el, BAD_CAST "xml:lang", BAD_CAST m->xml_lang); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_directive_free(MxDirective *m) { + if (!m) + return; + if (m->has_xml_lang) + free(m->xml_lang); + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_directive.h b/gen/test/c/mx/mx_directive.h new file mode 100644 index 000000000..007d854c3 --- /dev/null +++ b/gen/test/c/mx/mx_directive.h @@ -0,0 +1,46 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_DIRECTIVE_H_INCLUDED +#define MX_DIRECTIVE_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_tenths.h" + +typedef struct { + bool has_xml_lang; + char *xml_lang; /* attribute xml:lang */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + char *value; /* text content */ +} MxDirective; + +/* NULL on error; the message is in mx_error(). */ +MxDirective *mx_directive_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_directive_serialize(const MxDirective *m, xmlNodePtr parent, const char *tag); +void mx_directive_free(MxDirective *m); + +#endif /* MX_DIRECTIVE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_distance.c b/gen/test/c/mx/mx_distance.c new file mode 100644 index 000000000..bb25815ba --- /dev/null +++ b/gen/test/c/mx/mx_distance.c @@ -0,0 +1,75 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_distance.h" + +#include "mx_runtime.h" +#include +#include + +MxDistance *mx_distance_parse(xmlNodePtr el) { + MxDistance *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_distance_type_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_distance_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_tenths_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_distance_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_distance_serialize(const MxDistance *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + { + char *s = mx_tenths_to_string(m->value); + xmlAddChild(el, xmlNewText(BAD_CAST s)); + free(s); + } + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST m->type); + } + return el; +} + +void mx_distance_free(MxDistance *m) { + if (!m) + return; + if (m->has_type) + free(m->type); + free(m); +} diff --git a/gen/test/c/mx/mx_distance.h b/gen/test/c/mx/mx_distance.h new file mode 100644 index 000000000..afe49c02e --- /dev/null +++ b/gen/test/c/mx/mx_distance.h @@ -0,0 +1,29 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_DISTANCE_H_INCLUDED +#define MX_DISTANCE_H_INCLUDED + +#include +#include +#include +#include "mx_distance_type.h" +#include "mx_tenths.h" + +/* + * The distance element represents standard distances between notation elements in tenths. The type + * attribute defines what type of distance is being defined. Valid values include hyphen (for + * hyphens in lyrics) and beam. + */ +typedef struct { + bool has_type; + MxDistanceType type; /* attribute type */ + MxTenths value; /* text content */ +} MxDistance; + +/* NULL on error; the message is in mx_error(). */ +MxDistance *mx_distance_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_distance_serialize(const MxDistance *m, xmlNodePtr parent, const char *tag); +void mx_distance_free(MxDistance *m); + +#endif /* MX_DISTANCE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_document.c b/gen/test/c/mx/mx_document.c new file mode 100644 index 000000000..7cb3005b9 --- /dev/null +++ b/gen/test/c/mx/mx_document.c @@ -0,0 +1,83 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_document.h" + +#include "mx_runtime.h" +#include +#include + +MxDocument *mx_document_from_xdoc(xmlDocPtr doc) { + xmlNodePtr root = xmlDocGetRootElement(doc); + if (!root) { + mx_error_set("document has no root element"); + return NULL; + } + MxDocument *d = calloc(1, sizeof(*d)); + if (!d) + abort(); + size_t nscount = 0; + for (xmlNsPtr ns = root->nsDef; ns; ns = ns->next) + nscount++; + if (nscount) { + d->namespaces = calloc(nscount, sizeof(*d->namespaces)); + if (!d->namespaces) + abort(); + for (xmlNsPtr ns = root->nsDef; ns; ns = ns->next) { + MxNamespace *slot = &d->namespaces[d->namespaces_count++]; + slot->prefix = ns->prefix ? mx_strdup((const char *)ns->prefix) : NULL; + slot->href = mx_strdup((const char *)ns->href); + } + } + const char *tag = (const char *)root->name; + if (strcmp(tag, "score-partwise") == 0) { + d->score_partwise = mx_score_partwise_parse(root); + if (!d->score_partwise) { + mx_document_free(d); + return NULL; + } + } else if (strcmp(tag, "score-timewise") == 0) { + d->score_timewise = mx_score_timewise_parse(root); + if (!d->score_timewise) { + mx_document_free(d); + return NULL; + } + } else { + mx_error_set("unknown root element <%s>", tag); + mx_document_free(d); + return NULL; + } + return d; +} + +int mx_document_to_xdoc(const MxDocument *d, xmlDocPtr *out) { + *out = NULL; + xmlNodePtr root = NULL; + if (d->score_partwise) + root = mx_score_partwise_serialize(d->score_partwise, NULL, "score-partwise"); + else if (d->score_timewise) + root = mx_score_timewise_serialize(d->score_timewise, NULL, "score-timewise"); + if (!root) { + mx_error_set("document has no root"); + return -1; + } + xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); + xmlDocSetRootElement(doc, root); + for (size_t i = 0; i < d->namespaces_count; i++) + xmlNewNs(root, BAD_CAST d->namespaces[i].href, + BAD_CAST d->namespaces[i].prefix); + *out = doc; + return 0; +} + +void mx_document_free(MxDocument *d) { + if (!d) + return; + mx_score_partwise_free(d->score_partwise); + mx_score_timewise_free(d->score_timewise); + for (size_t i = 0; i < d->namespaces_count; i++) { + free(d->namespaces[i].prefix); + free(d->namespaces[i].href); + } + free(d->namespaces); + free(d); +} diff --git a/gen/test/c/mx/mx_document.h b/gen/test/c/mx/mx_document.h new file mode 100644 index 000000000..62c237da2 --- /dev/null +++ b/gen/test/c/mx/mx_document.h @@ -0,0 +1,31 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_DOCUMENT_H_INCLUDED +#define MX_DOCUMENT_H_INCLUDED + +#include +#include +#include "mx_score_partwise.h" +#include "mx_score_timewise.h" + +/* A namespace declaration preserved from the document root. */ +typedef struct { + char *prefix; /* NULL for the default namespace */ + char *href; +} MxNamespace; + +/* A parsed MusicXML document: exactly one root is set. */ +typedef struct { + MxScorePartwise *score_partwise; + MxScoreTimewise *score_timewise; + MxNamespace *namespaces; + size_t namespaces_count; +} MxDocument; + +/* NULL on error; the message is in mx_error(). */ +MxDocument *mx_document_from_xdoc(xmlDocPtr doc); +/* 0 on success; the caller owns *out. */ +int mx_document_to_xdoc(const MxDocument *d, xmlDocPtr *out); +void mx_document_free(MxDocument *d); + +#endif /* MX_DOCUMENT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_dynamics.c b/gen/test/c/mx/mx_dynamics.c new file mode 100644 index 000000000..4e39eb067 --- /dev/null +++ b/gen/test/c/mx/mx_dynamics.c @@ -0,0 +1,471 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_dynamics.h" + +#include "mx_runtime.h" +#include +#include + +MxDynamics *mx_dynamics_parse(xmlNodePtr el) { + MxDynamics *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "underline") == 0) { + m->has_underline = true; + m->underline = mx_number_of_lines_parse(s); + } else if (strcmp(aname, "overline") == 0) { + m->has_overline = true; + m->overline = mx_number_of_lines_parse(s); + } else if (strcmp(aname, "line-through") == 0) { + m->has_line_through = true; + m->line_through = mx_number_of_lines_parse(s); + } else if (strcmp(aname, "enclosure") == 0) { + m->has_enclosure = true; + m->enclosure = mx_enclosure_shape_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_dynamics_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxDynamicsChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "p") == 0) { + ch->p = mx_empty_parse(c); + if (!ch->p) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "pp") == 0) { + ch->pp = mx_empty_parse(c); + if (!ch->pp) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "ppp") == 0) { + ch->ppp = mx_empty_parse(c); + if (!ch->ppp) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "pppp") == 0) { + ch->pppp = mx_empty_parse(c); + if (!ch->pppp) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "ppppp") == 0) { + ch->ppppp = mx_empty_parse(c); + if (!ch->ppppp) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "pppppp") == 0) { + ch->pppppp = mx_empty_parse(c); + if (!ch->pppppp) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "f") == 0) { + ch->f = mx_empty_parse(c); + if (!ch->f) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "ff") == 0) { + ch->ff = mx_empty_parse(c); + if (!ch->ff) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "fff") == 0) { + ch->fff = mx_empty_parse(c); + if (!ch->fff) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "ffff") == 0) { + ch->ffff = mx_empty_parse(c); + if (!ch->ffff) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "fffff") == 0) { + ch->fffff = mx_empty_parse(c); + if (!ch->fffff) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "ffffff") == 0) { + ch->ffffff = mx_empty_parse(c); + if (!ch->ffffff) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "mp") == 0) { + ch->mp = mx_empty_parse(c); + if (!ch->mp) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "mf") == 0) { + ch->mf = mx_empty_parse(c); + if (!ch->mf) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "sf") == 0) { + ch->sf = mx_empty_parse(c); + if (!ch->sf) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "sfp") == 0) { + ch->sfp = mx_empty_parse(c); + if (!ch->sfp) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "sfpp") == 0) { + ch->sfpp = mx_empty_parse(c); + if (!ch->sfpp) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "fp") == 0) { + ch->fp = mx_empty_parse(c); + if (!ch->fp) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "rf") == 0) { + ch->rf = mx_empty_parse(c); + if (!ch->rf) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "rfz") == 0) { + ch->rfz = mx_empty_parse(c); + if (!ch->rfz) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "sfz") == 0) { + ch->sfz = mx_empty_parse(c); + if (!ch->sfz) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "sffz") == 0) { + ch->sffz = mx_empty_parse(c); + if (!ch->sffz) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "fz") == 0) { + ch->fz = mx_empty_parse(c); + if (!ch->fz) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "n") == 0) { + ch->n = mx_empty_parse(c); + if (!ch->n) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "pf") == 0) { + ch->pf = mx_empty_parse(c); + if (!ch->pf) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "sfzp") == 0) { + ch->sfzp = mx_empty_parse(c); + if (!ch->sfzp) { + mx_dynamics_free(m); + return NULL; + } + } else if (strcmp(tag, "other-dynamics") == 0) { + ch->other_dynamics = mx_other_text_parse(c); + if (!ch->other_dynamics) { + mx_dynamics_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_dynamics_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_dynamics_serialize(const MxDynamics *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_underline) { + char *s = mx_number_of_lines_to_string(m->underline); + xmlSetProp(el, BAD_CAST "underline", BAD_CAST s); + free(s); + } + if (m->has_overline) { + char *s = mx_number_of_lines_to_string(m->overline); + xmlSetProp(el, BAD_CAST "overline", BAD_CAST s); + free(s); + } + if (m->has_line_through) { + char *s = mx_number_of_lines_to_string(m->line_through); + xmlSetProp(el, BAD_CAST "line-through", BAD_CAST s); + free(s); + } + if (m->has_enclosure) { + xmlSetProp(el, BAD_CAST "enclosure", BAD_CAST mx_enclosure_shape_to_string(m->enclosure)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxDynamicsChild *ch = &m->children[i]; + if (ch->p) { + mx_empty_serialize(ch->p, el, "p"); + } else if (ch->pp) { + mx_empty_serialize(ch->pp, el, "pp"); + } else if (ch->ppp) { + mx_empty_serialize(ch->ppp, el, "ppp"); + } else if (ch->pppp) { + mx_empty_serialize(ch->pppp, el, "pppp"); + } else if (ch->ppppp) { + mx_empty_serialize(ch->ppppp, el, "ppppp"); + } else if (ch->pppppp) { + mx_empty_serialize(ch->pppppp, el, "pppppp"); + } else if (ch->f) { + mx_empty_serialize(ch->f, el, "f"); + } else if (ch->ff) { + mx_empty_serialize(ch->ff, el, "ff"); + } else if (ch->fff) { + mx_empty_serialize(ch->fff, el, "fff"); + } else if (ch->ffff) { + mx_empty_serialize(ch->ffff, el, "ffff"); + } else if (ch->fffff) { + mx_empty_serialize(ch->fffff, el, "fffff"); + } else if (ch->ffffff) { + mx_empty_serialize(ch->ffffff, el, "ffffff"); + } else if (ch->mp) { + mx_empty_serialize(ch->mp, el, "mp"); + } else if (ch->mf) { + mx_empty_serialize(ch->mf, el, "mf"); + } else if (ch->sf) { + mx_empty_serialize(ch->sf, el, "sf"); + } else if (ch->sfp) { + mx_empty_serialize(ch->sfp, el, "sfp"); + } else if (ch->sfpp) { + mx_empty_serialize(ch->sfpp, el, "sfpp"); + } else if (ch->fp) { + mx_empty_serialize(ch->fp, el, "fp"); + } else if (ch->rf) { + mx_empty_serialize(ch->rf, el, "rf"); + } else if (ch->rfz) { + mx_empty_serialize(ch->rfz, el, "rfz"); + } else if (ch->sfz) { + mx_empty_serialize(ch->sfz, el, "sfz"); + } else if (ch->sffz) { + mx_empty_serialize(ch->sffz, el, "sffz"); + } else if (ch->fz) { + mx_empty_serialize(ch->fz, el, "fz"); + } else if (ch->n) { + mx_empty_serialize(ch->n, el, "n"); + } else if (ch->pf) { + mx_empty_serialize(ch->pf, el, "pf"); + } else if (ch->sfzp) { + mx_empty_serialize(ch->sfzp, el, "sfzp"); + } else if (ch->other_dynamics) { + mx_other_text_serialize(ch->other_dynamics, el, "other-dynamics"); + } + } + return el; +} + +void mx_dynamics_free(MxDynamics *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxDynamicsChild *ch = &m->children[i]; + if (ch->p) + mx_empty_free(ch->p); + if (ch->pp) + mx_empty_free(ch->pp); + if (ch->ppp) + mx_empty_free(ch->ppp); + if (ch->pppp) + mx_empty_free(ch->pppp); + if (ch->ppppp) + mx_empty_free(ch->ppppp); + if (ch->pppppp) + mx_empty_free(ch->pppppp); + if (ch->f) + mx_empty_free(ch->f); + if (ch->ff) + mx_empty_free(ch->ff); + if (ch->fff) + mx_empty_free(ch->fff); + if (ch->ffff) + mx_empty_free(ch->ffff); + if (ch->fffff) + mx_empty_free(ch->fffff); + if (ch->ffffff) + mx_empty_free(ch->ffffff); + if (ch->mp) + mx_empty_free(ch->mp); + if (ch->mf) + mx_empty_free(ch->mf); + if (ch->sf) + mx_empty_free(ch->sf); + if (ch->sfp) + mx_empty_free(ch->sfp); + if (ch->sfpp) + mx_empty_free(ch->sfpp); + if (ch->fp) + mx_empty_free(ch->fp); + if (ch->rf) + mx_empty_free(ch->rf); + if (ch->rfz) + mx_empty_free(ch->rfz); + if (ch->sfz) + mx_empty_free(ch->sfz); + if (ch->sffz) + mx_empty_free(ch->sffz); + if (ch->fz) + mx_empty_free(ch->fz); + if (ch->n) + mx_empty_free(ch->n); + if (ch->pf) + mx_empty_free(ch->pf); + if (ch->sfzp) + mx_empty_free(ch->sfzp); + if (ch->other_dynamics) + mx_other_text_free(ch->other_dynamics); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_dynamics.h b/gen/test/c/mx/mx_dynamics.h new file mode 100644 index 000000000..f3ac316f0 --- /dev/null +++ b/gen/test/c/mx/mx_dynamics.h @@ -0,0 +1,115 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_DYNAMICS_H_INCLUDED +#define MX_DYNAMICS_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_empty.h" +#include "mx_enclosure_shape.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_center_right.h" +#include "mx_number_of_lines.h" +#include "mx_other_text.h" +#include "mx_tenths.h" +#include "mx_valign.h" + +/* + * Dynamics can be associated either with a note or a general musical direction. To avoid + * inconsistencies between and amongst the letter abbreviations for dynamics (what is sf vs. sfz, + * standing alone or with a trailing dynamic that is not always piano), we use the actual letters as + * the names of these dynamic elements. The other-dynamics element allows other dynamic marks that + * are not covered here, but many of those should perhaps be included in a more general musical + * direction element. Dynamics elements may also be combined to create marks not covered by a single + * element, such as sfmp. These letter dynamic symbols are separated from crescendo, decrescendo, + * and wedge indications. Dynamic representation is inconsistent in scores. Many things are assumed + * by the composer and left out, such as returns to original dynamics. Systematic representations + * are quite complex: for example, Humdrum has at least 3 representation formats related to + * dynamics. The MusicXML format captures what is in the score, but does not try to be optimal for + * analysis or synthesis of dynamics. + */ +/* One child element of MxDynamics: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxEmpty *p; + MxEmpty *pp; + MxEmpty *ppp; + MxEmpty *pppp; + MxEmpty *ppppp; + MxEmpty *pppppp; + MxEmpty *f; + MxEmpty *ff; + MxEmpty *fff; + MxEmpty *ffff; + MxEmpty *fffff; + MxEmpty *ffffff; + MxEmpty *mp; + MxEmpty *mf; + MxEmpty *sf; + MxEmpty *sfp; + MxEmpty *sfpp; + MxEmpty *fp; + MxEmpty *rf; + MxEmpty *rfz; + MxEmpty *sfz; + MxEmpty *sffz; + MxEmpty *fz; + MxEmpty *n; + MxEmpty *pf; + MxEmpty *sfzp; + MxOtherText *other_dynamics; +} MxDynamicsChild; + +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_underline; + MxNumberOfLines underline; /* attribute underline */ + bool has_overline; + MxNumberOfLines overline; /* attribute overline */ + bool has_line_through; + MxNumberOfLines line_through; /* attribute line-through */ + bool has_enclosure; + MxEnclosureShape enclosure; /* attribute enclosure */ + bool has_id; + char *id; /* attribute id */ + MxDynamicsChild *children; /* child elements in document order */ + size_t children_count; +} MxDynamics; + +/* NULL on error; the message is in mx_error(). */ +MxDynamics *mx_dynamics_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_dynamics_serialize(const MxDynamics *m, xmlNodePtr parent, const char *tag); +void mx_dynamics_free(MxDynamics *m); + +#endif /* MX_DYNAMICS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_elision.c b/gen/test/c/mx/mx_elision.c new file mode 100644 index 000000000..189376604 --- /dev/null +++ b/gen/test/c/mx/mx_elision.c @@ -0,0 +1,110 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_elision.h" + +#include "mx_runtime.h" +#include +#include + +MxElision *mx_elision_parse(xmlNodePtr el) { + MxElision *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "smufl") == 0) { + m->has_smufl = true; + m->smufl = mx_smufl_lyrics_glyph_name_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_elision_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_elision_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_elision_serialize(const MxElision *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_smufl) { + xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_elision_free(MxElision *m) { + if (!m) + return; + if (m->has_smufl) + free(m->smufl); + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_elision.h b/gen/test/c/mx/mx_elision.h new file mode 100644 index 000000000..85d988e31 --- /dev/null +++ b/gen/test/c/mx/mx_elision.h @@ -0,0 +1,47 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ELISION_H_INCLUDED +#define MX_ELISION_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_smufl_lyrics_glyph_name.h" + +/* + * The elision type represents an elision between lyric syllables. The text content specifies the + * symbol used to display the elision. Common values are a no-break space (Unicode 00A0), an + * underscore (Unicode 005F), or an undertie (Unicode 203F). If the text content is empty, the smufl + * attribute is used to specify the symbol to use. Its value is a SMuFL canonical glyph name that + * starts with lyrics. The SMuFL attribute is ignored if the elision glyph is already specified by + * the text content. If neither text content nor a smufl attribute are present, the elision glyph is + * application-specific. + */ +typedef struct { + bool has_smufl; + MxSMUFLLyricsGlyphName smufl; /* attribute smufl */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + char *value; /* text content */ +} MxElision; + +/* NULL on error; the message is in mx_error(). */ +MxElision *mx_elision_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_elision_serialize(const MxElision *m, xmlNodePtr parent, const char *tag); +void mx_elision_free(MxElision *m); + +#endif /* MX_ELISION_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_empty.c b/gen/test/c/mx/mx_empty.c new file mode 100644 index 000000000..eb93c7bd2 --- /dev/null +++ b/gen/test/c/mx/mx_empty.c @@ -0,0 +1,57 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_empty.h" + +#include "mx_runtime.h" +#include +#include + +MxEmpty *mx_empty_parse(xmlNodePtr el) { + MxEmpty *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_empty_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_empty_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_empty_serialize(const MxEmpty *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + (void)m; + return el; +} + +void mx_empty_free(MxEmpty *m) { + if (!m) + return; + free(m); +} diff --git a/gen/test/c/mx/mx_empty.h b/gen/test/c/mx/mx_empty.h new file mode 100644 index 000000000..af95e1aab --- /dev/null +++ b/gen/test/c/mx/mx_empty.h @@ -0,0 +1,23 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_EMPTY_H_INCLUDED +#define MX_EMPTY_H_INCLUDED + +#include +#include +#include + +/* + * The empty type represents an empty element with no attributes. + */ +typedef struct { + char unused; /* presence is the only information */ +} MxEmpty; + +/* NULL on error; the message is in mx_error(). */ +MxEmpty *mx_empty_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_empty_serialize(const MxEmpty *m, xmlNodePtr parent, const char *tag); +void mx_empty_free(MxEmpty *m); + +#endif /* MX_EMPTY_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_empty_font.c b/gen/test/c/mx/mx_empty_font.c new file mode 100644 index 000000000..60a768a10 --- /dev/null +++ b/gen/test/c/mx/mx_empty_font.c @@ -0,0 +1,86 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_empty_font.h" + +#include "mx_runtime.h" +#include +#include + +MxEmptyFont *mx_empty_font_parse(xmlNodePtr el) { + MxEmptyFont *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_empty_font_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_empty_font_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_empty_font_serialize(const MxEmptyFont *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + return el; +} + +void mx_empty_font_free(MxEmptyFont *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + free(m); +} diff --git a/gen/test/c/mx/mx_empty_font.h b/gen/test/c/mx/mx_empty_font.h new file mode 100644 index 000000000..c404bbc4c --- /dev/null +++ b/gen/test/c/mx/mx_empty_font.h @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_EMPTY_FONT_H_INCLUDED +#define MX_EMPTY_FONT_H_INCLUDED + +#include +#include +#include +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" + +/* + * The empty-font type represents an empty element with font attributes. + */ +typedef struct { + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ +} MxEmptyFont; + +/* NULL on error; the message is in mx_error(). */ +MxEmptyFont *mx_empty_font_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_empty_font_serialize(const MxEmptyFont *m, xmlNodePtr parent, const char *tag); +void mx_empty_font_free(MxEmptyFont *m); + +#endif /* MX_EMPTY_FONT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_empty_line.c b/gen/test/c/mx/mx_empty_line.c new file mode 100644 index 000000000..d4cd22111 --- /dev/null +++ b/gen/test/c/mx/mx_empty_line.c @@ -0,0 +1,166 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_empty_line.h" + +#include "mx_runtime.h" +#include +#include + +MxEmptyLine *mx_empty_line_parse(xmlNodePtr el) { + MxEmptyLine *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "line-shape") == 0) { + m->has_line_shape = true; + m->line_shape = mx_line_shape_parse(s); + } else if (strcmp(aname, "line-type") == 0) { + m->has_line_type = true; + m->line_type = mx_line_type_parse(s); + } else if (strcmp(aname, "line-length") == 0) { + m->has_line_length = true; + m->line_length = mx_line_length_parse(s); + } else if (strcmp(aname, "dash-length") == 0) { + m->has_dash_length = true; + m->dash_length = mx_tenths_parse(s); + } else if (strcmp(aname, "space-length") == 0) { + m->has_space_length = true; + m->space_length = mx_tenths_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_empty_line_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_empty_line_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_empty_line_serialize(const MxEmptyLine *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_line_shape) { + xmlSetProp(el, BAD_CAST "line-shape", BAD_CAST mx_line_shape_to_string(m->line_shape)); + } + if (m->has_line_type) { + xmlSetProp(el, BAD_CAST "line-type", BAD_CAST mx_line_type_to_string(m->line_type)); + } + if (m->has_line_length) { + xmlSetProp(el, BAD_CAST "line-length", BAD_CAST mx_line_length_to_string(m->line_length)); + } + if (m->has_dash_length) { + char *s = mx_tenths_to_string(m->dash_length); + xmlSetProp(el, BAD_CAST "dash-length", BAD_CAST s); + free(s); + } + if (m->has_space_length) { + char *s = mx_tenths_to_string(m->space_length); + xmlSetProp(el, BAD_CAST "space-length", BAD_CAST s); + free(s); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + return el; +} + +void mx_empty_line_free(MxEmptyLine *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_empty_line.h b/gen/test/c/mx/mx_empty_line.h new file mode 100644 index 000000000..e2452d0e3 --- /dev/null +++ b/gen/test/c/mx/mx_empty_line.h @@ -0,0 +1,63 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_EMPTY_LINE_H_INCLUDED +#define MX_EMPTY_LINE_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_line_length.h" +#include "mx_line_shape.h" +#include "mx_line_type.h" +#include "mx_tenths.h" + +/* + * The empty-line type represents an empty element with line-shape, line-type, line-length, + * dashed-formatting, print-style and placement attributes. + */ +typedef struct { + bool has_line_shape; + MxLineShape line_shape; /* attribute line-shape */ + bool has_line_type; + MxLineType line_type; /* attribute line-type */ + bool has_line_length; + MxLineLength line_length; /* attribute line-length */ + bool has_dash_length; + MxTenths dash_length; /* attribute dash-length */ + bool has_space_length; + MxTenths space_length; /* attribute space-length */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ +} MxEmptyLine; + +/* NULL on error; the message is in mx_error(). */ +MxEmptyLine *mx_empty_line_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_empty_line_serialize(const MxEmptyLine *m, xmlNodePtr parent, const char *tag); +void mx_empty_line_free(MxEmptyLine *m); + +#endif /* MX_EMPTY_LINE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_empty_placement.c b/gen/test/c/mx/mx_empty_placement.c new file mode 100644 index 000000000..f1e48e881 --- /dev/null +++ b/gen/test/c/mx/mx_empty_placement.c @@ -0,0 +1,132 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_empty_placement.h" + +#include "mx_runtime.h" +#include +#include + +MxEmptyPlacement *mx_empty_placement_parse(xmlNodePtr el) { + MxEmptyPlacement *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_empty_placement_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_empty_placement_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_empty_placement_serialize(const MxEmptyPlacement *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + return el; +} + +void mx_empty_placement_free(MxEmptyPlacement *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_empty_placement.h b/gen/test/c/mx/mx_empty_placement.h new file mode 100644 index 000000000..2204d2647 --- /dev/null +++ b/gen/test/c/mx/mx_empty_placement.h @@ -0,0 +1,49 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_EMPTY_PLACEMENT_H_INCLUDED +#define MX_EMPTY_PLACEMENT_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_tenths.h" + +/* + * The empty-placement type represents an empty element with print-style and placement attributes. + */ +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ +} MxEmptyPlacement; + +/* NULL on error; the message is in mx_error(). */ +MxEmptyPlacement *mx_empty_placement_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_empty_placement_serialize(const MxEmptyPlacement *m, xmlNodePtr parent, const char *tag); +void mx_empty_placement_free(MxEmptyPlacement *m); + +#endif /* MX_EMPTY_PLACEMENT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_empty_placement_smufl.c b/gen/test/c/mx/mx_empty_placement_smufl.c new file mode 100644 index 000000000..591a06712 --- /dev/null +++ b/gen/test/c/mx/mx_empty_placement_smufl.c @@ -0,0 +1,140 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_empty_placement_smufl.h" + +#include "mx_runtime.h" +#include +#include + +MxEmptyPlacementSMUFL *mx_empty_placement_smufl_parse(xmlNodePtr el) { + MxEmptyPlacementSMUFL *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "smufl") == 0) { + m->has_smufl = true; + m->smufl = mx_smufl_glyph_name_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_empty_placement_smufl_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_empty_placement_smufl_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_empty_placement_smufl_serialize(const MxEmptyPlacementSMUFL *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_smufl) { + xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); + } + return el; +} + +void mx_empty_placement_smufl_free(MxEmptyPlacementSMUFL *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_smufl) + free(m->smufl); + free(m); +} diff --git a/gen/test/c/mx/mx_empty_placement_smufl.h b/gen/test/c/mx/mx_empty_placement_smufl.h new file mode 100644 index 000000000..9e8533064 --- /dev/null +++ b/gen/test/c/mx/mx_empty_placement_smufl.h @@ -0,0 +1,53 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_EMPTY_PLACEMENT_SMUFL_H_INCLUDED +#define MX_EMPTY_PLACEMENT_SMUFL_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_smufl_glyph_name.h" +#include "mx_tenths.h" + +/* + * The empty-placement-smufl type represents an empty element with print-style, placement, and smufl + * attributes. + */ +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_smufl; + MxSMUFLGlyphName smufl; /* attribute smufl */ +} MxEmptyPlacementSMUFL; + +/* NULL on error; the message is in mx_error(). */ +MxEmptyPlacementSMUFL *mx_empty_placement_smufl_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_empty_placement_smufl_serialize(const MxEmptyPlacementSMUFL *m, xmlNodePtr parent, const char *tag); +void mx_empty_placement_smufl_free(MxEmptyPlacementSMUFL *m); + +#endif /* MX_EMPTY_PLACEMENT_SMUFL_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_empty_print_object_style_align.c b/gen/test/c/mx/mx_empty_print_object_style_align.c new file mode 100644 index 000000000..cfb739b24 --- /dev/null +++ b/gen/test/c/mx/mx_empty_print_object_style_align.c @@ -0,0 +1,144 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_empty_print_object_style_align.h" + +#include "mx_runtime.h" +#include +#include + +MxEmptyPrintObjectStyleAlign *mx_empty_print_object_style_align_parse(xmlNodePtr el) { + MxEmptyPrintObjectStyleAlign *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "print-object") == 0) { + m->has_print_object = true; + m->print_object = mx_yes_no_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_empty_print_object_style_align_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_empty_print_object_style_align_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_empty_print_object_style_align_serialize(const MxEmptyPrintObjectStyleAlign *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_print_object) { + xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + return el; +} + +void mx_empty_print_object_style_align_free(MxEmptyPrintObjectStyleAlign *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_empty_print_object_style_align.h b/gen/test/c/mx/mx_empty_print_object_style_align.h new file mode 100644 index 000000000..7a671fb89 --- /dev/null +++ b/gen/test/c/mx/mx_empty_print_object_style_align.h @@ -0,0 +1,56 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_EMPTY_PRINT_OBJECT_STYLE_ALIGN_H_INCLUDED +#define MX_EMPTY_PRINT_OBJECT_STYLE_ALIGN_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_center_right.h" +#include "mx_tenths.h" +#include "mx_valign.h" +#include "mx_yes_no.h" + +/* + * The empty-print-style-align-object type represents an empty element with print-object and + * print-style-align attribute groups. + */ +typedef struct { + bool has_print_object; + MxYesNo print_object; /* attribute print-object */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ +} MxEmptyPrintObjectStyleAlign; + +/* NULL on error; the message is in mx_error(). */ +MxEmptyPrintObjectStyleAlign *mx_empty_print_object_style_align_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_empty_print_object_style_align_serialize(const MxEmptyPrintObjectStyleAlign *m, xmlNodePtr parent, const char *tag); +void mx_empty_print_object_style_align_free(MxEmptyPrintObjectStyleAlign *m); + +#endif /* MX_EMPTY_PRINT_OBJECT_STYLE_ALIGN_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_empty_print_style_align_id.c b/gen/test/c/mx/mx_empty_print_style_align_id.c new file mode 100644 index 000000000..69af92405 --- /dev/null +++ b/gen/test/c/mx/mx_empty_print_style_align_id.c @@ -0,0 +1,146 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_empty_print_style_align_id.h" + +#include "mx_runtime.h" +#include +#include + +MxEmptyPrintStyleAlignID *mx_empty_print_style_align_id_parse(xmlNodePtr el) { + MxEmptyPrintStyleAlignID *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_empty_print_style_align_id_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_empty_print_style_align_id_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_empty_print_style_align_id_serialize(const MxEmptyPrintStyleAlignID *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_empty_print_style_align_id_free(MxEmptyPrintStyleAlignID *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + free(m); +} diff --git a/gen/test/c/mx/mx_empty_print_style_align_id.h b/gen/test/c/mx/mx_empty_print_style_align_id.h new file mode 100644 index 000000000..e42b2aa0e --- /dev/null +++ b/gen/test/c/mx/mx_empty_print_style_align_id.h @@ -0,0 +1,55 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_EMPTY_PRINT_STYLE_ALIGN_ID_H_INCLUDED +#define MX_EMPTY_PRINT_STYLE_ALIGN_ID_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_center_right.h" +#include "mx_tenths.h" +#include "mx_valign.h" + +/* + * The empty-print-style-align-id type represents an empty element with print-style-align and + * optional-unique-id attribute groups. + */ +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ + bool has_id; + char *id; /* attribute id */ +} MxEmptyPrintStyleAlignID; + +/* NULL on error; the message is in mx_error(). */ +MxEmptyPrintStyleAlignID *mx_empty_print_style_align_id_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_empty_print_style_align_id_serialize(const MxEmptyPrintStyleAlignID *m, xmlNodePtr parent, const char *tag); +void mx_empty_print_style_align_id_free(MxEmptyPrintStyleAlignID *m); + +#endif /* MX_EMPTY_PRINT_STYLE_ALIGN_ID_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_empty_trill_sound.c b/gen/test/c/mx/mx_empty_trill_sound.c new file mode 100644 index 000000000..ecd3a0e3c --- /dev/null +++ b/gen/test/c/mx/mx_empty_trill_sound.c @@ -0,0 +1,180 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_empty_trill_sound.h" + +#include "mx_runtime.h" +#include +#include + +MxEmptyTrillSound *mx_empty_trill_sound_parse(xmlNodePtr el) { + MxEmptyTrillSound *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "start-note") == 0) { + m->has_start_note = true; + m->start_note = mx_start_note_parse(s); + } else if (strcmp(aname, "trill-step") == 0) { + m->has_trill_step = true; + m->trill_step = mx_trill_step_parse(s); + } else if (strcmp(aname, "two-note-turn") == 0) { + m->has_two_note_turn = true; + m->two_note_turn = mx_two_note_turn_parse(s); + } else if (strcmp(aname, "accelerate") == 0) { + m->has_accelerate = true; + m->accelerate = mx_yes_no_parse(s); + } else if (strcmp(aname, "beats") == 0) { + m->has_beats = true; + m->beats = mx_trill_beats_parse(s); + } else if (strcmp(aname, "second-beat") == 0) { + m->has_second_beat = true; + m->second_beat = mx_percent_parse(s); + } else if (strcmp(aname, "last-beat") == 0) { + m->has_last_beat = true; + m->last_beat = mx_percent_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_empty_trill_sound_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_empty_trill_sound_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_empty_trill_sound_serialize(const MxEmptyTrillSound *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_start_note) { + xmlSetProp(el, BAD_CAST "start-note", BAD_CAST mx_start_note_to_string(m->start_note)); + } + if (m->has_trill_step) { + xmlSetProp(el, BAD_CAST "trill-step", BAD_CAST mx_trill_step_to_string(m->trill_step)); + } + if (m->has_two_note_turn) { + xmlSetProp(el, BAD_CAST "two-note-turn", BAD_CAST mx_two_note_turn_to_string(m->two_note_turn)); + } + if (m->has_accelerate) { + xmlSetProp(el, BAD_CAST "accelerate", BAD_CAST mx_yes_no_to_string(m->accelerate)); + } + if (m->has_beats) { + char *s = mx_trill_beats_to_string(m->beats); + xmlSetProp(el, BAD_CAST "beats", BAD_CAST s); + free(s); + } + if (m->has_second_beat) { + char *s = mx_percent_to_string(m->second_beat); + xmlSetProp(el, BAD_CAST "second-beat", BAD_CAST s); + free(s); + } + if (m->has_last_beat) { + char *s = mx_percent_to_string(m->last_beat); + xmlSetProp(el, BAD_CAST "last-beat", BAD_CAST s); + free(s); + } + return el; +} + +void mx_empty_trill_sound_free(MxEmptyTrillSound *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_empty_trill_sound.h b/gen/test/c/mx/mx_empty_trill_sound.h new file mode 100644 index 000000000..741623a2d --- /dev/null +++ b/gen/test/c/mx/mx_empty_trill_sound.h @@ -0,0 +1,70 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_EMPTY_TRILL_SOUND_H_INCLUDED +#define MX_EMPTY_TRILL_SOUND_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_percent.h" +#include "mx_start_note.h" +#include "mx_tenths.h" +#include "mx_trill_beats.h" +#include "mx_trill_step.h" +#include "mx_two_note_turn.h" +#include "mx_yes_no.h" + +/* + * The empty-trill-sound type represents an empty element with print-style, placement, and + * trill-sound attributes. + */ +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_start_note; + MxStartNote start_note; /* attribute start-note */ + bool has_trill_step; + MxTrillStep trill_step; /* attribute trill-step */ + bool has_two_note_turn; + MxTwoNoteTurn two_note_turn; /* attribute two-note-turn */ + bool has_accelerate; + MxYesNo accelerate; /* attribute accelerate */ + bool has_beats; + MxTrillBeats beats; /* attribute beats */ + bool has_second_beat; + MxPercent second_beat; /* attribute second-beat */ + bool has_last_beat; + MxPercent last_beat; /* attribute last-beat */ +} MxEmptyTrillSound; + +/* NULL on error; the message is in mx_error(). */ +MxEmptyTrillSound *mx_empty_trill_sound_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_empty_trill_sound_serialize(const MxEmptyTrillSound *m, xmlNodePtr parent, const char *tag); +void mx_empty_trill_sound_free(MxEmptyTrillSound *m); + +#endif /* MX_EMPTY_TRILL_SOUND_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_encoding.c b/gen/test/c/mx/mx_encoding.c new file mode 100644 index 000000000..0ab7e0c1b --- /dev/null +++ b/gen/test/c/mx/mx_encoding.c @@ -0,0 +1,125 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_encoding.h" + +#include "mx_runtime.h" +#include +#include + +MxEncoding *mx_encoding_parse(xmlNodePtr el) { + MxEncoding *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_encoding_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxEncodingChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "encoding-date") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->encoding_date = malloc(sizeof(*ch->encoding_date)); + if (!ch->encoding_date) + abort(); + *ch->encoding_date = mx_yyyy_mm_dd_parse(s); + xmlFree(text); + } else if (strcmp(tag, "encoder") == 0) { + ch->encoder = mx_typed_text_parse(c); + if (!ch->encoder) { + mx_encoding_free(m); + return NULL; + } + } else if (strcmp(tag, "software") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->software = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "encoding-description") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->encoding_description = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "supports") == 0) { + ch->supports = mx_supports_parse(c); + if (!ch->supports) { + mx_encoding_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_encoding_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_encoding_serialize(const MxEncoding *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxEncodingChild *ch = &m->children[i]; + if (ch->encoding_date) { + xmlNewTextChild(el, NULL, BAD_CAST "encoding-date", BAD_CAST (*ch->encoding_date)); + } else if (ch->encoder) { + mx_typed_text_serialize(ch->encoder, el, "encoder"); + } else if (ch->software) { + xmlNewTextChild(el, NULL, BAD_CAST "software", BAD_CAST ch->software); + } else if (ch->encoding_description) { + xmlNewTextChild(el, NULL, BAD_CAST "encoding-description", BAD_CAST ch->encoding_description); + } else if (ch->supports) { + mx_supports_serialize(ch->supports, el, "supports"); + } + } + return el; +} + +void mx_encoding_free(MxEncoding *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxEncodingChild *ch = &m->children[i]; + if (ch->encoding_date) { + free((*ch->encoding_date)); + free(ch->encoding_date); + } + if (ch->encoder) + mx_typed_text_free(ch->encoder); + free(ch->software); + free(ch->encoding_description); + if (ch->supports) + mx_supports_free(ch->supports); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_encoding.h b/gen/test/c/mx/mx_encoding.h new file mode 100644 index 000000000..adaa5942f --- /dev/null +++ b/gen/test/c/mx/mx_encoding.h @@ -0,0 +1,41 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ENCODING_H_INCLUDED +#define MX_ENCODING_H_INCLUDED + +#include +#include +#include +#include "mx_supports.h" +#include "mx_typed_text.h" +#include "mx_yyyy_mm_dd.h" + +/* + * The encoding element contains information about who did the digital encoding, when, with what + * software, and in what aspects. Standard type values for the encoder element are music, words, and + * arrangement, but other types may be used. The type attribute is only needed when there are + * multiple encoder elements. + */ +/* One child element of MxEncoding: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxYyyyMmDd *encoding_date; + MxTypedText *encoder; + char *software; + char *encoding_description; + MxSupports *supports; +} MxEncodingChild; + +typedef struct { + MxEncodingChild *children; /* child elements in document order */ + size_t children_count; +} MxEncoding; + +/* NULL on error; the message is in mx_error(). */ +MxEncoding *mx_encoding_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_encoding_serialize(const MxEncoding *m, xmlNodePtr parent, const char *tag); +void mx_encoding_free(MxEncoding *m); + +#endif /* MX_ENCODING_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_ending.c b/gen/test/c/mx/mx_ending.c new file mode 100644 index 000000000..4f2469431 --- /dev/null +++ b/gen/test/c/mx/mx_ending.c @@ -0,0 +1,178 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_ending.h" + +#include "mx_runtime.h" +#include +#include + +MxEnding *mx_ending_parse(xmlNodePtr el) { + MxEnding *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_ending_number_parse(s); + } else if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_discontinue_parse(s); + } else if (strcmp(aname, "end-length") == 0) { + m->has_end_length = true; + m->end_length = mx_tenths_parse(s); + } else if (strcmp(aname, "text-x") == 0) { + m->has_text_x = true; + m->text_x = mx_tenths_parse(s); + } else if (strcmp(aname, "text-y") == 0) { + m->has_text_y = true; + m->text_y = mx_tenths_parse(s); + } else if (strcmp(aname, "print-object") == 0) { + m->has_print_object = true; + m->print_object = mx_yes_no_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_ending_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_ending_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_ending_serialize(const MxEnding *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_number) { + xmlSetProp(el, BAD_CAST "number", BAD_CAST m->number); + } + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_discontinue_to_string(m->type)); + } + if (m->has_end_length) { + char *s = mx_tenths_to_string(m->end_length); + xmlSetProp(el, BAD_CAST "end-length", BAD_CAST s); + free(s); + } + if (m->has_text_x) { + char *s = mx_tenths_to_string(m->text_x); + xmlSetProp(el, BAD_CAST "text-x", BAD_CAST s); + free(s); + } + if (m->has_text_y) { + char *s = mx_tenths_to_string(m->text_y); + xmlSetProp(el, BAD_CAST "text-y", BAD_CAST s); + free(s); + } + if (m->has_print_object) { + xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_ending_free(MxEnding *m) { + if (!m) + return; + if (m->has_number) + free(m->number); + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_ending.h b/gen/test/c/mx/mx_ending.h new file mode 100644 index 000000000..2c574ba2e --- /dev/null +++ b/gen/test/c/mx/mx_ending.h @@ -0,0 +1,74 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ENDING_H_INCLUDED +#define MX_ENDING_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_ending_number.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_start_stop_discontinue.h" +#include "mx_tenths.h" +#include "mx_yes_no.h" + +/* + * The ending type represents multiple (e.g. first and second) endings. Typically, the start type is + * associated with the left barline of the first measure in an ending. The stop and discontinue + * types are associated with the right barline of the last measure in an ending. Stop is used when + * the ending mark concludes with a downward jog, as is typical for first endings. Discontinue is + * used when there is no downward jog, as is typical for second endings that do not conclude a + * piece. The length of the jog can be specified using the end-length attribute. The text-x and + * text-y attributes are offsets that specify where the baseline of the start of the ending text + * appears, relative to the start of the ending line. The number attribute reflects the numeric + * values of what is under the ending line. Single endings such as "1" or comma-separated multiple + * endings such as "1,2" may be used. The ending element text is used when the text displayed in the + * ending is different than what appears in the number attribute. The print-object element is used + * to indicate when an ending is present but not printed, as is often the case for many parts in a + * full score. + */ +typedef struct { + bool has_number; + MxEndingNumber number; /* attribute number */ + bool has_type; + MxStartStopDiscontinue type; /* attribute type */ + bool has_end_length; + MxTenths end_length; /* attribute end-length */ + bool has_text_x; + MxTenths text_x; /* attribute text-x */ + bool has_text_y; + MxTenths text_y; /* attribute text-y */ + bool has_print_object; + MxYesNo print_object; /* attribute print-object */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + char *value; /* text content */ +} MxEnding; + +/* NULL on error; the message is in mx_error(). */ +MxEnding *mx_ending_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_ending_serialize(const MxEnding *m, xmlNodePtr parent, const char *tag); +void mx_ending_free(MxEnding *m); + +#endif /* MX_ENDING_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_extend.c b/gen/test/c/mx/mx_extend.c new file mode 100644 index 000000000..9a041faa6 --- /dev/null +++ b/gen/test/c/mx/mx_extend.c @@ -0,0 +1,102 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_extend.h" + +#include "mx_runtime.h" +#include +#include + +MxExtend *mx_extend_parse(xmlNodePtr el) { + MxExtend *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_continue_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_extend_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_extend_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_extend_serialize(const MxExtend *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_continue_to_string(m->type)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_extend_free(MxExtend *m) { + if (!m) + return; + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_extend.h b/gen/test/c/mx/mx_extend.h new file mode 100644 index 000000000..055355435 --- /dev/null +++ b/gen/test/c/mx/mx_extend.h @@ -0,0 +1,39 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_EXTEND_H_INCLUDED +#define MX_EXTEND_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_start_stop_continue.h" +#include "mx_tenths.h" + +/* + * The extend type represents lyric word extension / melisma lines as well as figured bass + * extensions. The optional type and position attributes are added in Version 3.0 to provide better + * formatting control. + */ +typedef struct { + bool has_type; + MxStartStopContinue type; /* attribute type */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_color; + MxColor color; /* attribute color */ +} MxExtend; + +/* NULL on error; the message is in mx_error(). */ +MxExtend *mx_extend_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_extend_serialize(const MxExtend *m, xmlNodePtr parent, const char *tag); +void mx_extend_free(MxExtend *m); + +#endif /* MX_EXTEND_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_feature.c b/gen/test/c/mx/mx_feature.c new file mode 100644 index 000000000..9fd9791ad --- /dev/null +++ b/gen/test/c/mx/mx_feature.c @@ -0,0 +1,72 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_feature.h" + +#include "mx_runtime.h" +#include +#include + +MxFeature *mx_feature_parse(xmlNodePtr el) { + MxFeature *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_feature_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_feature_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_feature_serialize(const MxFeature *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST m->type); + } + return el; +} + +void mx_feature_free(MxFeature *m) { + if (!m) + return; + if (m->has_type) + free(m->type); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_feature.h b/gen/test/c/mx/mx_feature.h new file mode 100644 index 000000000..a33eee6ec --- /dev/null +++ b/gen/test/c/mx/mx_feature.h @@ -0,0 +1,27 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_FEATURE_H_INCLUDED +#define MX_FEATURE_H_INCLUDED + +#include +#include +#include + +/* + * The feature type is a part of the grouping element used for musical analysis. The type attribute + * represents the type of the feature and the element content represents its value. This type is + * flexible to allow for different analyses. + */ +typedef struct { + bool has_type; + char *type; /* attribute type */ + char *value; /* text content */ +} MxFeature; + +/* NULL on error; the message is in mx_error(). */ +MxFeature *mx_feature_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_feature_serialize(const MxFeature *m, xmlNodePtr parent, const char *tag); +void mx_feature_free(MxFeature *m); + +#endif /* MX_FEATURE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_fermata.c b/gen/test/c/mx/mx_fermata.c new file mode 100644 index 000000000..d91e1d2b2 --- /dev/null +++ b/gen/test/c/mx/mx_fermata.c @@ -0,0 +1,147 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_fermata.h" + +#include "mx_runtime.h" +#include +#include + +MxFermata *mx_fermata_parse(xmlNodePtr el) { + MxFermata *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_upright_inverted_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_fermata_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_fermata_shape_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_fermata_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_fermata_serialize(const MxFermata *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_fermata_shape_to_string(m->value))); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_upright_inverted_to_string(m->type)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_fermata_free(MxFermata *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + free(m); +} diff --git a/gen/test/c/mx/mx_fermata.h b/gen/test/c/mx/mx_fermata.h new file mode 100644 index 000000000..a1706deb1 --- /dev/null +++ b/gen/test/c/mx/mx_fermata.h @@ -0,0 +1,54 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_FERMATA_H_INCLUDED +#define MX_FERMATA_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_fermata_shape.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_tenths.h" +#include "mx_upright_inverted.h" + +/* + * The fermata text content represents the shape of the fermata sign. An empty fermata element + * represents a normal fermata. The fermata type is upright if not specified. + */ +typedef struct { + bool has_type; + MxUprightInverted type; /* attribute type */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_id; + char *id; /* attribute id */ + MxFermataShape value; /* text content */ +} MxFermata; + +/* NULL on error; the message is in mx_error(). */ +MxFermata *mx_fermata_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_fermata_serialize(const MxFermata *m, xmlNodePtr parent, const char *tag); +void mx_fermata_free(MxFermata *m); + +#endif /* MX_FERMATA_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_figure.c b/gen/test/c/mx/mx_figure.c new file mode 100644 index 000000000..ff54452e4 --- /dev/null +++ b/gen/test/c/mx/mx_figure.c @@ -0,0 +1,135 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_figure.h" + +#include "mx_runtime.h" +#include +#include + +MxFigure *mx_figure_parse(xmlNodePtr el) { + MxFigure *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_figure_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxFigureChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "prefix") == 0) { + ch->prefix = mx_style_text_parse(c); + if (!ch->prefix) { + mx_figure_free(m); + return NULL; + } + } else if (strcmp(tag, "figure-number") == 0) { + ch->figure_number = mx_style_text_parse(c); + if (!ch->figure_number) { + mx_figure_free(m); + return NULL; + } + } else if (strcmp(tag, "suffix") == 0) { + ch->suffix = mx_style_text_parse(c); + if (!ch->suffix) { + mx_figure_free(m); + return NULL; + } + } else if (strcmp(tag, "extend") == 0) { + ch->extend = mx_extend_parse(c); + if (!ch->extend) { + mx_figure_free(m); + return NULL; + } + } else if (strcmp(tag, "footnote") == 0) { + ch->footnote = mx_formatted_text_parse(c); + if (!ch->footnote) { + mx_figure_free(m); + return NULL; + } + } else if (strcmp(tag, "level") == 0) { + ch->level = mx_level_parse(c); + if (!ch->level) { + mx_figure_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_figure_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_figure_serialize(const MxFigure *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxFigureChild *ch = &m->children[i]; + if (ch->prefix) { + mx_style_text_serialize(ch->prefix, el, "prefix"); + } else if (ch->figure_number) { + mx_style_text_serialize(ch->figure_number, el, "figure-number"); + } else if (ch->suffix) { + mx_style_text_serialize(ch->suffix, el, "suffix"); + } else if (ch->extend) { + mx_extend_serialize(ch->extend, el, "extend"); + } else if (ch->footnote) { + mx_formatted_text_serialize(ch->footnote, el, "footnote"); + } else if (ch->level) { + mx_level_serialize(ch->level, el, "level"); + } + } + return el; +} + +void mx_figure_free(MxFigure *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxFigureChild *ch = &m->children[i]; + if (ch->prefix) + mx_style_text_free(ch->prefix); + if (ch->figure_number) + mx_style_text_free(ch->figure_number); + if (ch->suffix) + mx_style_text_free(ch->suffix); + if (ch->extend) + mx_extend_free(ch->extend); + if (ch->footnote) + mx_formatted_text_free(ch->footnote); + if (ch->level) + mx_level_free(ch->level); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_figure.h b/gen/test/c/mx/mx_figure.h new file mode 100644 index 000000000..59950e7f9 --- /dev/null +++ b/gen/test/c/mx/mx_figure.h @@ -0,0 +1,40 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_FIGURE_H_INCLUDED +#define MX_FIGURE_H_INCLUDED + +#include +#include +#include +#include "mx_extend.h" +#include "mx_formatted_text.h" +#include "mx_level.h" +#include "mx_style_text.h" + +/* + * The figure type represents a single figure within a figured-bass element. + */ +/* One child element of MxFigure: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxStyleText *prefix; + MxStyleText *figure_number; + MxStyleText *suffix; + MxExtend *extend; + MxFormattedText *footnote; + MxLevel *level; +} MxFigureChild; + +typedef struct { + MxFigureChild *children; /* child elements in document order */ + size_t children_count; +} MxFigure; + +/* NULL on error; the message is in mx_error(). */ +MxFigure *mx_figure_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_figure_serialize(const MxFigure *m, xmlNodePtr parent, const char *tag); +void mx_figure_free(MxFigure *m); + +#endif /* MX_FIGURE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_figured_bass.c b/gen/test/c/mx/mx_figured_bass.c new file mode 100644 index 000000000..56109c028 --- /dev/null +++ b/gen/test/c/mx/mx_figured_bass.c @@ -0,0 +1,226 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_figured_bass.h" + +#include "mx_runtime.h" +#include +#include + +MxFiguredBass *mx_figured_bass_parse(xmlNodePtr el) { + MxFiguredBass *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "parentheses") == 0) { + m->has_parentheses = true; + m->parentheses = mx_yes_no_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "print-dot") == 0) { + m->has_print_dot = true; + m->print_dot = mx_yes_no_parse(s); + } else if (strcmp(aname, "print-lyric") == 0) { + m->has_print_lyric = true; + m->print_lyric = mx_yes_no_parse(s); + } else if (strcmp(aname, "print-object") == 0) { + m->has_print_object = true; + m->print_object = mx_yes_no_parse(s); + } else if (strcmp(aname, "print-spacing") == 0) { + m->has_print_spacing = true; + m->print_spacing = mx_yes_no_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_figured_bass_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxFiguredBassChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "figure") == 0) { + ch->figure = mx_figure_parse(c); + if (!ch->figure) { + mx_figured_bass_free(m); + return NULL; + } + } else if (strcmp(tag, "duration") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->duration = malloc(sizeof(*ch->duration)); + if (!ch->duration) + abort(); + *ch->duration = mx_positive_divisions_parse(s); + xmlFree(text); + } else if (strcmp(tag, "footnote") == 0) { + ch->footnote = mx_formatted_text_parse(c); + if (!ch->footnote) { + mx_figured_bass_free(m); + return NULL; + } + } else if (strcmp(tag, "level") == 0) { + ch->level = mx_level_parse(c); + if (!ch->level) { + mx_figured_bass_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_figured_bass_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_figured_bass_serialize(const MxFiguredBass *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_parentheses) { + xmlSetProp(el, BAD_CAST "parentheses", BAD_CAST mx_yes_no_to_string(m->parentheses)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_print_dot) { + xmlSetProp(el, BAD_CAST "print-dot", BAD_CAST mx_yes_no_to_string(m->print_dot)); + } + if (m->has_print_lyric) { + xmlSetProp(el, BAD_CAST "print-lyric", BAD_CAST mx_yes_no_to_string(m->print_lyric)); + } + if (m->has_print_object) { + xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); + } + if (m->has_print_spacing) { + xmlSetProp(el, BAD_CAST "print-spacing", BAD_CAST mx_yes_no_to_string(m->print_spacing)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxFiguredBassChild *ch = &m->children[i]; + if (ch->figure) { + mx_figure_serialize(ch->figure, el, "figure"); + } else if (ch->duration) { + char *s = mx_positive_divisions_to_string((*ch->duration)); + xmlNewTextChild(el, NULL, BAD_CAST "duration", BAD_CAST s); + free(s); + } else if (ch->footnote) { + mx_formatted_text_serialize(ch->footnote, el, "footnote"); + } else if (ch->level) { + mx_level_serialize(ch->level, el, "level"); + } + } + return el; +} + +void mx_figured_bass_free(MxFiguredBass *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxFiguredBassChild *ch = &m->children[i]; + if (ch->figure) + mx_figure_free(ch->figure); + free(ch->duration); + if (ch->footnote) + mx_formatted_text_free(ch->footnote); + if (ch->level) + mx_level_free(ch->level); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_figured_bass.h b/gen/test/c/mx/mx_figured_bass.h new file mode 100644 index 000000000..c3fab1609 --- /dev/null +++ b/gen/test/c/mx/mx_figured_bass.h @@ -0,0 +1,78 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_FIGURED_BASS_H_INCLUDED +#define MX_FIGURED_BASS_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_figure.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_formatted_text.h" +#include "mx_level.h" +#include "mx_positive_divisions.h" +#include "mx_tenths.h" +#include "mx_yes_no.h" + +/* + * The figured-bass element represents figured bass notation. Figured bass elements take their + * position from the first regular note (not a grace note or chord note) that follows in score + * order. The optional duration element is used to indicate changes of figures under a note. Figures + * are ordered from top to bottom. The value of parentheses is "no" if not present. + */ +/* One child element of MxFiguredBass: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxFigure *figure; + MxPositiveDivisions *duration; + MxFormattedText *footnote; + MxLevel *level; +} MxFiguredBassChild; + +typedef struct { + bool has_parentheses; + MxYesNo parentheses; /* attribute parentheses */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_print_dot; + MxYesNo print_dot; /* attribute print-dot */ + bool has_print_lyric; + MxYesNo print_lyric; /* attribute print-lyric */ + bool has_print_object; + MxYesNo print_object; /* attribute print-object */ + bool has_print_spacing; + MxYesNo print_spacing; /* attribute print-spacing */ + bool has_id; + char *id; /* attribute id */ + MxFiguredBassChild *children; /* child elements in document order */ + size_t children_count; +} MxFiguredBass; + +/* NULL on error; the message is in mx_error(). */ +MxFiguredBass *mx_figured_bass_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_figured_bass_serialize(const MxFiguredBass *m, xmlNodePtr parent, const char *tag); +void mx_figured_bass_free(MxFiguredBass *m); + +#endif /* MX_FIGURED_BASS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_fingering.c b/gen/test/c/mx/mx_fingering.c new file mode 100644 index 000000000..be98146d2 --- /dev/null +++ b/gen/test/c/mx/mx_fingering.c @@ -0,0 +1,152 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_fingering.h" + +#include "mx_runtime.h" +#include +#include + +MxFingering *mx_fingering_parse(xmlNodePtr el) { + MxFingering *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "substitution") == 0) { + m->has_substitution = true; + m->substitution = mx_yes_no_parse(s); + } else if (strcmp(aname, "alternate") == 0) { + m->has_alternate = true; + m->alternate = mx_yes_no_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_fingering_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_fingering_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_fingering_serialize(const MxFingering *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_substitution) { + xmlSetProp(el, BAD_CAST "substitution", BAD_CAST mx_yes_no_to_string(m->substitution)); + } + if (m->has_alternate) { + xmlSetProp(el, BAD_CAST "alternate", BAD_CAST mx_yes_no_to_string(m->alternate)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + return el; +} + +void mx_fingering_free(MxFingering *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_fingering.h b/gen/test/c/mx/mx_fingering.h new file mode 100644 index 000000000..d4cbcbfaf --- /dev/null +++ b/gen/test/c/mx/mx_fingering.h @@ -0,0 +1,58 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_FINGERING_H_INCLUDED +#define MX_FINGERING_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_tenths.h" +#include "mx_yes_no.h" + +/* + * Fingering is typically indicated 1,2,3,4,5. Multiple fingerings may be given, typically to + * substitute fingerings in the middle of a note. The substitution and alternate values are "no" if + * the attribute is not present. For guitar and other fretted instruments, the fingering element + * represents the fretting finger; the pluck element represents the plucking finger. + */ +typedef struct { + bool has_substitution; + MxYesNo substitution; /* attribute substitution */ + bool has_alternate; + MxYesNo alternate; /* attribute alternate */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + char *value; /* text content */ +} MxFingering; + +/* NULL on error; the message is in mx_error(). */ +MxFingering *mx_fingering_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_fingering_serialize(const MxFingering *m, xmlNodePtr parent, const char *tag); +void mx_fingering_free(MxFingering *m); + +#endif /* MX_FINGERING_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_first_fret.c b/gen/test/c/mx/mx_first_fret.c new file mode 100644 index 000000000..9c314ca46 --- /dev/null +++ b/gen/test/c/mx/mx_first_fret.c @@ -0,0 +1,81 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_first_fret.h" + +#include "mx_runtime.h" +#include +#include + +MxFirstFret *mx_first_fret_parse(xmlNodePtr el) { + MxFirstFret *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "text") == 0) { + m->has_text = true; + m->text = mx_strdup(s); + } else if (strcmp(aname, "location") == 0) { + m->has_location = true; + m->location = mx_left_right_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_first_fret_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_parse_int(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_first_fret_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_first_fret_serialize(const MxFirstFret *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + { + char *s = mx_format_int(m->value); + xmlAddChild(el, xmlNewText(BAD_CAST s)); + free(s); + } + if (m->has_text) { + xmlSetProp(el, BAD_CAST "text", BAD_CAST m->text); + } + if (m->has_location) { + xmlSetProp(el, BAD_CAST "location", BAD_CAST mx_left_right_to_string(m->location)); + } + return el; +} + +void mx_first_fret_free(MxFirstFret *m) { + if (!m) + return; + if (m->has_text) + free(m->text); + free(m); +} diff --git a/gen/test/c/mx/mx_first_fret.h b/gen/test/c/mx/mx_first_fret.h new file mode 100644 index 000000000..fe6614f32 --- /dev/null +++ b/gen/test/c/mx/mx_first_fret.h @@ -0,0 +1,31 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_FIRST_FRET_H_INCLUDED +#define MX_FIRST_FRET_H_INCLUDED + +#include +#include +#include +#include "mx_left_right.h" + +/* + * The first-fret type indicates which fret is shown in the top space of the frame; it is fret 1 if + * the element is not present. The optional text attribute indicates how this is represented in the + * fret diagram, while the location attribute indicates whether the text appears to the left or + * right of the frame. + */ +typedef struct { + bool has_text; + char *text; /* attribute text */ + bool has_location; + MxLeftRight location; /* attribute location */ + long value; /* text content */ +} MxFirstFret; + +/* NULL on error; the message is in mx_error(). */ +MxFirstFret *mx_first_fret_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_first_fret_serialize(const MxFirstFret *m, xmlNodePtr parent, const char *tag); +void mx_first_fret_free(MxFirstFret *m); + +#endif /* MX_FIRST_FRET_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_formatted_symbol_id.c b/gen/test/c/mx/mx_formatted_symbol_id.c new file mode 100644 index 000000000..e0f545378 --- /dev/null +++ b/gen/test/c/mx/mx_formatted_symbol_id.c @@ -0,0 +1,224 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_formatted_symbol_id.h" + +#include "mx_runtime.h" +#include +#include + +MxFormattedSymbolID *mx_formatted_symbol_id_parse(xmlNodePtr el) { + MxFormattedSymbolID *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "justify") == 0) { + m->has_justify = true; + m->justify = mx_left_center_right_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else if (strcmp(aname, "underline") == 0) { + m->has_underline = true; + m->underline = mx_number_of_lines_parse(s); + } else if (strcmp(aname, "overline") == 0) { + m->has_overline = true; + m->overline = mx_number_of_lines_parse(s); + } else if (strcmp(aname, "line-through") == 0) { + m->has_line_through = true; + m->line_through = mx_number_of_lines_parse(s); + } else if (strcmp(aname, "rotation") == 0) { + m->has_rotation = true; + m->rotation = mx_rotation_degrees_parse(s); + } else if (strcmp(aname, "letter-spacing") == 0) { + m->has_letter_spacing = true; + m->letter_spacing = mx_number_or_normal_parse(s); + } else if (strcmp(aname, "line-height") == 0) { + m->has_line_height = true; + m->line_height = mx_number_or_normal_parse(s); + } else if (strcmp(aname, "dir") == 0) { + m->has_dir = true; + m->dir = mx_text_direction_parse(s); + } else if (strcmp(aname, "enclosure") == 0) { + m->has_enclosure = true; + m->enclosure = mx_enclosure_shape_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_formatted_symbol_id_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_smufl_glyph_name_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_formatted_symbol_id_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_formatted_symbol_id_serialize(const MxFormattedSymbolID *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_justify) { + xmlSetProp(el, BAD_CAST "justify", BAD_CAST mx_left_center_right_to_string(m->justify)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + if (m->has_underline) { + char *s = mx_number_of_lines_to_string(m->underline); + xmlSetProp(el, BAD_CAST "underline", BAD_CAST s); + free(s); + } + if (m->has_overline) { + char *s = mx_number_of_lines_to_string(m->overline); + xmlSetProp(el, BAD_CAST "overline", BAD_CAST s); + free(s); + } + if (m->has_line_through) { + char *s = mx_number_of_lines_to_string(m->line_through); + xmlSetProp(el, BAD_CAST "line-through", BAD_CAST s); + free(s); + } + if (m->has_rotation) { + char *s = mx_rotation_degrees_to_string(m->rotation); + xmlSetProp(el, BAD_CAST "rotation", BAD_CAST s); + free(s); + } + if (m->has_letter_spacing) { + char *s = mx_number_or_normal_to_string(m->letter_spacing); + xmlSetProp(el, BAD_CAST "letter-spacing", BAD_CAST s); + free(s); + } + if (m->has_line_height) { + char *s = mx_number_or_normal_to_string(m->line_height); + xmlSetProp(el, BAD_CAST "line-height", BAD_CAST s); + free(s); + } + if (m->has_dir) { + xmlSetProp(el, BAD_CAST "dir", BAD_CAST mx_text_direction_to_string(m->dir)); + } + if (m->has_enclosure) { + xmlSetProp(el, BAD_CAST "enclosure", BAD_CAST mx_enclosure_shape_to_string(m->enclosure)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_formatted_symbol_id_free(MxFormattedSymbolID *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_letter_spacing) + mx_number_or_normal_free(&m->letter_spacing); + if (m->has_line_height) + mx_number_or_normal_free(&m->line_height); + if (m->has_id) + free(m->id); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_formatted_symbol_id.h b/gen/test/c/mx/mx_formatted_symbol_id.h new file mode 100644 index 000000000..9ff8afda2 --- /dev/null +++ b/gen/test/c/mx/mx_formatted_symbol_id.h @@ -0,0 +1,80 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_FORMATTED_SYMBOL_ID_H_INCLUDED +#define MX_FORMATTED_SYMBOL_ID_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_enclosure_shape.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_center_right.h" +#include "mx_number_of_lines.h" +#include "mx_number_or_normal.h" +#include "mx_rotation_degrees.h" +#include "mx_smufl_glyph_name.h" +#include "mx_tenths.h" +#include "mx_text_direction.h" +#include "mx_valign.h" + +/* + * The formatted-symbol-id type represents a SMuFL musical symbol element with formatting and id + * attributes. + */ +typedef struct { + bool has_justify; + MxLeftCenterRight justify; /* attribute justify */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ + bool has_underline; + MxNumberOfLines underline; /* attribute underline */ + bool has_overline; + MxNumberOfLines overline; /* attribute overline */ + bool has_line_through; + MxNumberOfLines line_through; /* attribute line-through */ + bool has_rotation; + MxRotationDegrees rotation; /* attribute rotation */ + bool has_letter_spacing; + MxNumberOrNormal letter_spacing; /* attribute letter-spacing */ + bool has_line_height; + MxNumberOrNormal line_height; /* attribute line-height */ + bool has_dir; + MxTextDirection dir; /* attribute dir */ + bool has_enclosure; + MxEnclosureShape enclosure; /* attribute enclosure */ + bool has_id; + char *id; /* attribute id */ + MxSMUFLGlyphName value; /* text content */ +} MxFormattedSymbolID; + +/* NULL on error; the message is in mx_error(). */ +MxFormattedSymbolID *mx_formatted_symbol_id_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_formatted_symbol_id_serialize(const MxFormattedSymbolID *m, xmlNodePtr parent, const char *tag); +void mx_formatted_symbol_id_free(MxFormattedSymbolID *m); + +#endif /* MX_FORMATTED_SYMBOL_ID_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_formatted_text.c b/gen/test/c/mx/mx_formatted_text.c new file mode 100644 index 000000000..3cf259fad --- /dev/null +++ b/gen/test/c/mx/mx_formatted_text.c @@ -0,0 +1,232 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_formatted_text.h" + +#include "mx_runtime.h" +#include +#include + +MxFormattedText *mx_formatted_text_parse(xmlNodePtr el) { + MxFormattedText *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "xml:lang") == 0) { + m->has_xml_lang = true; + m->xml_lang = mx_strdup(s); + } else if (strcmp(aname, "xml:space") == 0) { + m->has_xml_space = true; + m->xml_space = mx_strdup(s); + } else if (strcmp(aname, "justify") == 0) { + m->has_justify = true; + m->justify = mx_left_center_right_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else if (strcmp(aname, "underline") == 0) { + m->has_underline = true; + m->underline = mx_number_of_lines_parse(s); + } else if (strcmp(aname, "overline") == 0) { + m->has_overline = true; + m->overline = mx_number_of_lines_parse(s); + } else if (strcmp(aname, "line-through") == 0) { + m->has_line_through = true; + m->line_through = mx_number_of_lines_parse(s); + } else if (strcmp(aname, "rotation") == 0) { + m->has_rotation = true; + m->rotation = mx_rotation_degrees_parse(s); + } else if (strcmp(aname, "letter-spacing") == 0) { + m->has_letter_spacing = true; + m->letter_spacing = mx_number_or_normal_parse(s); + } else if (strcmp(aname, "line-height") == 0) { + m->has_line_height = true; + m->line_height = mx_number_or_normal_parse(s); + } else if (strcmp(aname, "dir") == 0) { + m->has_dir = true; + m->dir = mx_text_direction_parse(s); + } else if (strcmp(aname, "enclosure") == 0) { + m->has_enclosure = true; + m->enclosure = mx_enclosure_shape_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_formatted_text_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_formatted_text_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_formatted_text_serialize(const MxFormattedText *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_xml_lang) { + xmlSetProp(el, BAD_CAST "xml:lang", BAD_CAST m->xml_lang); + } + if (m->has_xml_space) { + xmlSetProp(el, BAD_CAST "xml:space", BAD_CAST m->xml_space); + } + if (m->has_justify) { + xmlSetProp(el, BAD_CAST "justify", BAD_CAST mx_left_center_right_to_string(m->justify)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + if (m->has_underline) { + char *s = mx_number_of_lines_to_string(m->underline); + xmlSetProp(el, BAD_CAST "underline", BAD_CAST s); + free(s); + } + if (m->has_overline) { + char *s = mx_number_of_lines_to_string(m->overline); + xmlSetProp(el, BAD_CAST "overline", BAD_CAST s); + free(s); + } + if (m->has_line_through) { + char *s = mx_number_of_lines_to_string(m->line_through); + xmlSetProp(el, BAD_CAST "line-through", BAD_CAST s); + free(s); + } + if (m->has_rotation) { + char *s = mx_rotation_degrees_to_string(m->rotation); + xmlSetProp(el, BAD_CAST "rotation", BAD_CAST s); + free(s); + } + if (m->has_letter_spacing) { + char *s = mx_number_or_normal_to_string(m->letter_spacing); + xmlSetProp(el, BAD_CAST "letter-spacing", BAD_CAST s); + free(s); + } + if (m->has_line_height) { + char *s = mx_number_or_normal_to_string(m->line_height); + xmlSetProp(el, BAD_CAST "line-height", BAD_CAST s); + free(s); + } + if (m->has_dir) { + xmlSetProp(el, BAD_CAST "dir", BAD_CAST mx_text_direction_to_string(m->dir)); + } + if (m->has_enclosure) { + xmlSetProp(el, BAD_CAST "enclosure", BAD_CAST mx_enclosure_shape_to_string(m->enclosure)); + } + return el; +} + +void mx_formatted_text_free(MxFormattedText *m) { + if (!m) + return; + if (m->has_xml_lang) + free(m->xml_lang); + if (m->has_xml_space) + free(m->xml_space); + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_letter_spacing) + mx_number_or_normal_free(&m->letter_spacing); + if (m->has_line_height) + mx_number_or_normal_free(&m->line_height); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_formatted_text.h b/gen/test/c/mx/mx_formatted_text.h new file mode 100644 index 000000000..1289b9da5 --- /dev/null +++ b/gen/test/c/mx/mx_formatted_text.h @@ -0,0 +1,80 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_FORMATTED_TEXT_H_INCLUDED +#define MX_FORMATTED_TEXT_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_enclosure_shape.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_center_right.h" +#include "mx_number_of_lines.h" +#include "mx_number_or_normal.h" +#include "mx_rotation_degrees.h" +#include "mx_tenths.h" +#include "mx_text_direction.h" +#include "mx_valign.h" + +/* + * The formatted-text type represents a text element with text-formatting attributes. + */ +typedef struct { + bool has_xml_lang; + char *xml_lang; /* attribute xml:lang */ + bool has_xml_space; + char *xml_space; /* attribute xml:space */ + bool has_justify; + MxLeftCenterRight justify; /* attribute justify */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ + bool has_underline; + MxNumberOfLines underline; /* attribute underline */ + bool has_overline; + MxNumberOfLines overline; /* attribute overline */ + bool has_line_through; + MxNumberOfLines line_through; /* attribute line-through */ + bool has_rotation; + MxRotationDegrees rotation; /* attribute rotation */ + bool has_letter_spacing; + MxNumberOrNormal letter_spacing; /* attribute letter-spacing */ + bool has_line_height; + MxNumberOrNormal line_height; /* attribute line-height */ + bool has_dir; + MxTextDirection dir; /* attribute dir */ + bool has_enclosure; + MxEnclosureShape enclosure; /* attribute enclosure */ + char *value; /* text content */ +} MxFormattedText; + +/* NULL on error; the message is in mx_error(). */ +MxFormattedText *mx_formatted_text_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_formatted_text_serialize(const MxFormattedText *m, xmlNodePtr parent, const char *tag); +void mx_formatted_text_free(MxFormattedText *m); + +#endif /* MX_FORMATTED_TEXT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_formatted_text_id.c b/gen/test/c/mx/mx_formatted_text_id.c new file mode 100644 index 000000000..81c7fa155 --- /dev/null +++ b/gen/test/c/mx/mx_formatted_text_id.c @@ -0,0 +1,240 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_formatted_text_id.h" + +#include "mx_runtime.h" +#include +#include + +MxFormattedTextID *mx_formatted_text_id_parse(xmlNodePtr el) { + MxFormattedTextID *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "xml:lang") == 0) { + m->has_xml_lang = true; + m->xml_lang = mx_strdup(s); + } else if (strcmp(aname, "xml:space") == 0) { + m->has_xml_space = true; + m->xml_space = mx_strdup(s); + } else if (strcmp(aname, "justify") == 0) { + m->has_justify = true; + m->justify = mx_left_center_right_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else if (strcmp(aname, "underline") == 0) { + m->has_underline = true; + m->underline = mx_number_of_lines_parse(s); + } else if (strcmp(aname, "overline") == 0) { + m->has_overline = true; + m->overline = mx_number_of_lines_parse(s); + } else if (strcmp(aname, "line-through") == 0) { + m->has_line_through = true; + m->line_through = mx_number_of_lines_parse(s); + } else if (strcmp(aname, "rotation") == 0) { + m->has_rotation = true; + m->rotation = mx_rotation_degrees_parse(s); + } else if (strcmp(aname, "letter-spacing") == 0) { + m->has_letter_spacing = true; + m->letter_spacing = mx_number_or_normal_parse(s); + } else if (strcmp(aname, "line-height") == 0) { + m->has_line_height = true; + m->line_height = mx_number_or_normal_parse(s); + } else if (strcmp(aname, "dir") == 0) { + m->has_dir = true; + m->dir = mx_text_direction_parse(s); + } else if (strcmp(aname, "enclosure") == 0) { + m->has_enclosure = true; + m->enclosure = mx_enclosure_shape_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_formatted_text_id_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_formatted_text_id_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_formatted_text_id_serialize(const MxFormattedTextID *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_xml_lang) { + xmlSetProp(el, BAD_CAST "xml:lang", BAD_CAST m->xml_lang); + } + if (m->has_xml_space) { + xmlSetProp(el, BAD_CAST "xml:space", BAD_CAST m->xml_space); + } + if (m->has_justify) { + xmlSetProp(el, BAD_CAST "justify", BAD_CAST mx_left_center_right_to_string(m->justify)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + if (m->has_underline) { + char *s = mx_number_of_lines_to_string(m->underline); + xmlSetProp(el, BAD_CAST "underline", BAD_CAST s); + free(s); + } + if (m->has_overline) { + char *s = mx_number_of_lines_to_string(m->overline); + xmlSetProp(el, BAD_CAST "overline", BAD_CAST s); + free(s); + } + if (m->has_line_through) { + char *s = mx_number_of_lines_to_string(m->line_through); + xmlSetProp(el, BAD_CAST "line-through", BAD_CAST s); + free(s); + } + if (m->has_rotation) { + char *s = mx_rotation_degrees_to_string(m->rotation); + xmlSetProp(el, BAD_CAST "rotation", BAD_CAST s); + free(s); + } + if (m->has_letter_spacing) { + char *s = mx_number_or_normal_to_string(m->letter_spacing); + xmlSetProp(el, BAD_CAST "letter-spacing", BAD_CAST s); + free(s); + } + if (m->has_line_height) { + char *s = mx_number_or_normal_to_string(m->line_height); + xmlSetProp(el, BAD_CAST "line-height", BAD_CAST s); + free(s); + } + if (m->has_dir) { + xmlSetProp(el, BAD_CAST "dir", BAD_CAST mx_text_direction_to_string(m->dir)); + } + if (m->has_enclosure) { + xmlSetProp(el, BAD_CAST "enclosure", BAD_CAST mx_enclosure_shape_to_string(m->enclosure)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_formatted_text_id_free(MxFormattedTextID *m) { + if (!m) + return; + if (m->has_xml_lang) + free(m->xml_lang); + if (m->has_xml_space) + free(m->xml_space); + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_letter_spacing) + mx_number_or_normal_free(&m->letter_spacing); + if (m->has_line_height) + mx_number_or_normal_free(&m->line_height); + if (m->has_id) + free(m->id); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_formatted_text_id.h b/gen/test/c/mx/mx_formatted_text_id.h new file mode 100644 index 000000000..140bfd109 --- /dev/null +++ b/gen/test/c/mx/mx_formatted_text_id.h @@ -0,0 +1,82 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_FORMATTED_TEXT_ID_H_INCLUDED +#define MX_FORMATTED_TEXT_ID_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_enclosure_shape.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_center_right.h" +#include "mx_number_of_lines.h" +#include "mx_number_or_normal.h" +#include "mx_rotation_degrees.h" +#include "mx_tenths.h" +#include "mx_text_direction.h" +#include "mx_valign.h" + +/* + * The formatted-text-id type represents a text element with text-formatting and id attributes. + */ +typedef struct { + bool has_xml_lang; + char *xml_lang; /* attribute xml:lang */ + bool has_xml_space; + char *xml_space; /* attribute xml:space */ + bool has_justify; + MxLeftCenterRight justify; /* attribute justify */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ + bool has_underline; + MxNumberOfLines underline; /* attribute underline */ + bool has_overline; + MxNumberOfLines overline; /* attribute overline */ + bool has_line_through; + MxNumberOfLines line_through; /* attribute line-through */ + bool has_rotation; + MxRotationDegrees rotation; /* attribute rotation */ + bool has_letter_spacing; + MxNumberOrNormal letter_spacing; /* attribute letter-spacing */ + bool has_line_height; + MxNumberOrNormal line_height; /* attribute line-height */ + bool has_dir; + MxTextDirection dir; /* attribute dir */ + bool has_enclosure; + MxEnclosureShape enclosure; /* attribute enclosure */ + bool has_id; + char *id; /* attribute id */ + char *value; /* text content */ +} MxFormattedTextID; + +/* NULL on error; the message is in mx_error(). */ +MxFormattedTextID *mx_formatted_text_id_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_formatted_text_id_serialize(const MxFormattedTextID *m, xmlNodePtr parent, const char *tag); +void mx_formatted_text_id_free(MxFormattedTextID *m); + +#endif /* MX_FORMATTED_TEXT_ID_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_forward.c b/gen/test/c/mx/mx_forward.c new file mode 100644 index 000000000..b8384d756 --- /dev/null +++ b/gen/test/c/mx/mx_forward.c @@ -0,0 +1,129 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_forward.h" + +#include "mx_runtime.h" +#include +#include + +MxForward *mx_forward_parse(xmlNodePtr el) { + MxForward *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_forward_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxForwardChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "duration") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->duration = malloc(sizeof(*ch->duration)); + if (!ch->duration) + abort(); + *ch->duration = mx_positive_divisions_parse(s); + xmlFree(text); + } else if (strcmp(tag, "footnote") == 0) { + ch->footnote = mx_formatted_text_parse(c); + if (!ch->footnote) { + mx_forward_free(m); + return NULL; + } + } else if (strcmp(tag, "level") == 0) { + ch->level = mx_level_parse(c); + if (!ch->level) { + mx_forward_free(m); + return NULL; + } + } else if (strcmp(tag, "voice") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->voice = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "staff") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->staff = malloc(sizeof(*ch->staff)); + if (!ch->staff) + abort(); + *ch->staff = mx_parse_int(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_forward_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_forward_serialize(const MxForward *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxForwardChild *ch = &m->children[i]; + if (ch->duration) { + char *s = mx_positive_divisions_to_string((*ch->duration)); + xmlNewTextChild(el, NULL, BAD_CAST "duration", BAD_CAST s); + free(s); + } else if (ch->footnote) { + mx_formatted_text_serialize(ch->footnote, el, "footnote"); + } else if (ch->level) { + mx_level_serialize(ch->level, el, "level"); + } else if (ch->voice) { + xmlNewTextChild(el, NULL, BAD_CAST "voice", BAD_CAST ch->voice); + } else if (ch->staff) { + char *s = mx_format_int((*ch->staff)); + xmlNewTextChild(el, NULL, BAD_CAST "staff", BAD_CAST s); + free(s); + } + } + return el; +} + +void mx_forward_free(MxForward *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxForwardChild *ch = &m->children[i]; + free(ch->duration); + if (ch->footnote) + mx_formatted_text_free(ch->footnote); + if (ch->level) + mx_level_free(ch->level); + free(ch->voice); + free(ch->staff); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_forward.h b/gen/test/c/mx/mx_forward.h new file mode 100644 index 000000000..36afe321f --- /dev/null +++ b/gen/test/c/mx/mx_forward.h @@ -0,0 +1,41 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_FORWARD_H_INCLUDED +#define MX_FORWARD_H_INCLUDED + +#include +#include +#include +#include "mx_formatted_text.h" +#include "mx_level.h" +#include "mx_positive_divisions.h" + +/* + * The backup and forward elements are required to coordinate multiple voices in one part, including + * music on multiple staves. The forward element is generally used within voices and staves. + * Duration values should always be positive, and should not cross measure boundaries or mid-measure + * changes in the divisions value. + */ +/* One child element of MxForward: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxPositiveDivisions *duration; + MxFormattedText *footnote; + MxLevel *level; + char *voice; + long *staff; +} MxForwardChild; + +typedef struct { + MxForwardChild *children; /* child elements in document order */ + size_t children_count; +} MxForward; + +/* NULL on error; the message is in mx_error(). */ +MxForward *mx_forward_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_forward_serialize(const MxForward *m, xmlNodePtr parent, const char *tag); +void mx_forward_free(MxForward *m); + +#endif /* MX_FORWARD_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_frame.c b/gen/test/c/mx/mx_frame.c new file mode 100644 index 000000000..4dccab886 --- /dev/null +++ b/gen/test/c/mx/mx_frame.c @@ -0,0 +1,205 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_frame.h" + +#include "mx_runtime.h" +#include +#include + +MxFrame *mx_frame_parse(xmlNodePtr el) { + MxFrame *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "height") == 0) { + m->has_height = true; + m->height = mx_tenths_parse(s); + } else if (strcmp(aname, "width") == 0) { + m->has_width = true; + m->width = mx_tenths_parse(s); + } else if (strcmp(aname, "unplayed") == 0) { + m->has_unplayed = true; + m->unplayed = mx_strdup(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_image_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_frame_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxFrameChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "frame-strings") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->frame_strings = malloc(sizeof(*ch->frame_strings)); + if (!ch->frame_strings) + abort(); + *ch->frame_strings = mx_parse_int(s); + xmlFree(text); + } else if (strcmp(tag, "frame-frets") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->frame_frets = malloc(sizeof(*ch->frame_frets)); + if (!ch->frame_frets) + abort(); + *ch->frame_frets = mx_parse_int(s); + xmlFree(text); + } else if (strcmp(tag, "first-fret") == 0) { + ch->first_fret = mx_first_fret_parse(c); + if (!ch->first_fret) { + mx_frame_free(m); + return NULL; + } + } else if (strcmp(tag, "frame-note") == 0) { + ch->frame_note = mx_frame_note_parse(c); + if (!ch->frame_note) { + mx_frame_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_frame_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_frame_serialize(const MxFrame *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_height) { + char *s = mx_tenths_to_string(m->height); + xmlSetProp(el, BAD_CAST "height", BAD_CAST s); + free(s); + } + if (m->has_width) { + char *s = mx_tenths_to_string(m->width); + xmlSetProp(el, BAD_CAST "width", BAD_CAST s); + free(s); + } + if (m->has_unplayed) { + xmlSetProp(el, BAD_CAST "unplayed", BAD_CAST m->unplayed); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_image_to_string(m->valign)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxFrameChild *ch = &m->children[i]; + if (ch->frame_strings) { + char *s = mx_format_int((*ch->frame_strings)); + xmlNewTextChild(el, NULL, BAD_CAST "frame-strings", BAD_CAST s); + free(s); + } else if (ch->frame_frets) { + char *s = mx_format_int((*ch->frame_frets)); + xmlNewTextChild(el, NULL, BAD_CAST "frame-frets", BAD_CAST s); + free(s); + } else if (ch->first_fret) { + mx_first_fret_serialize(ch->first_fret, el, "first-fret"); + } else if (ch->frame_note) { + mx_frame_note_serialize(ch->frame_note, el, "frame-note"); + } + } + return el; +} + +void mx_frame_free(MxFrame *m) { + if (!m) + return; + if (m->has_unplayed) + free(m->unplayed); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxFrameChild *ch = &m->children[i]; + free(ch->frame_strings); + free(ch->frame_frets); + if (ch->first_fret) + mx_first_fret_free(ch->first_fret); + if (ch->frame_note) + mx_frame_note_free(ch->frame_note); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_frame.h b/gen/test/c/mx/mx_frame.h new file mode 100644 index 000000000..499e74bd5 --- /dev/null +++ b/gen/test/c/mx/mx_frame.h @@ -0,0 +1,66 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_FRAME_H_INCLUDED +#define MX_FRAME_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_first_fret.h" +#include "mx_frame_note.h" +#include "mx_left_center_right.h" +#include "mx_tenths.h" +#include "mx_valign_image.h" + +/* + * The frame type represents a frame or fretboard diagram used together with a chord symbol. The + * representation is based on the NIFF guitar grid with additional information. The frame type's + * unplayed attribute indicates what to display above a string that has no associated frame-note + * element. Typical values are x and the empty string. If the attribute is not present, the display + * of the unplayed string is application-defined. + */ +/* One child element of MxFrame: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + long *frame_strings; + long *frame_frets; + MxFirstFret *first_fret; + MxFrameNote *frame_note; +} MxFrameChild; + +typedef struct { + bool has_height; + MxTenths height; /* attribute height */ + bool has_width; + MxTenths width; /* attribute width */ + bool has_unplayed; + char *unplayed; /* attribute unplayed */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValignImage valign; /* attribute valign */ + bool has_id; + char *id; /* attribute id */ + MxFrameChild *children; /* child elements in document order */ + size_t children_count; +} MxFrame; + +/* NULL on error; the message is in mx_error(). */ +MxFrame *mx_frame_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_frame_serialize(const MxFrame *m, xmlNodePtr parent, const char *tag); +void mx_frame_free(MxFrame *m); + +#endif /* MX_FRAME_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_frame_note.c b/gen/test/c/mx/mx_frame_note.c new file mode 100644 index 000000000..5ae31d874 --- /dev/null +++ b/gen/test/c/mx/mx_frame_note.c @@ -0,0 +1,115 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_frame_note.h" + +#include "mx_runtime.h" +#include +#include + +MxFrameNote *mx_frame_note_parse(xmlNodePtr el) { + MxFrameNote *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_frame_note_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxFrameNoteChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "string") == 0) { + ch->string = mx_string_parse(c); + if (!ch->string) { + mx_frame_note_free(m); + return NULL; + } + } else if (strcmp(tag, "fret") == 0) { + ch->fret = mx_fret_parse(c); + if (!ch->fret) { + mx_frame_note_free(m); + return NULL; + } + } else if (strcmp(tag, "fingering") == 0) { + ch->fingering = mx_fingering_parse(c); + if (!ch->fingering) { + mx_frame_note_free(m); + return NULL; + } + } else if (strcmp(tag, "barre") == 0) { + ch->barre = mx_barre_parse(c); + if (!ch->barre) { + mx_frame_note_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_frame_note_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_frame_note_serialize(const MxFrameNote *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxFrameNoteChild *ch = &m->children[i]; + if (ch->string) { + mx_string_serialize(ch->string, el, "string"); + } else if (ch->fret) { + mx_fret_serialize(ch->fret, el, "fret"); + } else if (ch->fingering) { + mx_fingering_serialize(ch->fingering, el, "fingering"); + } else if (ch->barre) { + mx_barre_serialize(ch->barre, el, "barre"); + } + } + return el; +} + +void mx_frame_note_free(MxFrameNote *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxFrameNoteChild *ch = &m->children[i]; + if (ch->string) + mx_string_free(ch->string); + if (ch->fret) + mx_fret_free(ch->fret); + if (ch->fingering) + mx_fingering_free(ch->fingering); + if (ch->barre) + mx_barre_free(ch->barre); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_frame_note.h b/gen/test/c/mx/mx_frame_note.h new file mode 100644 index 000000000..14067c2de --- /dev/null +++ b/gen/test/c/mx/mx_frame_note.h @@ -0,0 +1,39 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_FRAME_NOTE_H_INCLUDED +#define MX_FRAME_NOTE_H_INCLUDED + +#include +#include +#include +#include "mx_barre.h" +#include "mx_fingering.h" +#include "mx_fret.h" +#include "mx_string.h" + +/* + * The frame-note type represents each note included in the frame. An open string will have a fret + * value of 0, while a muted string will not be associated with a frame-note element. + */ +/* One child element of MxFrameNote: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxString *string; + MxFret *fret; + MxFingering *fingering; + MxBarre *barre; +} MxFrameNoteChild; + +typedef struct { + MxFrameNoteChild *children; /* child elements in document order */ + size_t children_count; +} MxFrameNote; + +/* NULL on error; the message is in mx_error(). */ +MxFrameNote *mx_frame_note_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_frame_note_serialize(const MxFrameNote *m, xmlNodePtr parent, const char *tag); +void mx_frame_note_free(MxFrameNote *m); + +#endif /* MX_FRAME_NOTE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_fret.c b/gen/test/c/mx/mx_fret.c new file mode 100644 index 000000000..1bdde5201 --- /dev/null +++ b/gen/test/c/mx/mx_fret.c @@ -0,0 +1,105 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_fret.h" + +#include "mx_runtime.h" +#include +#include + +MxFret *mx_fret_parse(xmlNodePtr el) { + MxFret *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_fret_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_parse_int(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_fret_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_fret_serialize(const MxFret *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + { + char *s = mx_format_int(m->value); + xmlAddChild(el, xmlNewText(BAD_CAST s)); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_fret_free(MxFret *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_fret.h b/gen/test/c/mx/mx_fret.h new file mode 100644 index 000000000..5319fad91 --- /dev/null +++ b/gen/test/c/mx/mx_fret.h @@ -0,0 +1,39 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_FRET_H_INCLUDED +#define MX_FRET_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" + +/* + * The fret element is used with tablature notation and chord diagrams. Fret numbers start with 0 + * for an open string and 1 for the first fret. + */ +typedef struct { + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + long value; /* text content */ +} MxFret; + +/* NULL on error; the message is in mx_error(). */ +MxFret *mx_fret_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_fret_serialize(const MxFret *m, xmlNodePtr parent, const char *tag); +void mx_fret_free(MxFret *m); + +#endif /* MX_FRET_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_glass.c b/gen/test/c/mx/mx_glass.c new file mode 100644 index 000000000..d7f8d9c72 --- /dev/null +++ b/gen/test/c/mx/mx_glass.c @@ -0,0 +1,71 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_glass.h" + +#include "mx_runtime.h" +#include +#include + +MxGlass *mx_glass_parse(xmlNodePtr el) { + MxGlass *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "smufl") == 0) { + m->has_smufl = true; + m->smufl = mx_smufl_pictogram_glyph_name_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_glass_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_glass_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_glass_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_glass_serialize(const MxGlass *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_glass_value_to_string(m->value))); + if (m->has_smufl) { + xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); + } + return el; +} + +void mx_glass_free(MxGlass *m) { + if (!m) + return; + if (m->has_smufl) + free(m->smufl); + free(m); +} diff --git a/gen/test/c/mx/mx_glass.h b/gen/test/c/mx/mx_glass.h new file mode 100644 index 000000000..dc994062f --- /dev/null +++ b/gen/test/c/mx/mx_glass.h @@ -0,0 +1,29 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_GLASS_H_INCLUDED +#define MX_GLASS_H_INCLUDED + +#include +#include +#include +#include "mx_glass_value.h" +#include "mx_smufl_pictogram_glyph_name.h" + +/* + * The glass type represents pictograms for glass percussion instruments. The smufl attribute is + * used to distinguish different SMuFL glyphs for wind chimes in the chimes pictograms range, + * including those made of materials other than glass. + */ +typedef struct { + bool has_smufl; + MxSMUFLPictogramGlyphName smufl; /* attribute smufl */ + MxGlassValue value; /* text content */ +} MxGlass; + +/* NULL on error; the message is in mx_error(). */ +MxGlass *mx_glass_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_glass_serialize(const MxGlass *m, xmlNodePtr parent, const char *tag); +void mx_glass_free(MxGlass *m); + +#endif /* MX_GLASS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_glissando.c b/gen/test/c/mx/mx_glissando.c new file mode 100644 index 000000000..13a4cc200 --- /dev/null +++ b/gen/test/c/mx/mx_glissando.c @@ -0,0 +1,178 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_glissando.h" + +#include "mx_runtime.h" +#include +#include + +MxGlissando *mx_glissando_parse(xmlNodePtr el) { + MxGlissando *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_parse(s); + } else if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_number_level_parse(s); + } else if (strcmp(aname, "line-type") == 0) { + m->has_line_type = true; + m->line_type = mx_line_type_parse(s); + } else if (strcmp(aname, "dash-length") == 0) { + m->has_dash_length = true; + m->dash_length = mx_tenths_parse(s); + } else if (strcmp(aname, "space-length") == 0) { + m->has_space_length = true; + m->space_length = mx_tenths_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_glissando_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_glissando_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_glissando_serialize(const MxGlissando *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); + } + if (m->has_number) { + char *s = mx_number_level_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_line_type) { + xmlSetProp(el, BAD_CAST "line-type", BAD_CAST mx_line_type_to_string(m->line_type)); + } + if (m->has_dash_length) { + char *s = mx_tenths_to_string(m->dash_length); + xmlSetProp(el, BAD_CAST "dash-length", BAD_CAST s); + free(s); + } + if (m->has_space_length) { + char *s = mx_tenths_to_string(m->space_length); + xmlSetProp(el, BAD_CAST "space-length", BAD_CAST s); + free(s); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_glissando_free(MxGlissando *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_glissando.h b/gen/test/c/mx/mx_glissando.h new file mode 100644 index 000000000..6b297eb85 --- /dev/null +++ b/gen/test/c/mx/mx_glissando.h @@ -0,0 +1,65 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_GLISSANDO_H_INCLUDED +#define MX_GLISSANDO_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_line_type.h" +#include "mx_number_level.h" +#include "mx_start_stop.h" +#include "mx_tenths.h" + +/* + * Glissando and slide types both indicate rapidly moving from one pitch to the other so that + * individual notes are not discerned. The distinction is similar to that between NIFF's glissando + * and portamento elements. A glissando sounds the half notes in between the slide and defaults to a + * wavy line. The optional text is printed alongside the line. + */ +typedef struct { + bool has_type; + MxStartStop type; /* attribute type */ + bool has_number; + MxNumberLevel number; /* attribute number */ + bool has_line_type; + MxLineType line_type; /* attribute line-type */ + bool has_dash_length; + MxTenths dash_length; /* attribute dash-length */ + bool has_space_length; + MxTenths space_length; /* attribute space-length */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_id; + char *id; /* attribute id */ + char *value; /* text content */ +} MxGlissando; + +/* NULL on error; the message is in mx_error(). */ +MxGlissando *mx_glissando_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_glissando_serialize(const MxGlissando *m, xmlNodePtr parent, const char *tag); +void mx_glissando_free(MxGlissando *m); + +#endif /* MX_GLISSANDO_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_glyph.c b/gen/test/c/mx/mx_glyph.c new file mode 100644 index 000000000..3aa7f7892 --- /dev/null +++ b/gen/test/c/mx/mx_glyph.c @@ -0,0 +1,72 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_glyph.h" + +#include "mx_runtime.h" +#include +#include + +MxGlyph *mx_glyph_parse(xmlNodePtr el) { + MxGlyph *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_glyph_type_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_glyph_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_smufl_glyph_name_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_glyph_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_glyph_serialize(const MxGlyph *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST m->type); + } + return el; +} + +void mx_glyph_free(MxGlyph *m) { + if (!m) + return; + if (m->has_type) + free(m->type); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_glyph.h b/gen/test/c/mx/mx_glyph.h new file mode 100644 index 000000000..f26ec867d --- /dev/null +++ b/gen/test/c/mx/mx_glyph.h @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_GLYPH_H_INCLUDED +#define MX_GLYPH_H_INCLUDED + +#include +#include +#include +#include "mx_glyph_type.h" +#include "mx_smufl_glyph_name.h" + +/* + * The glyph element represents what SMuFL glyph should be used for different variations of symbols + * that are semantically identical. The type attribute specifies what type of glyph is being + * defined. The element value specifies what SMuFL glyph to use, including recommended stylistic + * alternates. The SMuFL glyph name should match the type. For instance, a type of quarter-rest + * would use values restQuarter, restQuarterOld, or restQuarterZ. A type of g-clef-ottava-bassa + * would use values gClef8vb, gClef8vbOld, or gClef8vbCClef. A type of octave-shift-up-8 would use + * values ottava, ottavaBassa, ottavaBassaBa, ottavaBassaVb, or octaveBassa. + */ +typedef struct { + bool has_type; + MxGlyphType type; /* attribute type */ + MxSMUFLGlyphName value; /* text content */ +} MxGlyph; + +/* NULL on error; the message is in mx_error(). */ +MxGlyph *mx_glyph_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_glyph_serialize(const MxGlyph *m, xmlNodePtr parent, const char *tag); +void mx_glyph_free(MxGlyph *m); + +#endif /* MX_GLYPH_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_grace.c b/gen/test/c/mx/mx_grace.c new file mode 100644 index 000000000..d4a287a18 --- /dev/null +++ b/gen/test/c/mx/mx_grace.c @@ -0,0 +1,86 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_grace.h" + +#include "mx_runtime.h" +#include +#include + +MxGrace *mx_grace_parse(xmlNodePtr el) { + MxGrace *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "steal-time-previous") == 0) { + m->has_steal_time_previous = true; + m->steal_time_previous = mx_percent_parse(s); + } else if (strcmp(aname, "steal-time-following") == 0) { + m->has_steal_time_following = true; + m->steal_time_following = mx_percent_parse(s); + } else if (strcmp(aname, "make-time") == 0) { + m->has_make_time = true; + m->make_time = mx_divisions_parse(s); + } else if (strcmp(aname, "slash") == 0) { + m->has_slash = true; + m->slash = mx_yes_no_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_grace_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_grace_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_grace_serialize(const MxGrace *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_steal_time_previous) { + char *s = mx_percent_to_string(m->steal_time_previous); + xmlSetProp(el, BAD_CAST "steal-time-previous", BAD_CAST s); + free(s); + } + if (m->has_steal_time_following) { + char *s = mx_percent_to_string(m->steal_time_following); + xmlSetProp(el, BAD_CAST "steal-time-following", BAD_CAST s); + free(s); + } + if (m->has_make_time) { + char *s = mx_divisions_to_string(m->make_time); + xmlSetProp(el, BAD_CAST "make-time", BAD_CAST s); + free(s); + } + if (m->has_slash) { + xmlSetProp(el, BAD_CAST "slash", BAD_CAST mx_yes_no_to_string(m->slash)); + } + return el; +} + +void mx_grace_free(MxGrace *m) { + if (!m) + return; + free(m); +} diff --git a/gen/test/c/mx/mx_grace.h b/gen/test/c/mx/mx_grace.h new file mode 100644 index 000000000..005b3d863 --- /dev/null +++ b/gen/test/c/mx/mx_grace.h @@ -0,0 +1,39 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_GRACE_H_INCLUDED +#define MX_GRACE_H_INCLUDED + +#include +#include +#include +#include "mx_divisions.h" +#include "mx_percent.h" +#include "mx_yes_no.h" + +/* + * The grace type indicates the presence of a grace note. The slash attribute for a grace note is + * yes for slashed eighth notes. The other grace note attributes come from MuseData sound + * suggestions. The steal-time-previous attribute indicates the percentage of time to steal from the + * previous note for the grace note. The steal-time-following attribute indicates the percentage of + * time to steal from the following note for the grace note, as for appoggiaturas. The make-time + * attribute indicates to make time, not steal time; the units are in real-time divisions for the + * grace note. + */ +typedef struct { + bool has_steal_time_previous; + MxPercent steal_time_previous; /* attribute steal-time-previous */ + bool has_steal_time_following; + MxPercent steal_time_following; /* attribute steal-time-following */ + bool has_make_time; + MxDivisions make_time; /* attribute make-time */ + bool has_slash; + MxYesNo slash; /* attribute slash */ +} MxGrace; + +/* NULL on error; the message is in mx_error(). */ +MxGrace *mx_grace_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_grace_serialize(const MxGrace *m, xmlNodePtr parent, const char *tag); +void mx_grace_free(MxGrace *m); + +#endif /* MX_GRACE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_group_barline.c b/gen/test/c/mx/mx_group_barline.c new file mode 100644 index 000000000..f2e37954f --- /dev/null +++ b/gen/test/c/mx/mx_group_barline.c @@ -0,0 +1,71 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_group_barline.h" + +#include "mx_runtime.h" +#include +#include + +MxGroupBarline *mx_group_barline_parse(xmlNodePtr el) { + MxGroupBarline *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_group_barline_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_group_barline_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_group_barline_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_group_barline_serialize(const MxGroupBarline *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_group_barline_value_to_string(m->value))); + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_group_barline_free(MxGroupBarline *m) { + if (!m) + return; + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_group_barline.h b/gen/test/c/mx/mx_group_barline.h new file mode 100644 index 000000000..adc336165 --- /dev/null +++ b/gen/test/c/mx/mx_group_barline.h @@ -0,0 +1,27 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_GROUP_BARLINE_H_INCLUDED +#define MX_GROUP_BARLINE_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_group_barline_value.h" + +/* + * The group-barline type indicates if the group should have common barlines. + */ +typedef struct { + bool has_color; + MxColor color; /* attribute color */ + MxGroupBarlineValue value; /* text content */ +} MxGroupBarline; + +/* NULL on error; the message is in mx_error(). */ +MxGroupBarline *mx_group_barline_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_group_barline_serialize(const MxGroupBarline *m, xmlNodePtr parent, const char *tag); +void mx_group_barline_free(MxGroupBarline *m); + +#endif /* MX_GROUP_BARLINE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_group_name.c b/gen/test/c/mx/mx_group_name.c new file mode 100644 index 000000000..9cd1634ac --- /dev/null +++ b/gen/test/c/mx/mx_group_name.c @@ -0,0 +1,140 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_group_name.h" + +#include "mx_runtime.h" +#include +#include + +MxGroupName *mx_group_name_parse(xmlNodePtr el) { + MxGroupName *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "justify") == 0) { + m->has_justify = true; + m->justify = mx_left_center_right_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_group_name_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_group_name_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_group_name_serialize(const MxGroupName *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_justify) { + xmlSetProp(el, BAD_CAST "justify", BAD_CAST mx_left_center_right_to_string(m->justify)); + } + return el; +} + +void mx_group_name_free(MxGroupName *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_group_name.h b/gen/test/c/mx/mx_group_name.h new file mode 100644 index 000000000..10e8601af --- /dev/null +++ b/gen/test/c/mx/mx_group_name.h @@ -0,0 +1,52 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_GROUP_NAME_H_INCLUDED +#define MX_GROUP_NAME_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_center_right.h" +#include "mx_tenths.h" + +/* + * The group-name type describes the name or abbreviation of a part-group element. Formatting + * attributes in the group-name type are deprecated in Version 2.0 in favor of the new + * group-name-display and group-abbreviation-display elements. + */ +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_justify; + MxLeftCenterRight justify; /* attribute justify */ + char *value; /* text content */ +} MxGroupName; + +/* NULL on error; the message is in mx_error(). */ +MxGroupName *mx_group_name_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_group_name_serialize(const MxGroupName *m, xmlNodePtr parent, const char *tag); +void mx_group_name_free(MxGroupName *m); + +#endif /* MX_GROUP_NAME_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_group_symbol.c b/gen/test/c/mx/mx_group_symbol.c new file mode 100644 index 000000000..b5c5a0cd0 --- /dev/null +++ b/gen/test/c/mx/mx_group_symbol.c @@ -0,0 +1,103 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_group_symbol.h" + +#include "mx_runtime.h" +#include +#include + +MxGroupSymbol *mx_group_symbol_parse(xmlNodePtr el) { + MxGroupSymbol *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_group_symbol_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_group_symbol_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_group_symbol_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_group_symbol_serialize(const MxGroupSymbol *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_group_symbol_value_to_string(m->value))); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_group_symbol_free(MxGroupSymbol *m) { + if (!m) + return; + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_group_symbol.h b/gen/test/c/mx/mx_group_symbol.h new file mode 100644 index 000000000..7172f6da3 --- /dev/null +++ b/gen/test/c/mx/mx_group_symbol.h @@ -0,0 +1,36 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_GROUP_SYMBOL_H_INCLUDED +#define MX_GROUP_SYMBOL_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_group_symbol_value.h" +#include "mx_tenths.h" + +/* + * The group-symbol type indicates how the symbol for a group is indicated in the score. + */ +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_color; + MxColor color; /* attribute color */ + MxGroupSymbolValue value; /* text content */ +} MxGroupSymbol; + +/* NULL on error; the message is in mx_error(). */ +MxGroupSymbol *mx_group_symbol_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_group_symbol_serialize(const MxGroupSymbol *m, xmlNodePtr parent, const char *tag); +void mx_group_symbol_free(MxGroupSymbol *m); + +#endif /* MX_GROUP_SYMBOL_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_grouping.c b/gen/test/c/mx/mx_grouping.c new file mode 100644 index 000000000..f0e6ccdf2 --- /dev/null +++ b/gen/test/c/mx/mx_grouping.c @@ -0,0 +1,115 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_grouping.h" + +#include "mx_runtime.h" +#include +#include + +MxGrouping *mx_grouping_parse(xmlNodePtr el) { + MxGrouping *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_single_parse(s); + } else if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_strdup(s); + } else if (strcmp(aname, "member-of") == 0) { + m->has_member_of = true; + m->member_of = mx_strdup(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_grouping_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxGroupingChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "feature") == 0) { + ch->feature = mx_feature_parse(c); + if (!ch->feature) { + mx_grouping_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_grouping_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_grouping_serialize(const MxGrouping *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_single_to_string(m->type)); + } + if (m->has_number) { + xmlSetProp(el, BAD_CAST "number", BAD_CAST m->number); + } + if (m->has_member_of) { + xmlSetProp(el, BAD_CAST "member-of", BAD_CAST m->member_of); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxGroupingChild *ch = &m->children[i]; + if (ch->feature) { + mx_feature_serialize(ch->feature, el, "feature"); + } + } + return el; +} + +void mx_grouping_free(MxGrouping *m) { + if (!m) + return; + if (m->has_number) + free(m->number); + if (m->has_member_of) + free(m->member_of); + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxGroupingChild *ch = &m->children[i]; + if (ch->feature) + mx_feature_free(ch->feature); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_grouping.h b/gen/test/c/mx/mx_grouping.h new file mode 100644 index 000000000..9a6f9674e --- /dev/null +++ b/gen/test/c/mx/mx_grouping.h @@ -0,0 +1,47 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_GROUPING_H_INCLUDED +#define MX_GROUPING_H_INCLUDED + +#include +#include +#include +#include "mx_feature.h" +#include "mx_start_stop_single.h" + +/* + * The grouping type is used for musical analysis. When the type attribute is "start" or "single", + * it usually contains one or more feature elements. The number attribute is used for distinguishing + * between overlapping and hierarchical groupings. The member-of attribute allows for easy + * distinguishing of what grouping elements are in what hierarchy. Feature elements contained within + * a "stop" type of grouping may be ignored. This element is flexible to allow for different types + * of analyses. Future versions of the MusicXML format may add elements that can represent more + * standardized categories of analysis data, allowing for easier data sharing. + */ +/* One child element of MxGrouping: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxFeature *feature; +} MxGroupingChild; + +typedef struct { + bool has_type; + MxStartStopSingle type; /* attribute type */ + bool has_number; + char *number; /* attribute number */ + bool has_member_of; + char *member_of; /* attribute member-of */ + bool has_id; + char *id; /* attribute id */ + MxGroupingChild *children; /* child elements in document order */ + size_t children_count; +} MxGrouping; + +/* NULL on error; the message is in mx_error(). */ +MxGrouping *mx_grouping_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_grouping_serialize(const MxGrouping *m, xmlNodePtr parent, const char *tag); +void mx_grouping_free(MxGrouping *m); + +#endif /* MX_GROUPING_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_hammer_on_pull_off.c b/gen/test/c/mx/mx_hammer_on_pull_off.c new file mode 100644 index 000000000..2a0972f0a --- /dev/null +++ b/gen/test/c/mx/mx_hammer_on_pull_off.c @@ -0,0 +1,154 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_hammer_on_pull_off.h" + +#include "mx_runtime.h" +#include +#include + +MxHammerOnPullOff *mx_hammer_on_pull_off_parse(xmlNodePtr el) { + MxHammerOnPullOff *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_parse(s); + } else if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_number_level_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_hammer_on_pull_off_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_hammer_on_pull_off_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_hammer_on_pull_off_serialize(const MxHammerOnPullOff *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); + } + if (m->has_number) { + char *s = mx_number_level_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + return el; +} + +void mx_hammer_on_pull_off_free(MxHammerOnPullOff *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_hammer_on_pull_off.h b/gen/test/c/mx/mx_hammer_on_pull_off.h new file mode 100644 index 000000000..9d3586706 --- /dev/null +++ b/gen/test/c/mx/mx_hammer_on_pull_off.h @@ -0,0 +1,60 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_HAMMER_ON_PULL_OFF_H_INCLUDED +#define MX_HAMMER_ON_PULL_OFF_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_number_level.h" +#include "mx_start_stop.h" +#include "mx_tenths.h" + +/* + * The hammer-on and pull-off elements are used in guitar and fretted instrument notation. Since a + * single slur can be marked over many notes, the hammer-on and pull-off elements are separate so + * the individual pair of notes can be specified. The element content can be used to specify how the + * hammer-on or pull-off should be notated. An empty element leaves this choice up to the + * application. + */ +typedef struct { + bool has_type; + MxStartStop type; /* attribute type */ + bool has_number; + MxNumberLevel number; /* attribute number */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + char *value; /* text content */ +} MxHammerOnPullOff; + +/* NULL on error; the message is in mx_error(). */ +MxHammerOnPullOff *mx_hammer_on_pull_off_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_hammer_on_pull_off_serialize(const MxHammerOnPullOff *m, xmlNodePtr parent, const char *tag); +void mx_hammer_on_pull_off_free(MxHammerOnPullOff *m); + +#endif /* MX_HAMMER_ON_PULL_OFF_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_handbell.c b/gen/test/c/mx/mx_handbell.c new file mode 100644 index 000000000..39540b048 --- /dev/null +++ b/gen/test/c/mx/mx_handbell.c @@ -0,0 +1,139 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_handbell.h" + +#include "mx_runtime.h" +#include +#include + +MxHandbell *mx_handbell_parse(xmlNodePtr el) { + MxHandbell *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_handbell_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_handbell_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_handbell_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_handbell_serialize(const MxHandbell *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_handbell_value_to_string(m->value))); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + return el; +} + +void mx_handbell_free(MxHandbell *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_handbell.h b/gen/test/c/mx/mx_handbell.h new file mode 100644 index 000000000..c1bc1b4be --- /dev/null +++ b/gen/test/c/mx/mx_handbell.h @@ -0,0 +1,52 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_HANDBELL_H_INCLUDED +#define MX_HANDBELL_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_handbell_value.h" +#include "mx_tenths.h" + +/* + * The handbell element represents notation for various techniques used in handbell and handchime + * music. + */ +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + MxHandbellValue value; /* text content */ +} MxHandbell; + +/* NULL on error; the message is in mx_error(). */ +MxHandbell *mx_handbell_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_handbell_serialize(const MxHandbell *m, xmlNodePtr parent, const char *tag); +void mx_handbell_free(MxHandbell *m); + +#endif /* MX_HANDBELL_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_harmon_closed.c b/gen/test/c/mx/mx_harmon_closed.c new file mode 100644 index 000000000..aea077b2d --- /dev/null +++ b/gen/test/c/mx/mx_harmon_closed.c @@ -0,0 +1,69 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_harmon_closed.h" + +#include "mx_runtime.h" +#include +#include + +MxHarmonClosed *mx_harmon_closed_parse(xmlNodePtr el) { + MxHarmonClosed *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "location") == 0) { + m->has_location = true; + m->location = mx_harmon_closed_location_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_harmon_closed_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_harmon_closed_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_harmon_closed_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_harmon_closed_serialize(const MxHarmonClosed *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_harmon_closed_value_to_string(m->value))); + if (m->has_location) { + xmlSetProp(el, BAD_CAST "location", BAD_CAST mx_harmon_closed_location_to_string(m->location)); + } + return el; +} + +void mx_harmon_closed_free(MxHarmonClosed *m) { + if (!m) + return; + free(m); +} diff --git a/gen/test/c/mx/mx_harmon_closed.h b/gen/test/c/mx/mx_harmon_closed.h new file mode 100644 index 000000000..79681ce44 --- /dev/null +++ b/gen/test/c/mx/mx_harmon_closed.h @@ -0,0 +1,29 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_HARMON_CLOSED_H_INCLUDED +#define MX_HARMON_CLOSED_H_INCLUDED + +#include +#include +#include +#include "mx_harmon_closed_location.h" +#include "mx_harmon_closed_value.h" + +/* + * The harmon-closed type represents whether the harmon mute is closed, open, or half-open. The + * optional location attribute indicates which portion of the symbol is filled in when the element + * value is half. + */ +typedef struct { + bool has_location; + MxHarmonClosedLocation location; /* attribute location */ + MxHarmonClosedValue value; /* text content */ +} MxHarmonClosed; + +/* NULL on error; the message is in mx_error(). */ +MxHarmonClosed *mx_harmon_closed_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_harmon_closed_serialize(const MxHarmonClosed *m, xmlNodePtr parent, const char *tag); +void mx_harmon_closed_free(MxHarmonClosed *m); + +#endif /* MX_HARMON_CLOSED_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_harmon_mute.c b/gen/test/c/mx/mx_harmon_mute.c new file mode 100644 index 000000000..dbc551d75 --- /dev/null +++ b/gen/test/c/mx/mx_harmon_mute.c @@ -0,0 +1,161 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_harmon_mute.h" + +#include "mx_runtime.h" +#include +#include + +MxHarmonMute *mx_harmon_mute_parse(xmlNodePtr el) { + MxHarmonMute *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_harmon_mute_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxHarmonMuteChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "harmon-closed") == 0) { + ch->harmon_closed = mx_harmon_closed_parse(c); + if (!ch->harmon_closed) { + mx_harmon_mute_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_harmon_mute_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_harmon_mute_serialize(const MxHarmonMute *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxHarmonMuteChild *ch = &m->children[i]; + if (ch->harmon_closed) { + mx_harmon_closed_serialize(ch->harmon_closed, el, "harmon-closed"); + } + } + return el; +} + +void mx_harmon_mute_free(MxHarmonMute *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + for (size_t i = 0; i < m->children_count; i++) { + MxHarmonMuteChild *ch = &m->children[i]; + if (ch->harmon_closed) + mx_harmon_closed_free(ch->harmon_closed); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_harmon_mute.h b/gen/test/c/mx/mx_harmon_mute.h new file mode 100644 index 000000000..297f80756 --- /dev/null +++ b/gen/test/c/mx/mx_harmon_mute.h @@ -0,0 +1,59 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_HARMON_MUTE_H_INCLUDED +#define MX_HARMON_MUTE_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_harmon_closed.h" +#include "mx_tenths.h" + +/* + * The harmon-mute type represents the symbols used for harmon mutes in brass notation. + */ +/* One child element of MxHarmonMute: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxHarmonClosed *harmon_closed; +} MxHarmonMuteChild; + +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + MxHarmonMuteChild *children; /* child elements in document order */ + size_t children_count; +} MxHarmonMute; + +/* NULL on error; the message is in mx_error(). */ +MxHarmonMute *mx_harmon_mute_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_harmon_mute_serialize(const MxHarmonMute *m, xmlNodePtr parent, const char *tag); +void mx_harmon_mute_free(MxHarmonMute *m); + +#endif /* MX_HARMON_MUTE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_harmonic.c b/gen/test/c/mx/mx_harmonic.c new file mode 100644 index 000000000..57d4380d4 --- /dev/null +++ b/gen/test/c/mx/mx_harmonic.c @@ -0,0 +1,207 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_harmonic.h" + +#include "mx_runtime.h" +#include +#include + +MxHarmonic *mx_harmonic_parse(xmlNodePtr el) { + MxHarmonic *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "print-object") == 0) { + m->has_print_object = true; + m->print_object = mx_yes_no_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_harmonic_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxHarmonicChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "natural") == 0) { + ch->natural = mx_empty_parse(c); + if (!ch->natural) { + mx_harmonic_free(m); + return NULL; + } + } else if (strcmp(tag, "artificial") == 0) { + ch->artificial = mx_empty_parse(c); + if (!ch->artificial) { + mx_harmonic_free(m); + return NULL; + } + } else if (strcmp(tag, "base-pitch") == 0) { + ch->base_pitch = mx_empty_parse(c); + if (!ch->base_pitch) { + mx_harmonic_free(m); + return NULL; + } + } else if (strcmp(tag, "touching-pitch") == 0) { + ch->touching_pitch = mx_empty_parse(c); + if (!ch->touching_pitch) { + mx_harmonic_free(m); + return NULL; + } + } else if (strcmp(tag, "sounding-pitch") == 0) { + ch->sounding_pitch = mx_empty_parse(c); + if (!ch->sounding_pitch) { + mx_harmonic_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_harmonic_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_harmonic_serialize(const MxHarmonic *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_print_object) { + xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxHarmonicChild *ch = &m->children[i]; + if (ch->natural) { + mx_empty_serialize(ch->natural, el, "natural"); + } else if (ch->artificial) { + mx_empty_serialize(ch->artificial, el, "artificial"); + } else if (ch->base_pitch) { + mx_empty_serialize(ch->base_pitch, el, "base-pitch"); + } else if (ch->touching_pitch) { + mx_empty_serialize(ch->touching_pitch, el, "touching-pitch"); + } else if (ch->sounding_pitch) { + mx_empty_serialize(ch->sounding_pitch, el, "sounding-pitch"); + } + } + return el; +} + +void mx_harmonic_free(MxHarmonic *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + for (size_t i = 0; i < m->children_count; i++) { + MxHarmonicChild *ch = &m->children[i]; + if (ch->natural) + mx_empty_free(ch->natural); + if (ch->artificial) + mx_empty_free(ch->artificial); + if (ch->base_pitch) + mx_empty_free(ch->base_pitch); + if (ch->touching_pitch) + mx_empty_free(ch->touching_pitch); + if (ch->sounding_pitch) + mx_empty_free(ch->sounding_pitch); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_harmonic.h b/gen/test/c/mx/mx_harmonic.h new file mode 100644 index 000000000..8af46de5b --- /dev/null +++ b/gen/test/c/mx/mx_harmonic.h @@ -0,0 +1,71 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_HARMONIC_H_INCLUDED +#define MX_HARMONIC_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_empty.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_tenths.h" +#include "mx_yes_no.h" + +/* + * The harmonic type indicates natural and artificial harmonics. Allowing the type of pitch to be + * specified, combined with controls for appearance/playback differences, allows both the notation + * and the sound to be represented. Artificial harmonics can add a notated touching-pitch; + * artificial pinch harmonics will usually not notate a touching pitch. The attributes for the + * harmonic element refer to the use of the circular harmonic symbol, typically but not always used + * with natural harmonics. + */ +/* One child element of MxHarmonic: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxEmpty *natural; + MxEmpty *artificial; + MxEmpty *base_pitch; + MxEmpty *touching_pitch; + MxEmpty *sounding_pitch; +} MxHarmonicChild; + +typedef struct { + bool has_print_object; + MxYesNo print_object; /* attribute print-object */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + MxHarmonicChild *children; /* child elements in document order */ + size_t children_count; +} MxHarmonic; + +/* NULL on error; the message is in mx_error(). */ +MxHarmonic *mx_harmonic_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_harmonic_serialize(const MxHarmonic *m, xmlNodePtr parent, const char *tag); +void mx_harmonic_free(MxHarmonic *m); + +#endif /* MX_HARMONIC_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_harmony.c b/gen/test/c/mx/mx_harmony.c new file mode 100644 index 000000000..5b1ac9385 --- /dev/null +++ b/gen/test/c/mx/mx_harmony.c @@ -0,0 +1,290 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_harmony.h" + +#include "mx_runtime.h" +#include +#include + +MxHarmony *mx_harmony_parse(xmlNodePtr el) { + MxHarmony *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_harmony_type_parse(s); + } else if (strcmp(aname, "print-frame") == 0) { + m->has_print_frame = true; + m->print_frame = mx_yes_no_parse(s); + } else if (strcmp(aname, "print-object") == 0) { + m->has_print_object = true; + m->print_object = mx_yes_no_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_harmony_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxHarmonyChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "root") == 0) { + ch->root = mx_root_parse(c); + if (!ch->root) { + mx_harmony_free(m); + return NULL; + } + } else if (strcmp(tag, "function") == 0) { + ch->function = mx_style_text_parse(c); + if (!ch->function) { + mx_harmony_free(m); + return NULL; + } + } else if (strcmp(tag, "kind") == 0) { + ch->kind = mx_kind_parse(c); + if (!ch->kind) { + mx_harmony_free(m); + return NULL; + } + } else if (strcmp(tag, "inversion") == 0) { + ch->inversion = mx_inversion_parse(c); + if (!ch->inversion) { + mx_harmony_free(m); + return NULL; + } + } else if (strcmp(tag, "bass") == 0) { + ch->bass = mx_bass_parse(c); + if (!ch->bass) { + mx_harmony_free(m); + return NULL; + } + } else if (strcmp(tag, "degree") == 0) { + ch->degree = mx_degree_parse(c); + if (!ch->degree) { + mx_harmony_free(m); + return NULL; + } + } else if (strcmp(tag, "frame") == 0) { + ch->frame = mx_frame_parse(c); + if (!ch->frame) { + mx_harmony_free(m); + return NULL; + } + } else if (strcmp(tag, "offset") == 0) { + ch->offset = mx_offset_parse(c); + if (!ch->offset) { + mx_harmony_free(m); + return NULL; + } + } else if (strcmp(tag, "footnote") == 0) { + ch->footnote = mx_formatted_text_parse(c); + if (!ch->footnote) { + mx_harmony_free(m); + return NULL; + } + } else if (strcmp(tag, "level") == 0) { + ch->level = mx_level_parse(c); + if (!ch->level) { + mx_harmony_free(m); + return NULL; + } + } else if (strcmp(tag, "staff") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->staff = malloc(sizeof(*ch->staff)); + if (!ch->staff) + abort(); + *ch->staff = mx_parse_int(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_harmony_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_harmony_serialize(const MxHarmony *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_harmony_type_to_string(m->type)); + } + if (m->has_print_frame) { + xmlSetProp(el, BAD_CAST "print-frame", BAD_CAST mx_yes_no_to_string(m->print_frame)); + } + if (m->has_print_object) { + xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxHarmonyChild *ch = &m->children[i]; + if (ch->root) { + mx_root_serialize(ch->root, el, "root"); + } else if (ch->function) { + mx_style_text_serialize(ch->function, el, "function"); + } else if (ch->kind) { + mx_kind_serialize(ch->kind, el, "kind"); + } else if (ch->inversion) { + mx_inversion_serialize(ch->inversion, el, "inversion"); + } else if (ch->bass) { + mx_bass_serialize(ch->bass, el, "bass"); + } else if (ch->degree) { + mx_degree_serialize(ch->degree, el, "degree"); + } else if (ch->frame) { + mx_frame_serialize(ch->frame, el, "frame"); + } else if (ch->offset) { + mx_offset_serialize(ch->offset, el, "offset"); + } else if (ch->footnote) { + mx_formatted_text_serialize(ch->footnote, el, "footnote"); + } else if (ch->level) { + mx_level_serialize(ch->level, el, "level"); + } else if (ch->staff) { + char *s = mx_format_int((*ch->staff)); + xmlNewTextChild(el, NULL, BAD_CAST "staff", BAD_CAST s); + free(s); + } + } + return el; +} + +void mx_harmony_free(MxHarmony *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxHarmonyChild *ch = &m->children[i]; + if (ch->root) + mx_root_free(ch->root); + if (ch->function) + mx_style_text_free(ch->function); + if (ch->kind) + mx_kind_free(ch->kind); + if (ch->inversion) + mx_inversion_free(ch->inversion); + if (ch->bass) + mx_bass_free(ch->bass); + if (ch->degree) + mx_degree_free(ch->degree); + if (ch->frame) + mx_frame_free(ch->frame); + if (ch->offset) + mx_offset_free(ch->offset); + if (ch->footnote) + mx_formatted_text_free(ch->footnote); + if (ch->level) + mx_level_free(ch->level); + free(ch->staff); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_harmony.h b/gen/test/c/mx/mx_harmony.h new file mode 100644 index 000000000..da5486747 --- /dev/null +++ b/gen/test/c/mx/mx_harmony.h @@ -0,0 +1,96 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_HARMONY_H_INCLUDED +#define MX_HARMONY_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_bass.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_degree.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_formatted_text.h" +#include "mx_frame.h" +#include "mx_harmony_type.h" +#include "mx_inversion.h" +#include "mx_kind.h" +#include "mx_level.h" +#include "mx_offset.h" +#include "mx_root.h" +#include "mx_style_text.h" +#include "mx_tenths.h" +#include "mx_yes_no.h" + +/* + * The harmony type is based on Humdrum's **harm encoding, extended to support chord symbols in + * popular music as well as functional harmony analysis in classical music. If there are alternate + * harmonies possible, this can be specified using multiple harmony elements differentiated by type. + * Explicit harmonies have all note present in the music; implied have some notes missing but + * implied; alternate represents alternate analyses. The harmony object may be used for analysis or + * for chord symbols. The print-object attribute controls whether or not anything is printed due to + * the harmony element. The print-frame attribute controls printing of a frame or fretboard diagram. + * The print-style attribute group sets the default for the harmony, but individual elements can + * override this with their own print-style values. + */ +/* One child element of MxHarmony: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxRoot *root; + MxStyleText *function; + MxKind *kind; + MxInversion *inversion; + MxBass *bass; + MxDegree *degree; + MxFrame *frame; + MxOffset *offset; + MxFormattedText *footnote; + MxLevel *level; + long *staff; +} MxHarmonyChild; + +typedef struct { + bool has_type; + MxHarmonyType type; /* attribute type */ + bool has_print_frame; + MxYesNo print_frame; /* attribute print-frame */ + bool has_print_object; + MxYesNo print_object; /* attribute print-object */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_id; + char *id; /* attribute id */ + MxHarmonyChild *children; /* child elements in document order */ + size_t children_count; +} MxHarmony; + +/* NULL on error; the message is in mx_error(). */ +MxHarmony *mx_harmony_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_harmony_serialize(const MxHarmony *m, xmlNodePtr parent, const char *tag); +void mx_harmony_free(MxHarmony *m); + +#endif /* MX_HARMONY_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_harp_pedals.c b/gen/test/c/mx/mx_harp_pedals.c new file mode 100644 index 000000000..35badd8a7 --- /dev/null +++ b/gen/test/c/mx/mx_harp_pedals.c @@ -0,0 +1,175 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_harp_pedals.h" + +#include "mx_runtime.h" +#include +#include + +MxHarpPedals *mx_harp_pedals_parse(xmlNodePtr el) { + MxHarpPedals *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_harp_pedals_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxHarpPedalsChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "pedal-tuning") == 0) { + ch->pedal_tuning = mx_pedal_tuning_parse(c); + if (!ch->pedal_tuning) { + mx_harp_pedals_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_harp_pedals_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_harp_pedals_serialize(const MxHarpPedals *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxHarpPedalsChild *ch = &m->children[i]; + if (ch->pedal_tuning) { + mx_pedal_tuning_serialize(ch->pedal_tuning, el, "pedal-tuning"); + } + } + return el; +} + +void mx_harp_pedals_free(MxHarpPedals *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxHarpPedalsChild *ch = &m->children[i]; + if (ch->pedal_tuning) + mx_pedal_tuning_free(ch->pedal_tuning); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_harp_pedals.h b/gen/test/c/mx/mx_harp_pedals.h new file mode 100644 index 000000000..b8c9d6b51 --- /dev/null +++ b/gen/test/c/mx/mx_harp_pedals.h @@ -0,0 +1,67 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_HARP_PEDALS_H_INCLUDED +#define MX_HARP_PEDALS_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_center_right.h" +#include "mx_pedal_tuning.h" +#include "mx_tenths.h" +#include "mx_valign.h" + +/* + * The harp-pedals type is used to create harp pedal diagrams. The pedal-step and pedal-alter + * elements use the same values as the step and alter elements. For easiest reading, the + * pedal-tuning elements should follow standard harp pedal order, with pedal-step values of D, C, B, + * E, F, G, and A. + */ +/* One child element of MxHarpPedals: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxPedalTuning *pedal_tuning; +} MxHarpPedalsChild; + +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ + bool has_id; + char *id; /* attribute id */ + MxHarpPedalsChild *children; /* child elements in document order */ + size_t children_count; +} MxHarpPedals; + +/* NULL on error; the message is in mx_error(). */ +MxHarpPedals *mx_harp_pedals_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_harp_pedals_serialize(const MxHarpPedals *m, xmlNodePtr parent, const char *tag); +void mx_harp_pedals_free(MxHarpPedals *m); + +#endif /* MX_HARP_PEDALS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_heel_toe.c b/gen/test/c/mx/mx_heel_toe.c new file mode 100644 index 000000000..cb1f835fe --- /dev/null +++ b/gen/test/c/mx/mx_heel_toe.c @@ -0,0 +1,138 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_heel_toe.h" + +#include "mx_runtime.h" +#include +#include + +MxHeelToe *mx_heel_toe_parse(xmlNodePtr el) { + MxHeelToe *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "substitution") == 0) { + m->has_substitution = true; + m->substitution = mx_yes_no_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_heel_toe_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_heel_toe_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_heel_toe_serialize(const MxHeelToe *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_substitution) { + xmlSetProp(el, BAD_CAST "substitution", BAD_CAST mx_yes_no_to_string(m->substitution)); + } + return el; +} + +void mx_heel_toe_free(MxHeelToe *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_heel_toe.h b/gen/test/c/mx/mx_heel_toe.h new file mode 100644 index 000000000..e5d89596f --- /dev/null +++ b/gen/test/c/mx/mx_heel_toe.h @@ -0,0 +1,54 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_HEEL_TOE_H_INCLUDED +#define MX_HEEL_TOE_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_empty_placement.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_tenths.h" +#include "mx_yes_no.h" + +/* + * The heel and toe elements are used with organ pedals. The substitution value is "no" if the + * attribute is not present. + */ +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_substitution; + MxYesNo substitution; /* attribute substitution */ +} MxHeelToe; + +/* NULL on error; the message is in mx_error(). */ +MxHeelToe *mx_heel_toe_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_heel_toe_serialize(const MxHeelToe *m, xmlNodePtr parent, const char *tag); +void mx_heel_toe_free(MxHeelToe *m); + +#endif /* MX_HEEL_TOE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_hole.c b/gen/test/c/mx/mx_hole.c new file mode 100644 index 000000000..5234e6e5e --- /dev/null +++ b/gen/test/c/mx/mx_hole.c @@ -0,0 +1,177 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_hole.h" + +#include "mx_runtime.h" +#include +#include + +MxHole *mx_hole_parse(xmlNodePtr el) { + MxHole *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_hole_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxHoleChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "hole-type") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->hole_type = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "hole-closed") == 0) { + ch->hole_closed = mx_hole_closed_parse(c); + if (!ch->hole_closed) { + mx_hole_free(m); + return NULL; + } + } else if (strcmp(tag, "hole-shape") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->hole_shape = mx_strdup(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_hole_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_hole_serialize(const MxHole *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxHoleChild *ch = &m->children[i]; + if (ch->hole_type) { + xmlNewTextChild(el, NULL, BAD_CAST "hole-type", BAD_CAST ch->hole_type); + } else if (ch->hole_closed) { + mx_hole_closed_serialize(ch->hole_closed, el, "hole-closed"); + } else if (ch->hole_shape) { + xmlNewTextChild(el, NULL, BAD_CAST "hole-shape", BAD_CAST ch->hole_shape); + } + } + return el; +} + +void mx_hole_free(MxHole *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + for (size_t i = 0; i < m->children_count; i++) { + MxHoleChild *ch = &m->children[i]; + free(ch->hole_type); + if (ch->hole_closed) + mx_hole_closed_free(ch->hole_closed); + free(ch->hole_shape); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_hole.h b/gen/test/c/mx/mx_hole.h new file mode 100644 index 000000000..7744fcbff --- /dev/null +++ b/gen/test/c/mx/mx_hole.h @@ -0,0 +1,62 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_HOLE_H_INCLUDED +#define MX_HOLE_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_hole_closed.h" +#include "mx_tenths.h" + +/* + * The hole type represents the symbols used for woodwind and brass fingerings as well as other + * notations. + */ +/* One child element of MxHole: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + char *hole_type; + MxHoleClosed *hole_closed; + char *hole_shape; +} MxHoleChild; + +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + MxHoleChild *children; /* child elements in document order */ + size_t children_count; +} MxHole; + +/* NULL on error; the message is in mx_error(). */ +MxHole *mx_hole_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_hole_serialize(const MxHole *m, xmlNodePtr parent, const char *tag); +void mx_hole_free(MxHole *m); + +#endif /* MX_HOLE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_hole_closed.c b/gen/test/c/mx/mx_hole_closed.c new file mode 100644 index 000000000..d468711c7 --- /dev/null +++ b/gen/test/c/mx/mx_hole_closed.c @@ -0,0 +1,69 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_hole_closed.h" + +#include "mx_runtime.h" +#include +#include + +MxHoleClosed *mx_hole_closed_parse(xmlNodePtr el) { + MxHoleClosed *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "location") == 0) { + m->has_location = true; + m->location = mx_hole_closed_location_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_hole_closed_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_hole_closed_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_hole_closed_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_hole_closed_serialize(const MxHoleClosed *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_hole_closed_value_to_string(m->value))); + if (m->has_location) { + xmlSetProp(el, BAD_CAST "location", BAD_CAST mx_hole_closed_location_to_string(m->location)); + } + return el; +} + +void mx_hole_closed_free(MxHoleClosed *m) { + if (!m) + return; + free(m); +} diff --git a/gen/test/c/mx/mx_hole_closed.h b/gen/test/c/mx/mx_hole_closed.h new file mode 100644 index 000000000..46990626c --- /dev/null +++ b/gen/test/c/mx/mx_hole_closed.h @@ -0,0 +1,29 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_HOLE_CLOSED_H_INCLUDED +#define MX_HOLE_CLOSED_H_INCLUDED + +#include +#include +#include +#include "mx_hole_closed_location.h" +#include "mx_hole_closed_value.h" + +/* + * The hole-closed type represents whether the hole is closed, open, or half-open. The optional + * location attribute indicates which portion of the hole is filled in when the element value is + * half. + */ +typedef struct { + bool has_location; + MxHoleClosedLocation location; /* attribute location */ + MxHoleClosedValue value; /* text content */ +} MxHoleClosed; + +/* NULL on error; the message is in mx_error(). */ +MxHoleClosed *mx_hole_closed_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_hole_closed_serialize(const MxHoleClosed *m, xmlNodePtr parent, const char *tag); +void mx_hole_closed_free(MxHoleClosed *m); + +#endif /* MX_HOLE_CLOSED_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_horizontal_turn.c b/gen/test/c/mx/mx_horizontal_turn.c new file mode 100644 index 000000000..937c7c857 --- /dev/null +++ b/gen/test/c/mx/mx_horizontal_turn.c @@ -0,0 +1,186 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_horizontal_turn.h" + +#include "mx_runtime.h" +#include +#include + +MxHorizontalTurn *mx_horizontal_turn_parse(xmlNodePtr el) { + MxHorizontalTurn *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "slash") == 0) { + m->has_slash = true; + m->slash = mx_yes_no_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "start-note") == 0) { + m->has_start_note = true; + m->start_note = mx_start_note_parse(s); + } else if (strcmp(aname, "trill-step") == 0) { + m->has_trill_step = true; + m->trill_step = mx_trill_step_parse(s); + } else if (strcmp(aname, "two-note-turn") == 0) { + m->has_two_note_turn = true; + m->two_note_turn = mx_two_note_turn_parse(s); + } else if (strcmp(aname, "accelerate") == 0) { + m->has_accelerate = true; + m->accelerate = mx_yes_no_parse(s); + } else if (strcmp(aname, "beats") == 0) { + m->has_beats = true; + m->beats = mx_trill_beats_parse(s); + } else if (strcmp(aname, "second-beat") == 0) { + m->has_second_beat = true; + m->second_beat = mx_percent_parse(s); + } else if (strcmp(aname, "last-beat") == 0) { + m->has_last_beat = true; + m->last_beat = mx_percent_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_horizontal_turn_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_horizontal_turn_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_horizontal_turn_serialize(const MxHorizontalTurn *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_slash) { + xmlSetProp(el, BAD_CAST "slash", BAD_CAST mx_yes_no_to_string(m->slash)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_start_note) { + xmlSetProp(el, BAD_CAST "start-note", BAD_CAST mx_start_note_to_string(m->start_note)); + } + if (m->has_trill_step) { + xmlSetProp(el, BAD_CAST "trill-step", BAD_CAST mx_trill_step_to_string(m->trill_step)); + } + if (m->has_two_note_turn) { + xmlSetProp(el, BAD_CAST "two-note-turn", BAD_CAST mx_two_note_turn_to_string(m->two_note_turn)); + } + if (m->has_accelerate) { + xmlSetProp(el, BAD_CAST "accelerate", BAD_CAST mx_yes_no_to_string(m->accelerate)); + } + if (m->has_beats) { + char *s = mx_trill_beats_to_string(m->beats); + xmlSetProp(el, BAD_CAST "beats", BAD_CAST s); + free(s); + } + if (m->has_second_beat) { + char *s = mx_percent_to_string(m->second_beat); + xmlSetProp(el, BAD_CAST "second-beat", BAD_CAST s); + free(s); + } + if (m->has_last_beat) { + char *s = mx_percent_to_string(m->last_beat); + xmlSetProp(el, BAD_CAST "last-beat", BAD_CAST s); + free(s); + } + return el; +} + +void mx_horizontal_turn_free(MxHorizontalTurn *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_horizontal_turn.h b/gen/test/c/mx/mx_horizontal_turn.h new file mode 100644 index 000000000..a13b2ea26 --- /dev/null +++ b/gen/test/c/mx/mx_horizontal_turn.h @@ -0,0 +1,73 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_HORIZONTAL_TURN_H_INCLUDED +#define MX_HORIZONTAL_TURN_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_percent.h" +#include "mx_start_note.h" +#include "mx_tenths.h" +#include "mx_trill_beats.h" +#include "mx_trill_step.h" +#include "mx_two_note_turn.h" +#include "mx_yes_no.h" + +/* + * The horizontal-turn type represents turn elements that are horizontal rather than vertical. These + * are empty elements with print-style, placement, trill-sound, and slash attributes. If the slash + * attribute is yes, then a vertical line is used to slash the turn; it is no by default. + */ +typedef struct { + bool has_slash; + MxYesNo slash; /* attribute slash */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_start_note; + MxStartNote start_note; /* attribute start-note */ + bool has_trill_step; + MxTrillStep trill_step; /* attribute trill-step */ + bool has_two_note_turn; + MxTwoNoteTurn two_note_turn; /* attribute two-note-turn */ + bool has_accelerate; + MxYesNo accelerate; /* attribute accelerate */ + bool has_beats; + MxTrillBeats beats; /* attribute beats */ + bool has_second_beat; + MxPercent second_beat; /* attribute second-beat */ + bool has_last_beat; + MxPercent last_beat; /* attribute last-beat */ +} MxHorizontalTurn; + +/* NULL on error; the message is in mx_error(). */ +MxHorizontalTurn *mx_horizontal_turn_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_horizontal_turn_serialize(const MxHorizontalTurn *m, xmlNodePtr parent, const char *tag); +void mx_horizontal_turn_free(MxHorizontalTurn *m); + +#endif /* MX_HORIZONTAL_TURN_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_identification.c b/gen/test/c/mx/mx_identification.c new file mode 100644 index 000000000..1e1d6a205 --- /dev/null +++ b/gen/test/c/mx/mx_identification.c @@ -0,0 +1,133 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_identification.h" + +#include "mx_runtime.h" +#include +#include + +MxIdentification *mx_identification_parse(xmlNodePtr el) { + MxIdentification *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_identification_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxIdentificationChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "creator") == 0) { + ch->creator = mx_typed_text_parse(c); + if (!ch->creator) { + mx_identification_free(m); + return NULL; + } + } else if (strcmp(tag, "rights") == 0) { + ch->rights = mx_typed_text_parse(c); + if (!ch->rights) { + mx_identification_free(m); + return NULL; + } + } else if (strcmp(tag, "encoding") == 0) { + ch->encoding = mx_encoding_parse(c); + if (!ch->encoding) { + mx_identification_free(m); + return NULL; + } + } else if (strcmp(tag, "source") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->source = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "relation") == 0) { + ch->relation = mx_typed_text_parse(c); + if (!ch->relation) { + mx_identification_free(m); + return NULL; + } + } else if (strcmp(tag, "miscellaneous") == 0) { + ch->miscellaneous = mx_miscellaneous_parse(c); + if (!ch->miscellaneous) { + mx_identification_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_identification_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_identification_serialize(const MxIdentification *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxIdentificationChild *ch = &m->children[i]; + if (ch->creator) { + mx_typed_text_serialize(ch->creator, el, "creator"); + } else if (ch->rights) { + mx_typed_text_serialize(ch->rights, el, "rights"); + } else if (ch->encoding) { + mx_encoding_serialize(ch->encoding, el, "encoding"); + } else if (ch->source) { + xmlNewTextChild(el, NULL, BAD_CAST "source", BAD_CAST ch->source); + } else if (ch->relation) { + mx_typed_text_serialize(ch->relation, el, "relation"); + } else if (ch->miscellaneous) { + mx_miscellaneous_serialize(ch->miscellaneous, el, "miscellaneous"); + } + } + return el; +} + +void mx_identification_free(MxIdentification *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxIdentificationChild *ch = &m->children[i]; + if (ch->creator) + mx_typed_text_free(ch->creator); + if (ch->rights) + mx_typed_text_free(ch->rights); + if (ch->encoding) + mx_encoding_free(ch->encoding); + free(ch->source); + if (ch->relation) + mx_typed_text_free(ch->relation); + if (ch->miscellaneous) + mx_miscellaneous_free(ch->miscellaneous); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_identification.h b/gen/test/c/mx/mx_identification.h new file mode 100644 index 000000000..7ffe9d82c --- /dev/null +++ b/gen/test/c/mx/mx_identification.h @@ -0,0 +1,41 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_IDENTIFICATION_H_INCLUDED +#define MX_IDENTIFICATION_H_INCLUDED + +#include +#include +#include +#include "mx_encoding.h" +#include "mx_miscellaneous.h" +#include "mx_typed_text.h" + +/* + * Identification contains basic metadata about the score. It includes the information in MuseData + * headers that may apply at a score-wide, movement-wide, or part-wide level. The creator, rights, + * source, and relation elements are based on Dublin Core. + */ +/* One child element of MxIdentification: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxTypedText *creator; + MxTypedText *rights; + MxEncoding *encoding; + char *source; + MxTypedText *relation; + MxMiscellaneous *miscellaneous; +} MxIdentificationChild; + +typedef struct { + MxIdentificationChild *children; /* child elements in document order */ + size_t children_count; +} MxIdentification; + +/* NULL on error; the message is in mx_error(). */ +MxIdentification *mx_identification_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_identification_serialize(const MxIdentification *m, xmlNodePtr parent, const char *tag); +void mx_identification_free(MxIdentification *m); + +#endif /* MX_IDENTIFICATION_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_image.c b/gen/test/c/mx/mx_image.c new file mode 100644 index 000000000..01670db54 --- /dev/null +++ b/gen/test/c/mx/mx_image.c @@ -0,0 +1,140 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_image.h" + +#include "mx_runtime.h" +#include +#include + +MxImage *mx_image_parse(xmlNodePtr el) { + MxImage *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "source") == 0) { + m->has_source = true; + m->source = mx_strdup(s); + } else if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_strdup(s); + } else if (strcmp(aname, "height") == 0) { + m->has_height = true; + m->height = mx_tenths_parse(s); + } else if (strcmp(aname, "width") == 0) { + m->has_width = true; + m->width = mx_tenths_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_image_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_image_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_image_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_image_serialize(const MxImage *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_source) { + xmlSetProp(el, BAD_CAST "source", BAD_CAST m->source); + } + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST m->type); + } + if (m->has_height) { + char *s = mx_tenths_to_string(m->height); + xmlSetProp(el, BAD_CAST "height", BAD_CAST s); + free(s); + } + if (m->has_width) { + char *s = mx_tenths_to_string(m->width); + xmlSetProp(el, BAD_CAST "width", BAD_CAST s); + free(s); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_image_to_string(m->valign)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_image_free(MxImage *m) { + if (!m) + return; + if (m->has_source) + free(m->source); + if (m->has_type) + free(m->type); + if (m->has_id) + free(m->id); + free(m); +} diff --git a/gen/test/c/mx/mx_image.h b/gen/test/c/mx/mx_image.h new file mode 100644 index 000000000..4c90c0228 --- /dev/null +++ b/gen/test/c/mx/mx_image.h @@ -0,0 +1,47 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_IMAGE_H_INCLUDED +#define MX_IMAGE_H_INCLUDED + +#include +#include +#include +#include "mx_left_center_right.h" +#include "mx_tenths.h" +#include "mx_valign_image.h" + +/* + * The image type is used to include graphical images in a score. + */ +typedef struct { + bool has_source; + char *source; /* attribute source */ + bool has_type; + char *type; /* attribute type */ + bool has_height; + MxTenths height; /* attribute height */ + bool has_width; + MxTenths width; /* attribute width */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValignImage valign; /* attribute valign */ + bool has_id; + char *id; /* attribute id */ +} MxImage; + +/* NULL on error; the message is in mx_error(). */ +MxImage *mx_image_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_image_serialize(const MxImage *m, xmlNodePtr parent, const char *tag); +void mx_image_free(MxImage *m); + +#endif /* MX_IMAGE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_instrument.c b/gen/test/c/mx/mx_instrument.c new file mode 100644 index 000000000..330af2e73 --- /dev/null +++ b/gen/test/c/mx/mx_instrument.c @@ -0,0 +1,64 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_instrument.h" + +#include "mx_runtime.h" +#include +#include + +MxInstrument *mx_instrument_parse(xmlNodePtr el) { + MxInstrument *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_instrument_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_instrument_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_instrument_serialize(const MxInstrument *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_instrument_free(MxInstrument *m) { + if (!m) + return; + if (m->has_id) + free(m->id); + free(m); +} diff --git a/gen/test/c/mx/mx_instrument.h b/gen/test/c/mx/mx_instrument.h new file mode 100644 index 000000000..890b3667f --- /dev/null +++ b/gen/test/c/mx/mx_instrument.h @@ -0,0 +1,26 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_INSTRUMENT_H_INCLUDED +#define MX_INSTRUMENT_H_INCLUDED + +#include +#include +#include + +/* + * The instrument type distinguishes between score-instrument elements in a score-part. The id + * attribute is an IDREF back to the score-instrument ID. If multiple score-instruments are + * specified on a score-part, there should be an instrument element for each note in the part. + */ +typedef struct { + bool has_id; + char *id; /* attribute id */ +} MxInstrument; + +/* NULL on error; the message is in mx_error(). */ +MxInstrument *mx_instrument_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_instrument_serialize(const MxInstrument *m, xmlNodePtr parent, const char *tag); +void mx_instrument_free(MxInstrument *m); + +#endif /* MX_INSTRUMENT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_interchangeable.c b/gen/test/c/mx/mx_interchangeable.c new file mode 100644 index 000000000..8705003d5 --- /dev/null +++ b/gen/test/c/mx/mx_interchangeable.c @@ -0,0 +1,114 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_interchangeable.h" + +#include "mx_runtime.h" +#include +#include + +MxInterchangeable *mx_interchangeable_parse(xmlNodePtr el) { + MxInterchangeable *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "symbol") == 0) { + m->has_symbol = true; + m->symbol = mx_time_symbol_parse(s); + } else if (strcmp(aname, "separator") == 0) { + m->has_separator = true; + m->separator = mx_time_separator_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_interchangeable_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxInterchangeableChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "time-relation") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->time_relation = malloc(sizeof(*ch->time_relation)); + if (!ch->time_relation) + abort(); + *ch->time_relation = mx_time_relation_parse(s); + xmlFree(text); + } else if (strcmp(tag, "beats") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->beats = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "beat-type") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->beat_type = mx_strdup(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_interchangeable_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_interchangeable_serialize(const MxInterchangeable *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_symbol) { + xmlSetProp(el, BAD_CAST "symbol", BAD_CAST mx_time_symbol_to_string(m->symbol)); + } + if (m->has_separator) { + xmlSetProp(el, BAD_CAST "separator", BAD_CAST mx_time_separator_to_string(m->separator)); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxInterchangeableChild *ch = &m->children[i]; + if (ch->time_relation) { + xmlNewTextChild(el, NULL, BAD_CAST "time-relation", BAD_CAST mx_time_relation_to_string((*ch->time_relation))); + } else if (ch->beats) { + xmlNewTextChild(el, NULL, BAD_CAST "beats", BAD_CAST ch->beats); + } else if (ch->beat_type) { + xmlNewTextChild(el, NULL, BAD_CAST "beat-type", BAD_CAST ch->beat_type); + } + } + return el; +} + +void mx_interchangeable_free(MxInterchangeable *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxInterchangeableChild *ch = &m->children[i]; + free(ch->time_relation); + free(ch->beats); + free(ch->beat_type); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_interchangeable.h b/gen/test/c/mx/mx_interchangeable.h new file mode 100644 index 000000000..cf5f889f5 --- /dev/null +++ b/gen/test/c/mx/mx_interchangeable.h @@ -0,0 +1,42 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_INTERCHANGEABLE_H_INCLUDED +#define MX_INTERCHANGEABLE_H_INCLUDED + +#include +#include +#include +#include "mx_time_relation.h" +#include "mx_time_separator.h" +#include "mx_time_symbol.h" + +/* + * The interchangeable type is used to represent the second in a pair of interchangeable dual time + * signatures, such as the 6/8 in 3/4 (6/8). A separate symbol attribute value is available compared + * to the time element's symbol attribute, which applies to the first of the dual time signatures. + */ +/* One child element of MxInterchangeable: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxTimeRelation *time_relation; + char *beats; + char *beat_type; +} MxInterchangeableChild; + +typedef struct { + bool has_symbol; + MxTimeSymbol symbol; /* attribute symbol */ + bool has_separator; + MxTimeSeparator separator; /* attribute separator */ + MxInterchangeableChild *children; /* child elements in document order */ + size_t children_count; +} MxInterchangeable; + +/* NULL on error; the message is in mx_error(). */ +MxInterchangeable *mx_interchangeable_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_interchangeable_serialize(const MxInterchangeable *m, xmlNodePtr parent, const char *tag); +void mx_interchangeable_free(MxInterchangeable *m); + +#endif /* MX_INTERCHANGEABLE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_inversion.c b/gen/test/c/mx/mx_inversion.c new file mode 100644 index 000000000..5992c6f3d --- /dev/null +++ b/gen/test/c/mx/mx_inversion.c @@ -0,0 +1,137 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_inversion.h" + +#include "mx_runtime.h" +#include +#include + +MxInversion *mx_inversion_parse(xmlNodePtr el) { + MxInversion *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_inversion_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_parse_int(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_inversion_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_inversion_serialize(const MxInversion *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + { + char *s = mx_format_int(m->value); + xmlAddChild(el, xmlNewText(BAD_CAST s)); + free(s); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_inversion_free(MxInversion *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_inversion.h b/gen/test/c/mx/mx_inversion.h new file mode 100644 index 000000000..0d0e7c20f --- /dev/null +++ b/gen/test/c/mx/mx_inversion.h @@ -0,0 +1,48 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_INVERSION_H_INCLUDED +#define MX_INVERSION_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_tenths.h" + +/* + * The inversion type represents harmony inversions. The value is a number indicating which + * inversion is used: 0 for root position, 1 for first inversion, etc. + */ +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + long value; /* text content */ +} MxInversion; + +/* NULL on error; the message is in mx_error(). */ +MxInversion *mx_inversion_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_inversion_serialize(const MxInversion *m, xmlNodePtr parent, const char *tag); +void mx_inversion_free(MxInversion *m); + +#endif /* MX_INVERSION_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_key.c b/gen/test/c/mx/mx_key.c new file mode 100644 index 000000000..f660a27c9 --- /dev/null +++ b/gen/test/c/mx/mx_key.c @@ -0,0 +1,248 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_key.h" + +#include "mx_runtime.h" +#include +#include + +MxKey *mx_key_parse(xmlNodePtr el) { + MxKey *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_staff_number_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "print-object") == 0) { + m->has_print_object = true; + m->print_object = mx_yes_no_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_key_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxKeyChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "cancel") == 0) { + ch->cancel = mx_cancel_parse(c); + if (!ch->cancel) { + mx_key_free(m); + return NULL; + } + } else if (strcmp(tag, "fifths") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->fifths = malloc(sizeof(*ch->fifths)); + if (!ch->fifths) + abort(); + *ch->fifths = mx_fifths_parse(s); + xmlFree(text); + } else if (strcmp(tag, "mode") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->mode = malloc(sizeof(*ch->mode)); + if (!ch->mode) + abort(); + *ch->mode = mx_mode_parse(s); + xmlFree(text); + } else if (strcmp(tag, "key-step") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->key_step = malloc(sizeof(*ch->key_step)); + if (!ch->key_step) + abort(); + *ch->key_step = mx_step_parse(s); + xmlFree(text); + } else if (strcmp(tag, "key-alter") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->key_alter = malloc(sizeof(*ch->key_alter)); + if (!ch->key_alter) + abort(); + *ch->key_alter = mx_semitones_parse(s); + xmlFree(text); + } else if (strcmp(tag, "key-accidental") == 0) { + ch->key_accidental = mx_key_accidental_parse(c); + if (!ch->key_accidental) { + mx_key_free(m); + return NULL; + } + } else if (strcmp(tag, "key-octave") == 0) { + ch->key_octave = mx_key_octave_parse(c); + if (!ch->key_octave) { + mx_key_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_key_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_key_serialize(const MxKey *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_number) { + char *s = mx_staff_number_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_print_object) { + xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxKeyChild *ch = &m->children[i]; + if (ch->cancel) { + mx_cancel_serialize(ch->cancel, el, "cancel"); + } else if (ch->fifths) { + char *s = mx_fifths_to_string((*ch->fifths)); + xmlNewTextChild(el, NULL, BAD_CAST "fifths", BAD_CAST s); + free(s); + } else if (ch->mode) { + xmlNewTextChild(el, NULL, BAD_CAST "mode", BAD_CAST (*ch->mode)); + } else if (ch->key_step) { + xmlNewTextChild(el, NULL, BAD_CAST "key-step", BAD_CAST mx_step_to_string((*ch->key_step))); + } else if (ch->key_alter) { + char *s = mx_semitones_to_string((*ch->key_alter)); + xmlNewTextChild(el, NULL, BAD_CAST "key-alter", BAD_CAST s); + free(s); + } else if (ch->key_accidental) { + mx_key_accidental_serialize(ch->key_accidental, el, "key-accidental"); + } else if (ch->key_octave) { + mx_key_octave_serialize(ch->key_octave, el, "key-octave"); + } + } + return el; +} + +void mx_key_free(MxKey *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxKeyChild *ch = &m->children[i]; + if (ch->cancel) + mx_cancel_free(ch->cancel); + free(ch->fifths); + if (ch->mode) { + free((*ch->mode)); + free(ch->mode); + } + free(ch->key_step); + free(ch->key_alter); + if (ch->key_accidental) + mx_key_accidental_free(ch->key_accidental); + if (ch->key_octave) + mx_key_octave_free(ch->key_octave); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_key.h b/gen/test/c/mx/mx_key.h new file mode 100644 index 000000000..88d70436a --- /dev/null +++ b/gen/test/c/mx/mx_key.h @@ -0,0 +1,79 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_KEY_H_INCLUDED +#define MX_KEY_H_INCLUDED + +#include +#include +#include +#include "mx_cancel.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_fifths.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_key_accidental.h" +#include "mx_key_octave.h" +#include "mx_mode.h" +#include "mx_semitones.h" +#include "mx_staff_number.h" +#include "mx_step.h" +#include "mx_tenths.h" +#include "mx_yes_no.h" + +/* + * The key type represents a key signature. Both traditional and non-traditional key signatures are + * supported. The optional number attribute refers to staff numbers. If absent, the key signature + * applies to all staves in the part. Key signatures appear at the start of each system unless the + * print-object attribute has been set to "no". + */ +/* One child element of MxKey: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxCancel *cancel; + MxFifths *fifths; + MxMode *mode; + MxStep *key_step; + MxSemitones *key_alter; + MxKeyAccidental *key_accidental; + MxKeyOctave *key_octave; +} MxKeyChild; + +typedef struct { + bool has_number; + MxStaffNumber number; /* attribute number */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_print_object; + MxYesNo print_object; /* attribute print-object */ + bool has_id; + char *id; /* attribute id */ + MxKeyChild *children; /* child elements in document order */ + size_t children_count; +} MxKey; + +/* NULL on error; the message is in mx_error(). */ +MxKey *mx_key_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_key_serialize(const MxKey *m, xmlNodePtr parent, const char *tag); +void mx_key_free(MxKey *m); + +#endif /* MX_KEY_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_key_accidental.c b/gen/test/c/mx/mx_key_accidental.c new file mode 100644 index 000000000..1741037a6 --- /dev/null +++ b/gen/test/c/mx/mx_key_accidental.c @@ -0,0 +1,71 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_key_accidental.h" + +#include "mx_runtime.h" +#include +#include + +MxKeyAccidental *mx_key_accidental_parse(xmlNodePtr el) { + MxKeyAccidental *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "smufl") == 0) { + m->has_smufl = true; + m->smufl = mx_smufl_accidental_glyph_name_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_key_accidental_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_accidental_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_key_accidental_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_key_accidental_serialize(const MxKeyAccidental *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_accidental_value_to_string(m->value))); + if (m->has_smufl) { + xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); + } + return el; +} + +void mx_key_accidental_free(MxKeyAccidental *m) { + if (!m) + return; + if (m->has_smufl) + free(m->smufl); + free(m); +} diff --git a/gen/test/c/mx/mx_key_accidental.h b/gen/test/c/mx/mx_key_accidental.h new file mode 100644 index 000000000..72113e958 --- /dev/null +++ b/gen/test/c/mx/mx_key_accidental.h @@ -0,0 +1,29 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_KEY_ACCIDENTAL_H_INCLUDED +#define MX_KEY_ACCIDENTAL_H_INCLUDED + +#include +#include +#include +#include "mx_accidental_value.h" +#include "mx_smufl_accidental_glyph_name.h" + +/* + * The key-accidental type indicates the accidental to be displayed in a non-traditional key + * signature, represented in the same manner as the accidental type without the formatting + * attributes. + */ +typedef struct { + bool has_smufl; + MxSMUFLAccidentalGlyphName smufl; /* attribute smufl */ + MxAccidentalValue value; /* text content */ +} MxKeyAccidental; + +/* NULL on error; the message is in mx_error(). */ +MxKeyAccidental *mx_key_accidental_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_key_accidental_serialize(const MxKeyAccidental *m, xmlNodePtr parent, const char *tag); +void mx_key_accidental_free(MxKeyAccidental *m); + +#endif /* MX_KEY_ACCIDENTAL_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_key_octave.c b/gen/test/c/mx/mx_key_octave.c new file mode 100644 index 000000000..43582aede --- /dev/null +++ b/gen/test/c/mx/mx_key_octave.c @@ -0,0 +1,81 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_key_octave.h" + +#include "mx_runtime.h" +#include +#include + +MxKeyOctave *mx_key_octave_parse(xmlNodePtr el) { + MxKeyOctave *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_parse_int(s); + } else if (strcmp(aname, "cancel") == 0) { + m->has_cancel = true; + m->cancel = mx_yes_no_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_key_octave_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_octave_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_key_octave_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_key_octave_serialize(const MxKeyOctave *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + { + char *s = mx_octave_to_string(m->value); + xmlAddChild(el, xmlNewText(BAD_CAST s)); + free(s); + } + if (m->has_number) { + char *s = mx_format_int(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_cancel) { + xmlSetProp(el, BAD_CAST "cancel", BAD_CAST mx_yes_no_to_string(m->cancel)); + } + return el; +} + +void mx_key_octave_free(MxKeyOctave *m) { + if (!m) + return; + free(m); +} diff --git a/gen/test/c/mx/mx_key_octave.h b/gen/test/c/mx/mx_key_octave.h new file mode 100644 index 000000000..cbaea53d7 --- /dev/null +++ b/gen/test/c/mx/mx_key_octave.h @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_KEY_OCTAVE_H_INCLUDED +#define MX_KEY_OCTAVE_H_INCLUDED + +#include +#include +#include +#include "mx_octave.h" +#include "mx_yes_no.h" + +/* + * The key-octave element specifies in which octave an element of a key signature appears. The + * content specifies the octave value using the same values as the display-octave element. The + * number attribute is a positive integer that refers to the key signature element in left-to-right + * order. If the cancel attribute is set to yes, then this number refers to the canceling key + * signature specified by the cancel element in the parent key element. The cancel attribute cannot + * be set to yes if there is no corresponding cancel element within the parent key element. It is no + * by default. + */ +typedef struct { + bool has_number; + long number; /* attribute number */ + bool has_cancel; + MxYesNo cancel; /* attribute cancel */ + MxOctave value; /* text content */ +} MxKeyOctave; + +/* NULL on error; the message is in mx_error(). */ +MxKeyOctave *mx_key_octave_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_key_octave_serialize(const MxKeyOctave *m, xmlNodePtr parent, const char *tag); +void mx_key_octave_free(MxKeyOctave *m); + +#endif /* MX_KEY_OCTAVE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_kind.c b/gen/test/c/mx/mx_kind.c new file mode 100644 index 000000000..3a9a3d209 --- /dev/null +++ b/gen/test/c/mx/mx_kind.c @@ -0,0 +1,177 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_kind.h" + +#include "mx_runtime.h" +#include +#include + +MxKind *mx_kind_parse(xmlNodePtr el) { + MxKind *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "use-symbols") == 0) { + m->has_use_symbols = true; + m->use_symbols = mx_yes_no_parse(s); + } else if (strcmp(aname, "text") == 0) { + m->has_text = true; + m->text = mx_strdup(s); + } else if (strcmp(aname, "stack-degrees") == 0) { + m->has_stack_degrees = true; + m->stack_degrees = mx_yes_no_parse(s); + } else if (strcmp(aname, "parentheses-degrees") == 0) { + m->has_parentheses_degrees = true; + m->parentheses_degrees = mx_yes_no_parse(s); + } else if (strcmp(aname, "bracket-degrees") == 0) { + m->has_bracket_degrees = true; + m->bracket_degrees = mx_yes_no_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_kind_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_kind_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_kind_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_kind_serialize(const MxKind *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_kind_value_to_string(m->value))); + if (m->has_use_symbols) { + xmlSetProp(el, BAD_CAST "use-symbols", BAD_CAST mx_yes_no_to_string(m->use_symbols)); + } + if (m->has_text) { + xmlSetProp(el, BAD_CAST "text", BAD_CAST m->text); + } + if (m->has_stack_degrees) { + xmlSetProp(el, BAD_CAST "stack-degrees", BAD_CAST mx_yes_no_to_string(m->stack_degrees)); + } + if (m->has_parentheses_degrees) { + xmlSetProp(el, BAD_CAST "parentheses-degrees", BAD_CAST mx_yes_no_to_string(m->parentheses_degrees)); + } + if (m->has_bracket_degrees) { + xmlSetProp(el, BAD_CAST "bracket-degrees", BAD_CAST mx_yes_no_to_string(m->bracket_degrees)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + return el; +} + +void mx_kind_free(MxKind *m) { + if (!m) + return; + if (m->has_text) + free(m->text); + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_kind.h b/gen/test/c/mx/mx_kind.h new file mode 100644 index 000000000..3c43a7ad2 --- /dev/null +++ b/gen/test/c/mx/mx_kind.h @@ -0,0 +1,80 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_KIND_H_INCLUDED +#define MX_KIND_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_kind_value.h" +#include "mx_left_center_right.h" +#include "mx_tenths.h" +#include "mx_valign.h" +#include "mx_yes_no.h" + +/* + * Kind indicates the type of chord. Degree elements can then add, subtract, or alter from these + * starting points The attributes are used to indicate the formatting of the symbol. Since the kind + * element is the constant in all the harmony-chord groups that can make up a polychord, many + * formatting attributes are here. The use-symbols attribute is yes if the kind should be + * represented when possible with harmony symbols rather than letters and numbers. These symbols + * include: major: a triangle, like Unicode 25B3 minor: -, like Unicode 002D augmented: +, like + * Unicode 002B diminished: °, like Unicode 00B0 half-diminished: ø, like Unicode 00F8 For the + * major-minor kind, only the minor symbol is used when use-symbols is yes. The major symbol is set + * using the symbol attribute in the degree-value element. The corresponding degree-alter value will + * usually be 0 in this case. The text attribute describes how the kind should be spelled in a + * score. If use-symbols is yes, the value of the text attribute follows the symbol. The + * stack-degrees attribute is yes if the degree elements should be stacked above each other. The + * parentheses-degrees attribute is yes if all the degrees should be in parentheses. The + * bracket-degrees attribute is yes if all the degrees should be in a bracket. If not specified, + * these values are implementation-specific. The alignment attributes are for the entire + * harmony-chord group of which this kind element is a part. + */ +typedef struct { + bool has_use_symbols; + MxYesNo use_symbols; /* attribute use-symbols */ + bool has_text; + char *text; /* attribute text */ + bool has_stack_degrees; + MxYesNo stack_degrees; /* attribute stack-degrees */ + bool has_parentheses_degrees; + MxYesNo parentheses_degrees; /* attribute parentheses-degrees */ + bool has_bracket_degrees; + MxYesNo bracket_degrees; /* attribute bracket-degrees */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ + MxKindValue value; /* text content */ +} MxKind; + +/* NULL on error; the message is in mx_error(). */ +MxKind *mx_kind_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_kind_serialize(const MxKind *m, xmlNodePtr parent, const char *tag); +void mx_kind_free(MxKind *m); + +#endif /* MX_KIND_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_level.c b/gen/test/c/mx/mx_level.c new file mode 100644 index 000000000..85a09d6fc --- /dev/null +++ b/gen/test/c/mx/mx_level.c @@ -0,0 +1,88 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_level.h" + +#include "mx_runtime.h" +#include +#include + +MxLevel *mx_level_parse(xmlNodePtr el) { + MxLevel *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "reference") == 0) { + m->has_reference = true; + m->reference = mx_yes_no_parse(s); + } else if (strcmp(aname, "parentheses") == 0) { + m->has_parentheses = true; + m->parentheses = mx_yes_no_parse(s); + } else if (strcmp(aname, "bracket") == 0) { + m->has_bracket = true; + m->bracket = mx_yes_no_parse(s); + } else if (strcmp(aname, "size") == 0) { + m->has_size = true; + m->size = mx_symbol_size_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_level_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_level_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_level_serialize(const MxLevel *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_reference) { + xmlSetProp(el, BAD_CAST "reference", BAD_CAST mx_yes_no_to_string(m->reference)); + } + if (m->has_parentheses) { + xmlSetProp(el, BAD_CAST "parentheses", BAD_CAST mx_yes_no_to_string(m->parentheses)); + } + if (m->has_bracket) { + xmlSetProp(el, BAD_CAST "bracket", BAD_CAST mx_yes_no_to_string(m->bracket)); + } + if (m->has_size) { + xmlSetProp(el, BAD_CAST "size", BAD_CAST mx_symbol_size_to_string(m->size)); + } + return el; +} + +void mx_level_free(MxLevel *m) { + if (!m) + return; + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_level.h b/gen/test/c/mx/mx_level.h new file mode 100644 index 000000000..856b2dff5 --- /dev/null +++ b/gen/test/c/mx/mx_level.h @@ -0,0 +1,37 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_LEVEL_H_INCLUDED +#define MX_LEVEL_H_INCLUDED + +#include +#include +#include +#include "mx_symbol_size.h" +#include "mx_yes_no.h" + +/* + * The level type is used to specify editorial information for different MusicXML elements. If the + * reference attribute for the level element is yes, this indicates editorial information that is + * for display only and should not affect playback. For instance, a modern edition of older music + * may set reference="yes" on the attributes containing the music's original clef, key, and time + * signature. It is no by default. + */ +typedef struct { + bool has_reference; + MxYesNo reference; /* attribute reference */ + bool has_parentheses; + MxYesNo parentheses; /* attribute parentheses */ + bool has_bracket; + MxYesNo bracket; /* attribute bracket */ + bool has_size; + MxSymbolSize size; /* attribute size */ + char *value; /* text content */ +} MxLevel; + +/* NULL on error; the message is in mx_error(). */ +MxLevel *mx_level_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_level_serialize(const MxLevel *m, xmlNodePtr parent, const char *tag); +void mx_level_free(MxLevel *m); + +#endif /* MX_LEVEL_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_line_width.c b/gen/test/c/mx/mx_line_width.c new file mode 100644 index 000000000..b22f9277f --- /dev/null +++ b/gen/test/c/mx/mx_line_width.c @@ -0,0 +1,75 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_line_width.h" + +#include "mx_runtime.h" +#include +#include + +MxLineWidth *mx_line_width_parse(xmlNodePtr el) { + MxLineWidth *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_line_width_type_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_line_width_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_tenths_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_line_width_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_line_width_serialize(const MxLineWidth *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + { + char *s = mx_tenths_to_string(m->value); + xmlAddChild(el, xmlNewText(BAD_CAST s)); + free(s); + } + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST m->type); + } + return el; +} + +void mx_line_width_free(MxLineWidth *m) { + if (!m) + return; + if (m->has_type) + free(m->type); + free(m); +} diff --git a/gen/test/c/mx/mx_line_width.h b/gen/test/c/mx/mx_line_width.h new file mode 100644 index 000000000..4b0027594 --- /dev/null +++ b/gen/test/c/mx/mx_line_width.h @@ -0,0 +1,30 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_LINE_WIDTH_H_INCLUDED +#define MX_LINE_WIDTH_H_INCLUDED + +#include +#include +#include +#include "mx_line_width_type.h" +#include "mx_tenths.h" + +/* + * The line-width type indicates the width of a line type in tenths. The type attribute defines what + * type of line is being defined. Values include beam, bracket, dashes, enclosure, ending, extend, + * heavy barline, leger, light barline, octave shift, pedal, slur middle, slur tip, staff, stem, tie + * middle, tie tip, tuplet bracket, and wedge. The text content is expressed in tenths. + */ +typedef struct { + bool has_type; + MxLineWidthType type; /* attribute type */ + MxTenths value; /* text content */ +} MxLineWidth; + +/* NULL on error; the message is in mx_error(). */ +MxLineWidth *mx_line_width_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_line_width_serialize(const MxLineWidth *m, xmlNodePtr parent, const char *tag); +void mx_line_width_free(MxLineWidth *m); + +#endif /* MX_LINE_WIDTH_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_link.c b/gen/test/c/mx/mx_link.c new file mode 100644 index 000000000..25f66734d --- /dev/null +++ b/gen/test/c/mx/mx_link.c @@ -0,0 +1,160 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_link.h" + +#include "mx_runtime.h" +#include +#include + +MxLink *mx_link_parse(xmlNodePtr el) { + MxLink *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "name") == 0) { + m->has_name = true; + m->name = mx_strdup(s); + } else if (strcmp(aname, "xlink:href") == 0) { + m->has_xlink_href = true; + m->xlink_href = mx_strdup(s); + } else if (strcmp(aname, "xlink:type") == 0) { + m->has_xlink_type = true; + m->xlink_type = mx_strdup(s); + } else if (strcmp(aname, "xlink:role") == 0) { + m->has_xlink_role = true; + m->xlink_role = mx_strdup(s); + } else if (strcmp(aname, "xlink:title") == 0) { + m->has_xlink_title = true; + m->xlink_title = mx_strdup(s); + } else if (strcmp(aname, "xlink:show") == 0) { + m->has_xlink_show = true; + m->xlink_show = mx_strdup(s); + } else if (strcmp(aname, "xlink:actuate") == 0) { + m->has_xlink_actuate = true; + m->xlink_actuate = mx_strdup(s); + } else if (strcmp(aname, "element") == 0) { + m->has_element = true; + m->element = mx_strdup(s); + } else if (strcmp(aname, "position") == 0) { + m->has_position = true; + m->position = mx_parse_int(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_link_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_link_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_link_serialize(const MxLink *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_name) { + xmlSetProp(el, BAD_CAST "name", BAD_CAST m->name); + } + if (m->has_xlink_href) { + xmlSetProp(el, BAD_CAST "xlink:href", BAD_CAST m->xlink_href); + } + if (m->has_xlink_type) { + xmlSetProp(el, BAD_CAST "xlink:type", BAD_CAST m->xlink_type); + } + if (m->has_xlink_role) { + xmlSetProp(el, BAD_CAST "xlink:role", BAD_CAST m->xlink_role); + } + if (m->has_xlink_title) { + xmlSetProp(el, BAD_CAST "xlink:title", BAD_CAST m->xlink_title); + } + if (m->has_xlink_show) { + xmlSetProp(el, BAD_CAST "xlink:show", BAD_CAST m->xlink_show); + } + if (m->has_xlink_actuate) { + xmlSetProp(el, BAD_CAST "xlink:actuate", BAD_CAST m->xlink_actuate); + } + if (m->has_element) { + xmlSetProp(el, BAD_CAST "element", BAD_CAST m->element); + } + if (m->has_position) { + char *s = mx_format_int(m->position); + xmlSetProp(el, BAD_CAST "position", BAD_CAST s); + free(s); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + return el; +} + +void mx_link_free(MxLink *m) { + if (!m) + return; + if (m->has_name) + free(m->name); + if (m->has_xlink_href) + free(m->xlink_href); + if (m->has_xlink_type) + free(m->xlink_type); + if (m->has_xlink_role) + free(m->xlink_role); + if (m->has_xlink_title) + free(m->xlink_title); + if (m->has_xlink_show) + free(m->xlink_show); + if (m->has_xlink_actuate) + free(m->xlink_actuate); + if (m->has_element) + free(m->element); + free(m); +} diff --git a/gen/test/c/mx/mx_link.h b/gen/test/c/mx/mx_link.h new file mode 100644 index 000000000..7245ec075 --- /dev/null +++ b/gen/test/c/mx/mx_link.h @@ -0,0 +1,51 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_LINK_H_INCLUDED +#define MX_LINK_H_INCLUDED + +#include +#include +#include +#include "mx_tenths.h" + +/* + * The link type serves as an outgoing simple XLink. It is also used to connect a MusicXML score + * with a MusicXML opus. If a relative link is used within a document that is part of a compressed + * MusicXML file, the link is relative to the root folder of the zip file. + */ +typedef struct { + bool has_name; + char *name; /* attribute name */ + bool has_xlink_href; + char *xlink_href; /* attribute xlink:href */ + bool has_xlink_type; + char *xlink_type; /* attribute xlink:type */ + bool has_xlink_role; + char *xlink_role; /* attribute xlink:role */ + bool has_xlink_title; + char *xlink_title; /* attribute xlink:title */ + bool has_xlink_show; + char *xlink_show; /* attribute xlink:show */ + bool has_xlink_actuate; + char *xlink_actuate; /* attribute xlink:actuate */ + bool has_element; + char *element; /* attribute element */ + bool has_position; + long position; /* attribute position */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ +} MxLink; + +/* NULL on error; the message is in mx_error(). */ +MxLink *mx_link_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_link_serialize(const MxLink *m, xmlNodePtr parent, const char *tag); +void mx_link_free(MxLink *m); + +#endif /* MX_LINK_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_lyric.c b/gen/test/c/mx/mx_lyric.c new file mode 100644 index 000000000..44c0d2b5c --- /dev/null +++ b/gen/test/c/mx/mx_lyric.c @@ -0,0 +1,266 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_lyric.h" + +#include "mx_runtime.h" +#include +#include + +MxLyric *mx_lyric_parse(xmlNodePtr el) { + MxLyric *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_strdup(s); + } else if (strcmp(aname, "name") == 0) { + m->has_name = true; + m->name = mx_strdup(s); + } else if (strcmp(aname, "time-only") == 0) { + m->has_time_only = true; + m->time_only = mx_time_only_parse(s); + } else if (strcmp(aname, "justify") == 0) { + m->has_justify = true; + m->justify = mx_left_center_right_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "print-object") == 0) { + m->has_print_object = true; + m->print_object = mx_yes_no_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_lyric_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxLyricChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "syllabic") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->syllabic = malloc(sizeof(*ch->syllabic)); + if (!ch->syllabic) + abort(); + *ch->syllabic = mx_syllabic_parse(s); + xmlFree(text); + } else if (strcmp(tag, "text") == 0) { + ch->text = mx_text_element_data_parse(c); + if (!ch->text) { + mx_lyric_free(m); + return NULL; + } + } else if (strcmp(tag, "elision") == 0) { + ch->elision = mx_elision_parse(c); + if (!ch->elision) { + mx_lyric_free(m); + return NULL; + } + } else if (strcmp(tag, "extend") == 0) { + ch->extend = mx_extend_parse(c); + if (!ch->extend) { + mx_lyric_free(m); + return NULL; + } + } else if (strcmp(tag, "laughing") == 0) { + ch->laughing = mx_empty_parse(c); + if (!ch->laughing) { + mx_lyric_free(m); + return NULL; + } + } else if (strcmp(tag, "humming") == 0) { + ch->humming = mx_empty_parse(c); + if (!ch->humming) { + mx_lyric_free(m); + return NULL; + } + } else if (strcmp(tag, "end-line") == 0) { + ch->end_line = mx_empty_parse(c); + if (!ch->end_line) { + mx_lyric_free(m); + return NULL; + } + } else if (strcmp(tag, "end-paragraph") == 0) { + ch->end_paragraph = mx_empty_parse(c); + if (!ch->end_paragraph) { + mx_lyric_free(m); + return NULL; + } + } else if (strcmp(tag, "footnote") == 0) { + ch->footnote = mx_formatted_text_parse(c); + if (!ch->footnote) { + mx_lyric_free(m); + return NULL; + } + } else if (strcmp(tag, "level") == 0) { + ch->level = mx_level_parse(c); + if (!ch->level) { + mx_lyric_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_lyric_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_lyric_serialize(const MxLyric *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_number) { + xmlSetProp(el, BAD_CAST "number", BAD_CAST m->number); + } + if (m->has_name) { + xmlSetProp(el, BAD_CAST "name", BAD_CAST m->name); + } + if (m->has_time_only) { + xmlSetProp(el, BAD_CAST "time-only", BAD_CAST m->time_only); + } + if (m->has_justify) { + xmlSetProp(el, BAD_CAST "justify", BAD_CAST mx_left_center_right_to_string(m->justify)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_print_object) { + xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxLyricChild *ch = &m->children[i]; + if (ch->syllabic) { + xmlNewTextChild(el, NULL, BAD_CAST "syllabic", BAD_CAST mx_syllabic_to_string((*ch->syllabic))); + } else if (ch->text) { + mx_text_element_data_serialize(ch->text, el, "text"); + } else if (ch->elision) { + mx_elision_serialize(ch->elision, el, "elision"); + } else if (ch->extend) { + mx_extend_serialize(ch->extend, el, "extend"); + } else if (ch->laughing) { + mx_empty_serialize(ch->laughing, el, "laughing"); + } else if (ch->humming) { + mx_empty_serialize(ch->humming, el, "humming"); + } else if (ch->end_line) { + mx_empty_serialize(ch->end_line, el, "end-line"); + } else if (ch->end_paragraph) { + mx_empty_serialize(ch->end_paragraph, el, "end-paragraph"); + } else if (ch->footnote) { + mx_formatted_text_serialize(ch->footnote, el, "footnote"); + } else if (ch->level) { + mx_level_serialize(ch->level, el, "level"); + } + } + return el; +} + +void mx_lyric_free(MxLyric *m) { + if (!m) + return; + if (m->has_number) + free(m->number); + if (m->has_name) + free(m->name); + if (m->has_time_only) + free(m->time_only); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxLyricChild *ch = &m->children[i]; + free(ch->syllabic); + if (ch->text) + mx_text_element_data_free(ch->text); + if (ch->elision) + mx_elision_free(ch->elision); + if (ch->extend) + mx_extend_free(ch->extend); + if (ch->laughing) + mx_empty_free(ch->laughing); + if (ch->humming) + mx_empty_free(ch->humming); + if (ch->end_line) + mx_empty_free(ch->end_line); + if (ch->end_paragraph) + mx_empty_free(ch->end_paragraph); + if (ch->footnote) + mx_formatted_text_free(ch->footnote); + if (ch->level) + mx_level_free(ch->level); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_lyric.h b/gen/test/c/mx/mx_lyric.h new file mode 100644 index 000000000..33af53a7d --- /dev/null +++ b/gen/test/c/mx/mx_lyric.h @@ -0,0 +1,86 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_LYRIC_H_INCLUDED +#define MX_LYRIC_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_elision.h" +#include "mx_empty.h" +#include "mx_extend.h" +#include "mx_formatted_text.h" +#include "mx_left_center_right.h" +#include "mx_level.h" +#include "mx_syllabic.h" +#include "mx_tenths.h" +#include "mx_text_element_data.h" +#include "mx_time_only.h" +#include "mx_yes_no.h" + +/* + * The lyric type represents text underlays for lyrics, based on Humdrum with support for other + * formats. Two text elements that are not separated by an elision element are part of the same + * syllable, but may have different text formatting. The MusicXML XSD is more strict than the DTD in + * enforcing this by disallowing a second syllabic element unless preceded by an elision element. + * The lyric number indicates multiple lines, though a name can be used as well (as in Finale's + * verse / chorus / section specification). Justification is center by default; placement is below + * by default. The print-object attribute can override a note's print-lyric attribute in cases where + * only some lyrics on a note are printed, as when lyrics for later verses are printed in a block of + * text rather than with each note. The time-only attribute precisely specifies which lyrics are to + * be sung which time through a repeated section. + */ +/* One child element of MxLyric: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxSyllabic *syllabic; + MxTextElementData *text; + MxElision *elision; + MxExtend *extend; + MxEmpty *laughing; + MxEmpty *humming; + MxEmpty *end_line; + MxEmpty *end_paragraph; + MxFormattedText *footnote; + MxLevel *level; +} MxLyricChild; + +typedef struct { + bool has_number; + char *number; /* attribute number */ + bool has_name; + char *name; /* attribute name */ + bool has_time_only; + MxTimeOnly time_only; /* attribute time-only */ + bool has_justify; + MxLeftCenterRight justify; /* attribute justify */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_color; + MxColor color; /* attribute color */ + bool has_print_object; + MxYesNo print_object; /* attribute print-object */ + bool has_id; + char *id; /* attribute id */ + MxLyricChild *children; /* child elements in document order */ + size_t children_count; +} MxLyric; + +/* NULL on error; the message is in mx_error(). */ +MxLyric *mx_lyric_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_lyric_serialize(const MxLyric *m, xmlNodePtr parent, const char *tag); +void mx_lyric_free(MxLyric *m); + +#endif /* MX_LYRIC_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_lyric_font.c b/gen/test/c/mx/mx_lyric_font.c new file mode 100644 index 000000000..453c1d514 --- /dev/null +++ b/gen/test/c/mx/mx_lyric_font.c @@ -0,0 +1,102 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_lyric_font.h" + +#include "mx_runtime.h" +#include +#include + +MxLyricFont *mx_lyric_font_parse(xmlNodePtr el) { + MxLyricFont *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_strdup(s); + } else if (strcmp(aname, "name") == 0) { + m->has_name = true; + m->name = mx_strdup(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_lyric_font_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_lyric_font_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_lyric_font_serialize(const MxLyricFont *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_number) { + xmlSetProp(el, BAD_CAST "number", BAD_CAST m->number); + } + if (m->has_name) { + xmlSetProp(el, BAD_CAST "name", BAD_CAST m->name); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + return el; +} + +void mx_lyric_font_free(MxLyricFont *m) { + if (!m) + return; + if (m->has_number) + free(m->number); + if (m->has_name) + free(m->name); + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + free(m); +} diff --git a/gen/test/c/mx/mx_lyric_font.h b/gen/test/c/mx/mx_lyric_font.h new file mode 100644 index 000000000..1fdcb7e18 --- /dev/null +++ b/gen/test/c/mx/mx_lyric_font.h @@ -0,0 +1,38 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_LYRIC_FONT_H_INCLUDED +#define MX_LYRIC_FONT_H_INCLUDED + +#include +#include +#include +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" + +/* + * The lyric-font type specifies the default font for a particular name and number of lyric. + */ +typedef struct { + bool has_number; + char *number; /* attribute number */ + bool has_name; + char *name; /* attribute name */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ +} MxLyricFont; + +/* NULL on error; the message is in mx_error(). */ +MxLyricFont *mx_lyric_font_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_lyric_font_serialize(const MxLyricFont *m, xmlNodePtr parent, const char *tag); +void mx_lyric_font_free(MxLyricFont *m); + +#endif /* MX_LYRIC_FONT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_lyric_language.c b/gen/test/c/mx/mx_lyric_language.c new file mode 100644 index 000000000..87564daa9 --- /dev/null +++ b/gen/test/c/mx/mx_lyric_language.c @@ -0,0 +1,80 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_lyric_language.h" + +#include "mx_runtime.h" +#include +#include + +MxLyricLanguage *mx_lyric_language_parse(xmlNodePtr el) { + MxLyricLanguage *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_strdup(s); + } else if (strcmp(aname, "name") == 0) { + m->has_name = true; + m->name = mx_strdup(s); + } else if (strcmp(aname, "xml:lang") == 0) { + m->has_xml_lang = true; + m->xml_lang = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_lyric_language_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_lyric_language_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_lyric_language_serialize(const MxLyricLanguage *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_number) { + xmlSetProp(el, BAD_CAST "number", BAD_CAST m->number); + } + if (m->has_name) { + xmlSetProp(el, BAD_CAST "name", BAD_CAST m->name); + } + if (m->has_xml_lang) { + xmlSetProp(el, BAD_CAST "xml:lang", BAD_CAST m->xml_lang); + } + return el; +} + +void mx_lyric_language_free(MxLyricLanguage *m) { + if (!m) + return; + if (m->has_number) + free(m->number); + if (m->has_name) + free(m->name); + if (m->has_xml_lang) + free(m->xml_lang); + free(m); +} diff --git a/gen/test/c/mx/mx_lyric_language.h b/gen/test/c/mx/mx_lyric_language.h new file mode 100644 index 000000000..79e457aec --- /dev/null +++ b/gen/test/c/mx/mx_lyric_language.h @@ -0,0 +1,28 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_LYRIC_LANGUAGE_H_INCLUDED +#define MX_LYRIC_LANGUAGE_H_INCLUDED + +#include +#include +#include + +/* + * The lyric-language type specifies the default language for a particular name and number of lyric. + */ +typedef struct { + bool has_number; + char *number; /* attribute number */ + bool has_name; + char *name; /* attribute name */ + bool has_xml_lang; + char *xml_lang; /* attribute xml:lang */ +} MxLyricLanguage; + +/* NULL on error; the message is in mx_error(). */ +MxLyricLanguage *mx_lyric_language_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_lyric_language_serialize(const MxLyricLanguage *m, xmlNodePtr parent, const char *tag); +void mx_lyric_language_free(MxLyricLanguage *m); + +#endif /* MX_LYRIC_LANGUAGE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_measure_layout.c b/gen/test/c/mx/mx_measure_layout.c new file mode 100644 index 000000000..0d0b1594b --- /dev/null +++ b/gen/test/c/mx/mx_measure_layout.c @@ -0,0 +1,88 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_measure_layout.h" + +#include "mx_runtime.h" +#include +#include + +MxMeasureLayout *mx_measure_layout_parse(xmlNodePtr el) { + MxMeasureLayout *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_measure_layout_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxMeasureLayoutChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "measure-distance") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->measure_distance = malloc(sizeof(*ch->measure_distance)); + if (!ch->measure_distance) + abort(); + *ch->measure_distance = mx_tenths_parse(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_measure_layout_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_measure_layout_serialize(const MxMeasureLayout *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxMeasureLayoutChild *ch = &m->children[i]; + if (ch->measure_distance) { + char *s = mx_tenths_to_string((*ch->measure_distance)); + xmlNewTextChild(el, NULL, BAD_CAST "measure-distance", BAD_CAST s); + free(s); + } + } + return el; +} + +void mx_measure_layout_free(MxMeasureLayout *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxMeasureLayoutChild *ch = &m->children[i]; + free(ch->measure_distance); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_measure_layout.h b/gen/test/c/mx/mx_measure_layout.h new file mode 100644 index 000000000..d3c7ef50b --- /dev/null +++ b/gen/test/c/mx/mx_measure_layout.h @@ -0,0 +1,32 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_MEASURE_LAYOUT_H_INCLUDED +#define MX_MEASURE_LAYOUT_H_INCLUDED + +#include +#include +#include +#include "mx_tenths.h" + +/* + * The measure-layout type includes the horizontal distance from the previous measure. + */ +/* One child element of MxMeasureLayout: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxTenths *measure_distance; +} MxMeasureLayoutChild; + +typedef struct { + MxMeasureLayoutChild *children; /* child elements in document order */ + size_t children_count; +} MxMeasureLayout; + +/* NULL on error; the message is in mx_error(). */ +MxMeasureLayout *mx_measure_layout_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_measure_layout_serialize(const MxMeasureLayout *m, xmlNodePtr parent, const char *tag); +void mx_measure_layout_free(MxMeasureLayout *m); + +#endif /* MX_MEASURE_LAYOUT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_measure_numbering.c b/gen/test/c/mx/mx_measure_numbering.c new file mode 100644 index 000000000..a99b326c6 --- /dev/null +++ b/gen/test/c/mx/mx_measure_numbering.c @@ -0,0 +1,145 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_measure_numbering.h" + +#include "mx_runtime.h" +#include +#include + +MxMeasureNumbering *mx_measure_numbering_parse(xmlNodePtr el) { + MxMeasureNumbering *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_measure_numbering_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_measure_numbering_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_measure_numbering_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_measure_numbering_serialize(const MxMeasureNumbering *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_measure_numbering_value_to_string(m->value))); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + return el; +} + +void mx_measure_numbering_free(MxMeasureNumbering *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_measure_numbering.h b/gen/test/c/mx/mx_measure_numbering.h new file mode 100644 index 000000000..f8636edef --- /dev/null +++ b/gen/test/c/mx/mx_measure_numbering.h @@ -0,0 +1,57 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_MEASURE_NUMBERING_H_INCLUDED +#define MX_MEASURE_NUMBERING_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_center_right.h" +#include "mx_measure_numbering_value.h" +#include "mx_tenths.h" +#include "mx_valign.h" + +/* + * The measure-numbering type describes how frequently measure numbers are displayed on this part. + * The number attribute from the measure element is used for printing. Measures with an implicit + * attribute set to "yes" never display a measure number, regardless of the measure-numbering + * setting. + */ +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ + MxMeasureNumberingValue value; /* text content */ +} MxMeasureNumbering; + +/* NULL on error; the message is in mx_error(). */ +MxMeasureNumbering *mx_measure_numbering_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_measure_numbering_serialize(const MxMeasureNumbering *m, xmlNodePtr parent, const char *tag); +void mx_measure_numbering_free(MxMeasureNumbering *m); + +#endif /* MX_MEASURE_NUMBERING_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_measure_repeat.c b/gen/test/c/mx/mx_measure_repeat.c new file mode 100644 index 000000000..3c1b4a88e --- /dev/null +++ b/gen/test/c/mx/mx_measure_repeat.c @@ -0,0 +1,82 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_measure_repeat.h" + +#include "mx_runtime.h" +#include +#include + +MxMeasureRepeat *mx_measure_repeat_parse(xmlNodePtr el) { + MxMeasureRepeat *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_parse(s); + } else if (strcmp(aname, "slashes") == 0) { + m->has_slashes = true; + m->slashes = mx_parse_int(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_measure_repeat_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_positive_integer_or_empty_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_measure_repeat_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_measure_repeat_serialize(const MxMeasureRepeat *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + { + char *s = mx_positive_integer_or_empty_to_string(m->value); + xmlAddChild(el, xmlNewText(BAD_CAST s)); + free(s); + } + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); + } + if (m->has_slashes) { + char *s = mx_format_int(m->slashes); + xmlSetProp(el, BAD_CAST "slashes", BAD_CAST s); + free(s); + } + return el; +} + +void mx_measure_repeat_free(MxMeasureRepeat *m) { + if (!m) + return; + mx_positive_integer_or_empty_free(&m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_measure_repeat.h b/gen/test/c/mx/mx_measure_repeat.h new file mode 100644 index 000000000..c8f767fb8 --- /dev/null +++ b/gen/test/c/mx/mx_measure_repeat.h @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_MEASURE_REPEAT_H_INCLUDED +#define MX_MEASURE_REPEAT_H_INCLUDED + +#include +#include +#include +#include "mx_positive_integer_or_empty.h" +#include "mx_start_stop.h" + +/* + * The measure-repeat type is used for both single and multiple measure repeats. The text of the + * element indicates the number of measures to be repeated in a single pattern. The slashes + * attribute specifies the number of slashes to use in the repeat sign. It is 1 if not specified. + * Both the start and the stop of the measure-repeat must be specified. The text of the element is + * ignored when the type is stop. The measure-repeat element specifies a notation style for + * repetitions. The actual music being repeated needs to be repeated within the MusicXML file. This + * element specifies the notation that indicates the repeat. + */ +typedef struct { + bool has_type; + MxStartStop type; /* attribute type */ + bool has_slashes; + long slashes; /* attribute slashes */ + MxPositiveIntegerOrEmpty value; /* text content */ +} MxMeasureRepeat; + +/* NULL on error; the message is in mx_error(). */ +MxMeasureRepeat *mx_measure_repeat_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_measure_repeat_serialize(const MxMeasureRepeat *m, xmlNodePtr parent, const char *tag); +void mx_measure_repeat_free(MxMeasureRepeat *m); + +#endif /* MX_MEASURE_REPEAT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_measure_style.c b/gen/test/c/mx/mx_measure_style.c new file mode 100644 index 000000000..5d38447f7 --- /dev/null +++ b/gen/test/c/mx/mx_measure_style.c @@ -0,0 +1,169 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_measure_style.h" + +#include "mx_runtime.h" +#include +#include + +MxMeasureStyle *mx_measure_style_parse(xmlNodePtr el) { + MxMeasureStyle *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_staff_number_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_measure_style_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxMeasureStyleChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "multiple-rest") == 0) { + ch->multiple_rest = mx_multiple_rest_parse(c); + if (!ch->multiple_rest) { + mx_measure_style_free(m); + return NULL; + } + } else if (strcmp(tag, "measure-repeat") == 0) { + ch->measure_repeat = mx_measure_repeat_parse(c); + if (!ch->measure_repeat) { + mx_measure_style_free(m); + return NULL; + } + } else if (strcmp(tag, "beat-repeat") == 0) { + ch->beat_repeat = mx_beat_repeat_parse(c); + if (!ch->beat_repeat) { + mx_measure_style_free(m); + return NULL; + } + } else if (strcmp(tag, "slash") == 0) { + ch->slash = mx_slash_parse(c); + if (!ch->slash) { + mx_measure_style_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_measure_style_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_measure_style_serialize(const MxMeasureStyle *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_number) { + char *s = mx_staff_number_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxMeasureStyleChild *ch = &m->children[i]; + if (ch->multiple_rest) { + mx_multiple_rest_serialize(ch->multiple_rest, el, "multiple-rest"); + } else if (ch->measure_repeat) { + mx_measure_repeat_serialize(ch->measure_repeat, el, "measure-repeat"); + } else if (ch->beat_repeat) { + mx_beat_repeat_serialize(ch->beat_repeat, el, "beat-repeat"); + } else if (ch->slash) { + mx_slash_serialize(ch->slash, el, "slash"); + } + } + return el; +} + +void mx_measure_style_free(MxMeasureStyle *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxMeasureStyleChild *ch = &m->children[i]; + if (ch->multiple_rest) + mx_multiple_rest_free(ch->multiple_rest); + if (ch->measure_repeat) + mx_measure_repeat_free(ch->measure_repeat); + if (ch->beat_repeat) + mx_beat_repeat_free(ch->beat_repeat); + if (ch->slash) + mx_slash_free(ch->slash); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_measure_style.h b/gen/test/c/mx/mx_measure_style.h new file mode 100644 index 000000000..5e902f1b6 --- /dev/null +++ b/gen/test/c/mx/mx_measure_style.h @@ -0,0 +1,64 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_MEASURE_STYLE_H_INCLUDED +#define MX_MEASURE_STYLE_H_INCLUDED + +#include +#include +#include +#include "mx_beat_repeat.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_measure_repeat.h" +#include "mx_multiple_rest.h" +#include "mx_slash.h" +#include "mx_staff_number.h" + +/* + * A measure-style indicates a special way to print partial to multiple measures within a part. This + * includes multiple rests over several measures, repeats of beats, single, or multiple measures, + * and use of slash notation. The multiple-rest and measure-repeat symbols indicate the number of + * measures covered in the element content. The beat-repeat and slash elements can cover partial + * measures. All but the multiple-rest element use a type attribute to indicate starting and + * stopping the use of the style. The optional number attribute specifies the staff number from top + * to bottom on the system, as with clef. + */ +/* One child element of MxMeasureStyle: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxMultipleRest *multiple_rest; + MxMeasureRepeat *measure_repeat; + MxBeatRepeat *beat_repeat; + MxSlash *slash; +} MxMeasureStyleChild; + +typedef struct { + bool has_number; + MxStaffNumber number; /* attribute number */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_id; + char *id; /* attribute id */ + MxMeasureStyleChild *children; /* child elements in document order */ + size_t children_count; +} MxMeasureStyle; + +/* NULL on error; the message is in mx_error(). */ +MxMeasureStyle *mx_measure_style_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_measure_style_serialize(const MxMeasureStyle *m, xmlNodePtr parent, const char *tag); +void mx_measure_style_free(MxMeasureStyle *m); + +#endif /* MX_MEASURE_STYLE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_metronome.c b/gen/test/c/mx/mx_metronome.c new file mode 100644 index 000000000..95233c7aa --- /dev/null +++ b/gen/test/c/mx/mx_metronome.c @@ -0,0 +1,246 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_metronome.h" + +#include "mx_runtime.h" +#include +#include + +MxMetronome *mx_metronome_parse(xmlNodePtr el) { + MxMetronome *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "parentheses") == 0) { + m->has_parentheses = true; + m->parentheses = mx_yes_no_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else if (strcmp(aname, "justify") == 0) { + m->has_justify = true; + m->justify = mx_left_center_right_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_metronome_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxMetronomeChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "beat-unit") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->beat_unit = malloc(sizeof(*ch->beat_unit)); + if (!ch->beat_unit) + abort(); + *ch->beat_unit = mx_note_type_value_parse(s); + xmlFree(text); + } else if (strcmp(tag, "beat-unit-dot") == 0) { + ch->beat_unit_dot = mx_empty_parse(c); + if (!ch->beat_unit_dot) { + mx_metronome_free(m); + return NULL; + } + } else if (strcmp(tag, "beat-unit-tied") == 0) { + ch->beat_unit_tied = mx_beat_unit_tied_parse(c); + if (!ch->beat_unit_tied) { + mx_metronome_free(m); + return NULL; + } + } else if (strcmp(tag, "per-minute") == 0) { + ch->per_minute = mx_per_minute_parse(c); + if (!ch->per_minute) { + mx_metronome_free(m); + return NULL; + } + } else if (strcmp(tag, "metronome-arrows") == 0) { + ch->metronome_arrows = mx_empty_parse(c); + if (!ch->metronome_arrows) { + mx_metronome_free(m); + return NULL; + } + } else if (strcmp(tag, "metronome-note") == 0) { + ch->metronome_note = mx_metronome_note_parse(c); + if (!ch->metronome_note) { + mx_metronome_free(m); + return NULL; + } + } else if (strcmp(tag, "metronome-relation") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->metronome_relation = mx_strdup(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_metronome_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_metronome_serialize(const MxMetronome *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_parentheses) { + xmlSetProp(el, BAD_CAST "parentheses", BAD_CAST mx_yes_no_to_string(m->parentheses)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + if (m->has_justify) { + xmlSetProp(el, BAD_CAST "justify", BAD_CAST mx_left_center_right_to_string(m->justify)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxMetronomeChild *ch = &m->children[i]; + if (ch->beat_unit) { + xmlNewTextChild(el, NULL, BAD_CAST "beat-unit", BAD_CAST mx_note_type_value_to_string((*ch->beat_unit))); + } else if (ch->beat_unit_dot) { + mx_empty_serialize(ch->beat_unit_dot, el, "beat-unit-dot"); + } else if (ch->beat_unit_tied) { + mx_beat_unit_tied_serialize(ch->beat_unit_tied, el, "beat-unit-tied"); + } else if (ch->per_minute) { + mx_per_minute_serialize(ch->per_minute, el, "per-minute"); + } else if (ch->metronome_arrows) { + mx_empty_serialize(ch->metronome_arrows, el, "metronome-arrows"); + } else if (ch->metronome_note) { + mx_metronome_note_serialize(ch->metronome_note, el, "metronome-note"); + } else if (ch->metronome_relation) { + xmlNewTextChild(el, NULL, BAD_CAST "metronome-relation", BAD_CAST ch->metronome_relation); + } + } + return el; +} + +void mx_metronome_free(MxMetronome *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxMetronomeChild *ch = &m->children[i]; + free(ch->beat_unit); + if (ch->beat_unit_dot) + mx_empty_free(ch->beat_unit_dot); + if (ch->beat_unit_tied) + mx_beat_unit_tied_free(ch->beat_unit_tied); + if (ch->per_minute) + mx_per_minute_free(ch->per_minute); + if (ch->metronome_arrows) + mx_empty_free(ch->metronome_arrows); + if (ch->metronome_note) + mx_metronome_note_free(ch->metronome_note); + free(ch->metronome_relation); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_metronome.h b/gen/test/c/mx/mx_metronome.h new file mode 100644 index 000000000..55f414e4b --- /dev/null +++ b/gen/test/c/mx/mx_metronome.h @@ -0,0 +1,85 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_METRONOME_H_INCLUDED +#define MX_METRONOME_H_INCLUDED + +#include +#include +#include +#include "mx_beat_unit_tied.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_empty.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_center_right.h" +#include "mx_metronome_note.h" +#include "mx_note_type_value.h" +#include "mx_per_minute.h" +#include "mx_tenths.h" +#include "mx_valign.h" +#include "mx_yes_no.h" + +/* + * The metronome type represents metronome marks and other metric relationships. The beat-unit group + * and per-minute element specify regular metronome marks. The metronome-note and metronome-relation + * elements allow for the specification of metric modulations and other metric relationships, such + * as swing tempo marks where two eighths are equated to a quarter note / eighth note triplet. Tied + * notes can be represented in both types of metronome marks by using the beat-unit-tied and + * metronome-tied elements. The parentheses attribute indicates whether or not to put the metronome + * mark in parentheses; its value is no if not specified. + */ +/* One child element of MxMetronome: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxNoteTypeValue *beat_unit; + MxEmpty *beat_unit_dot; + MxBeatUnitTied *beat_unit_tied; + MxPerMinute *per_minute; + MxEmpty *metronome_arrows; + MxMetronomeNote *metronome_note; + char *metronome_relation; +} MxMetronomeChild; + +typedef struct { + bool has_parentheses; + MxYesNo parentheses; /* attribute parentheses */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ + bool has_justify; + MxLeftCenterRight justify; /* attribute justify */ + bool has_id; + char *id; /* attribute id */ + MxMetronomeChild *children; /* child elements in document order */ + size_t children_count; +} MxMetronome; + +/* NULL on error; the message is in mx_error(). */ +MxMetronome *mx_metronome_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_metronome_serialize(const MxMetronome *m, xmlNodePtr parent, const char *tag); +void mx_metronome_free(MxMetronome *m); + +#endif /* MX_METRONOME_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_metronome_beam.c b/gen/test/c/mx/mx_metronome_beam.c new file mode 100644 index 000000000..769db6253 --- /dev/null +++ b/gen/test/c/mx/mx_metronome_beam.c @@ -0,0 +1,71 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_metronome_beam.h" + +#include "mx_runtime.h" +#include +#include + +MxMetronomeBeam *mx_metronome_beam_parse(xmlNodePtr el) { + MxMetronomeBeam *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_beam_level_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_metronome_beam_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_beam_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_metronome_beam_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_metronome_beam_serialize(const MxMetronomeBeam *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_beam_value_to_string(m->value))); + if (m->has_number) { + char *s = mx_beam_level_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + return el; +} + +void mx_metronome_beam_free(MxMetronomeBeam *m) { + if (!m) + return; + free(m); +} diff --git a/gen/test/c/mx/mx_metronome_beam.h b/gen/test/c/mx/mx_metronome_beam.h new file mode 100644 index 000000000..b246efca9 --- /dev/null +++ b/gen/test/c/mx/mx_metronome_beam.h @@ -0,0 +1,28 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_METRONOME_BEAM_H_INCLUDED +#define MX_METRONOME_BEAM_H_INCLUDED + +#include +#include +#include +#include "mx_beam_level.h" +#include "mx_beam_value.h" + +/* + * The metronome-beam type works like the beam type in defining metric relationships, but does not + * include all the attributes available in the beam type. + */ +typedef struct { + bool has_number; + MxBeamLevel number; /* attribute number */ + MxBeamValue value; /* text content */ +} MxMetronomeBeam; + +/* NULL on error; the message is in mx_error(). */ +MxMetronomeBeam *mx_metronome_beam_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_metronome_beam_serialize(const MxMetronomeBeam *m, xmlNodePtr parent, const char *tag); +void mx_metronome_beam_free(MxMetronomeBeam *m); + +#endif /* MX_METRONOME_BEAM_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_metronome_note.c b/gen/test/c/mx/mx_metronome_note.c new file mode 100644 index 000000000..4990ab755 --- /dev/null +++ b/gen/test/c/mx/mx_metronome_note.c @@ -0,0 +1,126 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_metronome_note.h" + +#include "mx_runtime.h" +#include +#include + +MxMetronomeNote *mx_metronome_note_parse(xmlNodePtr el) { + MxMetronomeNote *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_metronome_note_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxMetronomeNoteChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "metronome-type") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->metronome_type = malloc(sizeof(*ch->metronome_type)); + if (!ch->metronome_type) + abort(); + *ch->metronome_type = mx_note_type_value_parse(s); + xmlFree(text); + } else if (strcmp(tag, "metronome-dot") == 0) { + ch->metronome_dot = mx_empty_parse(c); + if (!ch->metronome_dot) { + mx_metronome_note_free(m); + return NULL; + } + } else if (strcmp(tag, "metronome-beam") == 0) { + ch->metronome_beam = mx_metronome_beam_parse(c); + if (!ch->metronome_beam) { + mx_metronome_note_free(m); + return NULL; + } + } else if (strcmp(tag, "metronome-tied") == 0) { + ch->metronome_tied = mx_metronome_tied_parse(c); + if (!ch->metronome_tied) { + mx_metronome_note_free(m); + return NULL; + } + } else if (strcmp(tag, "metronome-tuplet") == 0) { + ch->metronome_tuplet = mx_metronome_tuplet_parse(c); + if (!ch->metronome_tuplet) { + mx_metronome_note_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_metronome_note_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_metronome_note_serialize(const MxMetronomeNote *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxMetronomeNoteChild *ch = &m->children[i]; + if (ch->metronome_type) { + xmlNewTextChild(el, NULL, BAD_CAST "metronome-type", BAD_CAST mx_note_type_value_to_string((*ch->metronome_type))); + } else if (ch->metronome_dot) { + mx_empty_serialize(ch->metronome_dot, el, "metronome-dot"); + } else if (ch->metronome_beam) { + mx_metronome_beam_serialize(ch->metronome_beam, el, "metronome-beam"); + } else if (ch->metronome_tied) { + mx_metronome_tied_serialize(ch->metronome_tied, el, "metronome-tied"); + } else if (ch->metronome_tuplet) { + mx_metronome_tuplet_serialize(ch->metronome_tuplet, el, "metronome-tuplet"); + } + } + return el; +} + +void mx_metronome_note_free(MxMetronomeNote *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxMetronomeNoteChild *ch = &m->children[i]; + free(ch->metronome_type); + if (ch->metronome_dot) + mx_empty_free(ch->metronome_dot); + if (ch->metronome_beam) + mx_metronome_beam_free(ch->metronome_beam); + if (ch->metronome_tied) + mx_metronome_tied_free(ch->metronome_tied); + if (ch->metronome_tuplet) + mx_metronome_tuplet_free(ch->metronome_tuplet); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_metronome_note.h b/gen/test/c/mx/mx_metronome_note.h new file mode 100644 index 000000000..eab03ef6a --- /dev/null +++ b/gen/test/c/mx/mx_metronome_note.h @@ -0,0 +1,40 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_METRONOME_NOTE_H_INCLUDED +#define MX_METRONOME_NOTE_H_INCLUDED + +#include +#include +#include +#include "mx_empty.h" +#include "mx_metronome_beam.h" +#include "mx_metronome_tied.h" +#include "mx_metronome_tuplet.h" +#include "mx_note_type_value.h" + +/* + * The metronome-note type defines the appearance of a note within a metric relationship mark. + */ +/* One child element of MxMetronomeNote: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxNoteTypeValue *metronome_type; + MxEmpty *metronome_dot; + MxMetronomeBeam *metronome_beam; + MxMetronomeTied *metronome_tied; + MxMetronomeTuplet *metronome_tuplet; +} MxMetronomeNoteChild; + +typedef struct { + MxMetronomeNoteChild *children; /* child elements in document order */ + size_t children_count; +} MxMetronomeNote; + +/* NULL on error; the message is in mx_error(). */ +MxMetronomeNote *mx_metronome_note_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_metronome_note_serialize(const MxMetronomeNote *m, xmlNodePtr parent, const char *tag); +void mx_metronome_note_free(MxMetronomeNote *m); + +#endif /* MX_METRONOME_NOTE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_metronome_tied.c b/gen/test/c/mx/mx_metronome_tied.c new file mode 100644 index 000000000..42c587241 --- /dev/null +++ b/gen/test/c/mx/mx_metronome_tied.c @@ -0,0 +1,62 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_metronome_tied.h" + +#include "mx_runtime.h" +#include +#include + +MxMetronomeTied *mx_metronome_tied_parse(xmlNodePtr el) { + MxMetronomeTied *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_metronome_tied_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_metronome_tied_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_metronome_tied_serialize(const MxMetronomeTied *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); + } + return el; +} + +void mx_metronome_tied_free(MxMetronomeTied *m) { + if (!m) + return; + free(m); +} diff --git a/gen/test/c/mx/mx_metronome_tied.h b/gen/test/c/mx/mx_metronome_tied.h new file mode 100644 index 000000000..36f44a383 --- /dev/null +++ b/gen/test/c/mx/mx_metronome_tied.h @@ -0,0 +1,27 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_METRONOME_TIED_H_INCLUDED +#define MX_METRONOME_TIED_H_INCLUDED + +#include +#include +#include +#include "mx_start_stop.h" + +/* + * The metronome-tied indicates the presence of a tie within a metric relationship mark. As with the + * tied element, both the start and stop of the tie should be specified, in this case within + * separate metronome-note elements. + */ +typedef struct { + bool has_type; + MxStartStop type; /* attribute type */ +} MxMetronomeTied; + +/* NULL on error; the message is in mx_error(). */ +MxMetronomeTied *mx_metronome_tied_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_metronome_tied_serialize(const MxMetronomeTied *m, xmlNodePtr parent, const char *tag); +void mx_metronome_tied_free(MxMetronomeTied *m); + +#endif /* MX_METRONOME_TIED_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_metronome_tuplet.c b/gen/test/c/mx/mx_metronome_tuplet.c new file mode 100644 index 000000000..86499a228 --- /dev/null +++ b/gen/test/c/mx/mx_metronome_tuplet.c @@ -0,0 +1,140 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_metronome_tuplet.h" + +#include "mx_runtime.h" +#include +#include + +MxMetronomeTuplet *mx_metronome_tuplet_parse(xmlNodePtr el) { + MxMetronomeTuplet *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_parse(s); + } else if (strcmp(aname, "bracket") == 0) { + m->has_bracket = true; + m->bracket = mx_yes_no_parse(s); + } else if (strcmp(aname, "show-number") == 0) { + m->has_show_number = true; + m->show_number = mx_show_tuplet_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_metronome_tuplet_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxMetronomeTupletChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "actual-notes") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->actual_notes = malloc(sizeof(*ch->actual_notes)); + if (!ch->actual_notes) + abort(); + *ch->actual_notes = mx_parse_int(s); + xmlFree(text); + } else if (strcmp(tag, "normal-notes") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->normal_notes = malloc(sizeof(*ch->normal_notes)); + if (!ch->normal_notes) + abort(); + *ch->normal_notes = mx_parse_int(s); + xmlFree(text); + } else if (strcmp(tag, "normal-type") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->normal_type = malloc(sizeof(*ch->normal_type)); + if (!ch->normal_type) + abort(); + *ch->normal_type = mx_note_type_value_parse(s); + xmlFree(text); + } else if (strcmp(tag, "normal-dot") == 0) { + ch->normal_dot = mx_empty_parse(c); + if (!ch->normal_dot) { + mx_metronome_tuplet_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_metronome_tuplet_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_metronome_tuplet_serialize(const MxMetronomeTuplet *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); + } + if (m->has_bracket) { + xmlSetProp(el, BAD_CAST "bracket", BAD_CAST mx_yes_no_to_string(m->bracket)); + } + if (m->has_show_number) { + xmlSetProp(el, BAD_CAST "show-number", BAD_CAST mx_show_tuplet_to_string(m->show_number)); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxMetronomeTupletChild *ch = &m->children[i]; + if (ch->actual_notes) { + char *s = mx_format_int((*ch->actual_notes)); + xmlNewTextChild(el, NULL, BAD_CAST "actual-notes", BAD_CAST s); + free(s); + } else if (ch->normal_notes) { + char *s = mx_format_int((*ch->normal_notes)); + xmlNewTextChild(el, NULL, BAD_CAST "normal-notes", BAD_CAST s); + free(s); + } else if (ch->normal_type) { + xmlNewTextChild(el, NULL, BAD_CAST "normal-type", BAD_CAST mx_note_type_value_to_string((*ch->normal_type))); + } else if (ch->normal_dot) { + mx_empty_serialize(ch->normal_dot, el, "normal-dot"); + } + } + return el; +} + +void mx_metronome_tuplet_free(MxMetronomeTuplet *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxMetronomeTupletChild *ch = &m->children[i]; + free(ch->actual_notes); + free(ch->normal_notes); + free(ch->normal_type); + if (ch->normal_dot) + mx_empty_free(ch->normal_dot); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_metronome_tuplet.h b/gen/test/c/mx/mx_metronome_tuplet.h new file mode 100644 index 000000000..06f42cc12 --- /dev/null +++ b/gen/test/c/mx/mx_metronome_tuplet.h @@ -0,0 +1,47 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_METRONOME_TUPLET_H_INCLUDED +#define MX_METRONOME_TUPLET_H_INCLUDED + +#include +#include +#include +#include "mx_empty.h" +#include "mx_note_type_value.h" +#include "mx_show_tuplet.h" +#include "mx_start_stop.h" +#include "mx_time_modification.h" +#include "mx_yes_no.h" + +/* + * The metronome-tuplet type uses the same element structure as the time-modification element along + * with some attributes from the tuplet element. + */ +/* One child element of MxMetronomeTuplet: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + long *actual_notes; + long *normal_notes; + MxNoteTypeValue *normal_type; + MxEmpty *normal_dot; +} MxMetronomeTupletChild; + +typedef struct { + bool has_type; + MxStartStop type; /* attribute type */ + bool has_bracket; + MxYesNo bracket; /* attribute bracket */ + bool has_show_number; + MxShowTuplet show_number; /* attribute show-number */ + MxMetronomeTupletChild *children; /* child elements in document order */ + size_t children_count; +} MxMetronomeTuplet; + +/* NULL on error; the message is in mx_error(). */ +MxMetronomeTuplet *mx_metronome_tuplet_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_metronome_tuplet_serialize(const MxMetronomeTuplet *m, xmlNodePtr parent, const char *tag); +void mx_metronome_tuplet_free(MxMetronomeTuplet *m); + +#endif /* MX_METRONOME_TUPLET_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_midi_device.c b/gen/test/c/mx/mx_midi_device.c new file mode 100644 index 000000000..e9058ed9f --- /dev/null +++ b/gen/test/c/mx/mx_midi_device.c @@ -0,0 +1,80 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_midi_device.h" + +#include "mx_runtime.h" +#include +#include + +MxMIDIDevice *mx_midi_device_parse(xmlNodePtr el) { + MxMIDIDevice *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "port") == 0) { + m->has_port = true; + m->port = mx_midi_16_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_midi_device_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_midi_device_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_midi_device_serialize(const MxMIDIDevice *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_port) { + char *s = mx_midi_16_to_string(m->port); + xmlSetProp(el, BAD_CAST "port", BAD_CAST s); + free(s); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_midi_device_free(MxMIDIDevice *m) { + if (!m) + return; + if (m->has_id) + free(m->id); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_midi_device.h b/gen/test/c/mx/mx_midi_device.h new file mode 100644 index 000000000..0c310ad29 --- /dev/null +++ b/gen/test/c/mx/mx_midi_device.h @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_MIDI_DEVICE_H_INCLUDED +#define MX_MIDI_DEVICE_H_INCLUDED + +#include +#include +#include +#include "mx_midi_16.h" + +/* + * The midi-device type corresponds to the DeviceName meta event in Standard MIDI Files. The + * optional port attribute is a number from 1 to 16 that can be used with the unofficial MIDI port + * (or cable) meta event. Unlike the DeviceName meta event, there can be multiple midi-device + * elements per MusicXML part starting in MusicXML 3.0. The optional id attribute refers to the + * score-instrument assigned to this device. If missing, the device assignment affects all + * score-instrument elements in the score-part. + */ +typedef struct { + bool has_port; + MxMIDI16 port; /* attribute port */ + bool has_id; + char *id; /* attribute id */ + char *value; /* text content */ +} MxMIDIDevice; + +/* NULL on error; the message is in mx_error(). */ +MxMIDIDevice *mx_midi_device_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_midi_device_serialize(const MxMIDIDevice *m, xmlNodePtr parent, const char *tag); +void mx_midi_device_free(MxMIDIDevice *m); + +#endif /* MX_MIDI_DEVICE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_midi_instrument.c b/gen/test/c/mx/mx_midi_instrument.c new file mode 100644 index 000000000..69b82e48c --- /dev/null +++ b/gen/test/c/mx/mx_midi_instrument.c @@ -0,0 +1,182 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_midi_instrument.h" + +#include "mx_runtime.h" +#include +#include + +MxMIDIInstrument *mx_midi_instrument_parse(xmlNodePtr el) { + MxMIDIInstrument *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_midi_instrument_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxMIDIInstrumentChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "midi-channel") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->midi_channel = malloc(sizeof(*ch->midi_channel)); + if (!ch->midi_channel) + abort(); + *ch->midi_channel = mx_midi_16_parse(s); + xmlFree(text); + } else if (strcmp(tag, "midi-name") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->midi_name = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "midi-bank") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->midi_bank = malloc(sizeof(*ch->midi_bank)); + if (!ch->midi_bank) + abort(); + *ch->midi_bank = mx_midi_16384_parse(s); + xmlFree(text); + } else if (strcmp(tag, "midi-program") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->midi_program = malloc(sizeof(*ch->midi_program)); + if (!ch->midi_program) + abort(); + *ch->midi_program = mx_midi_128_parse(s); + xmlFree(text); + } else if (strcmp(tag, "midi-unpitched") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->midi_unpitched = malloc(sizeof(*ch->midi_unpitched)); + if (!ch->midi_unpitched) + abort(); + *ch->midi_unpitched = mx_midi_128_parse(s); + xmlFree(text); + } else if (strcmp(tag, "volume") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->volume = malloc(sizeof(*ch->volume)); + if (!ch->volume) + abort(); + *ch->volume = mx_percent_parse(s); + xmlFree(text); + } else if (strcmp(tag, "pan") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->pan = malloc(sizeof(*ch->pan)); + if (!ch->pan) + abort(); + *ch->pan = mx_rotation_degrees_parse(s); + xmlFree(text); + } else if (strcmp(tag, "elevation") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->elevation = malloc(sizeof(*ch->elevation)); + if (!ch->elevation) + abort(); + *ch->elevation = mx_rotation_degrees_parse(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_midi_instrument_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_midi_instrument_serialize(const MxMIDIInstrument *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxMIDIInstrumentChild *ch = &m->children[i]; + if (ch->midi_channel) { + char *s = mx_midi_16_to_string((*ch->midi_channel)); + xmlNewTextChild(el, NULL, BAD_CAST "midi-channel", BAD_CAST s); + free(s); + } else if (ch->midi_name) { + xmlNewTextChild(el, NULL, BAD_CAST "midi-name", BAD_CAST ch->midi_name); + } else if (ch->midi_bank) { + char *s = mx_midi_16384_to_string((*ch->midi_bank)); + xmlNewTextChild(el, NULL, BAD_CAST "midi-bank", BAD_CAST s); + free(s); + } else if (ch->midi_program) { + char *s = mx_midi_128_to_string((*ch->midi_program)); + xmlNewTextChild(el, NULL, BAD_CAST "midi-program", BAD_CAST s); + free(s); + } else if (ch->midi_unpitched) { + char *s = mx_midi_128_to_string((*ch->midi_unpitched)); + xmlNewTextChild(el, NULL, BAD_CAST "midi-unpitched", BAD_CAST s); + free(s); + } else if (ch->volume) { + char *s = mx_percent_to_string((*ch->volume)); + xmlNewTextChild(el, NULL, BAD_CAST "volume", BAD_CAST s); + free(s); + } else if (ch->pan) { + char *s = mx_rotation_degrees_to_string((*ch->pan)); + xmlNewTextChild(el, NULL, BAD_CAST "pan", BAD_CAST s); + free(s); + } else if (ch->elevation) { + char *s = mx_rotation_degrees_to_string((*ch->elevation)); + xmlNewTextChild(el, NULL, BAD_CAST "elevation", BAD_CAST s); + free(s); + } + } + return el; +} + +void mx_midi_instrument_free(MxMIDIInstrument *m) { + if (!m) + return; + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxMIDIInstrumentChild *ch = &m->children[i]; + free(ch->midi_channel); + free(ch->midi_name); + free(ch->midi_bank); + free(ch->midi_program); + free(ch->midi_unpitched); + free(ch->volume); + free(ch->pan); + free(ch->elevation); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_midi_instrument.h b/gen/test/c/mx/mx_midi_instrument.h new file mode 100644 index 000000000..0cd6b36df --- /dev/null +++ b/gen/test/c/mx/mx_midi_instrument.h @@ -0,0 +1,47 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_MIDI_INSTRUMENT_H_INCLUDED +#define MX_MIDI_INSTRUMENT_H_INCLUDED + +#include +#include +#include +#include "mx_midi_128.h" +#include "mx_midi_16.h" +#include "mx_midi_16384.h" +#include "mx_percent.h" +#include "mx_rotation_degrees.h" + +/* + * The midi-instrument type defines MIDI 1.0 instrument playback. The midi-instrument element can be + * a part of either the score-instrument element at the start of a part, or the sound element within + * a part. The id attribute refers to the score-instrument affected by the change. + */ +/* One child element of MxMIDIInstrument: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxMIDI16 *midi_channel; + char *midi_name; + MxMIDI16384 *midi_bank; + MxMIDI128 *midi_program; + MxMIDI128 *midi_unpitched; + MxPercent *volume; + MxRotationDegrees *pan; + MxRotationDegrees *elevation; +} MxMIDIInstrumentChild; + +typedef struct { + bool has_id; + char *id; /* attribute id */ + MxMIDIInstrumentChild *children; /* child elements in document order */ + size_t children_count; +} MxMIDIInstrument; + +/* NULL on error; the message is in mx_error(). */ +MxMIDIInstrument *mx_midi_instrument_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_midi_instrument_serialize(const MxMIDIInstrument *m, xmlNodePtr parent, const char *tag); +void mx_midi_instrument_free(MxMIDIInstrument *m); + +#endif /* MX_MIDI_INSTRUMENT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_miscellaneous.c b/gen/test/c/mx/mx_miscellaneous.c new file mode 100644 index 000000000..ef37066de --- /dev/null +++ b/gen/test/c/mx/mx_miscellaneous.c @@ -0,0 +1,85 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_miscellaneous.h" + +#include "mx_runtime.h" +#include +#include + +MxMiscellaneous *mx_miscellaneous_parse(xmlNodePtr el) { + MxMiscellaneous *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_miscellaneous_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxMiscellaneousChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "miscellaneous-field") == 0) { + ch->miscellaneous_field = mx_miscellaneous_field_parse(c); + if (!ch->miscellaneous_field) { + mx_miscellaneous_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_miscellaneous_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_miscellaneous_serialize(const MxMiscellaneous *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxMiscellaneousChild *ch = &m->children[i]; + if (ch->miscellaneous_field) { + mx_miscellaneous_field_serialize(ch->miscellaneous_field, el, "miscellaneous-field"); + } + } + return el; +} + +void mx_miscellaneous_free(MxMiscellaneous *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxMiscellaneousChild *ch = &m->children[i]; + if (ch->miscellaneous_field) + mx_miscellaneous_field_free(ch->miscellaneous_field); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_miscellaneous.h b/gen/test/c/mx/mx_miscellaneous.h new file mode 100644 index 000000000..0518d0a97 --- /dev/null +++ b/gen/test/c/mx/mx_miscellaneous.h @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_MISCELLANEOUS_H_INCLUDED +#define MX_MISCELLANEOUS_H_INCLUDED + +#include +#include +#include +#include "mx_miscellaneous_field.h" + +/* + * If a program has other metadata not yet supported in the MusicXML format, it can go in the + * miscellaneous element. The miscellaneous type puts each separate part of metadata into its own + * miscellaneous-field type. + */ +/* One child element of MxMiscellaneous: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxMiscellaneousField *miscellaneous_field; +} MxMiscellaneousChild; + +typedef struct { + MxMiscellaneousChild *children; /* child elements in document order */ + size_t children_count; +} MxMiscellaneous; + +/* NULL on error; the message is in mx_error(). */ +MxMiscellaneous *mx_miscellaneous_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_miscellaneous_serialize(const MxMiscellaneous *m, xmlNodePtr parent, const char *tag); +void mx_miscellaneous_free(MxMiscellaneous *m); + +#endif /* MX_MISCELLANEOUS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_miscellaneous_field.c b/gen/test/c/mx/mx_miscellaneous_field.c new file mode 100644 index 000000000..05ef3e140 --- /dev/null +++ b/gen/test/c/mx/mx_miscellaneous_field.c @@ -0,0 +1,72 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_miscellaneous_field.h" + +#include "mx_runtime.h" +#include +#include + +MxMiscellaneousField *mx_miscellaneous_field_parse(xmlNodePtr el) { + MxMiscellaneousField *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "name") == 0) { + m->has_name = true; + m->name = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_miscellaneous_field_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_miscellaneous_field_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_miscellaneous_field_serialize(const MxMiscellaneousField *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_name) { + xmlSetProp(el, BAD_CAST "name", BAD_CAST m->name); + } + return el; +} + +void mx_miscellaneous_field_free(MxMiscellaneousField *m) { + if (!m) + return; + if (m->has_name) + free(m->name); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_miscellaneous_field.h b/gen/test/c/mx/mx_miscellaneous_field.h new file mode 100644 index 000000000..20a234cbb --- /dev/null +++ b/gen/test/c/mx/mx_miscellaneous_field.h @@ -0,0 +1,27 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_MISCELLANEOUS_FIELD_H_INCLUDED +#define MX_MISCELLANEOUS_FIELD_H_INCLUDED + +#include +#include +#include + +/* + * If a program has other metadata not yet supported in the MusicXML format, each type of metadata + * can go in a miscellaneous-field element. The required name attribute indicates the type of + * metadata the element content represents. + */ +typedef struct { + bool has_name; + char *name; /* attribute name */ + char *value; /* text content */ +} MxMiscellaneousField; + +/* NULL on error; the message is in mx_error(). */ +MxMiscellaneousField *mx_miscellaneous_field_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_miscellaneous_field_serialize(const MxMiscellaneousField *m, xmlNodePtr parent, const char *tag); +void mx_miscellaneous_field_free(MxMiscellaneousField *m); + +#endif /* MX_MISCELLANEOUS_FIELD_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_mordent.c b/gen/test/c/mx/mx_mordent.c new file mode 100644 index 000000000..eef7f16b7 --- /dev/null +++ b/gen/test/c/mx/mx_mordent.c @@ -0,0 +1,198 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_mordent.h" + +#include "mx_runtime.h" +#include +#include + +MxMordent *mx_mordent_parse(xmlNodePtr el) { + MxMordent *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "start-note") == 0) { + m->has_start_note = true; + m->start_note = mx_start_note_parse(s); + } else if (strcmp(aname, "trill-step") == 0) { + m->has_trill_step = true; + m->trill_step = mx_trill_step_parse(s); + } else if (strcmp(aname, "two-note-turn") == 0) { + m->has_two_note_turn = true; + m->two_note_turn = mx_two_note_turn_parse(s); + } else if (strcmp(aname, "accelerate") == 0) { + m->has_accelerate = true; + m->accelerate = mx_yes_no_parse(s); + } else if (strcmp(aname, "beats") == 0) { + m->has_beats = true; + m->beats = mx_trill_beats_parse(s); + } else if (strcmp(aname, "second-beat") == 0) { + m->has_second_beat = true; + m->second_beat = mx_percent_parse(s); + } else if (strcmp(aname, "last-beat") == 0) { + m->has_last_beat = true; + m->last_beat = mx_percent_parse(s); + } else if (strcmp(aname, "long") == 0) { + m->has_long_ = true; + m->long_ = mx_yes_no_parse(s); + } else if (strcmp(aname, "approach") == 0) { + m->has_approach = true; + m->approach = mx_above_below_parse(s); + } else if (strcmp(aname, "departure") == 0) { + m->has_departure = true; + m->departure = mx_above_below_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_mordent_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_mordent_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_mordent_serialize(const MxMordent *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_start_note) { + xmlSetProp(el, BAD_CAST "start-note", BAD_CAST mx_start_note_to_string(m->start_note)); + } + if (m->has_trill_step) { + xmlSetProp(el, BAD_CAST "trill-step", BAD_CAST mx_trill_step_to_string(m->trill_step)); + } + if (m->has_two_note_turn) { + xmlSetProp(el, BAD_CAST "two-note-turn", BAD_CAST mx_two_note_turn_to_string(m->two_note_turn)); + } + if (m->has_accelerate) { + xmlSetProp(el, BAD_CAST "accelerate", BAD_CAST mx_yes_no_to_string(m->accelerate)); + } + if (m->has_beats) { + char *s = mx_trill_beats_to_string(m->beats); + xmlSetProp(el, BAD_CAST "beats", BAD_CAST s); + free(s); + } + if (m->has_second_beat) { + char *s = mx_percent_to_string(m->second_beat); + xmlSetProp(el, BAD_CAST "second-beat", BAD_CAST s); + free(s); + } + if (m->has_last_beat) { + char *s = mx_percent_to_string(m->last_beat); + xmlSetProp(el, BAD_CAST "last-beat", BAD_CAST s); + free(s); + } + if (m->has_long_) { + xmlSetProp(el, BAD_CAST "long", BAD_CAST mx_yes_no_to_string(m->long_)); + } + if (m->has_approach) { + xmlSetProp(el, BAD_CAST "approach", BAD_CAST mx_above_below_to_string(m->approach)); + } + if (m->has_departure) { + xmlSetProp(el, BAD_CAST "departure", BAD_CAST mx_above_below_to_string(m->departure)); + } + return el; +} + +void mx_mordent_free(MxMordent *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_mordent.h b/gen/test/c/mx/mx_mordent.h new file mode 100644 index 000000000..9e3680356 --- /dev/null +++ b/gen/test/c/mx/mx_mordent.h @@ -0,0 +1,79 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_MORDENT_H_INCLUDED +#define MX_MORDENT_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_empty_trill_sound.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_percent.h" +#include "mx_start_note.h" +#include "mx_tenths.h" +#include "mx_trill_beats.h" +#include "mx_trill_step.h" +#include "mx_two_note_turn.h" +#include "mx_yes_no.h" + +/* + * The mordent type is used for both represents the mordent sign with the vertical line and the + * inverted-mordent sign without the line. The long attribute is "no" by default. The approach and + * departure attributes are used for compound ornaments, indicating how the beginning and ending of + * the ornament look relative to the main part of the mordent. + */ +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_start_note; + MxStartNote start_note; /* attribute start-note */ + bool has_trill_step; + MxTrillStep trill_step; /* attribute trill-step */ + bool has_two_note_turn; + MxTwoNoteTurn two_note_turn; /* attribute two-note-turn */ + bool has_accelerate; + MxYesNo accelerate; /* attribute accelerate */ + bool has_beats; + MxTrillBeats beats; /* attribute beats */ + bool has_second_beat; + MxPercent second_beat; /* attribute second-beat */ + bool has_last_beat; + MxPercent last_beat; /* attribute last-beat */ + bool has_long_; + MxYesNo long_; /* attribute long */ + bool has_approach; + MxAboveBelow approach; /* attribute approach */ + bool has_departure; + MxAboveBelow departure; /* attribute departure */ +} MxMordent; + +/* NULL on error; the message is in mx_error(). */ +MxMordent *mx_mordent_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_mordent_serialize(const MxMordent *m, xmlNodePtr parent, const char *tag); +void mx_mordent_free(MxMordent *m); + +#endif /* MX_MORDENT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_multiple_rest.c b/gen/test/c/mx/mx_multiple_rest.c new file mode 100644 index 000000000..728cff5da --- /dev/null +++ b/gen/test/c/mx/mx_multiple_rest.c @@ -0,0 +1,74 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_multiple_rest.h" + +#include "mx_runtime.h" +#include +#include + +MxMultipleRest *mx_multiple_rest_parse(xmlNodePtr el) { + MxMultipleRest *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "use-symbols") == 0) { + m->has_use_symbols = true; + m->use_symbols = mx_yes_no_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_multiple_rest_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_positive_integer_or_empty_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_multiple_rest_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_multiple_rest_serialize(const MxMultipleRest *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + { + char *s = mx_positive_integer_or_empty_to_string(m->value); + xmlAddChild(el, xmlNewText(BAD_CAST s)); + free(s); + } + if (m->has_use_symbols) { + xmlSetProp(el, BAD_CAST "use-symbols", BAD_CAST mx_yes_no_to_string(m->use_symbols)); + } + return el; +} + +void mx_multiple_rest_free(MxMultipleRest *m) { + if (!m) + return; + mx_positive_integer_or_empty_free(&m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_multiple_rest.h b/gen/test/c/mx/mx_multiple_rest.h new file mode 100644 index 000000000..abb9216d4 --- /dev/null +++ b/gen/test/c/mx/mx_multiple_rest.h @@ -0,0 +1,29 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_MULTIPLE_REST_H_INCLUDED +#define MX_MULTIPLE_REST_H_INCLUDED + +#include +#include +#include +#include "mx_positive_integer_or_empty.h" +#include "mx_yes_no.h" + +/* + * The text of the multiple-rest type indicates the number of measures in the multiple rest. + * Multiple rests may use the 1-bar / 2-bar / 4-bar rest symbols, or a single shape. The use-symbols + * attribute indicates which to use; it is no if not specified. + */ +typedef struct { + bool has_use_symbols; + MxYesNo use_symbols; /* attribute use-symbols */ + MxPositiveIntegerOrEmpty value; /* text content */ +} MxMultipleRest; + +/* NULL on error; the message is in mx_error(). */ +MxMultipleRest *mx_multiple_rest_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_multiple_rest_serialize(const MxMultipleRest *m, xmlNodePtr parent, const char *tag); +void mx_multiple_rest_free(MxMultipleRest *m); + +#endif /* MX_MULTIPLE_REST_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_name_display.c b/gen/test/c/mx/mx_name_display.c new file mode 100644 index 000000000..e7d75ef11 --- /dev/null +++ b/gen/test/c/mx/mx_name_display.c @@ -0,0 +1,101 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_name_display.h" + +#include "mx_runtime.h" +#include +#include + +MxNameDisplay *mx_name_display_parse(xmlNodePtr el) { + MxNameDisplay *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "print-object") == 0) { + m->has_print_object = true; + m->print_object = mx_yes_no_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_name_display_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxNameDisplayChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "display-text") == 0) { + ch->display_text = mx_formatted_text_parse(c); + if (!ch->display_text) { + mx_name_display_free(m); + return NULL; + } + } else if (strcmp(tag, "accidental-text") == 0) { + ch->accidental_text = mx_accidental_text_parse(c); + if (!ch->accidental_text) { + mx_name_display_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_name_display_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_name_display_serialize(const MxNameDisplay *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_print_object) { + xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxNameDisplayChild *ch = &m->children[i]; + if (ch->display_text) { + mx_formatted_text_serialize(ch->display_text, el, "display-text"); + } else if (ch->accidental_text) { + mx_accidental_text_serialize(ch->accidental_text, el, "accidental-text"); + } + } + return el; +} + +void mx_name_display_free(MxNameDisplay *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxNameDisplayChild *ch = &m->children[i]; + if (ch->display_text) + mx_formatted_text_free(ch->display_text); + if (ch->accidental_text) + mx_accidental_text_free(ch->accidental_text); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_name_display.h b/gen/test/c/mx/mx_name_display.h new file mode 100644 index 000000000..52175c3e8 --- /dev/null +++ b/gen/test/c/mx/mx_name_display.h @@ -0,0 +1,40 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_NAME_DISPLAY_H_INCLUDED +#define MX_NAME_DISPLAY_H_INCLUDED + +#include +#include +#include +#include "mx_accidental_text.h" +#include "mx_formatted_text.h" +#include "mx_yes_no.h" + +/* + * The name-display type is used for exact formatting of multi-font text in part and group names to + * the left of the system. The print-object attribute can be used to determine what, if anything, is + * printed at the start of each system. Enclosure for the display-text element is none by default. + * Language for the display-text element is Italian ("it") by default. + */ +/* One child element of MxNameDisplay: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxFormattedText *display_text; + MxAccidentalText *accidental_text; +} MxNameDisplayChild; + +typedef struct { + bool has_print_object; + MxYesNo print_object; /* attribute print-object */ + MxNameDisplayChild *children; /* child elements in document order */ + size_t children_count; +} MxNameDisplay; + +/* NULL on error; the message is in mx_error(). */ +MxNameDisplay *mx_name_display_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_name_display_serialize(const MxNameDisplay *m, xmlNodePtr parent, const char *tag); +void mx_name_display_free(MxNameDisplay *m); + +#endif /* MX_NAME_DISPLAY_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_non_arpeggiate.c b/gen/test/c/mx/mx_non_arpeggiate.c new file mode 100644 index 000000000..6a66bba99 --- /dev/null +++ b/gen/test/c/mx/mx_non_arpeggiate.c @@ -0,0 +1,124 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_non_arpeggiate.h" + +#include "mx_runtime.h" +#include +#include + +MxNonArpeggiate *mx_non_arpeggiate_parse(xmlNodePtr el) { + MxNonArpeggiate *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_top_bottom_parse(s); + } else if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_number_level_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_non_arpeggiate_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_non_arpeggiate_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_non_arpeggiate_serialize(const MxNonArpeggiate *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_top_bottom_to_string(m->type)); + } + if (m->has_number) { + char *s = mx_number_level_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_non_arpeggiate_free(MxNonArpeggiate *m) { + if (!m) + return; + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + free(m); +} diff --git a/gen/test/c/mx/mx_non_arpeggiate.h b/gen/test/c/mx/mx_non_arpeggiate.h new file mode 100644 index 000000000..6dae629f0 --- /dev/null +++ b/gen/test/c/mx/mx_non_arpeggiate.h @@ -0,0 +1,47 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_NON_ARPEGGIATE_H_INCLUDED +#define MX_NON_ARPEGGIATE_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_number_level.h" +#include "mx_tenths.h" +#include "mx_top_bottom.h" + +/* + * The non-arpeggiate type indicates that this note is at the top or bottom of a bracket indicating + * to not arpeggiate these notes. Since this does not involve playback, it is only used on the top + * or bottom notes, not on each note as for the arpeggiate type. + */ +typedef struct { + bool has_type; + MxTopBottom type; /* attribute type */ + bool has_number; + MxNumberLevel number; /* attribute number */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_color; + MxColor color; /* attribute color */ + bool has_id; + char *id; /* attribute id */ +} MxNonArpeggiate; + +/* NULL on error; the message is in mx_error(). */ +MxNonArpeggiate *mx_non_arpeggiate_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_non_arpeggiate_serialize(const MxNonArpeggiate *m, xmlNodePtr parent, const char *tag); +void mx_non_arpeggiate_free(MxNonArpeggiate *m); + +#endif /* MX_NON_ARPEGGIATE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_notations.c b/gen/test/c/mx/mx_notations.c new file mode 100644 index 000000000..0faadf2fe --- /dev/null +++ b/gen/test/c/mx/mx_notations.c @@ -0,0 +1,249 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_notations.h" + +#include "mx_runtime.h" +#include +#include + +MxNotations *mx_notations_parse(xmlNodePtr el) { + MxNotations *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "print-object") == 0) { + m->has_print_object = true; + m->print_object = mx_yes_no_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_notations_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxNotationsChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "footnote") == 0) { + ch->footnote = mx_formatted_text_parse(c); + if (!ch->footnote) { + mx_notations_free(m); + return NULL; + } + } else if (strcmp(tag, "level") == 0) { + ch->level = mx_level_parse(c); + if (!ch->level) { + mx_notations_free(m); + return NULL; + } + } else if (strcmp(tag, "tied") == 0) { + ch->tied = mx_tied_parse(c); + if (!ch->tied) { + mx_notations_free(m); + return NULL; + } + } else if (strcmp(tag, "slur") == 0) { + ch->slur = mx_slur_parse(c); + if (!ch->slur) { + mx_notations_free(m); + return NULL; + } + } else if (strcmp(tag, "tuplet") == 0) { + ch->tuplet = mx_tuplet_parse(c); + if (!ch->tuplet) { + mx_notations_free(m); + return NULL; + } + } else if (strcmp(tag, "glissando") == 0) { + ch->glissando = mx_glissando_parse(c); + if (!ch->glissando) { + mx_notations_free(m); + return NULL; + } + } else if (strcmp(tag, "slide") == 0) { + ch->slide = mx_slide_parse(c); + if (!ch->slide) { + mx_notations_free(m); + return NULL; + } + } else if (strcmp(tag, "ornaments") == 0) { + ch->ornaments = mx_ornaments_parse(c); + if (!ch->ornaments) { + mx_notations_free(m); + return NULL; + } + } else if (strcmp(tag, "technical") == 0) { + ch->technical = mx_technical_parse(c); + if (!ch->technical) { + mx_notations_free(m); + return NULL; + } + } else if (strcmp(tag, "articulations") == 0) { + ch->articulations = mx_articulations_parse(c); + if (!ch->articulations) { + mx_notations_free(m); + return NULL; + } + } else if (strcmp(tag, "dynamics") == 0) { + ch->dynamics = mx_dynamics_parse(c); + if (!ch->dynamics) { + mx_notations_free(m); + return NULL; + } + } else if (strcmp(tag, "fermata") == 0) { + ch->fermata = mx_fermata_parse(c); + if (!ch->fermata) { + mx_notations_free(m); + return NULL; + } + } else if (strcmp(tag, "arpeggiate") == 0) { + ch->arpeggiate = mx_arpeggiate_parse(c); + if (!ch->arpeggiate) { + mx_notations_free(m); + return NULL; + } + } else if (strcmp(tag, "non-arpeggiate") == 0) { + ch->non_arpeggiate = mx_non_arpeggiate_parse(c); + if (!ch->non_arpeggiate) { + mx_notations_free(m); + return NULL; + } + } else if (strcmp(tag, "accidental-mark") == 0) { + ch->accidental_mark = mx_accidental_mark_parse(c); + if (!ch->accidental_mark) { + mx_notations_free(m); + return NULL; + } + } else if (strcmp(tag, "other-notation") == 0) { + ch->other_notation = mx_other_notation_parse(c); + if (!ch->other_notation) { + mx_notations_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_notations_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_notations_serialize(const MxNotations *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_print_object) { + xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxNotationsChild *ch = &m->children[i]; + if (ch->footnote) { + mx_formatted_text_serialize(ch->footnote, el, "footnote"); + } else if (ch->level) { + mx_level_serialize(ch->level, el, "level"); + } else if (ch->tied) { + mx_tied_serialize(ch->tied, el, "tied"); + } else if (ch->slur) { + mx_slur_serialize(ch->slur, el, "slur"); + } else if (ch->tuplet) { + mx_tuplet_serialize(ch->tuplet, el, "tuplet"); + } else if (ch->glissando) { + mx_glissando_serialize(ch->glissando, el, "glissando"); + } else if (ch->slide) { + mx_slide_serialize(ch->slide, el, "slide"); + } else if (ch->ornaments) { + mx_ornaments_serialize(ch->ornaments, el, "ornaments"); + } else if (ch->technical) { + mx_technical_serialize(ch->technical, el, "technical"); + } else if (ch->articulations) { + mx_articulations_serialize(ch->articulations, el, "articulations"); + } else if (ch->dynamics) { + mx_dynamics_serialize(ch->dynamics, el, "dynamics"); + } else if (ch->fermata) { + mx_fermata_serialize(ch->fermata, el, "fermata"); + } else if (ch->arpeggiate) { + mx_arpeggiate_serialize(ch->arpeggiate, el, "arpeggiate"); + } else if (ch->non_arpeggiate) { + mx_non_arpeggiate_serialize(ch->non_arpeggiate, el, "non-arpeggiate"); + } else if (ch->accidental_mark) { + mx_accidental_mark_serialize(ch->accidental_mark, el, "accidental-mark"); + } else if (ch->other_notation) { + mx_other_notation_serialize(ch->other_notation, el, "other-notation"); + } + } + return el; +} + +void mx_notations_free(MxNotations *m) { + if (!m) + return; + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxNotationsChild *ch = &m->children[i]; + if (ch->footnote) + mx_formatted_text_free(ch->footnote); + if (ch->level) + mx_level_free(ch->level); + if (ch->tied) + mx_tied_free(ch->tied); + if (ch->slur) + mx_slur_free(ch->slur); + if (ch->tuplet) + mx_tuplet_free(ch->tuplet); + if (ch->glissando) + mx_glissando_free(ch->glissando); + if (ch->slide) + mx_slide_free(ch->slide); + if (ch->ornaments) + mx_ornaments_free(ch->ornaments); + if (ch->technical) + mx_technical_free(ch->technical); + if (ch->articulations) + mx_articulations_free(ch->articulations); + if (ch->dynamics) + mx_dynamics_free(ch->dynamics); + if (ch->fermata) + mx_fermata_free(ch->fermata); + if (ch->arpeggiate) + mx_arpeggiate_free(ch->arpeggiate); + if (ch->non_arpeggiate) + mx_non_arpeggiate_free(ch->non_arpeggiate); + if (ch->accidental_mark) + mx_accidental_mark_free(ch->accidental_mark); + if (ch->other_notation) + mx_other_notation_free(ch->other_notation); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_notations.h b/gen/test/c/mx/mx_notations.h new file mode 100644 index 000000000..5abfdc1fe --- /dev/null +++ b/gen/test/c/mx/mx_notations.h @@ -0,0 +1,70 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_NOTATIONS_H_INCLUDED +#define MX_NOTATIONS_H_INCLUDED + +#include +#include +#include +#include "mx_accidental_mark.h" +#include "mx_arpeggiate.h" +#include "mx_articulations.h" +#include "mx_dynamics.h" +#include "mx_fermata.h" +#include "mx_formatted_text.h" +#include "mx_glissando.h" +#include "mx_level.h" +#include "mx_non_arpeggiate.h" +#include "mx_ornaments.h" +#include "mx_other_notation.h" +#include "mx_slide.h" +#include "mx_slur.h" +#include "mx_technical.h" +#include "mx_tied.h" +#include "mx_tuplet.h" +#include "mx_yes_no.h" + +/* + * Notations refer to musical notations, not XML notations. Multiple notations are allowed in order + * to represent multiple editorial levels. The print-object attribute, added in Version 3.0, allows + * notations to represent details of performance technique, such as fingerings, without having them + * appear in the score. + */ +/* One child element of MxNotations: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxFormattedText *footnote; + MxLevel *level; + MxTied *tied; + MxSlur *slur; + MxTuplet *tuplet; + MxGlissando *glissando; + MxSlide *slide; + MxOrnaments *ornaments; + MxTechnical *technical; + MxArticulations *articulations; + MxDynamics *dynamics; + MxFermata *fermata; + MxArpeggiate *arpeggiate; + MxNonArpeggiate *non_arpeggiate; + MxAccidentalMark *accidental_mark; + MxOtherNotation *other_notation; +} MxNotationsChild; + +typedef struct { + bool has_print_object; + MxYesNo print_object; /* attribute print-object */ + bool has_id; + char *id; /* attribute id */ + MxNotationsChild *children; /* child elements in document order */ + size_t children_count; +} MxNotations; + +/* NULL on error; the message is in mx_error(). */ +MxNotations *mx_notations_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_notations_serialize(const MxNotations *m, xmlNodePtr parent, const char *tag); +void mx_notations_free(MxNotations *m); + +#endif /* MX_NOTATIONS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_note.c b/gen/test/c/mx/mx_note.c new file mode 100644 index 000000000..0c9372a1b --- /dev/null +++ b/gen/test/c/mx/mx_note.c @@ -0,0 +1,473 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_note.h" + +#include "mx_runtime.h" +#include +#include + +MxNote *mx_note_parse(xmlNodePtr el) { + MxNote *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "print-leger") == 0) { + m->has_print_leger = true; + m->print_leger = mx_yes_no_parse(s); + } else if (strcmp(aname, "dynamics") == 0) { + m->has_dynamics = true; + m->dynamics = mx_non_negative_decimal_parse(s); + } else if (strcmp(aname, "end-dynamics") == 0) { + m->has_end_dynamics = true; + m->end_dynamics = mx_non_negative_decimal_parse(s); + } else if (strcmp(aname, "attack") == 0) { + m->has_attack = true; + m->attack = mx_divisions_parse(s); + } else if (strcmp(aname, "release") == 0) { + m->has_release = true; + m->release = mx_divisions_parse(s); + } else if (strcmp(aname, "time-only") == 0) { + m->has_time_only = true; + m->time_only = mx_time_only_parse(s); + } else if (strcmp(aname, "pizzicato") == 0) { + m->has_pizzicato = true; + m->pizzicato = mx_yes_no_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "print-dot") == 0) { + m->has_print_dot = true; + m->print_dot = mx_yes_no_parse(s); + } else if (strcmp(aname, "print-lyric") == 0) { + m->has_print_lyric = true; + m->print_lyric = mx_yes_no_parse(s); + } else if (strcmp(aname, "print-object") == 0) { + m->has_print_object = true; + m->print_object = mx_yes_no_parse(s); + } else if (strcmp(aname, "print-spacing") == 0) { + m->has_print_spacing = true; + m->print_spacing = mx_yes_no_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_note_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxNoteChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "grace") == 0) { + ch->grace = mx_grace_parse(c); + if (!ch->grace) { + mx_note_free(m); + return NULL; + } + } else if (strcmp(tag, "chord") == 0) { + ch->chord = mx_empty_parse(c); + if (!ch->chord) { + mx_note_free(m); + return NULL; + } + } else if (strcmp(tag, "pitch") == 0) { + ch->pitch = mx_pitch_parse(c); + if (!ch->pitch) { + mx_note_free(m); + return NULL; + } + } else if (strcmp(tag, "unpitched") == 0) { + ch->unpitched = mx_unpitched_parse(c); + if (!ch->unpitched) { + mx_note_free(m); + return NULL; + } + } else if (strcmp(tag, "rest") == 0) { + ch->rest = mx_rest_parse(c); + if (!ch->rest) { + mx_note_free(m); + return NULL; + } + } else if (strcmp(tag, "tie") == 0) { + ch->tie = mx_tie_parse(c); + if (!ch->tie) { + mx_note_free(m); + return NULL; + } + } else if (strcmp(tag, "cue") == 0) { + ch->cue = mx_empty_parse(c); + if (!ch->cue) { + mx_note_free(m); + return NULL; + } + } else if (strcmp(tag, "duration") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->duration = malloc(sizeof(*ch->duration)); + if (!ch->duration) + abort(); + *ch->duration = mx_positive_divisions_parse(s); + xmlFree(text); + } else if (strcmp(tag, "instrument") == 0) { + ch->instrument = mx_instrument_parse(c); + if (!ch->instrument) { + mx_note_free(m); + return NULL; + } + } else if (strcmp(tag, "footnote") == 0) { + ch->footnote = mx_formatted_text_parse(c); + if (!ch->footnote) { + mx_note_free(m); + return NULL; + } + } else if (strcmp(tag, "level") == 0) { + ch->level = mx_level_parse(c); + if (!ch->level) { + mx_note_free(m); + return NULL; + } + } else if (strcmp(tag, "voice") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->voice = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "type") == 0) { + ch->type = mx_note_type_parse(c); + if (!ch->type) { + mx_note_free(m); + return NULL; + } + } else if (strcmp(tag, "dot") == 0) { + ch->dot = mx_empty_placement_parse(c); + if (!ch->dot) { + mx_note_free(m); + return NULL; + } + } else if (strcmp(tag, "accidental") == 0) { + ch->accidental = mx_accidental_parse(c); + if (!ch->accidental) { + mx_note_free(m); + return NULL; + } + } else if (strcmp(tag, "time-modification") == 0) { + ch->time_modification = mx_time_modification_parse(c); + if (!ch->time_modification) { + mx_note_free(m); + return NULL; + } + } else if (strcmp(tag, "stem") == 0) { + ch->stem = mx_stem_parse(c); + if (!ch->stem) { + mx_note_free(m); + return NULL; + } + } else if (strcmp(tag, "notehead") == 0) { + ch->notehead = mx_notehead_parse(c); + if (!ch->notehead) { + mx_note_free(m); + return NULL; + } + } else if (strcmp(tag, "notehead-text") == 0) { + ch->notehead_text = mx_notehead_text_parse(c); + if (!ch->notehead_text) { + mx_note_free(m); + return NULL; + } + } else if (strcmp(tag, "staff") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->staff = malloc(sizeof(*ch->staff)); + if (!ch->staff) + abort(); + *ch->staff = mx_parse_int(s); + xmlFree(text); + } else if (strcmp(tag, "beam") == 0) { + ch->beam = mx_beam_parse(c); + if (!ch->beam) { + mx_note_free(m); + return NULL; + } + } else if (strcmp(tag, "notations") == 0) { + ch->notations = mx_notations_parse(c); + if (!ch->notations) { + mx_note_free(m); + return NULL; + } + } else if (strcmp(tag, "lyric") == 0) { + ch->lyric = mx_lyric_parse(c); + if (!ch->lyric) { + mx_note_free(m); + return NULL; + } + } else if (strcmp(tag, "play") == 0) { + ch->play = mx_play_parse(c); + if (!ch->play) { + mx_note_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_note_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_note_serialize(const MxNote *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_print_leger) { + xmlSetProp(el, BAD_CAST "print-leger", BAD_CAST mx_yes_no_to_string(m->print_leger)); + } + if (m->has_dynamics) { + char *s = mx_non_negative_decimal_to_string(m->dynamics); + xmlSetProp(el, BAD_CAST "dynamics", BAD_CAST s); + free(s); + } + if (m->has_end_dynamics) { + char *s = mx_non_negative_decimal_to_string(m->end_dynamics); + xmlSetProp(el, BAD_CAST "end-dynamics", BAD_CAST s); + free(s); + } + if (m->has_attack) { + char *s = mx_divisions_to_string(m->attack); + xmlSetProp(el, BAD_CAST "attack", BAD_CAST s); + free(s); + } + if (m->has_release) { + char *s = mx_divisions_to_string(m->release); + xmlSetProp(el, BAD_CAST "release", BAD_CAST s); + free(s); + } + if (m->has_time_only) { + xmlSetProp(el, BAD_CAST "time-only", BAD_CAST m->time_only); + } + if (m->has_pizzicato) { + xmlSetProp(el, BAD_CAST "pizzicato", BAD_CAST mx_yes_no_to_string(m->pizzicato)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_print_dot) { + xmlSetProp(el, BAD_CAST "print-dot", BAD_CAST mx_yes_no_to_string(m->print_dot)); + } + if (m->has_print_lyric) { + xmlSetProp(el, BAD_CAST "print-lyric", BAD_CAST mx_yes_no_to_string(m->print_lyric)); + } + if (m->has_print_object) { + xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); + } + if (m->has_print_spacing) { + xmlSetProp(el, BAD_CAST "print-spacing", BAD_CAST mx_yes_no_to_string(m->print_spacing)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxNoteChild *ch = &m->children[i]; + if (ch->grace) { + mx_grace_serialize(ch->grace, el, "grace"); + } else if (ch->chord) { + mx_empty_serialize(ch->chord, el, "chord"); + } else if (ch->pitch) { + mx_pitch_serialize(ch->pitch, el, "pitch"); + } else if (ch->unpitched) { + mx_unpitched_serialize(ch->unpitched, el, "unpitched"); + } else if (ch->rest) { + mx_rest_serialize(ch->rest, el, "rest"); + } else if (ch->tie) { + mx_tie_serialize(ch->tie, el, "tie"); + } else if (ch->cue) { + mx_empty_serialize(ch->cue, el, "cue"); + } else if (ch->duration) { + char *s = mx_positive_divisions_to_string((*ch->duration)); + xmlNewTextChild(el, NULL, BAD_CAST "duration", BAD_CAST s); + free(s); + } else if (ch->instrument) { + mx_instrument_serialize(ch->instrument, el, "instrument"); + } else if (ch->footnote) { + mx_formatted_text_serialize(ch->footnote, el, "footnote"); + } else if (ch->level) { + mx_level_serialize(ch->level, el, "level"); + } else if (ch->voice) { + xmlNewTextChild(el, NULL, BAD_CAST "voice", BAD_CAST ch->voice); + } else if (ch->type) { + mx_note_type_serialize(ch->type, el, "type"); + } else if (ch->dot) { + mx_empty_placement_serialize(ch->dot, el, "dot"); + } else if (ch->accidental) { + mx_accidental_serialize(ch->accidental, el, "accidental"); + } else if (ch->time_modification) { + mx_time_modification_serialize(ch->time_modification, el, "time-modification"); + } else if (ch->stem) { + mx_stem_serialize(ch->stem, el, "stem"); + } else if (ch->notehead) { + mx_notehead_serialize(ch->notehead, el, "notehead"); + } else if (ch->notehead_text) { + mx_notehead_text_serialize(ch->notehead_text, el, "notehead-text"); + } else if (ch->staff) { + char *s = mx_format_int((*ch->staff)); + xmlNewTextChild(el, NULL, BAD_CAST "staff", BAD_CAST s); + free(s); + } else if (ch->beam) { + mx_beam_serialize(ch->beam, el, "beam"); + } else if (ch->notations) { + mx_notations_serialize(ch->notations, el, "notations"); + } else if (ch->lyric) { + mx_lyric_serialize(ch->lyric, el, "lyric"); + } else if (ch->play) { + mx_play_serialize(ch->play, el, "play"); + } + } + return el; +} + +void mx_note_free(MxNote *m) { + if (!m) + return; + if (m->has_time_only) + free(m->time_only); + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxNoteChild *ch = &m->children[i]; + if (ch->grace) + mx_grace_free(ch->grace); + if (ch->chord) + mx_empty_free(ch->chord); + if (ch->pitch) + mx_pitch_free(ch->pitch); + if (ch->unpitched) + mx_unpitched_free(ch->unpitched); + if (ch->rest) + mx_rest_free(ch->rest); + if (ch->tie) + mx_tie_free(ch->tie); + if (ch->cue) + mx_empty_free(ch->cue); + free(ch->duration); + if (ch->instrument) + mx_instrument_free(ch->instrument); + if (ch->footnote) + mx_formatted_text_free(ch->footnote); + if (ch->level) + mx_level_free(ch->level); + free(ch->voice); + if (ch->type) + mx_note_type_free(ch->type); + if (ch->dot) + mx_empty_placement_free(ch->dot); + if (ch->accidental) + mx_accidental_free(ch->accidental); + if (ch->time_modification) + mx_time_modification_free(ch->time_modification); + if (ch->stem) + mx_stem_free(ch->stem); + if (ch->notehead) + mx_notehead_free(ch->notehead); + if (ch->notehead_text) + mx_notehead_text_free(ch->notehead_text); + free(ch->staff); + if (ch->beam) + mx_beam_free(ch->beam); + if (ch->notations) + mx_notations_free(ch->notations); + if (ch->lyric) + mx_lyric_free(ch->lyric); + if (ch->play) + mx_play_free(ch->play); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_note.h b/gen/test/c/mx/mx_note.h new file mode 100644 index 000000000..68812a118 --- /dev/null +++ b/gen/test/c/mx/mx_note.h @@ -0,0 +1,147 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_NOTE_H_INCLUDED +#define MX_NOTE_H_INCLUDED + +#include +#include +#include +#include "mx_accidental.h" +#include "mx_beam.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_divisions.h" +#include "mx_empty.h" +#include "mx_empty_placement.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_formatted_text.h" +#include "mx_grace.h" +#include "mx_instrument.h" +#include "mx_level.h" +#include "mx_lyric.h" +#include "mx_non_negative_decimal.h" +#include "mx_notations.h" +#include "mx_note_type.h" +#include "mx_notehead.h" +#include "mx_notehead_text.h" +#include "mx_pitch.h" +#include "mx_play.h" +#include "mx_positive_divisions.h" +#include "mx_rest.h" +#include "mx_stem.h" +#include "mx_tenths.h" +#include "mx_tie.h" +#include "mx_time_modification.h" +#include "mx_time_only.h" +#include "mx_unpitched.h" +#include "mx_yes_no.h" + +/* + * Notes are the most common type of MusicXML data. The MusicXML format keeps the MuseData + * distinction between elements used for sound information and elements used for notation + * information (e.g., tie is used for sound, tied for notation). Thus grace notes do not have a + * duration element. Cue notes have a duration element, as do forward elements, but no tie elements. + * Having these two types of information available can make interchange considerably easier, as some + * programs handle one type of information much more readily than the other. The print-leger + * attribute is used to indicate whether leger lines are printed. Notes without leger lines are used + * to indicate indeterminate high and low notes. By default, it is set to yes. If print-object is + * set to no, print-leger is interpreted to also be set to no if not present. This attribute is + * ignored for rests. The dynamics and end-dynamics attributes correspond to MIDI 1.0's Note On and + * Note Off velocities, respectively. They are expressed in terms of percentages of the default + * forte value (90 for MIDI 1.0). The attack and release attributes are used to alter the starting + * and stopping time of the note from when it would otherwise occur based on the flow of durations - + * information that is specific to a performance. They are expressed in terms of divisions, either + * positive or negative. A note that starts a tie should not have a release attribute, and a note + * that stops a tie should not have an attack attribute. The attack and release attributes are + * independent of each other. The attack attribute only changes the starting time of a note, and the + * release attribute only changes the stopping time of a note. If a note is played only particular + * times through a repeat, the time-only attribute shows which times to play the note. The pizzicato + * attribute is used when just this note is sounded pizzicato, vs. the pizzicato element which + * changes overall playback between pizzicato and arco. + */ +/* One child element of MxNote: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxGrace *grace; + MxEmpty *chord; + MxPitch *pitch; + MxUnpitched *unpitched; + MxRest *rest; + MxTie *tie; + MxEmpty *cue; + MxPositiveDivisions *duration; + MxInstrument *instrument; + MxFormattedText *footnote; + MxLevel *level; + char *voice; + MxNoteType *type; + MxEmptyPlacement *dot; + MxAccidental *accidental; + MxTimeModification *time_modification; + MxStem *stem; + MxNotehead *notehead; + MxNoteheadText *notehead_text; + long *staff; + MxBeam *beam; + MxNotations *notations; + MxLyric *lyric; + MxPlay *play; +} MxNoteChild; + +typedef struct { + bool has_print_leger; + MxYesNo print_leger; /* attribute print-leger */ + bool has_dynamics; + MxNonNegativeDecimal dynamics; /* attribute dynamics */ + bool has_end_dynamics; + MxNonNegativeDecimal end_dynamics; /* attribute end-dynamics */ + bool has_attack; + MxDivisions attack; /* attribute attack */ + bool has_release; + MxDivisions release; /* attribute release */ + bool has_time_only; + MxTimeOnly time_only; /* attribute time-only */ + bool has_pizzicato; + MxYesNo pizzicato; /* attribute pizzicato */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_print_dot; + MxYesNo print_dot; /* attribute print-dot */ + bool has_print_lyric; + MxYesNo print_lyric; /* attribute print-lyric */ + bool has_print_object; + MxYesNo print_object; /* attribute print-object */ + bool has_print_spacing; + MxYesNo print_spacing; /* attribute print-spacing */ + bool has_id; + char *id; /* attribute id */ + MxNoteChild *children; /* child elements in document order */ + size_t children_count; +} MxNote; + +/* NULL on error; the message is in mx_error(). */ +MxNote *mx_note_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_note_serialize(const MxNote *m, xmlNodePtr parent, const char *tag); +void mx_note_free(MxNote *m); + +#endif /* MX_NOTE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_note_size.c b/gen/test/c/mx/mx_note_size.c new file mode 100644 index 000000000..4663cbb54 --- /dev/null +++ b/gen/test/c/mx/mx_note_size.c @@ -0,0 +1,73 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_note_size.h" + +#include "mx_runtime.h" +#include +#include + +MxNoteSize *mx_note_size_parse(xmlNodePtr el) { + MxNoteSize *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_note_size_type_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_note_size_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_non_negative_decimal_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_note_size_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_note_size_serialize(const MxNoteSize *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + { + char *s = mx_non_negative_decimal_to_string(m->value); + xmlAddChild(el, xmlNewText(BAD_CAST s)); + free(s); + } + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_note_size_type_to_string(m->type)); + } + return el; +} + +void mx_note_size_free(MxNoteSize *m) { + if (!m) + return; + free(m); +} diff --git a/gen/test/c/mx/mx_note_size.h b/gen/test/c/mx/mx_note_size.h new file mode 100644 index 000000000..01e78f81f --- /dev/null +++ b/gen/test/c/mx/mx_note_size.h @@ -0,0 +1,32 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_NOTE_SIZE_H_INCLUDED +#define MX_NOTE_SIZE_H_INCLUDED + +#include +#include +#include +#include "mx_non_negative_decimal.h" +#include "mx_note_size_type.h" + +/* + * The note-size type indicates the percentage of the regular note size to use for notes with a cue + * and large size as defined in the type element. The grace type is used for notes of cue size that + * that include a grace element. The cue type is used for all other notes with cue size, whether + * defined explicitly or implicitly via a cue element. The large type is used for notes of large + * size. The text content represent the numeric percentage. A value of 100 would be identical to the + * size of a regular note as defined by the music font. + */ +typedef struct { + bool has_type; + MxNoteSizeType type; /* attribute type */ + MxNonNegativeDecimal value; /* text content */ +} MxNoteSize; + +/* NULL on error; the message is in mx_error(). */ +MxNoteSize *mx_note_size_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_note_size_serialize(const MxNoteSize *m, xmlNodePtr parent, const char *tag); +void mx_note_size_free(MxNoteSize *m); + +#endif /* MX_NOTE_SIZE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_note_type.c b/gen/test/c/mx/mx_note_type.c new file mode 100644 index 000000000..e6385431a --- /dev/null +++ b/gen/test/c/mx/mx_note_type.c @@ -0,0 +1,69 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_note_type.h" + +#include "mx_runtime.h" +#include +#include + +MxNoteType *mx_note_type_parse(xmlNodePtr el) { + MxNoteType *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "size") == 0) { + m->has_size = true; + m->size = mx_symbol_size_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_note_type_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_note_type_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_note_type_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_note_type_serialize(const MxNoteType *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_note_type_value_to_string(m->value))); + if (m->has_size) { + xmlSetProp(el, BAD_CAST "size", BAD_CAST mx_symbol_size_to_string(m->size)); + } + return el; +} + +void mx_note_type_free(MxNoteType *m) { + if (!m) + return; + free(m); +} diff --git a/gen/test/c/mx/mx_note_type.h b/gen/test/c/mx/mx_note_type.h new file mode 100644 index 000000000..2f15bc3b1 --- /dev/null +++ b/gen/test/c/mx/mx_note_type.h @@ -0,0 +1,30 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_NOTE_TYPE_H_INCLUDED +#define MX_NOTE_TYPE_H_INCLUDED + +#include +#include +#include +#include "mx_note_type_value.h" +#include "mx_symbol_size.h" + +/* + * The note-type type indicates the graphic note type. Values range from 1024th to maxima. The size + * attribute indicates full, cue, grace-cue, or large size. The default is full for regular notes, + * grace-cue for notes that contain both grace and cue elements, and cue for notes that contain + * either a cue or a grace element, but not both. + */ +typedef struct { + bool has_size; + MxSymbolSize size; /* attribute size */ + MxNoteTypeValue value; /* text content */ +} MxNoteType; + +/* NULL on error; the message is in mx_error(). */ +MxNoteType *mx_note_type_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_note_type_serialize(const MxNoteType *m, xmlNodePtr parent, const char *tag); +void mx_note_type_free(MxNoteType *m); + +#endif /* MX_NOTE_TYPE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_notehead.c b/gen/test/c/mx/mx_notehead.c new file mode 100644 index 000000000..6784f018d --- /dev/null +++ b/gen/test/c/mx/mx_notehead.c @@ -0,0 +1,121 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_notehead.h" + +#include "mx_runtime.h" +#include +#include + +MxNotehead *mx_notehead_parse(xmlNodePtr el) { + MxNotehead *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "filled") == 0) { + m->has_filled = true; + m->filled = mx_yes_no_parse(s); + } else if (strcmp(aname, "parentheses") == 0) { + m->has_parentheses = true; + m->parentheses = mx_yes_no_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "smufl") == 0) { + m->has_smufl = true; + m->smufl = mx_smufl_glyph_name_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_notehead_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_notehead_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_notehead_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_notehead_serialize(const MxNotehead *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_notehead_value_to_string(m->value))); + if (m->has_filled) { + xmlSetProp(el, BAD_CAST "filled", BAD_CAST mx_yes_no_to_string(m->filled)); + } + if (m->has_parentheses) { + xmlSetProp(el, BAD_CAST "parentheses", BAD_CAST mx_yes_no_to_string(m->parentheses)); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_smufl) { + xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); + } + return el; +} + +void mx_notehead_free(MxNotehead *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_smufl) + free(m->smufl); + free(m); +} diff --git a/gen/test/c/mx/mx_notehead.h b/gen/test/c/mx/mx_notehead.h new file mode 100644 index 000000000..707fe4d8a --- /dev/null +++ b/gen/test/c/mx/mx_notehead.h @@ -0,0 +1,55 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_NOTEHEAD_H_INCLUDED +#define MX_NOTEHEAD_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_notehead_value.h" +#include "mx_smufl_glyph_name.h" +#include "mx_yes_no.h" + +/* + * The notehead type indicates shapes other than the open and closed ovals associated with note + * durations. The smufl attribute can be used to specify a particular notehead, allowing application + * interoperability without requiring every SMuFL glyph to have a MusicXML element equivalent. This + * attribute can be used either with the "other" value, or to refine a specific notehead value such + * as "cluster". Noteheads in the SMuFL "Note name noteheads" range (U+E150–U+E1AF) should not use + * the smufl attribute or the "other" value, but instead use the notehead-text element. For the + * enclosed shapes, the default is to be hollow for half notes and longer, and filled otherwise. The + * filled attribute can be set to change this if needed. If the parentheses attribute is set to yes, + * the notehead is parenthesized. It is no by default. + */ +typedef struct { + bool has_filled; + MxYesNo filled; /* attribute filled */ + bool has_parentheses; + MxYesNo parentheses; /* attribute parentheses */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_smufl; + MxSMUFLGlyphName smufl; /* attribute smufl */ + MxNoteheadValue value; /* text content */ +} MxNotehead; + +/* NULL on error; the message is in mx_error(). */ +MxNotehead *mx_notehead_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_notehead_serialize(const MxNotehead *m, xmlNodePtr parent, const char *tag); +void mx_notehead_free(MxNotehead *m); + +#endif /* MX_NOTEHEAD_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_notehead_text.c b/gen/test/c/mx/mx_notehead_text.c new file mode 100644 index 000000000..b6f9bcd0a --- /dev/null +++ b/gen/test/c/mx/mx_notehead_text.c @@ -0,0 +1,95 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_notehead_text.h" + +#include "mx_runtime.h" +#include +#include + +MxNoteheadText *mx_notehead_text_parse(xmlNodePtr el) { + MxNoteheadText *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_notehead_text_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxNoteheadTextChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "display-text") == 0) { + ch->display_text = mx_formatted_text_parse(c); + if (!ch->display_text) { + mx_notehead_text_free(m); + return NULL; + } + } else if (strcmp(tag, "accidental-text") == 0) { + ch->accidental_text = mx_accidental_text_parse(c); + if (!ch->accidental_text) { + mx_notehead_text_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_notehead_text_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_notehead_text_serialize(const MxNoteheadText *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxNoteheadTextChild *ch = &m->children[i]; + if (ch->display_text) { + mx_formatted_text_serialize(ch->display_text, el, "display-text"); + } else if (ch->accidental_text) { + mx_accidental_text_serialize(ch->accidental_text, el, "accidental-text"); + } + } + return el; +} + +void mx_notehead_text_free(MxNoteheadText *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxNoteheadTextChild *ch = &m->children[i]; + if (ch->display_text) + mx_formatted_text_free(ch->display_text); + if (ch->accidental_text) + mx_accidental_text_free(ch->accidental_text); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_notehead_text.h b/gen/test/c/mx/mx_notehead_text.h new file mode 100644 index 000000000..d95d5c43c --- /dev/null +++ b/gen/test/c/mx/mx_notehead_text.h @@ -0,0 +1,37 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_NOTEHEAD_TEXT_H_INCLUDED +#define MX_NOTEHEAD_TEXT_H_INCLUDED + +#include +#include +#include +#include "mx_accidental_text.h" +#include "mx_formatted_text.h" + +/* + * The notehead-text type represents text that is displayed inside a notehead, as is done in some + * educational music. It is not needed for the numbers used in tablature or jianpu notation. The + * presence of a TAB or jianpu clefs is sufficient to indicate that numbers are used. The + * display-text and accidental-text elements allow display of fully formatted text and accidentals. + */ +/* One child element of MxNoteheadText: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxFormattedText *display_text; + MxAccidentalText *accidental_text; +} MxNoteheadTextChild; + +typedef struct { + MxNoteheadTextChild *children; /* child elements in document order */ + size_t children_count; +} MxNoteheadText; + +/* NULL on error; the message is in mx_error(). */ +MxNoteheadText *mx_notehead_text_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_notehead_text_serialize(const MxNoteheadText *m, xmlNodePtr parent, const char *tag); +void mx_notehead_text_free(MxNoteheadText *m); + +#endif /* MX_NOTEHEAD_TEXT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_octave_shift.c b/gen/test/c/mx/mx_octave_shift.c new file mode 100644 index 000000000..79d4825d3 --- /dev/null +++ b/gen/test/c/mx/mx_octave_shift.c @@ -0,0 +1,172 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_octave_shift.h" + +#include "mx_runtime.h" +#include +#include + +MxOctaveShift *mx_octave_shift_parse(xmlNodePtr el) { + MxOctaveShift *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_up_down_stop_continue_parse(s); + } else if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_number_level_parse(s); + } else if (strcmp(aname, "size") == 0) { + m->has_size = true; + m->size = mx_parse_int(s); + } else if (strcmp(aname, "dash-length") == 0) { + m->has_dash_length = true; + m->dash_length = mx_tenths_parse(s); + } else if (strcmp(aname, "space-length") == 0) { + m->has_space_length = true; + m->space_length = mx_tenths_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_octave_shift_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_octave_shift_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_octave_shift_serialize(const MxOctaveShift *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_up_down_stop_continue_to_string(m->type)); + } + if (m->has_number) { + char *s = mx_number_level_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_size) { + char *s = mx_format_int(m->size); + xmlSetProp(el, BAD_CAST "size", BAD_CAST s); + free(s); + } + if (m->has_dash_length) { + char *s = mx_tenths_to_string(m->dash_length); + xmlSetProp(el, BAD_CAST "dash-length", BAD_CAST s); + free(s); + } + if (m->has_space_length) { + char *s = mx_tenths_to_string(m->space_length); + xmlSetProp(el, BAD_CAST "space-length", BAD_CAST s); + free(s); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_octave_shift_free(MxOctaveShift *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + free(m); +} diff --git a/gen/test/c/mx/mx_octave_shift.h b/gen/test/c/mx/mx_octave_shift.h new file mode 100644 index 000000000..20b917420 --- /dev/null +++ b/gen/test/c/mx/mx_octave_shift.h @@ -0,0 +1,63 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_OCTAVE_SHIFT_H_INCLUDED +#define MX_OCTAVE_SHIFT_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_number_level.h" +#include "mx_tenths.h" +#include "mx_up_down_stop_continue.h" + +/* + * The octave shift type indicates where notes are shifted up or down from their true pitched values + * because of printing difficulty. Thus a treble clef line noted with 8va will be indicated with an + * octave-shift down from the pitch data indicated in the notes. A size of 8 indicates one octave; a + * size of 15 indicates two octaves. + */ +typedef struct { + bool has_type; + MxUpDownStopContinue type; /* attribute type */ + bool has_number; + MxNumberLevel number; /* attribute number */ + bool has_size; + long size; /* attribute size */ + bool has_dash_length; + MxTenths dash_length; /* attribute dash-length */ + bool has_space_length; + MxTenths space_length; /* attribute space-length */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_id; + char *id; /* attribute id */ +} MxOctaveShift; + +/* NULL on error; the message is in mx_error(). */ +MxOctaveShift *mx_octave_shift_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_octave_shift_serialize(const MxOctaveShift *m, xmlNodePtr parent, const char *tag); +void mx_octave_shift_free(MxOctaveShift *m); + +#endif /* MX_OCTAVE_SHIFT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_offset.c b/gen/test/c/mx/mx_offset.c new file mode 100644 index 000000000..85824cb6c --- /dev/null +++ b/gen/test/c/mx/mx_offset.c @@ -0,0 +1,73 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_offset.h" + +#include "mx_runtime.h" +#include +#include + +MxOffset *mx_offset_parse(xmlNodePtr el) { + MxOffset *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "sound") == 0) { + m->has_sound = true; + m->sound = mx_yes_no_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_offset_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_divisions_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_offset_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_offset_serialize(const MxOffset *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + { + char *s = mx_divisions_to_string(m->value); + xmlAddChild(el, xmlNewText(BAD_CAST s)); + free(s); + } + if (m->has_sound) { + xmlSetProp(el, BAD_CAST "sound", BAD_CAST mx_yes_no_to_string(m->sound)); + } + return el; +} + +void mx_offset_free(MxOffset *m) { + if (!m) + return; + free(m); +} diff --git a/gen/test/c/mx/mx_offset.h b/gen/test/c/mx/mx_offset.h new file mode 100644 index 000000000..8cd9587dd --- /dev/null +++ b/gen/test/c/mx/mx_offset.h @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_OFFSET_H_INCLUDED +#define MX_OFFSET_H_INCLUDED + +#include +#include +#include +#include "mx_divisions.h" +#include "mx_yes_no.h" + +/* + * An offset is represented in terms of divisions, and indicates where the direction will appear + * relative to the current musical location. This affects the visual appearance of the direction. If + * the sound attribute is "yes", then the offset affects playback too. If the sound attribute is + * "no", then any sound associated with the direction takes effect at the current location. The + * sound attribute is "no" by default for compatibility with earlier versions of the MusicXML + * format. If an element within a direction includes a default-x attribute, the offset value will be + * ignored when determining the appearance of that element. + */ +typedef struct { + bool has_sound; + MxYesNo sound; /* attribute sound */ + MxDivisions value; /* text content */ +} MxOffset; + +/* NULL on error; the message is in mx_error(). */ +MxOffset *mx_offset_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_offset_serialize(const MxOffset *m, xmlNodePtr parent, const char *tag); +void mx_offset_free(MxOffset *m); + +#endif /* MX_OFFSET_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_opus.c b/gen/test/c/mx/mx_opus.c new file mode 100644 index 000000000..cc592d60d --- /dev/null +++ b/gen/test/c/mx/mx_opus.c @@ -0,0 +1,104 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_opus.h" + +#include "mx_runtime.h" +#include +#include + +MxOpus *mx_opus_parse(xmlNodePtr el) { + MxOpus *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "xlink:href") == 0) { + m->has_xlink_href = true; + m->xlink_href = mx_strdup(s); + } else if (strcmp(aname, "xlink:type") == 0) { + m->has_xlink_type = true; + m->xlink_type = mx_strdup(s); + } else if (strcmp(aname, "xlink:role") == 0) { + m->has_xlink_role = true; + m->xlink_role = mx_strdup(s); + } else if (strcmp(aname, "xlink:title") == 0) { + m->has_xlink_title = true; + m->xlink_title = mx_strdup(s); + } else if (strcmp(aname, "xlink:show") == 0) { + m->has_xlink_show = true; + m->xlink_show = mx_strdup(s); + } else if (strcmp(aname, "xlink:actuate") == 0) { + m->has_xlink_actuate = true; + m->xlink_actuate = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_opus_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_opus_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_opus_serialize(const MxOpus *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_xlink_href) { + xmlSetProp(el, BAD_CAST "xlink:href", BAD_CAST m->xlink_href); + } + if (m->has_xlink_type) { + xmlSetProp(el, BAD_CAST "xlink:type", BAD_CAST m->xlink_type); + } + if (m->has_xlink_role) { + xmlSetProp(el, BAD_CAST "xlink:role", BAD_CAST m->xlink_role); + } + if (m->has_xlink_title) { + xmlSetProp(el, BAD_CAST "xlink:title", BAD_CAST m->xlink_title); + } + if (m->has_xlink_show) { + xmlSetProp(el, BAD_CAST "xlink:show", BAD_CAST m->xlink_show); + } + if (m->has_xlink_actuate) { + xmlSetProp(el, BAD_CAST "xlink:actuate", BAD_CAST m->xlink_actuate); + } + return el; +} + +void mx_opus_free(MxOpus *m) { + if (!m) + return; + if (m->has_xlink_href) + free(m->xlink_href); + if (m->has_xlink_type) + free(m->xlink_type); + if (m->has_xlink_role) + free(m->xlink_role); + if (m->has_xlink_title) + free(m->xlink_title); + if (m->has_xlink_show) + free(m->xlink_show); + if (m->has_xlink_actuate) + free(m->xlink_actuate); + free(m); +} diff --git a/gen/test/c/mx/mx_opus.h b/gen/test/c/mx/mx_opus.h new file mode 100644 index 000000000..2dff1cafe --- /dev/null +++ b/gen/test/c/mx/mx_opus.h @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_OPUS_H_INCLUDED +#define MX_OPUS_H_INCLUDED + +#include +#include +#include + +/* + * The opus type represents a link to a MusicXML opus document that composes multiple MusicXML + * scores into a collection. + */ +typedef struct { + bool has_xlink_href; + char *xlink_href; /* attribute xlink:href */ + bool has_xlink_type; + char *xlink_type; /* attribute xlink:type */ + bool has_xlink_role; + char *xlink_role; /* attribute xlink:role */ + bool has_xlink_title; + char *xlink_title; /* attribute xlink:title */ + bool has_xlink_show; + char *xlink_show; /* attribute xlink:show */ + bool has_xlink_actuate; + char *xlink_actuate; /* attribute xlink:actuate */ +} MxOpus; + +/* NULL on error; the message is in mx_error(). */ +MxOpus *mx_opus_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_opus_serialize(const MxOpus *m, xmlNodePtr parent, const char *tag); +void mx_opus_free(MxOpus *m); + +#endif /* MX_OPUS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_ornaments.c b/gen/test/c/mx/mx_ornaments.c new file mode 100644 index 000000000..253999dc4 --- /dev/null +++ b/gen/test/c/mx/mx_ornaments.c @@ -0,0 +1,243 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_ornaments.h" + +#include "mx_runtime.h" +#include +#include + +MxOrnaments *mx_ornaments_parse(xmlNodePtr el) { + MxOrnaments *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_ornaments_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxOrnamentsChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "trill-mark") == 0) { + ch->trill_mark = mx_empty_trill_sound_parse(c); + if (!ch->trill_mark) { + mx_ornaments_free(m); + return NULL; + } + } else if (strcmp(tag, "turn") == 0) { + ch->turn = mx_horizontal_turn_parse(c); + if (!ch->turn) { + mx_ornaments_free(m); + return NULL; + } + } else if (strcmp(tag, "delayed-turn") == 0) { + ch->delayed_turn = mx_horizontal_turn_parse(c); + if (!ch->delayed_turn) { + mx_ornaments_free(m); + return NULL; + } + } else if (strcmp(tag, "inverted-turn") == 0) { + ch->inverted_turn = mx_horizontal_turn_parse(c); + if (!ch->inverted_turn) { + mx_ornaments_free(m); + return NULL; + } + } else if (strcmp(tag, "delayed-inverted-turn") == 0) { + ch->delayed_inverted_turn = mx_horizontal_turn_parse(c); + if (!ch->delayed_inverted_turn) { + mx_ornaments_free(m); + return NULL; + } + } else if (strcmp(tag, "vertical-turn") == 0) { + ch->vertical_turn = mx_empty_trill_sound_parse(c); + if (!ch->vertical_turn) { + mx_ornaments_free(m); + return NULL; + } + } else if (strcmp(tag, "inverted-vertical-turn") == 0) { + ch->inverted_vertical_turn = mx_empty_trill_sound_parse(c); + if (!ch->inverted_vertical_turn) { + mx_ornaments_free(m); + return NULL; + } + } else if (strcmp(tag, "shake") == 0) { + ch->shake = mx_empty_trill_sound_parse(c); + if (!ch->shake) { + mx_ornaments_free(m); + return NULL; + } + } else if (strcmp(tag, "wavy-line") == 0) { + ch->wavy_line = mx_wavy_line_parse(c); + if (!ch->wavy_line) { + mx_ornaments_free(m); + return NULL; + } + } else if (strcmp(tag, "mordent") == 0) { + ch->mordent = mx_mordent_parse(c); + if (!ch->mordent) { + mx_ornaments_free(m); + return NULL; + } + } else if (strcmp(tag, "inverted-mordent") == 0) { + ch->inverted_mordent = mx_mordent_parse(c); + if (!ch->inverted_mordent) { + mx_ornaments_free(m); + return NULL; + } + } else if (strcmp(tag, "schleifer") == 0) { + ch->schleifer = mx_empty_placement_parse(c); + if (!ch->schleifer) { + mx_ornaments_free(m); + return NULL; + } + } else if (strcmp(tag, "tremolo") == 0) { + ch->tremolo = mx_tremolo_parse(c); + if (!ch->tremolo) { + mx_ornaments_free(m); + return NULL; + } + } else if (strcmp(tag, "haydn") == 0) { + ch->haydn = mx_empty_trill_sound_parse(c); + if (!ch->haydn) { + mx_ornaments_free(m); + return NULL; + } + } else if (strcmp(tag, "other-ornament") == 0) { + ch->other_ornament = mx_other_placement_text_parse(c); + if (!ch->other_ornament) { + mx_ornaments_free(m); + return NULL; + } + } else if (strcmp(tag, "accidental-mark") == 0) { + ch->accidental_mark = mx_accidental_mark_parse(c); + if (!ch->accidental_mark) { + mx_ornaments_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_ornaments_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_ornaments_serialize(const MxOrnaments *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxOrnamentsChild *ch = &m->children[i]; + if (ch->trill_mark) { + mx_empty_trill_sound_serialize(ch->trill_mark, el, "trill-mark"); + } else if (ch->turn) { + mx_horizontal_turn_serialize(ch->turn, el, "turn"); + } else if (ch->delayed_turn) { + mx_horizontal_turn_serialize(ch->delayed_turn, el, "delayed-turn"); + } else if (ch->inverted_turn) { + mx_horizontal_turn_serialize(ch->inverted_turn, el, "inverted-turn"); + } else if (ch->delayed_inverted_turn) { + mx_horizontal_turn_serialize(ch->delayed_inverted_turn, el, "delayed-inverted-turn"); + } else if (ch->vertical_turn) { + mx_empty_trill_sound_serialize(ch->vertical_turn, el, "vertical-turn"); + } else if (ch->inverted_vertical_turn) { + mx_empty_trill_sound_serialize(ch->inverted_vertical_turn, el, "inverted-vertical-turn"); + } else if (ch->shake) { + mx_empty_trill_sound_serialize(ch->shake, el, "shake"); + } else if (ch->wavy_line) { + mx_wavy_line_serialize(ch->wavy_line, el, "wavy-line"); + } else if (ch->mordent) { + mx_mordent_serialize(ch->mordent, el, "mordent"); + } else if (ch->inverted_mordent) { + mx_mordent_serialize(ch->inverted_mordent, el, "inverted-mordent"); + } else if (ch->schleifer) { + mx_empty_placement_serialize(ch->schleifer, el, "schleifer"); + } else if (ch->tremolo) { + mx_tremolo_serialize(ch->tremolo, el, "tremolo"); + } else if (ch->haydn) { + mx_empty_trill_sound_serialize(ch->haydn, el, "haydn"); + } else if (ch->other_ornament) { + mx_other_placement_text_serialize(ch->other_ornament, el, "other-ornament"); + } else if (ch->accidental_mark) { + mx_accidental_mark_serialize(ch->accidental_mark, el, "accidental-mark"); + } + } + return el; +} + +void mx_ornaments_free(MxOrnaments *m) { + if (!m) + return; + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxOrnamentsChild *ch = &m->children[i]; + if (ch->trill_mark) + mx_empty_trill_sound_free(ch->trill_mark); + if (ch->turn) + mx_horizontal_turn_free(ch->turn); + if (ch->delayed_turn) + mx_horizontal_turn_free(ch->delayed_turn); + if (ch->inverted_turn) + mx_horizontal_turn_free(ch->inverted_turn); + if (ch->delayed_inverted_turn) + mx_horizontal_turn_free(ch->delayed_inverted_turn); + if (ch->vertical_turn) + mx_empty_trill_sound_free(ch->vertical_turn); + if (ch->inverted_vertical_turn) + mx_empty_trill_sound_free(ch->inverted_vertical_turn); + if (ch->shake) + mx_empty_trill_sound_free(ch->shake); + if (ch->wavy_line) + mx_wavy_line_free(ch->wavy_line); + if (ch->mordent) + mx_mordent_free(ch->mordent); + if (ch->inverted_mordent) + mx_mordent_free(ch->inverted_mordent); + if (ch->schleifer) + mx_empty_placement_free(ch->schleifer); + if (ch->tremolo) + mx_tremolo_free(ch->tremolo); + if (ch->haydn) + mx_empty_trill_sound_free(ch->haydn); + if (ch->other_ornament) + mx_other_placement_text_free(ch->other_ornament); + if (ch->accidental_mark) + mx_accidental_mark_free(ch->accidental_mark); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_ornaments.h b/gen/test/c/mx/mx_ornaments.h new file mode 100644 index 000000000..d8d908f91 --- /dev/null +++ b/gen/test/c/mx/mx_ornaments.h @@ -0,0 +1,58 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ORNAMENTS_H_INCLUDED +#define MX_ORNAMENTS_H_INCLUDED + +#include +#include +#include +#include "mx_accidental_mark.h" +#include "mx_empty_placement.h" +#include "mx_empty_trill_sound.h" +#include "mx_horizontal_turn.h" +#include "mx_mordent.h" +#include "mx_other_placement_text.h" +#include "mx_tremolo.h" +#include "mx_wavy_line.h" + +/* + * Ornaments can be any of several types, followed optionally by accidentals. The accidental-mark + * element's content is represented the same as an accidental element, but with a different name to + * reflect the different musical meaning. + */ +/* One child element of MxOrnaments: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxEmptyTrillSound *trill_mark; + MxHorizontalTurn *turn; + MxHorizontalTurn *delayed_turn; + MxHorizontalTurn *inverted_turn; + MxHorizontalTurn *delayed_inverted_turn; + MxEmptyTrillSound *vertical_turn; + MxEmptyTrillSound *inverted_vertical_turn; + MxEmptyTrillSound *shake; + MxWavyLine *wavy_line; + MxMordent *mordent; + MxMordent *inverted_mordent; + MxEmptyPlacement *schleifer; + MxTremolo *tremolo; + MxEmptyTrillSound *haydn; + MxOtherPlacementText *other_ornament; + MxAccidentalMark *accidental_mark; +} MxOrnamentsChild; + +typedef struct { + bool has_id; + char *id; /* attribute id */ + MxOrnamentsChild *children; /* child elements in document order */ + size_t children_count; +} MxOrnaments; + +/* NULL on error; the message is in mx_error(). */ +MxOrnaments *mx_ornaments_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_ornaments_serialize(const MxOrnaments *m, xmlNodePtr parent, const char *tag); +void mx_ornaments_free(MxOrnaments *m); + +#endif /* MX_ORNAMENTS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_other_appearance.c b/gen/test/c/mx/mx_other_appearance.c new file mode 100644 index 000000000..282941576 --- /dev/null +++ b/gen/test/c/mx/mx_other_appearance.c @@ -0,0 +1,72 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_other_appearance.h" + +#include "mx_runtime.h" +#include +#include + +MxOtherAppearance *mx_other_appearance_parse(xmlNodePtr el) { + MxOtherAppearance *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_other_appearance_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_other_appearance_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_other_appearance_serialize(const MxOtherAppearance *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST m->type); + } + return el; +} + +void mx_other_appearance_free(MxOtherAppearance *m) { + if (!m) + return; + if (m->has_type) + free(m->type); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_other_appearance.h b/gen/test/c/mx/mx_other_appearance.h new file mode 100644 index 000000000..0b2cd822e --- /dev/null +++ b/gen/test/c/mx/mx_other_appearance.h @@ -0,0 +1,27 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_OTHER_APPEARANCE_H_INCLUDED +#define MX_OTHER_APPEARANCE_H_INCLUDED + +#include +#include +#include + +/* + * The other-appearance type is used to define any graphical settings not yet in the current version + * of the MusicXML format. This allows extended representation, though without application + * interoperability. + */ +typedef struct { + bool has_type; + char *type; /* attribute type */ + char *value; /* text content */ +} MxOtherAppearance; + +/* NULL on error; the message is in mx_error(). */ +MxOtherAppearance *mx_other_appearance_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_other_appearance_serialize(const MxOtherAppearance *m, xmlNodePtr parent, const char *tag); +void mx_other_appearance_free(MxOtherAppearance *m); + +#endif /* MX_OTHER_APPEARANCE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_other_direction.c b/gen/test/c/mx/mx_other_direction.c new file mode 100644 index 000000000..7bad2481c --- /dev/null +++ b/gen/test/c/mx/mx_other_direction.c @@ -0,0 +1,168 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_other_direction.h" + +#include "mx_runtime.h" +#include +#include + +MxOtherDirection *mx_other_direction_parse(xmlNodePtr el) { + MxOtherDirection *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "print-object") == 0) { + m->has_print_object = true; + m->print_object = mx_yes_no_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else if (strcmp(aname, "smufl") == 0) { + m->has_smufl = true; + m->smufl = mx_smufl_glyph_name_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_other_direction_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_other_direction_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_other_direction_serialize(const MxOtherDirection *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_print_object) { + xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + if (m->has_smufl) { + xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_other_direction_free(MxOtherDirection *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_smufl) + free(m->smufl); + if (m->has_id) + free(m->id); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_other_direction.h b/gen/test/c/mx/mx_other_direction.h new file mode 100644 index 000000000..e1f8b015f --- /dev/null +++ b/gen/test/c/mx/mx_other_direction.h @@ -0,0 +1,65 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_OTHER_DIRECTION_H_INCLUDED +#define MX_OTHER_DIRECTION_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_center_right.h" +#include "mx_smufl_glyph_name.h" +#include "mx_tenths.h" +#include "mx_valign.h" +#include "mx_yes_no.h" + +/* + * The other-direction type is used to define any direction symbols not yet in the MusicXML format. + * The smufl attribute can be used to specify a particular direction symbol, allowing application + * interoperability without requiring every SMuFL glyph to have a MusicXML element equivalent. Using + * the other-direction type without the smufl attribute allows for extended representation, though + * without application interoperability. + */ +typedef struct { + bool has_print_object; + MxYesNo print_object; /* attribute print-object */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ + bool has_smufl; + MxSMUFLGlyphName smufl; /* attribute smufl */ + bool has_id; + char *id; /* attribute id */ + char *value; /* text content */ +} MxOtherDirection; + +/* NULL on error; the message is in mx_error(). */ +MxOtherDirection *mx_other_direction_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_other_direction_serialize(const MxOtherDirection *m, xmlNodePtr parent, const char *tag); +void mx_other_direction_free(MxOtherDirection *m); + +#endif /* MX_OTHER_DIRECTION_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_other_notation.c b/gen/test/c/mx/mx_other_notation.c new file mode 100644 index 000000000..a1f4d69b7 --- /dev/null +++ b/gen/test/c/mx/mx_other_notation.c @@ -0,0 +1,176 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_other_notation.h" + +#include "mx_runtime.h" +#include +#include + +MxOtherNotation *mx_other_notation_parse(xmlNodePtr el) { + MxOtherNotation *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_single_parse(s); + } else if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_number_level_parse(s); + } else if (strcmp(aname, "print-object") == 0) { + m->has_print_object = true; + m->print_object = mx_yes_no_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "smufl") == 0) { + m->has_smufl = true; + m->smufl = mx_smufl_glyph_name_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_other_notation_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_other_notation_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_other_notation_serialize(const MxOtherNotation *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_single_to_string(m->type)); + } + if (m->has_number) { + char *s = mx_number_level_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_print_object) { + xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_smufl) { + xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_other_notation_free(MxOtherNotation *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_smufl) + free(m->smufl); + if (m->has_id) + free(m->id); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_other_notation.h b/gen/test/c/mx/mx_other_notation.h new file mode 100644 index 000000000..76db6518d --- /dev/null +++ b/gen/test/c/mx/mx_other_notation.h @@ -0,0 +1,69 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_OTHER_NOTATION_H_INCLUDED +#define MX_OTHER_NOTATION_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_number_level.h" +#include "mx_smufl_glyph_name.h" +#include "mx_start_stop_single.h" +#include "mx_tenths.h" +#include "mx_yes_no.h" + +/* + * The other-notation type is used to define any notations not yet in the MusicXML format. It + * handles notations where more specific extension elements such as other-dynamics and + * other-technical are not appropriate. The smufl attribute can be used to specify a particular + * notation, allowing application interoperability without requiring every SMuFL glyph to have a + * MusicXML element equivalent. Using the other-notation type without the smufl attribute allows for + * extended representation, though without application interoperability. + */ +typedef struct { + bool has_type; + MxStartStopSingle type; /* attribute type */ + bool has_number; + MxNumberLevel number; /* attribute number */ + bool has_print_object; + MxYesNo print_object; /* attribute print-object */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_smufl; + MxSMUFLGlyphName smufl; /* attribute smufl */ + bool has_id; + char *id; /* attribute id */ + char *value; /* text content */ +} MxOtherNotation; + +/* NULL on error; the message is in mx_error(). */ +MxOtherNotation *mx_other_notation_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_other_notation_serialize(const MxOtherNotation *m, xmlNodePtr parent, const char *tag); +void mx_other_notation_free(MxOtherNotation *m); + +#endif /* MX_OTHER_NOTATION_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_other_placement_text.c b/gen/test/c/mx/mx_other_placement_text.c new file mode 100644 index 000000000..8837332a1 --- /dev/null +++ b/gen/test/c/mx/mx_other_placement_text.c @@ -0,0 +1,148 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_other_placement_text.h" + +#include "mx_runtime.h" +#include +#include + +MxOtherPlacementText *mx_other_placement_text_parse(xmlNodePtr el) { + MxOtherPlacementText *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "smufl") == 0) { + m->has_smufl = true; + m->smufl = mx_smufl_glyph_name_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_other_placement_text_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_other_placement_text_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_other_placement_text_serialize(const MxOtherPlacementText *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_smufl) { + xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); + } + return el; +} + +void mx_other_placement_text_free(MxOtherPlacementText *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_smufl) + free(m->smufl); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_other_placement_text.h b/gen/test/c/mx/mx_other_placement_text.h new file mode 100644 index 000000000..d8de7c638 --- /dev/null +++ b/gen/test/c/mx/mx_other_placement_text.h @@ -0,0 +1,55 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_OTHER_PLACEMENT_TEXT_H_INCLUDED +#define MX_OTHER_PLACEMENT_TEXT_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_smufl_glyph_name.h" +#include "mx_tenths.h" + +/* + * The other-placement-text type represents a text element with print-style, placement, and smufl + * attribute groups. This type is used by MusicXML notation extension elements to allow + * specification of specific SMuFL glyphs without needed to add every glyph as a MusicXML element. + */ +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_smufl; + MxSMUFLGlyphName smufl; /* attribute smufl */ + char *value; /* text content */ +} MxOtherPlacementText; + +/* NULL on error; the message is in mx_error(). */ +MxOtherPlacementText *mx_other_placement_text_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_other_placement_text_serialize(const MxOtherPlacementText *m, xmlNodePtr parent, const char *tag); +void mx_other_placement_text_free(MxOtherPlacementText *m); + +#endif /* MX_OTHER_PLACEMENT_TEXT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_other_play.c b/gen/test/c/mx/mx_other_play.c new file mode 100644 index 000000000..2a0f4e1d1 --- /dev/null +++ b/gen/test/c/mx/mx_other_play.c @@ -0,0 +1,72 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_other_play.h" + +#include "mx_runtime.h" +#include +#include + +MxOtherPlay *mx_other_play_parse(xmlNodePtr el) { + MxOtherPlay *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_other_play_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_other_play_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_other_play_serialize(const MxOtherPlay *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST m->type); + } + return el; +} + +void mx_other_play_free(MxOtherPlay *m) { + if (!m) + return; + if (m->has_type) + free(m->type); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_other_play.h b/gen/test/c/mx/mx_other_play.h new file mode 100644 index 000000000..474c683dd --- /dev/null +++ b/gen/test/c/mx/mx_other_play.h @@ -0,0 +1,26 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_OTHER_PLAY_H_INCLUDED +#define MX_OTHER_PLAY_H_INCLUDED + +#include +#include +#include + +/* + * The other-play element represents other types of playback. The required type attribute indicates + * the type of playback to which the element content applies. + */ +typedef struct { + bool has_type; + char *type; /* attribute type */ + char *value; /* text content */ +} MxOtherPlay; + +/* NULL on error; the message is in mx_error(). */ +MxOtherPlay *mx_other_play_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_other_play_serialize(const MxOtherPlay *m, xmlNodePtr parent, const char *tag); +void mx_other_play_free(MxOtherPlay *m); + +#endif /* MX_OTHER_PLAY_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_other_text.c b/gen/test/c/mx/mx_other_text.c new file mode 100644 index 000000000..ab6a8edb3 --- /dev/null +++ b/gen/test/c/mx/mx_other_text.c @@ -0,0 +1,72 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_other_text.h" + +#include "mx_runtime.h" +#include +#include + +MxOtherText *mx_other_text_parse(xmlNodePtr el) { + MxOtherText *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "smufl") == 0) { + m->has_smufl = true; + m->smufl = mx_smufl_glyph_name_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_other_text_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_other_text_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_other_text_serialize(const MxOtherText *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_smufl) { + xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); + } + return el; +} + +void mx_other_text_free(MxOtherText *m) { + if (!m) + return; + if (m->has_smufl) + free(m->smufl); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_other_text.h b/gen/test/c/mx/mx_other_text.h new file mode 100644 index 000000000..c6ea0059e --- /dev/null +++ b/gen/test/c/mx/mx_other_text.h @@ -0,0 +1,28 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_OTHER_TEXT_H_INCLUDED +#define MX_OTHER_TEXT_H_INCLUDED + +#include +#include +#include +#include "mx_smufl_glyph_name.h" + +/* + * The other-text type represents a text element with a smufl attribute group. This type is used by + * MusicXML direction extension elements to allow specification of specific SMuFL glyphs without + * needed to add every glyph as a MusicXML element. + */ +typedef struct { + bool has_smufl; + MxSMUFLGlyphName smufl; /* attribute smufl */ + char *value; /* text content */ +} MxOtherText; + +/* NULL on error; the message is in mx_error(). */ +MxOtherText *mx_other_text_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_other_text_serialize(const MxOtherText *m, xmlNodePtr parent, const char *tag); +void mx_other_text_free(MxOtherText *m); + +#endif /* MX_OTHER_TEXT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_page_layout.c b/gen/test/c/mx/mx_page_layout.c new file mode 100644 index 000000000..b85818c8a --- /dev/null +++ b/gen/test/c/mx/mx_page_layout.c @@ -0,0 +1,111 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_page_layout.h" + +#include "mx_runtime.h" +#include +#include + +MxPageLayout *mx_page_layout_parse(xmlNodePtr el) { + MxPageLayout *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_page_layout_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxPageLayoutChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "page-height") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->page_height = malloc(sizeof(*ch->page_height)); + if (!ch->page_height) + abort(); + *ch->page_height = mx_tenths_parse(s); + xmlFree(text); + } else if (strcmp(tag, "page-width") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->page_width = malloc(sizeof(*ch->page_width)); + if (!ch->page_width) + abort(); + *ch->page_width = mx_tenths_parse(s); + xmlFree(text); + } else if (strcmp(tag, "page-margins") == 0) { + ch->page_margins = mx_page_margins_parse(c); + if (!ch->page_margins) { + mx_page_layout_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_page_layout_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_page_layout_serialize(const MxPageLayout *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxPageLayoutChild *ch = &m->children[i]; + if (ch->page_height) { + char *s = mx_tenths_to_string((*ch->page_height)); + xmlNewTextChild(el, NULL, BAD_CAST "page-height", BAD_CAST s); + free(s); + } else if (ch->page_width) { + char *s = mx_tenths_to_string((*ch->page_width)); + xmlNewTextChild(el, NULL, BAD_CAST "page-width", BAD_CAST s); + free(s); + } else if (ch->page_margins) { + mx_page_margins_serialize(ch->page_margins, el, "page-margins"); + } + } + return el; +} + +void mx_page_layout_free(MxPageLayout *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxPageLayoutChild *ch = &m->children[i]; + free(ch->page_height); + free(ch->page_width); + if (ch->page_margins) + mx_page_margins_free(ch->page_margins); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_page_layout.h b/gen/test/c/mx/mx_page_layout.h new file mode 100644 index 000000000..4f0fc9014 --- /dev/null +++ b/gen/test/c/mx/mx_page_layout.h @@ -0,0 +1,38 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PAGE_LAYOUT_H_INCLUDED +#define MX_PAGE_LAYOUT_H_INCLUDED + +#include +#include +#include +#include "mx_page_margins.h" +#include "mx_tenths.h" + +/* + * Page layout can be defined both in score-wide defaults and in the print element. Page margins are + * specified either for both even and odd pages, or via separate odd and even page number values. + * The type is not needed when used as part of a print element. If omitted when used in the defaults + * element, "both" is the default. + */ +/* One child element of MxPageLayout: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxTenths *page_height; + MxTenths *page_width; + MxPageMargins *page_margins; +} MxPageLayoutChild; + +typedef struct { + MxPageLayoutChild *children; /* child elements in document order */ + size_t children_count; +} MxPageLayout; + +/* NULL on error; the message is in mx_error(). */ +MxPageLayout *mx_page_layout_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_page_layout_serialize(const MxPageLayout *m, xmlNodePtr parent, const char *tag); +void mx_page_layout_free(MxPageLayout *m); + +#endif /* MX_PAGE_LAYOUT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_page_margins.c b/gen/test/c/mx/mx_page_margins.c new file mode 100644 index 000000000..338464a2a --- /dev/null +++ b/gen/test/c/mx/mx_page_margins.c @@ -0,0 +1,133 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_page_margins.h" + +#include "mx_runtime.h" +#include +#include + +MxPageMargins *mx_page_margins_parse(xmlNodePtr el) { + MxPageMargins *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_margin_type_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_page_margins_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxPageMarginsChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "left-margin") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->left_margin = malloc(sizeof(*ch->left_margin)); + if (!ch->left_margin) + abort(); + *ch->left_margin = mx_tenths_parse(s); + xmlFree(text); + } else if (strcmp(tag, "right-margin") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->right_margin = malloc(sizeof(*ch->right_margin)); + if (!ch->right_margin) + abort(); + *ch->right_margin = mx_tenths_parse(s); + xmlFree(text); + } else if (strcmp(tag, "top-margin") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->top_margin = malloc(sizeof(*ch->top_margin)); + if (!ch->top_margin) + abort(); + *ch->top_margin = mx_tenths_parse(s); + xmlFree(text); + } else if (strcmp(tag, "bottom-margin") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->bottom_margin = malloc(sizeof(*ch->bottom_margin)); + if (!ch->bottom_margin) + abort(); + *ch->bottom_margin = mx_tenths_parse(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_page_margins_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_page_margins_serialize(const MxPageMargins *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_margin_type_to_string(m->type)); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxPageMarginsChild *ch = &m->children[i]; + if (ch->left_margin) { + char *s = mx_tenths_to_string((*ch->left_margin)); + xmlNewTextChild(el, NULL, BAD_CAST "left-margin", BAD_CAST s); + free(s); + } else if (ch->right_margin) { + char *s = mx_tenths_to_string((*ch->right_margin)); + xmlNewTextChild(el, NULL, BAD_CAST "right-margin", BAD_CAST s); + free(s); + } else if (ch->top_margin) { + char *s = mx_tenths_to_string((*ch->top_margin)); + xmlNewTextChild(el, NULL, BAD_CAST "top-margin", BAD_CAST s); + free(s); + } else if (ch->bottom_margin) { + char *s = mx_tenths_to_string((*ch->bottom_margin)); + xmlNewTextChild(el, NULL, BAD_CAST "bottom-margin", BAD_CAST s); + free(s); + } + } + return el; +} + +void mx_page_margins_free(MxPageMargins *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxPageMarginsChild *ch = &m->children[i]; + free(ch->left_margin); + free(ch->right_margin); + free(ch->top_margin); + free(ch->bottom_margin); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_page_margins.h b/gen/test/c/mx/mx_page_margins.h new file mode 100644 index 000000000..236f8c086 --- /dev/null +++ b/gen/test/c/mx/mx_page_margins.h @@ -0,0 +1,40 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PAGE_MARGINS_H_INCLUDED +#define MX_PAGE_MARGINS_H_INCLUDED + +#include +#include +#include +#include "mx_margin_type.h" +#include "mx_tenths.h" + +/* + * Page margins are specified either for both even and odd pages, or via separate odd and even page + * number values. The type attribute is not needed when used as part of a print element. If omitted + * when the page-margins type is used in the defaults element, "both" is the default value. + */ +/* One child element of MxPageMargins: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxTenths *left_margin; + MxTenths *right_margin; + MxTenths *top_margin; + MxTenths *bottom_margin; +} MxPageMarginsChild; + +typedef struct { + bool has_type; + MxMarginType type; /* attribute type */ + MxPageMarginsChild *children; /* child elements in document order */ + size_t children_count; +} MxPageMargins; + +/* NULL on error; the message is in mx_error(). */ +MxPageMargins *mx_page_margins_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_page_margins_serialize(const MxPageMargins *m, xmlNodePtr parent, const char *tag); +void mx_page_margins_free(MxPageMargins *m); + +#endif /* MX_PAGE_MARGINS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_part_group.c b/gen/test/c/mx/mx_part_group.c new file mode 100644 index 000000000..1e84454b2 --- /dev/null +++ b/gen/test/c/mx/mx_part_group.c @@ -0,0 +1,179 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_part_group.h" + +#include "mx_runtime.h" +#include +#include + +MxPartGroup *mx_part_group_parse(xmlNodePtr el) { + MxPartGroup *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_parse(s); + } else if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_part_group_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxPartGroupChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "group-name") == 0) { + ch->group_name = mx_group_name_parse(c); + if (!ch->group_name) { + mx_part_group_free(m); + return NULL; + } + } else if (strcmp(tag, "group-name-display") == 0) { + ch->group_name_display = mx_name_display_parse(c); + if (!ch->group_name_display) { + mx_part_group_free(m); + return NULL; + } + } else if (strcmp(tag, "group-abbreviation") == 0) { + ch->group_abbreviation = mx_group_name_parse(c); + if (!ch->group_abbreviation) { + mx_part_group_free(m); + return NULL; + } + } else if (strcmp(tag, "group-abbreviation-display") == 0) { + ch->group_abbreviation_display = mx_name_display_parse(c); + if (!ch->group_abbreviation_display) { + mx_part_group_free(m); + return NULL; + } + } else if (strcmp(tag, "group-symbol") == 0) { + ch->group_symbol = mx_group_symbol_parse(c); + if (!ch->group_symbol) { + mx_part_group_free(m); + return NULL; + } + } else if (strcmp(tag, "group-barline") == 0) { + ch->group_barline = mx_group_barline_parse(c); + if (!ch->group_barline) { + mx_part_group_free(m); + return NULL; + } + } else if (strcmp(tag, "group-time") == 0) { + ch->group_time = mx_empty_parse(c); + if (!ch->group_time) { + mx_part_group_free(m); + return NULL; + } + } else if (strcmp(tag, "footnote") == 0) { + ch->footnote = mx_formatted_text_parse(c); + if (!ch->footnote) { + mx_part_group_free(m); + return NULL; + } + } else if (strcmp(tag, "level") == 0) { + ch->level = mx_level_parse(c); + if (!ch->level) { + mx_part_group_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_part_group_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_part_group_serialize(const MxPartGroup *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); + } + if (m->has_number) { + xmlSetProp(el, BAD_CAST "number", BAD_CAST m->number); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxPartGroupChild *ch = &m->children[i]; + if (ch->group_name) { + mx_group_name_serialize(ch->group_name, el, "group-name"); + } else if (ch->group_name_display) { + mx_name_display_serialize(ch->group_name_display, el, "group-name-display"); + } else if (ch->group_abbreviation) { + mx_group_name_serialize(ch->group_abbreviation, el, "group-abbreviation"); + } else if (ch->group_abbreviation_display) { + mx_name_display_serialize(ch->group_abbreviation_display, el, "group-abbreviation-display"); + } else if (ch->group_symbol) { + mx_group_symbol_serialize(ch->group_symbol, el, "group-symbol"); + } else if (ch->group_barline) { + mx_group_barline_serialize(ch->group_barline, el, "group-barline"); + } else if (ch->group_time) { + mx_empty_serialize(ch->group_time, el, "group-time"); + } else if (ch->footnote) { + mx_formatted_text_serialize(ch->footnote, el, "footnote"); + } else if (ch->level) { + mx_level_serialize(ch->level, el, "level"); + } + } + return el; +} + +void mx_part_group_free(MxPartGroup *m) { + if (!m) + return; + if (m->has_number) + free(m->number); + for (size_t i = 0; i < m->children_count; i++) { + MxPartGroupChild *ch = &m->children[i]; + if (ch->group_name) + mx_group_name_free(ch->group_name); + if (ch->group_name_display) + mx_name_display_free(ch->group_name_display); + if (ch->group_abbreviation) + mx_group_name_free(ch->group_abbreviation); + if (ch->group_abbreviation_display) + mx_name_display_free(ch->group_abbreviation_display); + if (ch->group_symbol) + mx_group_symbol_free(ch->group_symbol); + if (ch->group_barline) + mx_group_barline_free(ch->group_barline); + if (ch->group_time) + mx_empty_free(ch->group_time); + if (ch->footnote) + mx_formatted_text_free(ch->footnote); + if (ch->level) + mx_level_free(ch->level); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_part_group.h b/gen/test/c/mx/mx_part_group.h new file mode 100644 index 000000000..c0a962b90 --- /dev/null +++ b/gen/test/c/mx/mx_part_group.h @@ -0,0 +1,60 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PART_GROUP_H_INCLUDED +#define MX_PART_GROUP_H_INCLUDED + +#include +#include +#include +#include "mx_empty.h" +#include "mx_formatted_text.h" +#include "mx_group_barline.h" +#include "mx_group_name.h" +#include "mx_group_symbol.h" +#include "mx_level.h" +#include "mx_name_display.h" +#include "mx_start_stop.h" + +/* + * The part-group element indicates groupings of parts in the score, usually indicated by braces and + * brackets. Braces that are used for multi-staff parts should be defined in the attributes element + * for that part. The part-group start element appears before the first score-part in the group. The + * part-group stop element appears after the last score-part in the group. The number attribute is + * used to distinguish overlapping and nested part-groups, not the sequence of groups. As with + * parts, groups can have a name and abbreviation. Values for the child elements are ignored at the + * stop of a group. A part-group element is not needed for a single multi-staff part. By default, + * multi-staff parts include a brace symbol and (if appropriate given the bar-style) common + * barlines. The symbol formatting for a multi-staff part can be more fully specified using the + * part-symbol element. + */ +/* One child element of MxPartGroup: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxGroupName *group_name; + MxNameDisplay *group_name_display; + MxGroupName *group_abbreviation; + MxNameDisplay *group_abbreviation_display; + MxGroupSymbol *group_symbol; + MxGroupBarline *group_barline; + MxEmpty *group_time; + MxFormattedText *footnote; + MxLevel *level; +} MxPartGroupChild; + +typedef struct { + bool has_type; + MxStartStop type; /* attribute type */ + bool has_number; + char *number; /* attribute number */ + MxPartGroupChild *children; /* child elements in document order */ + size_t children_count; +} MxPartGroup; + +/* NULL on error; the message is in mx_error(). */ +MxPartGroup *mx_part_group_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_part_group_serialize(const MxPartGroup *m, xmlNodePtr parent, const char *tag); +void mx_part_group_free(MxPartGroup *m); + +#endif /* MX_PART_GROUP_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_part_list.c b/gen/test/c/mx/mx_part_list.c new file mode 100644 index 000000000..2ef7dcf04 --- /dev/null +++ b/gen/test/c/mx/mx_part_list.c @@ -0,0 +1,95 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_part_list.h" + +#include "mx_runtime.h" +#include +#include + +MxPartList *mx_part_list_parse(xmlNodePtr el) { + MxPartList *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_part_list_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxPartListChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "part-group") == 0) { + ch->part_group = mx_part_group_parse(c); + if (!ch->part_group) { + mx_part_list_free(m); + return NULL; + } + } else if (strcmp(tag, "score-part") == 0) { + ch->score_part = mx_score_part_parse(c); + if (!ch->score_part) { + mx_part_list_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_part_list_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_part_list_serialize(const MxPartList *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxPartListChild *ch = &m->children[i]; + if (ch->part_group) { + mx_part_group_serialize(ch->part_group, el, "part-group"); + } else if (ch->score_part) { + mx_score_part_serialize(ch->score_part, el, "score-part"); + } + } + return el; +} + +void mx_part_list_free(MxPartList *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxPartListChild *ch = &m->children[i]; + if (ch->part_group) + mx_part_group_free(ch->part_group); + if (ch->score_part) + mx_score_part_free(ch->score_part); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_part_list.h b/gen/test/c/mx/mx_part_list.h new file mode 100644 index 000000000..73c00f0e5 --- /dev/null +++ b/gen/test/c/mx/mx_part_list.h @@ -0,0 +1,39 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PART_LIST_H_INCLUDED +#define MX_PART_LIST_H_INCLUDED + +#include +#include +#include +#include "mx_part_group.h" +#include "mx_score_part.h" + +/* + * The part-list identifies the different musical parts in this movement. Each part has an ID that + * is used later within the musical data. Since parts may be encoded separately and combined later, + * identification elements are present at both the score and score-part levels. There must be at + * least one score-part, combined as desired with part-group elements that indicate braces and + * brackets. Parts are ordered from top to bottom in a score based on the order in which they appear + * in the part-list. + */ +/* One child element of MxPartList: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxPartGroup *part_group; + MxScorePart *score_part; +} MxPartListChild; + +typedef struct { + MxPartListChild *children; /* child elements in document order */ + size_t children_count; +} MxPartList; + +/* NULL on error; the message is in mx_error(). */ +MxPartList *mx_part_list_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_part_list_serialize(const MxPartList *m, xmlNodePtr parent, const char *tag); +void mx_part_list_free(MxPartList *m); + +#endif /* MX_PART_LIST_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_part_name.c b/gen/test/c/mx/mx_part_name.c new file mode 100644 index 000000000..4a9c83793 --- /dev/null +++ b/gen/test/c/mx/mx_part_name.c @@ -0,0 +1,146 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_part_name.h" + +#include "mx_runtime.h" +#include +#include + +MxPartName *mx_part_name_parse(xmlNodePtr el) { + MxPartName *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "print-object") == 0) { + m->has_print_object = true; + m->print_object = mx_yes_no_parse(s); + } else if (strcmp(aname, "justify") == 0) { + m->has_justify = true; + m->justify = mx_left_center_right_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_part_name_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_part_name_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_part_name_serialize(const MxPartName *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_print_object) { + xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); + } + if (m->has_justify) { + xmlSetProp(el, BAD_CAST "justify", BAD_CAST mx_left_center_right_to_string(m->justify)); + } + return el; +} + +void mx_part_name_free(MxPartName *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_part_name.h b/gen/test/c/mx/mx_part_name.h new file mode 100644 index 000000000..0a4bfa49d --- /dev/null +++ b/gen/test/c/mx/mx_part_name.h @@ -0,0 +1,55 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PART_NAME_H_INCLUDED +#define MX_PART_NAME_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_center_right.h" +#include "mx_tenths.h" +#include "mx_yes_no.h" + +/* + * The part-name type describes the name or abbreviation of a score-part element. Formatting + * attributes for the part-name element are deprecated in Version 2.0 in favor of the new + * part-name-display and part-abbreviation-display elements. + */ +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_print_object; + MxYesNo print_object; /* attribute print-object */ + bool has_justify; + MxLeftCenterRight justify; /* attribute justify */ + char *value; /* text content */ +} MxPartName; + +/* NULL on error; the message is in mx_error(). */ +MxPartName *mx_part_name_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_part_name_serialize(const MxPartName *m, xmlNodePtr parent, const char *tag); +void mx_part_name_free(MxPartName *m); + +#endif /* MX_PART_NAME_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_part_symbol.c b/gen/test/c/mx/mx_part_symbol.c new file mode 100644 index 000000000..6d6f9eb94 --- /dev/null +++ b/gen/test/c/mx/mx_part_symbol.c @@ -0,0 +1,119 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_part_symbol.h" + +#include "mx_runtime.h" +#include +#include + +MxPartSymbol *mx_part_symbol_parse(xmlNodePtr el) { + MxPartSymbol *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "top-staff") == 0) { + m->has_top_staff = true; + m->top_staff = mx_staff_number_parse(s); + } else if (strcmp(aname, "bottom-staff") == 0) { + m->has_bottom_staff = true; + m->bottom_staff = mx_staff_number_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_part_symbol_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_group_symbol_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_part_symbol_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_part_symbol_serialize(const MxPartSymbol *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_group_symbol_value_to_string(m->value))); + if (m->has_top_staff) { + char *s = mx_staff_number_to_string(m->top_staff); + xmlSetProp(el, BAD_CAST "top-staff", BAD_CAST s); + free(s); + } + if (m->has_bottom_staff) { + char *s = mx_staff_number_to_string(m->bottom_staff); + xmlSetProp(el, BAD_CAST "bottom-staff", BAD_CAST s); + free(s); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_part_symbol_free(MxPartSymbol *m) { + if (!m) + return; + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_part_symbol.h b/gen/test/c/mx/mx_part_symbol.h new file mode 100644 index 000000000..5b62a3f7d --- /dev/null +++ b/gen/test/c/mx/mx_part_symbol.h @@ -0,0 +1,45 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PART_SYMBOL_H_INCLUDED +#define MX_PART_SYMBOL_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_group_symbol_value.h" +#include "mx_staff_number.h" +#include "mx_tenths.h" + +/* + * The part-symbol type indicates how a symbol for a multi-staff part is indicated in the score; + * brace is the default value. The top-staff and bottom-staff elements are used when the brace does + * not extend across the entire part. For example, in a 3-staff organ part, the top-staff will + * typically be 1 for the right hand, while the bottom-staff will typically be 2 for the left hand. + * Staff 3 for the pedals is usually outside the brace. + */ +typedef struct { + bool has_top_staff; + MxStaffNumber top_staff; /* attribute top-staff */ + bool has_bottom_staff; + MxStaffNumber bottom_staff; /* attribute bottom-staff */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_color; + MxColor color; /* attribute color */ + MxGroupSymbolValue value; /* text content */ +} MxPartSymbol; + +/* NULL on error; the message is in mx_error(). */ +MxPartSymbol *mx_part_symbol_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_part_symbol_serialize(const MxPartSymbol *m, xmlNodePtr parent, const char *tag); +void mx_part_symbol_free(MxPartSymbol *m); + +#endif /* MX_PART_SYMBOL_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_partwise_measure.c b/gen/test/c/mx/mx_partwise_measure.c new file mode 100644 index 000000000..38b452a86 --- /dev/null +++ b/gen/test/c/mx/mx_partwise_measure.c @@ -0,0 +1,249 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_partwise_measure.h" + +#include "mx_runtime.h" +#include +#include + +MxPartwiseMeasure *mx_partwise_measure_parse(xmlNodePtr el) { + MxPartwiseMeasure *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_strdup(s); + } else if (strcmp(aname, "text") == 0) { + m->has_text = true; + m->text = mx_measure_text_parse(s); + } else if (strcmp(aname, "implicit") == 0) { + m->has_implicit = true; + m->implicit = mx_yes_no_parse(s); + } else if (strcmp(aname, "non-controlling") == 0) { + m->has_non_controlling = true; + m->non_controlling = mx_yes_no_parse(s); + } else if (strcmp(aname, "width") == 0) { + m->has_width = true; + m->width = mx_tenths_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_partwise_measure_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxPartwiseMeasureChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "note") == 0) { + ch->note = mx_note_parse(c); + if (!ch->note) { + mx_partwise_measure_free(m); + return NULL; + } + } else if (strcmp(tag, "backup") == 0) { + ch->backup = mx_backup_parse(c); + if (!ch->backup) { + mx_partwise_measure_free(m); + return NULL; + } + } else if (strcmp(tag, "forward") == 0) { + ch->forward = mx_forward_parse(c); + if (!ch->forward) { + mx_partwise_measure_free(m); + return NULL; + } + } else if (strcmp(tag, "direction") == 0) { + ch->direction = mx_direction_parse(c); + if (!ch->direction) { + mx_partwise_measure_free(m); + return NULL; + } + } else if (strcmp(tag, "attributes") == 0) { + ch->attributes = mx_attributes_parse(c); + if (!ch->attributes) { + mx_partwise_measure_free(m); + return NULL; + } + } else if (strcmp(tag, "harmony") == 0) { + ch->harmony = mx_harmony_parse(c); + if (!ch->harmony) { + mx_partwise_measure_free(m); + return NULL; + } + } else if (strcmp(tag, "figured-bass") == 0) { + ch->figured_bass = mx_figured_bass_parse(c); + if (!ch->figured_bass) { + mx_partwise_measure_free(m); + return NULL; + } + } else if (strcmp(tag, "print") == 0) { + ch->print = mx_print_parse(c); + if (!ch->print) { + mx_partwise_measure_free(m); + return NULL; + } + } else if (strcmp(tag, "sound") == 0) { + ch->sound = mx_sound_parse(c); + if (!ch->sound) { + mx_partwise_measure_free(m); + return NULL; + } + } else if (strcmp(tag, "barline") == 0) { + ch->barline = mx_barline_parse(c); + if (!ch->barline) { + mx_partwise_measure_free(m); + return NULL; + } + } else if (strcmp(tag, "grouping") == 0) { + ch->grouping = mx_grouping_parse(c); + if (!ch->grouping) { + mx_partwise_measure_free(m); + return NULL; + } + } else if (strcmp(tag, "link") == 0) { + ch->link = mx_link_parse(c); + if (!ch->link) { + mx_partwise_measure_free(m); + return NULL; + } + } else if (strcmp(tag, "bookmark") == 0) { + ch->bookmark = mx_bookmark_parse(c); + if (!ch->bookmark) { + mx_partwise_measure_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_partwise_measure_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_partwise_measure_serialize(const MxPartwiseMeasure *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_number) { + xmlSetProp(el, BAD_CAST "number", BAD_CAST m->number); + } + if (m->has_text) { + xmlSetProp(el, BAD_CAST "text", BAD_CAST m->text); + } + if (m->has_implicit) { + xmlSetProp(el, BAD_CAST "implicit", BAD_CAST mx_yes_no_to_string(m->implicit)); + } + if (m->has_non_controlling) { + xmlSetProp(el, BAD_CAST "non-controlling", BAD_CAST mx_yes_no_to_string(m->non_controlling)); + } + if (m->has_width) { + char *s = mx_tenths_to_string(m->width); + xmlSetProp(el, BAD_CAST "width", BAD_CAST s); + free(s); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxPartwiseMeasureChild *ch = &m->children[i]; + if (ch->note) { + mx_note_serialize(ch->note, el, "note"); + } else if (ch->backup) { + mx_backup_serialize(ch->backup, el, "backup"); + } else if (ch->forward) { + mx_forward_serialize(ch->forward, el, "forward"); + } else if (ch->direction) { + mx_direction_serialize(ch->direction, el, "direction"); + } else if (ch->attributes) { + mx_attributes_serialize(ch->attributes, el, "attributes"); + } else if (ch->harmony) { + mx_harmony_serialize(ch->harmony, el, "harmony"); + } else if (ch->figured_bass) { + mx_figured_bass_serialize(ch->figured_bass, el, "figured-bass"); + } else if (ch->print) { + mx_print_serialize(ch->print, el, "print"); + } else if (ch->sound) { + mx_sound_serialize(ch->sound, el, "sound"); + } else if (ch->barline) { + mx_barline_serialize(ch->barline, el, "barline"); + } else if (ch->grouping) { + mx_grouping_serialize(ch->grouping, el, "grouping"); + } else if (ch->link) { + mx_link_serialize(ch->link, el, "link"); + } else if (ch->bookmark) { + mx_bookmark_serialize(ch->bookmark, el, "bookmark"); + } + } + return el; +} + +void mx_partwise_measure_free(MxPartwiseMeasure *m) { + if (!m) + return; + if (m->has_number) + free(m->number); + if (m->has_text) + free(m->text); + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxPartwiseMeasureChild *ch = &m->children[i]; + if (ch->note) + mx_note_free(ch->note); + if (ch->backup) + mx_backup_free(ch->backup); + if (ch->forward) + mx_forward_free(ch->forward); + if (ch->direction) + mx_direction_free(ch->direction); + if (ch->attributes) + mx_attributes_free(ch->attributes); + if (ch->harmony) + mx_harmony_free(ch->harmony); + if (ch->figured_bass) + mx_figured_bass_free(ch->figured_bass); + if (ch->print) + mx_print_free(ch->print); + if (ch->sound) + mx_sound_free(ch->sound); + if (ch->barline) + mx_barline_free(ch->barline); + if (ch->grouping) + mx_grouping_free(ch->grouping); + if (ch->link) + mx_link_free(ch->link); + if (ch->bookmark) + mx_bookmark_free(ch->bookmark); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_partwise_measure.h b/gen/test/c/mx/mx_partwise_measure.h new file mode 100644 index 000000000..3ccc3b77a --- /dev/null +++ b/gen/test/c/mx/mx_partwise_measure.h @@ -0,0 +1,68 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PARTWISE_MEASURE_H_INCLUDED +#define MX_PARTWISE_MEASURE_H_INCLUDED + +#include +#include +#include +#include "mx_attributes.h" +#include "mx_backup.h" +#include "mx_barline.h" +#include "mx_bookmark.h" +#include "mx_direction.h" +#include "mx_figured_bass.h" +#include "mx_forward.h" +#include "mx_grouping.h" +#include "mx_harmony.h" +#include "mx_link.h" +#include "mx_measure_text.h" +#include "mx_note.h" +#include "mx_print.h" +#include "mx_sound.h" +#include "mx_tenths.h" +#include "mx_yes_no.h" + +/* One child element of MxPartwiseMeasure: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxNote *note; + MxBackup *backup; + MxForward *forward; + MxDirection *direction; + MxAttributes *attributes; + MxHarmony *harmony; + MxFiguredBass *figured_bass; + MxPrint *print; + MxSound *sound; + MxBarline *barline; + MxGrouping *grouping; + MxLink *link; + MxBookmark *bookmark; +} MxPartwiseMeasureChild; + +typedef struct { + bool has_number; + char *number; /* attribute number */ + bool has_text; + MxMeasureText text; /* attribute text */ + bool has_implicit; + MxYesNo implicit; /* attribute implicit */ + bool has_non_controlling; + MxYesNo non_controlling; /* attribute non-controlling */ + bool has_width; + MxTenths width; /* attribute width */ + bool has_id; + char *id; /* attribute id */ + MxPartwiseMeasureChild *children; /* child elements in document order */ + size_t children_count; +} MxPartwiseMeasure; + +/* NULL on error; the message is in mx_error(). */ +MxPartwiseMeasure *mx_partwise_measure_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_partwise_measure_serialize(const MxPartwiseMeasure *m, xmlNodePtr parent, const char *tag); +void mx_partwise_measure_free(MxPartwiseMeasure *m); + +#endif /* MX_PARTWISE_MEASURE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_partwise_part.c b/gen/test/c/mx/mx_partwise_part.c new file mode 100644 index 000000000..6be873e91 --- /dev/null +++ b/gen/test/c/mx/mx_partwise_part.c @@ -0,0 +1,93 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_partwise_part.h" + +#include "mx_runtime.h" +#include +#include + +MxPartwisePart *mx_partwise_part_parse(xmlNodePtr el) { + MxPartwisePart *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_partwise_part_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxPartwisePartChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "measure") == 0) { + ch->measure = mx_partwise_measure_parse(c); + if (!ch->measure) { + mx_partwise_part_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_partwise_part_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_partwise_part_serialize(const MxPartwisePart *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxPartwisePartChild *ch = &m->children[i]; + if (ch->measure) { + mx_partwise_measure_serialize(ch->measure, el, "measure"); + } + } + return el; +} + +void mx_partwise_part_free(MxPartwisePart *m) { + if (!m) + return; + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxPartwisePartChild *ch = &m->children[i]; + if (ch->measure) + mx_partwise_measure_free(ch->measure); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_partwise_part.h b/gen/test/c/mx/mx_partwise_part.h new file mode 100644 index 000000000..ac6b3e985 --- /dev/null +++ b/gen/test/c/mx/mx_partwise_part.h @@ -0,0 +1,31 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PARTWISE_PART_H_INCLUDED +#define MX_PARTWISE_PART_H_INCLUDED + +#include +#include +#include +#include "mx_partwise_measure.h" + +/* One child element of MxPartwisePart: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxPartwiseMeasure *measure; +} MxPartwisePartChild; + +typedef struct { + bool has_id; + char *id; /* attribute id */ + MxPartwisePartChild *children; /* child elements in document order */ + size_t children_count; +} MxPartwisePart; + +/* NULL on error; the message is in mx_error(). */ +MxPartwisePart *mx_partwise_part_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_partwise_part_serialize(const MxPartwisePart *m, xmlNodePtr parent, const char *tag); +void mx_partwise_part_free(MxPartwisePart *m); + +#endif /* MX_PARTWISE_PART_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_pedal.c b/gen/test/c/mx/mx_pedal.c new file mode 100644 index 000000000..da1e59cbf --- /dev/null +++ b/gen/test/c/mx/mx_pedal.c @@ -0,0 +1,178 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_pedal.h" + +#include "mx_runtime.h" +#include +#include + +MxPedal *mx_pedal_parse(xmlNodePtr el) { + MxPedal *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_pedal_type_parse(s); + } else if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_number_level_parse(s); + } else if (strcmp(aname, "line") == 0) { + m->has_line = true; + m->line = mx_yes_no_parse(s); + } else if (strcmp(aname, "sign") == 0) { + m->has_sign = true; + m->sign = mx_yes_no_parse(s); + } else if (strcmp(aname, "abbreviated") == 0) { + m->has_abbreviated = true; + m->abbreviated = mx_yes_no_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_pedal_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_pedal_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_pedal_serialize(const MxPedal *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_pedal_type_to_string(m->type)); + } + if (m->has_number) { + char *s = mx_number_level_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_line) { + xmlSetProp(el, BAD_CAST "line", BAD_CAST mx_yes_no_to_string(m->line)); + } + if (m->has_sign) { + xmlSetProp(el, BAD_CAST "sign", BAD_CAST mx_yes_no_to_string(m->sign)); + } + if (m->has_abbreviated) { + xmlSetProp(el, BAD_CAST "abbreviated", BAD_CAST mx_yes_no_to_string(m->abbreviated)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_pedal_free(MxPedal *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + free(m); +} diff --git a/gen/test/c/mx/mx_pedal.h b/gen/test/c/mx/mx_pedal.h new file mode 100644 index 000000000..029485cb8 --- /dev/null +++ b/gen/test/c/mx/mx_pedal.h @@ -0,0 +1,77 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PEDAL_H_INCLUDED +#define MX_PEDAL_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_center_right.h" +#include "mx_number_level.h" +#include "mx_pedal_type.h" +#include "mx_tenths.h" +#include "mx_valign.h" +#include "mx_yes_no.h" + +/* + * The pedal type represents piano pedal marks. In MusicXML 3.1 this includes sostenuto as well as + * damper pedal marks. The line attribute is yes if pedal lines are used. The sign attribute is yes + * if Ped, Sost, and * signs are used. For MusicXML 2.0 compatibility, the sign attribute is yes by + * default if the line attribute is no, and is no by default if the line attribute is yes. If the + * sign attribute is set to yes and the type is start or sostenuto, the abbreviated attribute is yes + * if the short P and S signs are used, and no if the full Ped and Sost signs are used. It is no by + * default. Otherwise the abbreviated attribute is ignored. The change and continue types are used + * when the line attribute is yes. The change type indicates a pedal lift and retake indicated with + * an inverted V marking. The continue type allows more precise formatting across system breaks and + * for more complex pedaling lines. The alignment attributes are ignored if the line attribute is + * yes. + */ +typedef struct { + bool has_type; + MxPedalType type; /* attribute type */ + bool has_number; + MxNumberLevel number; /* attribute number */ + bool has_line; + MxYesNo line; /* attribute line */ + bool has_sign; + MxYesNo sign; /* attribute sign */ + bool has_abbreviated; + MxYesNo abbreviated; /* attribute abbreviated */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ + bool has_id; + char *id; /* attribute id */ +} MxPedal; + +/* NULL on error; the message is in mx_error(). */ +MxPedal *mx_pedal_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_pedal_serialize(const MxPedal *m, xmlNodePtr parent, const char *tag); +void mx_pedal_free(MxPedal *m); + +#endif /* MX_PEDAL_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_pedal_tuning.c b/gen/test/c/mx/mx_pedal_tuning.c new file mode 100644 index 000000000..a6a7f78bd --- /dev/null +++ b/gen/test/c/mx/mx_pedal_tuning.c @@ -0,0 +1,99 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_pedal_tuning.h" + +#include "mx_runtime.h" +#include +#include + +MxPedalTuning *mx_pedal_tuning_parse(xmlNodePtr el) { + MxPedalTuning *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_pedal_tuning_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxPedalTuningChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "pedal-step") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->pedal_step = malloc(sizeof(*ch->pedal_step)); + if (!ch->pedal_step) + abort(); + *ch->pedal_step = mx_step_parse(s); + xmlFree(text); + } else if (strcmp(tag, "pedal-alter") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->pedal_alter = malloc(sizeof(*ch->pedal_alter)); + if (!ch->pedal_alter) + abort(); + *ch->pedal_alter = mx_semitones_parse(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_pedal_tuning_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_pedal_tuning_serialize(const MxPedalTuning *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxPedalTuningChild *ch = &m->children[i]; + if (ch->pedal_step) { + xmlNewTextChild(el, NULL, BAD_CAST "pedal-step", BAD_CAST mx_step_to_string((*ch->pedal_step))); + } else if (ch->pedal_alter) { + char *s = mx_semitones_to_string((*ch->pedal_alter)); + xmlNewTextChild(el, NULL, BAD_CAST "pedal-alter", BAD_CAST s); + free(s); + } + } + return el; +} + +void mx_pedal_tuning_free(MxPedalTuning *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxPedalTuningChild *ch = &m->children[i]; + free(ch->pedal_step); + free(ch->pedal_alter); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_pedal_tuning.h b/gen/test/c/mx/mx_pedal_tuning.h new file mode 100644 index 000000000..ece3fa729 --- /dev/null +++ b/gen/test/c/mx/mx_pedal_tuning.h @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PEDAL_TUNING_H_INCLUDED +#define MX_PEDAL_TUNING_H_INCLUDED + +#include +#include +#include +#include "mx_semitones.h" +#include "mx_step.h" + +/* + * The pedal-tuning type specifies the tuning of a single harp pedal. + */ +/* One child element of MxPedalTuning: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxStep *pedal_step; + MxSemitones *pedal_alter; +} MxPedalTuningChild; + +typedef struct { + MxPedalTuningChild *children; /* child elements in document order */ + size_t children_count; +} MxPedalTuning; + +/* NULL on error; the message is in mx_error(). */ +MxPedalTuning *mx_pedal_tuning_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_pedal_tuning_serialize(const MxPedalTuning *m, xmlNodePtr parent, const char *tag); +void mx_pedal_tuning_free(MxPedalTuning *m); + +#endif /* MX_PEDAL_TUNING_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_per_minute.c b/gen/test/c/mx/mx_per_minute.c new file mode 100644 index 000000000..ba4dab0b1 --- /dev/null +++ b/gen/test/c/mx/mx_per_minute.c @@ -0,0 +1,94 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_per_minute.h" + +#include "mx_runtime.h" +#include +#include + +MxPerMinute *mx_per_minute_parse(xmlNodePtr el) { + MxPerMinute *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_per_minute_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_per_minute_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_per_minute_serialize(const MxPerMinute *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + return el; +} + +void mx_per_minute_free(MxPerMinute *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_per_minute.h b/gen/test/c/mx/mx_per_minute.h new file mode 100644 index 000000000..6184853b0 --- /dev/null +++ b/gen/test/c/mx/mx_per_minute.h @@ -0,0 +1,38 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PER_MINUTE_H_INCLUDED +#define MX_PER_MINUTE_H_INCLUDED + +#include +#include +#include +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" + +/* + * The per-minute type can be a number, or a text description including numbers. If a font is + * specified, it overrides the font specified for the overall metronome element. This allows + * separate specification of a music font for the beat-unit and a text font for the numeric value, + * in cases where a single metronome font is not used. + */ +typedef struct { + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + char *value; /* text content */ +} MxPerMinute; + +/* NULL on error; the message is in mx_error(). */ +MxPerMinute *mx_per_minute_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_per_minute_serialize(const MxPerMinute *m, xmlNodePtr parent, const char *tag); +void mx_per_minute_free(MxPerMinute *m); + +#endif /* MX_PER_MINUTE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_percussion.c b/gen/test/c/mx/mx_percussion.c new file mode 100644 index 000000000..861230af4 --- /dev/null +++ b/gen/test/c/mx/mx_percussion.c @@ -0,0 +1,286 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_percussion.h" + +#include "mx_runtime.h" +#include +#include + +MxPercussion *mx_percussion_parse(xmlNodePtr el) { + MxPercussion *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else if (strcmp(aname, "enclosure") == 0) { + m->has_enclosure = true; + m->enclosure = mx_enclosure_shape_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_percussion_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxPercussionChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "glass") == 0) { + ch->glass = mx_glass_parse(c); + if (!ch->glass) { + mx_percussion_free(m); + return NULL; + } + } else if (strcmp(tag, "metal") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->metal = malloc(sizeof(*ch->metal)); + if (!ch->metal) + abort(); + *ch->metal = mx_metal_parse(s); + xmlFree(text); + } else if (strcmp(tag, "wood") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->wood = malloc(sizeof(*ch->wood)); + if (!ch->wood) + abort(); + *ch->wood = mx_wood_parse(s); + xmlFree(text); + } else if (strcmp(tag, "pitched") == 0) { + ch->pitched = mx_pitched_parse(c); + if (!ch->pitched) { + mx_percussion_free(m); + return NULL; + } + } else if (strcmp(tag, "membrane") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->membrane = malloc(sizeof(*ch->membrane)); + if (!ch->membrane) + abort(); + *ch->membrane = mx_membrane_parse(s); + xmlFree(text); + } else if (strcmp(tag, "effect") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->effect = malloc(sizeof(*ch->effect)); + if (!ch->effect) + abort(); + *ch->effect = mx_effect_parse(s); + xmlFree(text); + } else if (strcmp(tag, "timpani") == 0) { + ch->timpani = mx_empty_parse(c); + if (!ch->timpani) { + mx_percussion_free(m); + return NULL; + } + } else if (strcmp(tag, "beater") == 0) { + ch->beater = mx_beater_parse(c); + if (!ch->beater) { + mx_percussion_free(m); + return NULL; + } + } else if (strcmp(tag, "stick") == 0) { + ch->stick = mx_stick_parse(c); + if (!ch->stick) { + mx_percussion_free(m); + return NULL; + } + } else if (strcmp(tag, "stick-location") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->stick_location = malloc(sizeof(*ch->stick_location)); + if (!ch->stick_location) + abort(); + *ch->stick_location = mx_stick_location_parse(s); + xmlFree(text); + } else if (strcmp(tag, "other-percussion") == 0) { + ch->other_percussion = mx_other_text_parse(c); + if (!ch->other_percussion) { + mx_percussion_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_percussion_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_percussion_serialize(const MxPercussion *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + if (m->has_enclosure) { + xmlSetProp(el, BAD_CAST "enclosure", BAD_CAST mx_enclosure_shape_to_string(m->enclosure)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxPercussionChild *ch = &m->children[i]; + if (ch->glass) { + mx_glass_serialize(ch->glass, el, "glass"); + } else if (ch->metal) { + xmlNewTextChild(el, NULL, BAD_CAST "metal", BAD_CAST mx_metal_to_string((*ch->metal))); + } else if (ch->wood) { + xmlNewTextChild(el, NULL, BAD_CAST "wood", BAD_CAST mx_wood_to_string((*ch->wood))); + } else if (ch->pitched) { + mx_pitched_serialize(ch->pitched, el, "pitched"); + } else if (ch->membrane) { + xmlNewTextChild(el, NULL, BAD_CAST "membrane", BAD_CAST mx_membrane_to_string((*ch->membrane))); + } else if (ch->effect) { + xmlNewTextChild(el, NULL, BAD_CAST "effect", BAD_CAST mx_effect_to_string((*ch->effect))); + } else if (ch->timpani) { + mx_empty_serialize(ch->timpani, el, "timpani"); + } else if (ch->beater) { + mx_beater_serialize(ch->beater, el, "beater"); + } else if (ch->stick) { + mx_stick_serialize(ch->stick, el, "stick"); + } else if (ch->stick_location) { + xmlNewTextChild(el, NULL, BAD_CAST "stick-location", BAD_CAST mx_stick_location_to_string((*ch->stick_location))); + } else if (ch->other_percussion) { + mx_other_text_serialize(ch->other_percussion, el, "other-percussion"); + } + } + return el; +} + +void mx_percussion_free(MxPercussion *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxPercussionChild *ch = &m->children[i]; + if (ch->glass) + mx_glass_free(ch->glass); + free(ch->metal); + free(ch->wood); + if (ch->pitched) + mx_pitched_free(ch->pitched); + free(ch->membrane); + free(ch->effect); + if (ch->timpani) + mx_empty_free(ch->timpani); + if (ch->beater) + mx_beater_free(ch->beater); + if (ch->stick) + mx_stick_free(ch->stick); + free(ch->stick_location); + if (ch->other_percussion) + mx_other_text_free(ch->other_percussion); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_percussion.h b/gen/test/c/mx/mx_percussion.h new file mode 100644 index 000000000..905cb3960 --- /dev/null +++ b/gen/test/c/mx/mx_percussion.h @@ -0,0 +1,90 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PERCUSSION_H_INCLUDED +#define MX_PERCUSSION_H_INCLUDED + +#include +#include +#include +#include "mx_beater.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_effect.h" +#include "mx_empty.h" +#include "mx_enclosure_shape.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_glass.h" +#include "mx_left_center_right.h" +#include "mx_membrane.h" +#include "mx_metal.h" +#include "mx_other_text.h" +#include "mx_pitched.h" +#include "mx_stick.h" +#include "mx_stick_location.h" +#include "mx_tenths.h" +#include "mx_valign.h" +#include "mx_wood.h" + +/* + * The percussion element is used to define percussion pictogram symbols. Definitions for these + * symbols can be found in Kurt Stone's "Music Notation in the Twentieth Century" on pages 206-212 + * and 223. Some values are added to these based on how usage has evolved in the 30 years since + * Stone's book was published. + */ +/* One child element of MxPercussion: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxGlass *glass; + MxMetal *metal; + MxWood *wood; + MxPitched *pitched; + MxMembrane *membrane; + MxEffect *effect; + MxEmpty *timpani; + MxBeater *beater; + MxStick *stick; + MxStickLocation *stick_location; + MxOtherText *other_percussion; +} MxPercussionChild; + +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ + bool has_enclosure; + MxEnclosureShape enclosure; /* attribute enclosure */ + bool has_id; + char *id; /* attribute id */ + MxPercussionChild *children; /* child elements in document order */ + size_t children_count; +} MxPercussion; + +/* NULL on error; the message is in mx_error(). */ +MxPercussion *mx_percussion_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_percussion_serialize(const MxPercussion *m, xmlNodePtr parent, const char *tag); +void mx_percussion_free(MxPercussion *m); + +#endif /* MX_PERCUSSION_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_pitch.c b/gen/test/c/mx/mx_pitch.c new file mode 100644 index 000000000..4a5dc88f0 --- /dev/null +++ b/gen/test/c/mx/mx_pitch.c @@ -0,0 +1,112 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_pitch.h" + +#include "mx_runtime.h" +#include +#include + +MxPitch *mx_pitch_parse(xmlNodePtr el) { + MxPitch *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_pitch_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxPitchChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "step") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->step = malloc(sizeof(*ch->step)); + if (!ch->step) + abort(); + *ch->step = mx_step_parse(s); + xmlFree(text); + } else if (strcmp(tag, "alter") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->alter = malloc(sizeof(*ch->alter)); + if (!ch->alter) + abort(); + *ch->alter = mx_semitones_parse(s); + xmlFree(text); + } else if (strcmp(tag, "octave") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->octave = malloc(sizeof(*ch->octave)); + if (!ch->octave) + abort(); + *ch->octave = mx_octave_parse(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_pitch_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_pitch_serialize(const MxPitch *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxPitchChild *ch = &m->children[i]; + if (ch->step) { + xmlNewTextChild(el, NULL, BAD_CAST "step", BAD_CAST mx_step_to_string((*ch->step))); + } else if (ch->alter) { + char *s = mx_semitones_to_string((*ch->alter)); + xmlNewTextChild(el, NULL, BAD_CAST "alter", BAD_CAST s); + free(s); + } else if (ch->octave) { + char *s = mx_octave_to_string((*ch->octave)); + xmlNewTextChild(el, NULL, BAD_CAST "octave", BAD_CAST s); + free(s); + } + } + return el; +} + +void mx_pitch_free(MxPitch *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxPitchChild *ch = &m->children[i]; + free(ch->step); + free(ch->alter); + free(ch->octave); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_pitch.h b/gen/test/c/mx/mx_pitch.h new file mode 100644 index 000000000..be5469a74 --- /dev/null +++ b/gen/test/c/mx/mx_pitch.h @@ -0,0 +1,37 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PITCH_H_INCLUDED +#define MX_PITCH_H_INCLUDED + +#include +#include +#include +#include "mx_octave.h" +#include "mx_semitones.h" +#include "mx_step.h" + +/* + * Pitch is represented as a combination of the step of the diatonic scale, the chromatic + * alteration, and the octave. + */ +/* One child element of MxPitch: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxStep *step; + MxSemitones *alter; + MxOctave *octave; +} MxPitchChild; + +typedef struct { + MxPitchChild *children; /* child elements in document order */ + size_t children_count; +} MxPitch; + +/* NULL on error; the message is in mx_error(). */ +MxPitch *mx_pitch_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_pitch_serialize(const MxPitch *m, xmlNodePtr parent, const char *tag); +void mx_pitch_free(MxPitch *m); + +#endif /* MX_PITCH_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_pitched.c b/gen/test/c/mx/mx_pitched.c new file mode 100644 index 000000000..6e1ad9140 --- /dev/null +++ b/gen/test/c/mx/mx_pitched.c @@ -0,0 +1,71 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_pitched.h" + +#include "mx_runtime.h" +#include +#include + +MxPitched *mx_pitched_parse(xmlNodePtr el) { + MxPitched *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "smufl") == 0) { + m->has_smufl = true; + m->smufl = mx_smufl_pictogram_glyph_name_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_pitched_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_pitched_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_pitched_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_pitched_serialize(const MxPitched *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_pitched_value_to_string(m->value))); + if (m->has_smufl) { + xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); + } + return el; +} + +void mx_pitched_free(MxPitched *m) { + if (!m) + return; + if (m->has_smufl) + free(m->smufl); + free(m); +} diff --git a/gen/test/c/mx/mx_pitched.h b/gen/test/c/mx/mx_pitched.h new file mode 100644 index 000000000..04e8e5f55 --- /dev/null +++ b/gen/test/c/mx/mx_pitched.h @@ -0,0 +1,29 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PITCHED_H_INCLUDED +#define MX_PITCHED_H_INCLUDED + +#include +#include +#include +#include "mx_pitched_value.h" +#include "mx_smufl_pictogram_glyph_name.h" + +/* + * The pitched-value type represents pictograms for pitched percussion instruments. The smufl + * attribute is used to distinguish different SMuFL glyphs for a particular pictogram within the + * tuned mallet percussion pictograms range. + */ +typedef struct { + bool has_smufl; + MxSMUFLPictogramGlyphName smufl; /* attribute smufl */ + MxPitchedValue value; /* text content */ +} MxPitched; + +/* NULL on error; the message is in mx_error(). */ +MxPitched *mx_pitched_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_pitched_serialize(const MxPitched *m, xmlNodePtr parent, const char *tag); +void mx_pitched_free(MxPitched *m); + +#endif /* MX_PITCHED_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_placement_text.c b/gen/test/c/mx/mx_placement_text.c new file mode 100644 index 000000000..18dbeff87 --- /dev/null +++ b/gen/test/c/mx/mx_placement_text.c @@ -0,0 +1,140 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_placement_text.h" + +#include "mx_runtime.h" +#include +#include + +MxPlacementText *mx_placement_text_parse(xmlNodePtr el) { + MxPlacementText *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_placement_text_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_placement_text_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_placement_text_serialize(const MxPlacementText *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + return el; +} + +void mx_placement_text_free(MxPlacementText *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_placement_text.h b/gen/test/c/mx/mx_placement_text.h new file mode 100644 index 000000000..9061925ca --- /dev/null +++ b/gen/test/c/mx/mx_placement_text.h @@ -0,0 +1,51 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PLACEMENT_TEXT_H_INCLUDED +#define MX_PLACEMENT_TEXT_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_tenths.h" + +/* + * The placement-text type represents a text element with print-style and placement attribute + * groups. + */ +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + char *value; /* text content */ +} MxPlacementText; + +/* NULL on error; the message is in mx_error(). */ +MxPlacementText *mx_placement_text_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_placement_text_serialize(const MxPlacementText *m, xmlNodePtr parent, const char *tag); +void mx_placement_text_free(MxPlacementText *m); + +#endif /* MX_PLACEMENT_TEXT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_play.c b/gen/test/c/mx/mx_play.c new file mode 100644 index 000000000..ffd62ea72 --- /dev/null +++ b/gen/test/c/mx/mx_play.c @@ -0,0 +1,123 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_play.h" + +#include "mx_runtime.h" +#include +#include + +MxPlay *mx_play_parse(xmlNodePtr el) { + MxPlay *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_play_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxPlayChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "ipa") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->ipa = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "mute") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->mute = malloc(sizeof(*ch->mute)); + if (!ch->mute) + abort(); + *ch->mute = mx_mute_parse(s); + xmlFree(text); + } else if (strcmp(tag, "semi-pitched") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->semi_pitched = malloc(sizeof(*ch->semi_pitched)); + if (!ch->semi_pitched) + abort(); + *ch->semi_pitched = mx_semi_pitched_parse(s); + xmlFree(text); + } else if (strcmp(tag, "other-play") == 0) { + ch->other_play = mx_other_play_parse(c); + if (!ch->other_play) { + mx_play_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_play_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_play_serialize(const MxPlay *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxPlayChild *ch = &m->children[i]; + if (ch->ipa) { + xmlNewTextChild(el, NULL, BAD_CAST "ipa", BAD_CAST ch->ipa); + } else if (ch->mute) { + xmlNewTextChild(el, NULL, BAD_CAST "mute", BAD_CAST mx_mute_to_string((*ch->mute))); + } else if (ch->semi_pitched) { + xmlNewTextChild(el, NULL, BAD_CAST "semi-pitched", BAD_CAST mx_semi_pitched_to_string((*ch->semi_pitched))); + } else if (ch->other_play) { + mx_other_play_serialize(ch->other_play, el, "other-play"); + } + } + return el; +} + +void mx_play_free(MxPlay *m) { + if (!m) + return; + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxPlayChild *ch = &m->children[i]; + free(ch->ipa); + free(ch->mute); + free(ch->semi_pitched); + if (ch->other_play) + mx_other_play_free(ch->other_play); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_play.h b/gen/test/c/mx/mx_play.h new file mode 100644 index 000000000..82ecf2662 --- /dev/null +++ b/gen/test/c/mx/mx_play.h @@ -0,0 +1,42 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PLAY_H_INCLUDED +#define MX_PLAY_H_INCLUDED + +#include +#include +#include +#include "mx_mute.h" +#include "mx_other_play.h" +#include "mx_semi_pitched.h" + +/* + * The play type, new in Version 3.0, specifies playback techniques to be used in conjunction with + * the instrument-sound element. When used as part of a sound element, it applies to all notes going + * forward in score order. In multi-instrument parts, the affected instrument should be specified + * using the id attribute. When used as part of a note element, it applies to the current note only. + */ +/* One child element of MxPlay: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + char *ipa; + MxMute *mute; + MxSemiPitched *semi_pitched; + MxOtherPlay *other_play; +} MxPlayChild; + +typedef struct { + bool has_id; + char *id; /* attribute id */ + MxPlayChild *children; /* child elements in document order */ + size_t children_count; +} MxPlay; + +/* NULL on error; the message is in mx_error(). */ +MxPlay *mx_play_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_play_serialize(const MxPlay *m, xmlNodePtr parent, const char *tag); +void mx_play_free(MxPlay *m); + +#endif /* MX_PLAY_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_principal_voice.c b/gen/test/c/mx/mx_principal_voice.c new file mode 100644 index 000000000..77fb3e992 --- /dev/null +++ b/gen/test/c/mx/mx_principal_voice.c @@ -0,0 +1,166 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_principal_voice.h" + +#include "mx_runtime.h" +#include +#include + +MxPrincipalVoice *mx_principal_voice_parse(xmlNodePtr el) { + MxPrincipalVoice *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_parse(s); + } else if (strcmp(aname, "symbol") == 0) { + m->has_symbol = true; + m->symbol = mx_principal_voice_symbol_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_principal_voice_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_principal_voice_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_principal_voice_serialize(const MxPrincipalVoice *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); + } + if (m->has_symbol) { + xmlSetProp(el, BAD_CAST "symbol", BAD_CAST mx_principal_voice_symbol_to_string(m->symbol)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_principal_voice_free(MxPrincipalVoice *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_principal_voice.h b/gen/test/c/mx/mx_principal_voice.h new file mode 100644 index 000000000..4885a9afc --- /dev/null +++ b/gen/test/c/mx/mx_principal_voice.h @@ -0,0 +1,65 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PRINCIPAL_VOICE_H_INCLUDED +#define MX_PRINCIPAL_VOICE_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_center_right.h" +#include "mx_principal_voice_symbol.h" +#include "mx_start_stop.h" +#include "mx_tenths.h" +#include "mx_valign.h" + +/* + * The principal-voice element represents principal and secondary voices in a score, either for + * analysis or for square bracket symbols that appear in a score. The symbol attribute indicates the + * type of symbol used at the start of the principal-voice. The content of the principal-voice + * element is used for analysis and may be any text value. When used for analysis separate from any + * printed score markings, the symbol attribute should be set to "none". + */ +typedef struct { + bool has_type; + MxStartStop type; /* attribute type */ + bool has_symbol; + MxPrincipalVoiceSymbol symbol; /* attribute symbol */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ + bool has_id; + char *id; /* attribute id */ + char *value; /* text content */ +} MxPrincipalVoice; + +/* NULL on error; the message is in mx_error(). */ +MxPrincipalVoice *mx_principal_voice_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_principal_voice_serialize(const MxPrincipalVoice *m, xmlNodePtr parent, const char *tag); +void mx_principal_voice_free(MxPrincipalVoice *m); + +#endif /* MX_PRINCIPAL_VOICE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_print.c b/gen/test/c/mx/mx_print.c new file mode 100644 index 000000000..2e969e100 --- /dev/null +++ b/gen/test/c/mx/mx_print.c @@ -0,0 +1,189 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_print.h" + +#include "mx_runtime.h" +#include +#include + +MxPrint *mx_print_parse(xmlNodePtr el) { + MxPrint *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "staff-spacing") == 0) { + m->has_staff_spacing = true; + m->staff_spacing = mx_tenths_parse(s); + } else if (strcmp(aname, "new-system") == 0) { + m->has_new_system = true; + m->new_system = mx_yes_no_parse(s); + } else if (strcmp(aname, "new-page") == 0) { + m->has_new_page = true; + m->new_page = mx_yes_no_parse(s); + } else if (strcmp(aname, "blank-page") == 0) { + m->has_blank_page = true; + m->blank_page = mx_parse_int(s); + } else if (strcmp(aname, "page-number") == 0) { + m->has_page_number = true; + m->page_number = mx_strdup(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_print_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxPrintChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "page-layout") == 0) { + ch->page_layout = mx_page_layout_parse(c); + if (!ch->page_layout) { + mx_print_free(m); + return NULL; + } + } else if (strcmp(tag, "system-layout") == 0) { + ch->system_layout = mx_system_layout_parse(c); + if (!ch->system_layout) { + mx_print_free(m); + return NULL; + } + } else if (strcmp(tag, "staff-layout") == 0) { + ch->staff_layout = mx_staff_layout_parse(c); + if (!ch->staff_layout) { + mx_print_free(m); + return NULL; + } + } else if (strcmp(tag, "measure-layout") == 0) { + ch->measure_layout = mx_measure_layout_parse(c); + if (!ch->measure_layout) { + mx_print_free(m); + return NULL; + } + } else if (strcmp(tag, "measure-numbering") == 0) { + ch->measure_numbering = mx_measure_numbering_parse(c); + if (!ch->measure_numbering) { + mx_print_free(m); + return NULL; + } + } else if (strcmp(tag, "part-name-display") == 0) { + ch->part_name_display = mx_name_display_parse(c); + if (!ch->part_name_display) { + mx_print_free(m); + return NULL; + } + } else if (strcmp(tag, "part-abbreviation-display") == 0) { + ch->part_abbreviation_display = mx_name_display_parse(c); + if (!ch->part_abbreviation_display) { + mx_print_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_print_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_print_serialize(const MxPrint *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_staff_spacing) { + char *s = mx_tenths_to_string(m->staff_spacing); + xmlSetProp(el, BAD_CAST "staff-spacing", BAD_CAST s); + free(s); + } + if (m->has_new_system) { + xmlSetProp(el, BAD_CAST "new-system", BAD_CAST mx_yes_no_to_string(m->new_system)); + } + if (m->has_new_page) { + xmlSetProp(el, BAD_CAST "new-page", BAD_CAST mx_yes_no_to_string(m->new_page)); + } + if (m->has_blank_page) { + char *s = mx_format_int(m->blank_page); + xmlSetProp(el, BAD_CAST "blank-page", BAD_CAST s); + free(s); + } + if (m->has_page_number) { + xmlSetProp(el, BAD_CAST "page-number", BAD_CAST m->page_number); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxPrintChild *ch = &m->children[i]; + if (ch->page_layout) { + mx_page_layout_serialize(ch->page_layout, el, "page-layout"); + } else if (ch->system_layout) { + mx_system_layout_serialize(ch->system_layout, el, "system-layout"); + } else if (ch->staff_layout) { + mx_staff_layout_serialize(ch->staff_layout, el, "staff-layout"); + } else if (ch->measure_layout) { + mx_measure_layout_serialize(ch->measure_layout, el, "measure-layout"); + } else if (ch->measure_numbering) { + mx_measure_numbering_serialize(ch->measure_numbering, el, "measure-numbering"); + } else if (ch->part_name_display) { + mx_name_display_serialize(ch->part_name_display, el, "part-name-display"); + } else if (ch->part_abbreviation_display) { + mx_name_display_serialize(ch->part_abbreviation_display, el, "part-abbreviation-display"); + } + } + return el; +} + +void mx_print_free(MxPrint *m) { + if (!m) + return; + if (m->has_page_number) + free(m->page_number); + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxPrintChild *ch = &m->children[i]; + if (ch->page_layout) + mx_page_layout_free(ch->page_layout); + if (ch->system_layout) + mx_system_layout_free(ch->system_layout); + if (ch->staff_layout) + mx_staff_layout_free(ch->staff_layout); + if (ch->measure_layout) + mx_measure_layout_free(ch->measure_layout); + if (ch->measure_numbering) + mx_measure_numbering_free(ch->measure_numbering); + if (ch->part_name_display) + mx_name_display_free(ch->part_name_display); + if (ch->part_abbreviation_display) + mx_name_display_free(ch->part_abbreviation_display); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_print.h b/gen/test/c/mx/mx_print.h new file mode 100644 index 000000000..9eaf5667e --- /dev/null +++ b/gen/test/c/mx/mx_print.h @@ -0,0 +1,63 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_PRINT_H_INCLUDED +#define MX_PRINT_H_INCLUDED + +#include +#include +#include +#include "mx_measure_layout.h" +#include "mx_measure_numbering.h" +#include "mx_name_display.h" +#include "mx_page_layout.h" +#include "mx_staff_layout.h" +#include "mx_system_layout.h" +#include "mx_tenths.h" +#include "mx_yes_no.h" + +/* + * The print type contains general printing parameters, including the layout elements defined in the + * layout.mod file. The part-name-display and part-abbreviation-display elements used in the + * score.mod file may also be used here to change how a part name or abbreviation is displayed over + * the course of a piece. They take effect when the current measure or a succeeding measure starts a + * new system. Layout elements in a print statement only apply to the current page, system, staff, + * or measure. Music that follows continues to take the default values from the layout included in + * the defaults element. + */ +/* One child element of MxPrint: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxPageLayout *page_layout; + MxSystemLayout *system_layout; + MxStaffLayout *staff_layout; + MxMeasureLayout *measure_layout; + MxMeasureNumbering *measure_numbering; + MxNameDisplay *part_name_display; + MxNameDisplay *part_abbreviation_display; +} MxPrintChild; + +typedef struct { + bool has_staff_spacing; + MxTenths staff_spacing; /* attribute staff-spacing */ + bool has_new_system; + MxYesNo new_system; /* attribute new-system */ + bool has_new_page; + MxYesNo new_page; /* attribute new-page */ + bool has_blank_page; + long blank_page; /* attribute blank-page */ + bool has_page_number; + char *page_number; /* attribute page-number */ + bool has_id; + char *id; /* attribute id */ + MxPrintChild *children; /* child elements in document order */ + size_t children_count; +} MxPrint; + +/* NULL on error; the message is in mx_error(). */ +MxPrint *mx_print_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_print_serialize(const MxPrint *m, xmlNodePtr parent, const char *tag); +void mx_print_free(MxPrint *m); + +#endif /* MX_PRINT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_repeat.c b/gen/test/c/mx/mx_repeat.c new file mode 100644 index 000000000..7cc276840 --- /dev/null +++ b/gen/test/c/mx/mx_repeat.c @@ -0,0 +1,76 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_repeat.h" + +#include "mx_runtime.h" +#include +#include + +MxRepeat *mx_repeat_parse(xmlNodePtr el) { + MxRepeat *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "direction") == 0) { + m->has_direction = true; + m->direction = mx_backward_forward_parse(s); + } else if (strcmp(aname, "times") == 0) { + m->has_times = true; + m->times = mx_parse_int(s); + } else if (strcmp(aname, "winged") == 0) { + m->has_winged = true; + m->winged = mx_winged_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_repeat_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_repeat_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_repeat_serialize(const MxRepeat *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_direction) { + xmlSetProp(el, BAD_CAST "direction", BAD_CAST mx_backward_forward_to_string(m->direction)); + } + if (m->has_times) { + char *s = mx_format_int(m->times); + xmlSetProp(el, BAD_CAST "times", BAD_CAST s); + free(s); + } + if (m->has_winged) { + xmlSetProp(el, BAD_CAST "winged", BAD_CAST mx_winged_to_string(m->winged)); + } + return el; +} + +void mx_repeat_free(MxRepeat *m) { + if (!m) + return; + free(m); +} diff --git a/gen/test/c/mx/mx_repeat.h b/gen/test/c/mx/mx_repeat.h new file mode 100644 index 000000000..911f5521f --- /dev/null +++ b/gen/test/c/mx/mx_repeat.h @@ -0,0 +1,32 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_REPEAT_H_INCLUDED +#define MX_REPEAT_H_INCLUDED + +#include +#include +#include +#include "mx_backward_forward.h" +#include "mx_winged.h" + +/* + * The repeat type represents repeat marks. The start of the repeat has a forward direction while + * the end of the repeat has a backward direction. Backward repeats that are not part of an ending + * can use the times attribute to indicate the number of times the repeated section is played. + */ +typedef struct { + bool has_direction; + MxBackwardForward direction; /* attribute direction */ + bool has_times; + long times; /* attribute times */ + bool has_winged; + MxWinged winged; /* attribute winged */ +} MxRepeat; + +/* NULL on error; the message is in mx_error(). */ +MxRepeat *mx_repeat_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_repeat_serialize(const MxRepeat *m, xmlNodePtr parent, const char *tag); +void mx_repeat_free(MxRepeat *m); + +#endif /* MX_REPEAT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_rest.c b/gen/test/c/mx/mx_rest.c new file mode 100644 index 000000000..f21d2b1e2 --- /dev/null +++ b/gen/test/c/mx/mx_rest.c @@ -0,0 +1,105 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_rest.h" + +#include "mx_runtime.h" +#include +#include + +MxRest *mx_rest_parse(xmlNodePtr el) { + MxRest *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "measure") == 0) { + m->has_measure = true; + m->measure = mx_yes_no_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_rest_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxRestChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "display-step") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->display_step = malloc(sizeof(*ch->display_step)); + if (!ch->display_step) + abort(); + *ch->display_step = mx_step_parse(s); + xmlFree(text); + } else if (strcmp(tag, "display-octave") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->display_octave = malloc(sizeof(*ch->display_octave)); + if (!ch->display_octave) + abort(); + *ch->display_octave = mx_octave_parse(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_rest_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_rest_serialize(const MxRest *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_measure) { + xmlSetProp(el, BAD_CAST "measure", BAD_CAST mx_yes_no_to_string(m->measure)); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxRestChild *ch = &m->children[i]; + if (ch->display_step) { + xmlNewTextChild(el, NULL, BAD_CAST "display-step", BAD_CAST mx_step_to_string((*ch->display_step))); + } else if (ch->display_octave) { + char *s = mx_octave_to_string((*ch->display_octave)); + xmlNewTextChild(el, NULL, BAD_CAST "display-octave", BAD_CAST s); + free(s); + } + } + return el; +} + +void mx_rest_free(MxRest *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxRestChild *ch = &m->children[i]; + free(ch->display_step); + free(ch->display_octave); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_rest.h b/gen/test/c/mx/mx_rest.h new file mode 100644 index 000000000..bb998892a --- /dev/null +++ b/gen/test/c/mx/mx_rest.h @@ -0,0 +1,39 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_REST_H_INCLUDED +#define MX_REST_H_INCLUDED + +#include +#include +#include +#include "mx_octave.h" +#include "mx_step.h" +#include "mx_yes_no.h" + +/* + * The rest element indicates notated rests or silences. Rest elements are usually empty, but + * placement on the staff can be specified using display-step and display-octave elements. If the + * measure attribute is set to yes, this indicates this is a complete measure rest. + */ +/* One child element of MxRest: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxStep *display_step; + MxOctave *display_octave; +} MxRestChild; + +typedef struct { + bool has_measure; + MxYesNo measure; /* attribute measure */ + MxRestChild *children; /* child elements in document order */ + size_t children_count; +} MxRest; + +/* NULL on error; the message is in mx_error(). */ +MxRest *mx_rest_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_rest_serialize(const MxRest *m, xmlNodePtr parent, const char *tag); +void mx_rest_free(MxRest *m); + +#endif /* MX_REST_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_root.c b/gen/test/c/mx/mx_root.c new file mode 100644 index 000000000..9c63f6118 --- /dev/null +++ b/gen/test/c/mx/mx_root.c @@ -0,0 +1,95 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_root.h" + +#include "mx_runtime.h" +#include +#include + +MxRoot *mx_root_parse(xmlNodePtr el) { + MxRoot *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_root_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxRootChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "root-step") == 0) { + ch->root_step = mx_root_step_parse(c); + if (!ch->root_step) { + mx_root_free(m); + return NULL; + } + } else if (strcmp(tag, "root-alter") == 0) { + ch->root_alter = mx_root_alter_parse(c); + if (!ch->root_alter) { + mx_root_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_root_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_root_serialize(const MxRoot *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxRootChild *ch = &m->children[i]; + if (ch->root_step) { + mx_root_step_serialize(ch->root_step, el, "root-step"); + } else if (ch->root_alter) { + mx_root_alter_serialize(ch->root_alter, el, "root-alter"); + } + } + return el; +} + +void mx_root_free(MxRoot *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxRootChild *ch = &m->children[i]; + if (ch->root_step) + mx_root_step_free(ch->root_step); + if (ch->root_alter) + mx_root_alter_free(ch->root_alter); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_root.h b/gen/test/c/mx/mx_root.h new file mode 100644 index 000000000..b49c59b49 --- /dev/null +++ b/gen/test/c/mx/mx_root.h @@ -0,0 +1,37 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ROOT_H_INCLUDED +#define MX_ROOT_H_INCLUDED + +#include +#include +#include +#include "mx_root_alter.h" +#include "mx_root_step.h" + +/* + * The root type indicates a pitch like C, D, E vs. a function indication like I, II, III. It is + * used with chord symbols in popular music. The root element has a root-step and optional + * root-alter element similar to the step and alter elements, but renamed to distinguish the + * different musical meanings. + */ +/* One child element of MxRoot: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxRootStep *root_step; + MxRootAlter *root_alter; +} MxRootChild; + +typedef struct { + MxRootChild *children; /* child elements in document order */ + size_t children_count; +} MxRoot; + +/* NULL on error; the message is in mx_error(). */ +MxRoot *mx_root_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_root_serialize(const MxRoot *m, xmlNodePtr parent, const char *tag); +void mx_root_free(MxRoot *m); + +#endif /* MX_ROOT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_root_alter.c b/gen/test/c/mx/mx_root_alter.c new file mode 100644 index 000000000..2f2abc730 --- /dev/null +++ b/gen/test/c/mx/mx_root_alter.c @@ -0,0 +1,149 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_root_alter.h" + +#include "mx_runtime.h" +#include +#include + +MxRootAlter *mx_root_alter_parse(xmlNodePtr el) { + MxRootAlter *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "location") == 0) { + m->has_location = true; + m->location = mx_left_right_parse(s); + } else if (strcmp(aname, "print-object") == 0) { + m->has_print_object = true; + m->print_object = mx_yes_no_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_root_alter_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_semitones_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_root_alter_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_root_alter_serialize(const MxRootAlter *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + { + char *s = mx_semitones_to_string(m->value); + xmlAddChild(el, xmlNewText(BAD_CAST s)); + free(s); + } + if (m->has_location) { + xmlSetProp(el, BAD_CAST "location", BAD_CAST mx_left_right_to_string(m->location)); + } + if (m->has_print_object) { + xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_root_alter_free(MxRootAlter *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_root_alter.h b/gen/test/c/mx/mx_root_alter.h new file mode 100644 index 000000000..820c4f8c8 --- /dev/null +++ b/gen/test/c/mx/mx_root_alter.h @@ -0,0 +1,58 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ROOT_ALTER_H_INCLUDED +#define MX_ROOT_ALTER_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_right.h" +#include "mx_semitones.h" +#include "mx_tenths.h" +#include "mx_yes_no.h" + +/* + * The root-alter type represents the chromatic alteration of the root of the current chord within + * the harmony element. In some chord styles, the text for the root-step element may include + * root-alter information. In that case, the print-object attribute of the root-alter element can be + * set to no. The location attribute indicates whether the alteration should appear to the left or + * the right of the root-step; it is right by default. + */ +typedef struct { + bool has_location; + MxLeftRight location; /* attribute location */ + bool has_print_object; + MxYesNo print_object; /* attribute print-object */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + MxSemitones value; /* text content */ +} MxRootAlter; + +/* NULL on error; the message is in mx_error(). */ +MxRootAlter *mx_root_alter_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_root_alter_serialize(const MxRootAlter *m, xmlNodePtr parent, const char *tag); +void mx_root_alter_free(MxRootAlter *m); + +#endif /* MX_ROOT_ALTER_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_root_step.c b/gen/test/c/mx/mx_root_step.c new file mode 100644 index 000000000..6a5d612f3 --- /dev/null +++ b/gen/test/c/mx/mx_root_step.c @@ -0,0 +1,141 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_root_step.h" + +#include "mx_runtime.h" +#include +#include + +MxRootStep *mx_root_step_parse(xmlNodePtr el) { + MxRootStep *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "text") == 0) { + m->has_text = true; + m->text = mx_strdup(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_root_step_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_step_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_root_step_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_root_step_serialize(const MxRootStep *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_step_to_string(m->value))); + if (m->has_text) { + xmlSetProp(el, BAD_CAST "text", BAD_CAST m->text); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_root_step_free(MxRootStep *m) { + if (!m) + return; + if (m->has_text) + free(m->text); + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_root_step.h b/gen/test/c/mx/mx_root_step.h new file mode 100644 index 000000000..9ac0ce3d5 --- /dev/null +++ b/gen/test/c/mx/mx_root_step.h @@ -0,0 +1,52 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_ROOT_STEP_H_INCLUDED +#define MX_ROOT_STEP_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_step.h" +#include "mx_tenths.h" + +/* + * The root-step type represents the pitch step of the root of the current chord within the harmony + * element. The text attribute indicates how the root should appear in a score if not using the + * element contents. + */ +typedef struct { + bool has_text; + char *text; /* attribute text */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + MxStep value; /* text content */ +} MxRootStep; + +/* NULL on error; the message is in mx_error(). */ +MxRootStep *mx_root_step_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_root_step_serialize(const MxRootStep *m, xmlNodePtr parent, const char *tag); +void mx_root_step_free(MxRootStep *m); + +#endif /* MX_ROOT_STEP_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_runtime.c b/gen/test/c/mx/mx_runtime.c index 1bbc9fc36..92290b799 100644 --- a/gen/test/c/mx/mx_runtime.c +++ b/gen/test/c/mx/mx_runtime.c @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -94,3 +95,16 @@ char *mx_strdup(const char *s) { memcpy(out, s, n); return out; } + +static char mx_error_buf[512]; + +void mx_error_set(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + vsnprintf(mx_error_buf, sizeof(mx_error_buf), fmt, args); + va_end(args); +} + +const char *mx_error(void) { + return mx_error_buf; +} diff --git a/gen/test/c/mx/mx_runtime.h b/gen/test/c/mx/mx_runtime.h index 110093ef3..1d840410b 100644 --- a/gen/test/c/mx/mx_runtime.h +++ b/gen/test/c/mx/mx_runtime.h @@ -23,4 +23,9 @@ char *mx_format_int(long v); /* strdup that maps NULL to "". Malloc'd; caller frees. */ char *mx_strdup(const char *s); +/* The parse-failure message channel: parse functions returning NULL set it; + the caller reads it before the next parse. Static storage. */ +void mx_error_set(const char *fmt, ...); +const char *mx_error(void); + #endif /* MX_RUNTIME_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_scaling.c b/gen/test/c/mx/mx_scaling.c new file mode 100644 index 000000000..94dbc1756 --- /dev/null +++ b/gen/test/c/mx/mx_scaling.c @@ -0,0 +1,101 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_scaling.h" + +#include "mx_runtime.h" +#include +#include + +MxScaling *mx_scaling_parse(xmlNodePtr el) { + MxScaling *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_scaling_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxScalingChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "millimeters") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->millimeters = malloc(sizeof(*ch->millimeters)); + if (!ch->millimeters) + abort(); + *ch->millimeters = mx_millimeters_parse(s); + xmlFree(text); + } else if (strcmp(tag, "tenths") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->tenths = malloc(sizeof(*ch->tenths)); + if (!ch->tenths) + abort(); + *ch->tenths = mx_tenths_parse(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_scaling_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_scaling_serialize(const MxScaling *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxScalingChild *ch = &m->children[i]; + if (ch->millimeters) { + char *s = mx_millimeters_to_string((*ch->millimeters)); + xmlNewTextChild(el, NULL, BAD_CAST "millimeters", BAD_CAST s); + free(s); + } else if (ch->tenths) { + char *s = mx_tenths_to_string((*ch->tenths)); + xmlNewTextChild(el, NULL, BAD_CAST "tenths", BAD_CAST s); + free(s); + } + } + return el; +} + +void mx_scaling_free(MxScaling *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxScalingChild *ch = &m->children[i]; + free(ch->millimeters); + free(ch->tenths); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_scaling.h b/gen/test/c/mx/mx_scaling.h new file mode 100644 index 000000000..185bba441 --- /dev/null +++ b/gen/test/c/mx/mx_scaling.h @@ -0,0 +1,38 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SCALING_H_INCLUDED +#define MX_SCALING_H_INCLUDED + +#include +#include +#include +#include "mx_millimeters.h" +#include "mx_tenths.h" + +/* + * Margins, page sizes, and distances are all measured in tenths to keep MusicXML data in a + * consistent coordinate system as much as possible. The translation to absolute units is done with + * the scaling type, which specifies how many millimeters are equal to how many tenths. For a staff + * height of 7 mm, millimeters would be set to 7 while tenths is set to 40. The ability to set a + * formula rather than a single scaling factor helps avoid roundoff errors. + */ +/* One child element of MxScaling: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxMillimeters *millimeters; + MxTenths *tenths; +} MxScalingChild; + +typedef struct { + MxScalingChild *children; /* child elements in document order */ + size_t children_count; +} MxScaling; + +/* NULL on error; the message is in mx_error(). */ +MxScaling *mx_scaling_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_scaling_serialize(const MxScaling *m, xmlNodePtr parent, const char *tag); +void mx_scaling_free(MxScaling *m); + +#endif /* MX_SCALING_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_scordatura.c b/gen/test/c/mx/mx_scordatura.c new file mode 100644 index 000000000..813b7f73c --- /dev/null +++ b/gen/test/c/mx/mx_scordatura.c @@ -0,0 +1,93 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_scordatura.h" + +#include "mx_runtime.h" +#include +#include + +MxScordatura *mx_scordatura_parse(xmlNodePtr el) { + MxScordatura *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_scordatura_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxScordaturaChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "accord") == 0) { + ch->accord = mx_accord_parse(c); + if (!ch->accord) { + mx_scordatura_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_scordatura_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_scordatura_serialize(const MxScordatura *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxScordaturaChild *ch = &m->children[i]; + if (ch->accord) { + mx_accord_serialize(ch->accord, el, "accord"); + } + } + return el; +} + +void mx_scordatura_free(MxScordatura *m) { + if (!m) + return; + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxScordaturaChild *ch = &m->children[i]; + if (ch->accord) + mx_accord_free(ch->accord); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_scordatura.h b/gen/test/c/mx/mx_scordatura.h new file mode 100644 index 000000000..bd5b71bca --- /dev/null +++ b/gen/test/c/mx/mx_scordatura.h @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SCORDATURA_H_INCLUDED +#define MX_SCORDATURA_H_INCLUDED + +#include +#include +#include +#include "mx_accord.h" + +/* + * Scordatura string tunings are represented by a series of accord elements, similar to the + * staff-tuning elements. Strings are numbered from high to low. + */ +/* One child element of MxScordatura: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxAccord *accord; +} MxScordaturaChild; + +typedef struct { + bool has_id; + char *id; /* attribute id */ + MxScordaturaChild *children; /* child elements in document order */ + size_t children_count; +} MxScordatura; + +/* NULL on error; the message is in mx_error(). */ +MxScordatura *mx_scordatura_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_scordatura_serialize(const MxScordatura *m, xmlNodePtr parent, const char *tag); +void mx_scordatura_free(MxScordatura *m); + +#endif /* MX_SCORDATURA_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_score_instrument.c b/gen/test/c/mx/mx_score_instrument.c new file mode 100644 index 000000000..09c4dd6e7 --- /dev/null +++ b/gen/test/c/mx/mx_score_instrument.c @@ -0,0 +1,151 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_score_instrument.h" + +#include "mx_runtime.h" +#include +#include + +MxScoreInstrument *mx_score_instrument_parse(xmlNodePtr el) { + MxScoreInstrument *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_score_instrument_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxScoreInstrumentChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "instrument-name") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->instrument_name = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "instrument-abbreviation") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->instrument_abbreviation = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "instrument-sound") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->instrument_sound = malloc(sizeof(*ch->instrument_sound)); + if (!ch->instrument_sound) + abort(); + *ch->instrument_sound = mx_instrument_sound_parse(s); + xmlFree(text); + } else if (strcmp(tag, "solo") == 0) { + ch->solo = mx_empty_parse(c); + if (!ch->solo) { + mx_score_instrument_free(m); + return NULL; + } + } else if (strcmp(tag, "ensemble") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->ensemble = malloc(sizeof(*ch->ensemble)); + if (!ch->ensemble) + abort(); + *ch->ensemble = mx_positive_integer_or_empty_parse(s); + xmlFree(text); + } else if (strcmp(tag, "virtual-instrument") == 0) { + ch->virtual_instrument = mx_virtual_instrument_parse(c); + if (!ch->virtual_instrument) { + mx_score_instrument_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_score_instrument_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_score_instrument_serialize(const MxScoreInstrument *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxScoreInstrumentChild *ch = &m->children[i]; + if (ch->instrument_name) { + xmlNewTextChild(el, NULL, BAD_CAST "instrument-name", BAD_CAST ch->instrument_name); + } else if (ch->instrument_abbreviation) { + xmlNewTextChild(el, NULL, BAD_CAST "instrument-abbreviation", BAD_CAST ch->instrument_abbreviation); + } else if (ch->instrument_sound) { + char *s = mx_instrument_sound_to_string((*ch->instrument_sound)); + xmlNewTextChild(el, NULL, BAD_CAST "instrument-sound", BAD_CAST s); + free(s); + } else if (ch->solo) { + mx_empty_serialize(ch->solo, el, "solo"); + } else if (ch->ensemble) { + char *s = mx_positive_integer_or_empty_to_string((*ch->ensemble)); + xmlNewTextChild(el, NULL, BAD_CAST "ensemble", BAD_CAST s); + free(s); + } else if (ch->virtual_instrument) { + mx_virtual_instrument_serialize(ch->virtual_instrument, el, "virtual-instrument"); + } + } + return el; +} + +void mx_score_instrument_free(MxScoreInstrument *m) { + if (!m) + return; + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxScoreInstrumentChild *ch = &m->children[i]; + free(ch->instrument_name); + free(ch->instrument_abbreviation); + if (ch->instrument_sound) { + mx_instrument_sound_free(&(*ch->instrument_sound)); + free(ch->instrument_sound); + } + if (ch->solo) + mx_empty_free(ch->solo); + if (ch->ensemble) { + mx_positive_integer_or_empty_free(&(*ch->ensemble)); + free(ch->ensemble); + } + if (ch->virtual_instrument) + mx_virtual_instrument_free(ch->virtual_instrument); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_score_instrument.h b/gen/test/c/mx/mx_score_instrument.h new file mode 100644 index 000000000..d2b8345b8 --- /dev/null +++ b/gen/test/c/mx/mx_score_instrument.h @@ -0,0 +1,47 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SCORE_INSTRUMENT_H_INCLUDED +#define MX_SCORE_INSTRUMENT_H_INCLUDED + +#include +#include +#include +#include "mx_empty.h" +#include "mx_instrument_sound.h" +#include "mx_positive_integer_or_empty.h" +#include "mx_virtual_instrument.h" + +/* + * The score-instrument type represents a single instrument within a score-part. As with the + * score-part type, each score-instrument has a required ID attribute, a name, and an optional + * abbreviation. A score-instrument type is also required if the score specifies MIDI 1.0 channels, + * banks, or programs. An initial midi-instrument assignment can also be made here. MusicXML + * software should be able to automatically assign reasonable channels and instruments without these + * elements in simple cases, such as where part names match General MIDI instrument names. + */ +/* One child element of MxScoreInstrument: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + char *instrument_name; + char *instrument_abbreviation; + MxInstrumentSound *instrument_sound; + MxEmpty *solo; + MxPositiveIntegerOrEmpty *ensemble; + MxVirtualInstrument *virtual_instrument; +} MxScoreInstrumentChild; + +typedef struct { + bool has_id; + char *id; /* attribute id */ + MxScoreInstrumentChild *children; /* child elements in document order */ + size_t children_count; +} MxScoreInstrument; + +/* NULL on error; the message is in mx_error(). */ +MxScoreInstrument *mx_score_instrument_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_score_instrument_serialize(const MxScoreInstrument *m, xmlNodePtr parent, const char *tag); +void mx_score_instrument_free(MxScoreInstrument *m); + +#endif /* MX_SCORE_INSTRUMENT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_score_part.c b/gen/test/c/mx/mx_score_part.c new file mode 100644 index 000000000..6ac54ac7f --- /dev/null +++ b/gen/test/c/mx/mx_score_part.c @@ -0,0 +1,171 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_score_part.h" + +#include "mx_runtime.h" +#include +#include + +MxScorePart *mx_score_part_parse(xmlNodePtr el) { + MxScorePart *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_score_part_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxScorePartChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "identification") == 0) { + ch->identification = mx_identification_parse(c); + if (!ch->identification) { + mx_score_part_free(m); + return NULL; + } + } else if (strcmp(tag, "part-name") == 0) { + ch->part_name = mx_part_name_parse(c); + if (!ch->part_name) { + mx_score_part_free(m); + return NULL; + } + } else if (strcmp(tag, "part-name-display") == 0) { + ch->part_name_display = mx_name_display_parse(c); + if (!ch->part_name_display) { + mx_score_part_free(m); + return NULL; + } + } else if (strcmp(tag, "part-abbreviation") == 0) { + ch->part_abbreviation = mx_part_name_parse(c); + if (!ch->part_abbreviation) { + mx_score_part_free(m); + return NULL; + } + } else if (strcmp(tag, "part-abbreviation-display") == 0) { + ch->part_abbreviation_display = mx_name_display_parse(c); + if (!ch->part_abbreviation_display) { + mx_score_part_free(m); + return NULL; + } + } else if (strcmp(tag, "group") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->group = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "score-instrument") == 0) { + ch->score_instrument = mx_score_instrument_parse(c); + if (!ch->score_instrument) { + mx_score_part_free(m); + return NULL; + } + } else if (strcmp(tag, "midi-device") == 0) { + ch->midi_device = mx_midi_device_parse(c); + if (!ch->midi_device) { + mx_score_part_free(m); + return NULL; + } + } else if (strcmp(tag, "midi-instrument") == 0) { + ch->midi_instrument = mx_midi_instrument_parse(c); + if (!ch->midi_instrument) { + mx_score_part_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_score_part_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_score_part_serialize(const MxScorePart *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxScorePartChild *ch = &m->children[i]; + if (ch->identification) { + mx_identification_serialize(ch->identification, el, "identification"); + } else if (ch->part_name) { + mx_part_name_serialize(ch->part_name, el, "part-name"); + } else if (ch->part_name_display) { + mx_name_display_serialize(ch->part_name_display, el, "part-name-display"); + } else if (ch->part_abbreviation) { + mx_part_name_serialize(ch->part_abbreviation, el, "part-abbreviation"); + } else if (ch->part_abbreviation_display) { + mx_name_display_serialize(ch->part_abbreviation_display, el, "part-abbreviation-display"); + } else if (ch->group) { + xmlNewTextChild(el, NULL, BAD_CAST "group", BAD_CAST ch->group); + } else if (ch->score_instrument) { + mx_score_instrument_serialize(ch->score_instrument, el, "score-instrument"); + } else if (ch->midi_device) { + mx_midi_device_serialize(ch->midi_device, el, "midi-device"); + } else if (ch->midi_instrument) { + mx_midi_instrument_serialize(ch->midi_instrument, el, "midi-instrument"); + } + } + return el; +} + +void mx_score_part_free(MxScorePart *m) { + if (!m) + return; + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxScorePartChild *ch = &m->children[i]; + if (ch->identification) + mx_identification_free(ch->identification); + if (ch->part_name) + mx_part_name_free(ch->part_name); + if (ch->part_name_display) + mx_name_display_free(ch->part_name_display); + if (ch->part_abbreviation) + mx_part_name_free(ch->part_abbreviation); + if (ch->part_abbreviation_display) + mx_name_display_free(ch->part_abbreviation_display); + free(ch->group); + if (ch->score_instrument) + mx_score_instrument_free(ch->score_instrument); + if (ch->midi_device) + mx_midi_device_free(ch->midi_device); + if (ch->midi_instrument) + mx_midi_instrument_free(ch->midi_instrument); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_score_part.h b/gen/test/c/mx/mx_score_part.h new file mode 100644 index 000000000..4ddd1f95b --- /dev/null +++ b/gen/test/c/mx/mx_score_part.h @@ -0,0 +1,50 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SCORE_PART_H_INCLUDED +#define MX_SCORE_PART_H_INCLUDED + +#include +#include +#include +#include "mx_identification.h" +#include "mx_midi_device.h" +#include "mx_midi_instrument.h" +#include "mx_name_display.h" +#include "mx_part_name.h" +#include "mx_score_instrument.h" + +/* + * Each MusicXML part corresponds to a track in a Standard MIDI Format 1 file. The score-instrument + * elements are used when there are multiple instruments per track. The midi-device element is used + * to make a MIDI device or port assignment for the given track or specific MIDI instruments. + * Initial midi-instrument assignments may be made here as well. + */ +/* One child element of MxScorePart: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxIdentification *identification; + MxPartName *part_name; + MxNameDisplay *part_name_display; + MxPartName *part_abbreviation; + MxNameDisplay *part_abbreviation_display; + char *group; + MxScoreInstrument *score_instrument; + MxMIDIDevice *midi_device; + MxMIDIInstrument *midi_instrument; +} MxScorePartChild; + +typedef struct { + bool has_id; + char *id; /* attribute id */ + MxScorePartChild *children; /* child elements in document order */ + size_t children_count; +} MxScorePart; + +/* NULL on error; the message is in mx_error(). */ +MxScorePart *mx_score_part_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_score_part_serialize(const MxScorePart *m, xmlNodePtr parent, const char *tag); +void mx_score_part_free(MxScorePart *m); + +#endif /* MX_SCORE_PART_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_score_partwise.c b/gen/test/c/mx/mx_score_partwise.c new file mode 100644 index 000000000..43fe4902b --- /dev/null +++ b/gen/test/c/mx/mx_score_partwise.c @@ -0,0 +1,159 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_score_partwise.h" + +#include "mx_runtime.h" +#include +#include + +MxScorePartwise *mx_score_partwise_parse(xmlNodePtr el) { + MxScorePartwise *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "version") == 0) { + m->has_version = true; + m->version = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_score_partwise_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxScorePartwiseChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "work") == 0) { + ch->work = mx_work_parse(c); + if (!ch->work) { + mx_score_partwise_free(m); + return NULL; + } + } else if (strcmp(tag, "movement-number") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->movement_number = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "movement-title") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->movement_title = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "identification") == 0) { + ch->identification = mx_identification_parse(c); + if (!ch->identification) { + mx_score_partwise_free(m); + return NULL; + } + } else if (strcmp(tag, "defaults") == 0) { + ch->defaults = mx_defaults_parse(c); + if (!ch->defaults) { + mx_score_partwise_free(m); + return NULL; + } + } else if (strcmp(tag, "credit") == 0) { + ch->credit = mx_credit_parse(c); + if (!ch->credit) { + mx_score_partwise_free(m); + return NULL; + } + } else if (strcmp(tag, "part-list") == 0) { + ch->part_list = mx_part_list_parse(c); + if (!ch->part_list) { + mx_score_partwise_free(m); + return NULL; + } + } else if (strcmp(tag, "part") == 0) { + ch->part = mx_partwise_part_parse(c); + if (!ch->part) { + mx_score_partwise_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_score_partwise_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_score_partwise_serialize(const MxScorePartwise *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_version) { + xmlSetProp(el, BAD_CAST "version", BAD_CAST m->version); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxScorePartwiseChild *ch = &m->children[i]; + if (ch->work) { + mx_work_serialize(ch->work, el, "work"); + } else if (ch->movement_number) { + xmlNewTextChild(el, NULL, BAD_CAST "movement-number", BAD_CAST ch->movement_number); + } else if (ch->movement_title) { + xmlNewTextChild(el, NULL, BAD_CAST "movement-title", BAD_CAST ch->movement_title); + } else if (ch->identification) { + mx_identification_serialize(ch->identification, el, "identification"); + } else if (ch->defaults) { + mx_defaults_serialize(ch->defaults, el, "defaults"); + } else if (ch->credit) { + mx_credit_serialize(ch->credit, el, "credit"); + } else if (ch->part_list) { + mx_part_list_serialize(ch->part_list, el, "part-list"); + } else if (ch->part) { + mx_partwise_part_serialize(ch->part, el, "part"); + } + } + return el; +} + +void mx_score_partwise_free(MxScorePartwise *m) { + if (!m) + return; + if (m->has_version) + free(m->version); + for (size_t i = 0; i < m->children_count; i++) { + MxScorePartwiseChild *ch = &m->children[i]; + if (ch->work) + mx_work_free(ch->work); + free(ch->movement_number); + free(ch->movement_title); + if (ch->identification) + mx_identification_free(ch->identification); + if (ch->defaults) + mx_defaults_free(ch->defaults); + if (ch->credit) + mx_credit_free(ch->credit); + if (ch->part_list) + mx_part_list_free(ch->part_list); + if (ch->part) + mx_partwise_part_free(ch->part); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_score_partwise.h b/gen/test/c/mx/mx_score_partwise.h new file mode 100644 index 000000000..80827f819 --- /dev/null +++ b/gen/test/c/mx/mx_score_partwise.h @@ -0,0 +1,43 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SCORE_PARTWISE_H_INCLUDED +#define MX_SCORE_PARTWISE_H_INCLUDED + +#include +#include +#include +#include "mx_credit.h" +#include "mx_defaults.h" +#include "mx_identification.h" +#include "mx_part_list.h" +#include "mx_partwise_part.h" +#include "mx_work.h" + +/* One child element of MxScorePartwise: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxWork *work; + char *movement_number; + char *movement_title; + MxIdentification *identification; + MxDefaults *defaults; + MxCredit *credit; + MxPartList *part_list; + MxPartwisePart *part; +} MxScorePartwiseChild; + +typedef struct { + bool has_version; + char *version; /* attribute version */ + MxScorePartwiseChild *children; /* child elements in document order */ + size_t children_count; +} MxScorePartwise; + +/* NULL on error; the message is in mx_error(). */ +MxScorePartwise *mx_score_partwise_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_score_partwise_serialize(const MxScorePartwise *m, xmlNodePtr parent, const char *tag); +void mx_score_partwise_free(MxScorePartwise *m); + +#endif /* MX_SCORE_PARTWISE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_score_timewise.c b/gen/test/c/mx/mx_score_timewise.c new file mode 100644 index 000000000..d2a4a5471 --- /dev/null +++ b/gen/test/c/mx/mx_score_timewise.c @@ -0,0 +1,159 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_score_timewise.h" + +#include "mx_runtime.h" +#include +#include + +MxScoreTimewise *mx_score_timewise_parse(xmlNodePtr el) { + MxScoreTimewise *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "version") == 0) { + m->has_version = true; + m->version = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_score_timewise_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxScoreTimewiseChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "work") == 0) { + ch->work = mx_work_parse(c); + if (!ch->work) { + mx_score_timewise_free(m); + return NULL; + } + } else if (strcmp(tag, "movement-number") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->movement_number = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "movement-title") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->movement_title = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "identification") == 0) { + ch->identification = mx_identification_parse(c); + if (!ch->identification) { + mx_score_timewise_free(m); + return NULL; + } + } else if (strcmp(tag, "defaults") == 0) { + ch->defaults = mx_defaults_parse(c); + if (!ch->defaults) { + mx_score_timewise_free(m); + return NULL; + } + } else if (strcmp(tag, "credit") == 0) { + ch->credit = mx_credit_parse(c); + if (!ch->credit) { + mx_score_timewise_free(m); + return NULL; + } + } else if (strcmp(tag, "part-list") == 0) { + ch->part_list = mx_part_list_parse(c); + if (!ch->part_list) { + mx_score_timewise_free(m); + return NULL; + } + } else if (strcmp(tag, "measure") == 0) { + ch->measure = mx_timewise_measure_parse(c); + if (!ch->measure) { + mx_score_timewise_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_score_timewise_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_score_timewise_serialize(const MxScoreTimewise *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_version) { + xmlSetProp(el, BAD_CAST "version", BAD_CAST m->version); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxScoreTimewiseChild *ch = &m->children[i]; + if (ch->work) { + mx_work_serialize(ch->work, el, "work"); + } else if (ch->movement_number) { + xmlNewTextChild(el, NULL, BAD_CAST "movement-number", BAD_CAST ch->movement_number); + } else if (ch->movement_title) { + xmlNewTextChild(el, NULL, BAD_CAST "movement-title", BAD_CAST ch->movement_title); + } else if (ch->identification) { + mx_identification_serialize(ch->identification, el, "identification"); + } else if (ch->defaults) { + mx_defaults_serialize(ch->defaults, el, "defaults"); + } else if (ch->credit) { + mx_credit_serialize(ch->credit, el, "credit"); + } else if (ch->part_list) { + mx_part_list_serialize(ch->part_list, el, "part-list"); + } else if (ch->measure) { + mx_timewise_measure_serialize(ch->measure, el, "measure"); + } + } + return el; +} + +void mx_score_timewise_free(MxScoreTimewise *m) { + if (!m) + return; + if (m->has_version) + free(m->version); + for (size_t i = 0; i < m->children_count; i++) { + MxScoreTimewiseChild *ch = &m->children[i]; + if (ch->work) + mx_work_free(ch->work); + free(ch->movement_number); + free(ch->movement_title); + if (ch->identification) + mx_identification_free(ch->identification); + if (ch->defaults) + mx_defaults_free(ch->defaults); + if (ch->credit) + mx_credit_free(ch->credit); + if (ch->part_list) + mx_part_list_free(ch->part_list); + if (ch->measure) + mx_timewise_measure_free(ch->measure); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_score_timewise.h b/gen/test/c/mx/mx_score_timewise.h new file mode 100644 index 000000000..2c9970da4 --- /dev/null +++ b/gen/test/c/mx/mx_score_timewise.h @@ -0,0 +1,43 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SCORE_TIMEWISE_H_INCLUDED +#define MX_SCORE_TIMEWISE_H_INCLUDED + +#include +#include +#include +#include "mx_credit.h" +#include "mx_defaults.h" +#include "mx_identification.h" +#include "mx_part_list.h" +#include "mx_timewise_measure.h" +#include "mx_work.h" + +/* One child element of MxScoreTimewise: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxWork *work; + char *movement_number; + char *movement_title; + MxIdentification *identification; + MxDefaults *defaults; + MxCredit *credit; + MxPartList *part_list; + MxTimewiseMeasure *measure; +} MxScoreTimewiseChild; + +typedef struct { + bool has_version; + char *version; /* attribute version */ + MxScoreTimewiseChild *children; /* child elements in document order */ + size_t children_count; +} MxScoreTimewise; + +/* NULL on error; the message is in mx_error(). */ +MxScoreTimewise *mx_score_timewise_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_score_timewise_serialize(const MxScoreTimewise *m, xmlNodePtr parent, const char *tag); +void mx_score_timewise_free(MxScoreTimewise *m); + +#endif /* MX_SCORE_TIMEWISE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_segno.c b/gen/test/c/mx/mx_segno.c new file mode 100644 index 000000000..1a534ce84 --- /dev/null +++ b/gen/test/c/mx/mx_segno.c @@ -0,0 +1,154 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_segno.h" + +#include "mx_runtime.h" +#include +#include + +MxSegno *mx_segno_parse(xmlNodePtr el) { + MxSegno *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "smufl") == 0) { + m->has_smufl = true; + m->smufl = mx_smufl_segno_glyph_name_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_segno_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_segno_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_segno_serialize(const MxSegno *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_smufl) { + xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_segno_free(MxSegno *m) { + if (!m) + return; + if (m->has_smufl) + free(m->smufl); + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + free(m); +} diff --git a/gen/test/c/mx/mx_segno.h b/gen/test/c/mx/mx_segno.h new file mode 100644 index 000000000..db76da79c --- /dev/null +++ b/gen/test/c/mx/mx_segno.h @@ -0,0 +1,58 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SEGNO_H_INCLUDED +#define MX_SEGNO_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_center_right.h" +#include "mx_smufl_segno_glyph_name.h" +#include "mx_tenths.h" +#include "mx_valign.h" + +/* + * The segno type is the visual indicator of a segno sign. The exact glyph can be specified with the + * smufl attribute. A sound element is also needed to guide playback applications reliably. + */ +typedef struct { + bool has_smufl; + MxSMUFLSegnoGlyphName smufl; /* attribute smufl */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ + bool has_id; + char *id; /* attribute id */ +} MxSegno; + +/* NULL on error; the message is in mx_error(). */ +MxSegno *mx_segno_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_segno_serialize(const MxSegno *m, xmlNodePtr parent, const char *tag); +void mx_segno_free(MxSegno *m); + +#endif /* MX_SEGNO_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_slash.c b/gen/test/c/mx/mx_slash.c new file mode 100644 index 000000000..d86c70e31 --- /dev/null +++ b/gen/test/c/mx/mx_slash.c @@ -0,0 +1,122 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_slash.h" + +#include "mx_runtime.h" +#include +#include + +MxSlash *mx_slash_parse(xmlNodePtr el) { + MxSlash *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_parse(s); + } else if (strcmp(aname, "use-dots") == 0) { + m->has_use_dots = true; + m->use_dots = mx_yes_no_parse(s); + } else if (strcmp(aname, "use-stems") == 0) { + m->has_use_stems = true; + m->use_stems = mx_yes_no_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_slash_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxSlashChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "slash-type") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->slash_type = malloc(sizeof(*ch->slash_type)); + if (!ch->slash_type) + abort(); + *ch->slash_type = mx_note_type_value_parse(s); + xmlFree(text); + } else if (strcmp(tag, "slash-dot") == 0) { + ch->slash_dot = mx_empty_parse(c); + if (!ch->slash_dot) { + mx_slash_free(m); + return NULL; + } + } else if (strcmp(tag, "except-voice") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->except_voice = mx_strdup(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_slash_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_slash_serialize(const MxSlash *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); + } + if (m->has_use_dots) { + xmlSetProp(el, BAD_CAST "use-dots", BAD_CAST mx_yes_no_to_string(m->use_dots)); + } + if (m->has_use_stems) { + xmlSetProp(el, BAD_CAST "use-stems", BAD_CAST mx_yes_no_to_string(m->use_stems)); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxSlashChild *ch = &m->children[i]; + if (ch->slash_type) { + xmlNewTextChild(el, NULL, BAD_CAST "slash-type", BAD_CAST mx_note_type_value_to_string((*ch->slash_type))); + } else if (ch->slash_dot) { + mx_empty_serialize(ch->slash_dot, el, "slash-dot"); + } else if (ch->except_voice) { + xmlNewTextChild(el, NULL, BAD_CAST "except-voice", BAD_CAST ch->except_voice); + } + } + return el; +} + +void mx_slash_free(MxSlash *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxSlashChild *ch = &m->children[i]; + free(ch->slash_type); + if (ch->slash_dot) + mx_empty_free(ch->slash_dot); + free(ch->except_voice); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_slash.h b/gen/test/c/mx/mx_slash.h new file mode 100644 index 000000000..e8b609775 --- /dev/null +++ b/gen/test/c/mx/mx_slash.h @@ -0,0 +1,47 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SLASH_H_INCLUDED +#define MX_SLASH_H_INCLUDED + +#include +#include +#include +#include "mx_empty.h" +#include "mx_note_type_value.h" +#include "mx_start_stop.h" +#include "mx_yes_no.h" + +/* + * The slash type is used to indicate that slash notation is to be used. If the slash is on every + * beat, use-stems is no (the default). To indicate rhythms but not pitches, use-stems is set to + * yes. The type attribute indicates whether this is the start or stop of a slash notation style. + * The use-dots attribute works as for the beat-repeat element, and only has effect if use-stems is + * no. + */ +/* One child element of MxSlash: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxNoteTypeValue *slash_type; + MxEmpty *slash_dot; + char *except_voice; +} MxSlashChild; + +typedef struct { + bool has_type; + MxStartStop type; /* attribute type */ + bool has_use_dots; + MxYesNo use_dots; /* attribute use-dots */ + bool has_use_stems; + MxYesNo use_stems; /* attribute use-stems */ + MxSlashChild *children; /* child elements in document order */ + size_t children_count; +} MxSlash; + +/* NULL on error; the message is in mx_error(). */ +MxSlash *mx_slash_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_slash_serialize(const MxSlash *m, xmlNodePtr parent, const char *tag); +void mx_slash_free(MxSlash *m); + +#endif /* MX_SLASH_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_slide.c b/gen/test/c/mx/mx_slide.c new file mode 100644 index 000000000..163a41a26 --- /dev/null +++ b/gen/test/c/mx/mx_slide.c @@ -0,0 +1,208 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_slide.h" + +#include "mx_runtime.h" +#include +#include + +MxSlide *mx_slide_parse(xmlNodePtr el) { + MxSlide *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_parse(s); + } else if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_number_level_parse(s); + } else if (strcmp(aname, "line-type") == 0) { + m->has_line_type = true; + m->line_type = mx_line_type_parse(s); + } else if (strcmp(aname, "dash-length") == 0) { + m->has_dash_length = true; + m->dash_length = mx_tenths_parse(s); + } else if (strcmp(aname, "space-length") == 0) { + m->has_space_length = true; + m->space_length = mx_tenths_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "accelerate") == 0) { + m->has_accelerate = true; + m->accelerate = mx_yes_no_parse(s); + } else if (strcmp(aname, "beats") == 0) { + m->has_beats = true; + m->beats = mx_trill_beats_parse(s); + } else if (strcmp(aname, "first-beat") == 0) { + m->has_first_beat = true; + m->first_beat = mx_percent_parse(s); + } else if (strcmp(aname, "last-beat") == 0) { + m->has_last_beat = true; + m->last_beat = mx_percent_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_slide_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_slide_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_slide_serialize(const MxSlide *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); + } + if (m->has_number) { + char *s = mx_number_level_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_line_type) { + xmlSetProp(el, BAD_CAST "line-type", BAD_CAST mx_line_type_to_string(m->line_type)); + } + if (m->has_dash_length) { + char *s = mx_tenths_to_string(m->dash_length); + xmlSetProp(el, BAD_CAST "dash-length", BAD_CAST s); + free(s); + } + if (m->has_space_length) { + char *s = mx_tenths_to_string(m->space_length); + xmlSetProp(el, BAD_CAST "space-length", BAD_CAST s); + free(s); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_accelerate) { + xmlSetProp(el, BAD_CAST "accelerate", BAD_CAST mx_yes_no_to_string(m->accelerate)); + } + if (m->has_beats) { + char *s = mx_trill_beats_to_string(m->beats); + xmlSetProp(el, BAD_CAST "beats", BAD_CAST s); + free(s); + } + if (m->has_first_beat) { + char *s = mx_percent_to_string(m->first_beat); + xmlSetProp(el, BAD_CAST "first-beat", BAD_CAST s); + free(s); + } + if (m->has_last_beat) { + char *s = mx_percent_to_string(m->last_beat); + xmlSetProp(el, BAD_CAST "last-beat", BAD_CAST s); + free(s); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_slide_free(MxSlide *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_slide.h b/gen/test/c/mx/mx_slide.h new file mode 100644 index 000000000..9ddafcf50 --- /dev/null +++ b/gen/test/c/mx/mx_slide.h @@ -0,0 +1,76 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SLIDE_H_INCLUDED +#define MX_SLIDE_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_line_type.h" +#include "mx_number_level.h" +#include "mx_percent.h" +#include "mx_start_stop.h" +#include "mx_tenths.h" +#include "mx_trill_beats.h" +#include "mx_yes_no.h" + +/* + * Glissando and slide types both indicate rapidly moving from one pitch to the other so that + * individual notes are not discerned. The distinction is similar to that between NIFF's glissando + * and portamento elements. A slide is continuous between two notes and defaults to a solid line. + * The optional text for a is printed alongside the line. + */ +typedef struct { + bool has_type; + MxStartStop type; /* attribute type */ + bool has_number; + MxNumberLevel number; /* attribute number */ + bool has_line_type; + MxLineType line_type; /* attribute line-type */ + bool has_dash_length; + MxTenths dash_length; /* attribute dash-length */ + bool has_space_length; + MxTenths space_length; /* attribute space-length */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_accelerate; + MxYesNo accelerate; /* attribute accelerate */ + bool has_beats; + MxTrillBeats beats; /* attribute beats */ + bool has_first_beat; + MxPercent first_beat; /* attribute first-beat */ + bool has_last_beat; + MxPercent last_beat; /* attribute last-beat */ + bool has_id; + char *id; /* attribute id */ + char *value; /* text content */ +} MxSlide; + +/* NULL on error; the message is in mx_error(). */ +MxSlide *mx_slide_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_slide_serialize(const MxSlide *m, xmlNodePtr parent, const char *tag); +void mx_slide_free(MxSlide *m); + +#endif /* MX_SLIDE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_slur.c b/gen/test/c/mx/mx_slur.c new file mode 100644 index 000000000..8c7d59b81 --- /dev/null +++ b/gen/test/c/mx/mx_slur.c @@ -0,0 +1,200 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_slur.h" + +#include "mx_runtime.h" +#include +#include + +MxSlur *mx_slur_parse(xmlNodePtr el) { + MxSlur *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_continue_parse(s); + } else if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_number_level_parse(s); + } else if (strcmp(aname, "line-type") == 0) { + m->has_line_type = true; + m->line_type = mx_line_type_parse(s); + } else if (strcmp(aname, "dash-length") == 0) { + m->has_dash_length = true; + m->dash_length = mx_tenths_parse(s); + } else if (strcmp(aname, "space-length") == 0) { + m->has_space_length = true; + m->space_length = mx_tenths_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "orientation") == 0) { + m->has_orientation = true; + m->orientation = mx_over_under_parse(s); + } else if (strcmp(aname, "bezier-x") == 0) { + m->has_bezier_x = true; + m->bezier_x = mx_tenths_parse(s); + } else if (strcmp(aname, "bezier-y") == 0) { + m->has_bezier_y = true; + m->bezier_y = mx_tenths_parse(s); + } else if (strcmp(aname, "bezier-x2") == 0) { + m->has_bezier_x2 = true; + m->bezier_x2 = mx_tenths_parse(s); + } else if (strcmp(aname, "bezier-y2") == 0) { + m->has_bezier_y2 = true; + m->bezier_y2 = mx_tenths_parse(s); + } else if (strcmp(aname, "bezier-offset") == 0) { + m->has_bezier_offset = true; + m->bezier_offset = mx_divisions_parse(s); + } else if (strcmp(aname, "bezier-offset2") == 0) { + m->has_bezier_offset2 = true; + m->bezier_offset2 = mx_divisions_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_slur_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_slur_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_slur_serialize(const MxSlur *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_continue_to_string(m->type)); + } + if (m->has_number) { + char *s = mx_number_level_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_line_type) { + xmlSetProp(el, BAD_CAST "line-type", BAD_CAST mx_line_type_to_string(m->line_type)); + } + if (m->has_dash_length) { + char *s = mx_tenths_to_string(m->dash_length); + xmlSetProp(el, BAD_CAST "dash-length", BAD_CAST s); + free(s); + } + if (m->has_space_length) { + char *s = mx_tenths_to_string(m->space_length); + xmlSetProp(el, BAD_CAST "space-length", BAD_CAST s); + free(s); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_orientation) { + xmlSetProp(el, BAD_CAST "orientation", BAD_CAST mx_over_under_to_string(m->orientation)); + } + if (m->has_bezier_x) { + char *s = mx_tenths_to_string(m->bezier_x); + xmlSetProp(el, BAD_CAST "bezier-x", BAD_CAST s); + free(s); + } + if (m->has_bezier_y) { + char *s = mx_tenths_to_string(m->bezier_y); + xmlSetProp(el, BAD_CAST "bezier-y", BAD_CAST s); + free(s); + } + if (m->has_bezier_x2) { + char *s = mx_tenths_to_string(m->bezier_x2); + xmlSetProp(el, BAD_CAST "bezier-x2", BAD_CAST s); + free(s); + } + if (m->has_bezier_y2) { + char *s = mx_tenths_to_string(m->bezier_y2); + xmlSetProp(el, BAD_CAST "bezier-y2", BAD_CAST s); + free(s); + } + if (m->has_bezier_offset) { + char *s = mx_divisions_to_string(m->bezier_offset); + xmlSetProp(el, BAD_CAST "bezier-offset", BAD_CAST s); + free(s); + } + if (m->has_bezier_offset2) { + char *s = mx_divisions_to_string(m->bezier_offset2); + xmlSetProp(el, BAD_CAST "bezier-offset2", BAD_CAST s); + free(s); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_slur_free(MxSlur *m) { + if (!m) + return; + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + free(m); +} diff --git a/gen/test/c/mx/mx_slur.h b/gen/test/c/mx/mx_slur.h new file mode 100644 index 000000000..ddb0eeef9 --- /dev/null +++ b/gen/test/c/mx/mx_slur.h @@ -0,0 +1,70 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SLUR_H_INCLUDED +#define MX_SLUR_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_divisions.h" +#include "mx_line_type.h" +#include "mx_number_level.h" +#include "mx_over_under.h" +#include "mx_start_stop_continue.h" +#include "mx_tenths.h" + +/* + * Slur types are empty. Most slurs are represented with two elements: one with a start type, and + * one with a stop type. Slurs can add more elements using a continue type. This is typically used + * to specify the formatting of cross-system slurs, or to specify the shape of very complex slurs. + */ +typedef struct { + bool has_type; + MxStartStopContinue type; /* attribute type */ + bool has_number; + MxNumberLevel number; /* attribute number */ + bool has_line_type; + MxLineType line_type; /* attribute line-type */ + bool has_dash_length; + MxTenths dash_length; /* attribute dash-length */ + bool has_space_length; + MxTenths space_length; /* attribute space-length */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_orientation; + MxOverUnder orientation; /* attribute orientation */ + bool has_bezier_x; + MxTenths bezier_x; /* attribute bezier-x */ + bool has_bezier_y; + MxTenths bezier_y; /* attribute bezier-y */ + bool has_bezier_x2; + MxTenths bezier_x2; /* attribute bezier-x2 */ + bool has_bezier_y2; + MxTenths bezier_y2; /* attribute bezier-y2 */ + bool has_bezier_offset; + MxDivisions bezier_offset; /* attribute bezier-offset */ + bool has_bezier_offset2; + MxDivisions bezier_offset2; /* attribute bezier-offset2 */ + bool has_color; + MxColor color; /* attribute color */ + bool has_id; + char *id; /* attribute id */ +} MxSlur; + +/* NULL on error; the message is in mx_error(). */ +MxSlur *mx_slur_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_slur_serialize(const MxSlur *m, xmlNodePtr parent, const char *tag); +void mx_slur_free(MxSlur *m); + +#endif /* MX_SLUR_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_sound.c b/gen/test/c/mx/mx_sound.c new file mode 100644 index 000000000..531bcdf70 --- /dev/null +++ b/gen/test/c/mx/mx_sound.c @@ -0,0 +1,259 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_sound.h" + +#include "mx_runtime.h" +#include +#include + +MxSound *mx_sound_parse(xmlNodePtr el) { + MxSound *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "tempo") == 0) { + m->has_tempo = true; + m->tempo = mx_non_negative_decimal_parse(s); + } else if (strcmp(aname, "dynamics") == 0) { + m->has_dynamics = true; + m->dynamics = mx_non_negative_decimal_parse(s); + } else if (strcmp(aname, "dacapo") == 0) { + m->has_dacapo = true; + m->dacapo = mx_yes_no_parse(s); + } else if (strcmp(aname, "segno") == 0) { + m->has_segno = true; + m->segno = mx_strdup(s); + } else if (strcmp(aname, "dalsegno") == 0) { + m->has_dalsegno = true; + m->dalsegno = mx_strdup(s); + } else if (strcmp(aname, "coda") == 0) { + m->has_coda = true; + m->coda = mx_strdup(s); + } else if (strcmp(aname, "tocoda") == 0) { + m->has_tocoda = true; + m->tocoda = mx_strdup(s); + } else if (strcmp(aname, "divisions") == 0) { + m->has_divisions = true; + m->divisions = mx_divisions_parse(s); + } else if (strcmp(aname, "forward-repeat") == 0) { + m->has_forward_repeat = true; + m->forward_repeat = mx_yes_no_parse(s); + } else if (strcmp(aname, "fine") == 0) { + m->has_fine = true; + m->fine = mx_strdup(s); + } else if (strcmp(aname, "time-only") == 0) { + m->has_time_only = true; + m->time_only = mx_time_only_parse(s); + } else if (strcmp(aname, "pizzicato") == 0) { + m->has_pizzicato = true; + m->pizzicato = mx_yes_no_parse(s); + } else if (strcmp(aname, "pan") == 0) { + m->has_pan = true; + m->pan = mx_rotation_degrees_parse(s); + } else if (strcmp(aname, "elevation") == 0) { + m->has_elevation = true; + m->elevation = mx_rotation_degrees_parse(s); + } else if (strcmp(aname, "damper-pedal") == 0) { + m->has_damper_pedal = true; + m->damper_pedal = mx_yes_no_number_parse(s); + } else if (strcmp(aname, "soft-pedal") == 0) { + m->has_soft_pedal = true; + m->soft_pedal = mx_yes_no_number_parse(s); + } else if (strcmp(aname, "sostenuto-pedal") == 0) { + m->has_sostenuto_pedal = true; + m->sostenuto_pedal = mx_yes_no_number_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_sound_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxSoundChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "midi-device") == 0) { + ch->midi_device = mx_midi_device_parse(c); + if (!ch->midi_device) { + mx_sound_free(m); + return NULL; + } + } else if (strcmp(tag, "midi-instrument") == 0) { + ch->midi_instrument = mx_midi_instrument_parse(c); + if (!ch->midi_instrument) { + mx_sound_free(m); + return NULL; + } + } else if (strcmp(tag, "play") == 0) { + ch->play = mx_play_parse(c); + if (!ch->play) { + mx_sound_free(m); + return NULL; + } + } else if (strcmp(tag, "offset") == 0) { + ch->offset = mx_offset_parse(c); + if (!ch->offset) { + mx_sound_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_sound_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_sound_serialize(const MxSound *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_tempo) { + char *s = mx_non_negative_decimal_to_string(m->tempo); + xmlSetProp(el, BAD_CAST "tempo", BAD_CAST s); + free(s); + } + if (m->has_dynamics) { + char *s = mx_non_negative_decimal_to_string(m->dynamics); + xmlSetProp(el, BAD_CAST "dynamics", BAD_CAST s); + free(s); + } + if (m->has_dacapo) { + xmlSetProp(el, BAD_CAST "dacapo", BAD_CAST mx_yes_no_to_string(m->dacapo)); + } + if (m->has_segno) { + xmlSetProp(el, BAD_CAST "segno", BAD_CAST m->segno); + } + if (m->has_dalsegno) { + xmlSetProp(el, BAD_CAST "dalsegno", BAD_CAST m->dalsegno); + } + if (m->has_coda) { + xmlSetProp(el, BAD_CAST "coda", BAD_CAST m->coda); + } + if (m->has_tocoda) { + xmlSetProp(el, BAD_CAST "tocoda", BAD_CAST m->tocoda); + } + if (m->has_divisions) { + char *s = mx_divisions_to_string(m->divisions); + xmlSetProp(el, BAD_CAST "divisions", BAD_CAST s); + free(s); + } + if (m->has_forward_repeat) { + xmlSetProp(el, BAD_CAST "forward-repeat", BAD_CAST mx_yes_no_to_string(m->forward_repeat)); + } + if (m->has_fine) { + xmlSetProp(el, BAD_CAST "fine", BAD_CAST m->fine); + } + if (m->has_time_only) { + xmlSetProp(el, BAD_CAST "time-only", BAD_CAST m->time_only); + } + if (m->has_pizzicato) { + xmlSetProp(el, BAD_CAST "pizzicato", BAD_CAST mx_yes_no_to_string(m->pizzicato)); + } + if (m->has_pan) { + char *s = mx_rotation_degrees_to_string(m->pan); + xmlSetProp(el, BAD_CAST "pan", BAD_CAST s); + free(s); + } + if (m->has_elevation) { + char *s = mx_rotation_degrees_to_string(m->elevation); + xmlSetProp(el, BAD_CAST "elevation", BAD_CAST s); + free(s); + } + if (m->has_damper_pedal) { + char *s = mx_yes_no_number_to_string(m->damper_pedal); + xmlSetProp(el, BAD_CAST "damper-pedal", BAD_CAST s); + free(s); + } + if (m->has_soft_pedal) { + char *s = mx_yes_no_number_to_string(m->soft_pedal); + xmlSetProp(el, BAD_CAST "soft-pedal", BAD_CAST s); + free(s); + } + if (m->has_sostenuto_pedal) { + char *s = mx_yes_no_number_to_string(m->sostenuto_pedal); + xmlSetProp(el, BAD_CAST "sostenuto-pedal", BAD_CAST s); + free(s); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxSoundChild *ch = &m->children[i]; + if (ch->midi_device) { + mx_midi_device_serialize(ch->midi_device, el, "midi-device"); + } else if (ch->midi_instrument) { + mx_midi_instrument_serialize(ch->midi_instrument, el, "midi-instrument"); + } else if (ch->play) { + mx_play_serialize(ch->play, el, "play"); + } else if (ch->offset) { + mx_offset_serialize(ch->offset, el, "offset"); + } + } + return el; +} + +void mx_sound_free(MxSound *m) { + if (!m) + return; + if (m->has_segno) + free(m->segno); + if (m->has_dalsegno) + free(m->dalsegno); + if (m->has_coda) + free(m->coda); + if (m->has_tocoda) + free(m->tocoda); + if (m->has_fine) + free(m->fine); + if (m->has_time_only) + free(m->time_only); + if (m->has_damper_pedal) + mx_yes_no_number_free(&m->damper_pedal); + if (m->has_soft_pedal) + mx_yes_no_number_free(&m->soft_pedal); + if (m->has_sostenuto_pedal) + mx_yes_no_number_free(&m->sostenuto_pedal); + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxSoundChild *ch = &m->children[i]; + if (ch->midi_device) + mx_midi_device_free(ch->midi_device); + if (ch->midi_instrument) + mx_midi_instrument_free(ch->midi_instrument); + if (ch->play) + mx_play_free(ch->play); + if (ch->offset) + mx_offset_free(ch->offset); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_sound.h b/gen/test/c/mx/mx_sound.h new file mode 100644 index 000000000..3d4cf3505 --- /dev/null +++ b/gen/test/c/mx/mx_sound.h @@ -0,0 +1,115 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SOUND_H_INCLUDED +#define MX_SOUND_H_INCLUDED + +#include +#include +#include +#include "mx_divisions.h" +#include "mx_midi_device.h" +#include "mx_midi_instrument.h" +#include "mx_non_negative_decimal.h" +#include "mx_offset.h" +#include "mx_play.h" +#include "mx_rotation_degrees.h" +#include "mx_time_only.h" +#include "mx_yes_no.h" +#include "mx_yes_no_number.h" + +/* + * The sound element contains general playback parameters. They can stand alone within a + * part/measure, or be a component element within a direction. Tempo is expressed in quarter notes + * per minute. If 0, the sound-generating program should prompt the user at the time of compiling a + * sound (MIDI) file. Dynamics (or MIDI velocity) are expressed as a percentage of the default forte + * value (90 for MIDI 1.0). Dacapo indicates to go back to the beginning of the movement. When used + * it always has the value "yes". Segno and dalsegno are used for backwards jumps to a segno sign; + * coda and tocoda are used for forward jumps to a coda sign. If there are multiple jumps, the value + * of these parameters can be used to name and distinguish them. If segno or coda is used, the + * divisions attribute can also be used to indicate the number of divisions per quarter note. + * Otherwise sound and MIDI generating programs may have to recompute this. By default, a dalsegno + * or dacapo attribute indicates that the jump should occur the first time through, while a tocoda + * attribute indicates the jump should occur the second time through. The time that jumps occur can + * be changed by using the time-only attribute. Forward-repeat is used when a forward repeat sign is + * implied, and usually follows a bar line. When used it always has the value of "yes". The fine + * attribute follows the final note or rest in a movement with a da capo or dal segno direction. If + * numeric, the value represents the actual duration of the final note or rest, which can be + * ambiguous in written notation and different among parts and voices. The value may also be "yes" + * to indicate no change to the final duration. If the sound element applies only particular times + * through a repeat, the time-only attribute indicates which times to apply the sound element. + * Pizzicato in a sound element effects all following notes. Yes indicates pizzicato, no indicates + * arco. The pan and elevation attributes are deprecated in Version 2.0. The pan and elevation + * elements in the midi-instrument element should be used instead. The meaning of the pan and + * elevation attributes is the same as for the pan and elevation elements. If both are present, the + * mid-instrument elements take priority. The damper-pedal, soft-pedal, and sostenuto-pedal + * attributes effect playback of the three common piano pedals and their MIDI controller + * equivalents. The yes value indicates the pedal is depressed; no indicates the pedal is released. + * A numeric value from 0 to 100 may also be used for half pedaling. This value is the percentage + * that the pedal is depressed. A value of 0 is equivalent to no, and a value of 100 is equivalent + * to yes. MIDI devices, MIDI instruments, and playback techniques are changed using the + * midi-device, midi-instrument, and play elements. When there are multiple instances of these + * elements, they should be grouped together by instrument using the id attribute values. The offset + * element is used to indicate that the sound takes place offset from the current score position. If + * the sound element is a child of a direction element, the sound offset element overrides the + * direction offset element if both elements are present. Note that the offset reflects the intended + * musical position for the change in sound. It should not be used to compensate for latency issues + * in particular hardware configurations. + */ +/* One child element of MxSound: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxMIDIDevice *midi_device; + MxMIDIInstrument *midi_instrument; + MxPlay *play; + MxOffset *offset; +} MxSoundChild; + +typedef struct { + bool has_tempo; + MxNonNegativeDecimal tempo; /* attribute tempo */ + bool has_dynamics; + MxNonNegativeDecimal dynamics; /* attribute dynamics */ + bool has_dacapo; + MxYesNo dacapo; /* attribute dacapo */ + bool has_segno; + char *segno; /* attribute segno */ + bool has_dalsegno; + char *dalsegno; /* attribute dalsegno */ + bool has_coda; + char *coda; /* attribute coda */ + bool has_tocoda; + char *tocoda; /* attribute tocoda */ + bool has_divisions; + MxDivisions divisions; /* attribute divisions */ + bool has_forward_repeat; + MxYesNo forward_repeat; /* attribute forward-repeat */ + bool has_fine; + char *fine; /* attribute fine */ + bool has_time_only; + MxTimeOnly time_only; /* attribute time-only */ + bool has_pizzicato; + MxYesNo pizzicato; /* attribute pizzicato */ + bool has_pan; + MxRotationDegrees pan; /* attribute pan */ + bool has_elevation; + MxRotationDegrees elevation; /* attribute elevation */ + bool has_damper_pedal; + MxYesNoNumber damper_pedal; /* attribute damper-pedal */ + bool has_soft_pedal; + MxYesNoNumber soft_pedal; /* attribute soft-pedal */ + bool has_sostenuto_pedal; + MxYesNoNumber sostenuto_pedal; /* attribute sostenuto-pedal */ + bool has_id; + char *id; /* attribute id */ + MxSoundChild *children; /* child elements in document order */ + size_t children_count; +} MxSound; + +/* NULL on error; the message is in mx_error(). */ +MxSound *mx_sound_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_sound_serialize(const MxSound *m, xmlNodePtr parent, const char *tag); +void mx_sound_free(MxSound *m); + +#endif /* MX_SOUND_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_staff_details.c b/gen/test/c/mx/mx_staff_details.c new file mode 100644 index 000000000..2803f6626 --- /dev/null +++ b/gen/test/c/mx/mx_staff_details.c @@ -0,0 +1,161 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_staff_details.h" + +#include "mx_runtime.h" +#include +#include + +MxStaffDetails *mx_staff_details_parse(xmlNodePtr el) { + MxStaffDetails *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_staff_number_parse(s); + } else if (strcmp(aname, "show-frets") == 0) { + m->has_show_frets = true; + m->show_frets = mx_show_frets_parse(s); + } else if (strcmp(aname, "print-object") == 0) { + m->has_print_object = true; + m->print_object = mx_yes_no_parse(s); + } else if (strcmp(aname, "print-spacing") == 0) { + m->has_print_spacing = true; + m->print_spacing = mx_yes_no_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_staff_details_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxStaffDetailsChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "staff-type") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->staff_type = malloc(sizeof(*ch->staff_type)); + if (!ch->staff_type) + abort(); + *ch->staff_type = mx_staff_type_parse(s); + xmlFree(text); + } else if (strcmp(tag, "staff-lines") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->staff_lines = malloc(sizeof(*ch->staff_lines)); + if (!ch->staff_lines) + abort(); + *ch->staff_lines = mx_parse_int(s); + xmlFree(text); + } else if (strcmp(tag, "staff-tuning") == 0) { + ch->staff_tuning = mx_staff_tuning_parse(c); + if (!ch->staff_tuning) { + mx_staff_details_free(m); + return NULL; + } + } else if (strcmp(tag, "capo") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->capo = malloc(sizeof(*ch->capo)); + if (!ch->capo) + abort(); + *ch->capo = mx_parse_int(s); + xmlFree(text); + } else if (strcmp(tag, "staff-size") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->staff_size = malloc(sizeof(*ch->staff_size)); + if (!ch->staff_size) + abort(); + *ch->staff_size = mx_non_negative_decimal_parse(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_staff_details_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_staff_details_serialize(const MxStaffDetails *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_number) { + char *s = mx_staff_number_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_show_frets) { + xmlSetProp(el, BAD_CAST "show-frets", BAD_CAST mx_show_frets_to_string(m->show_frets)); + } + if (m->has_print_object) { + xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); + } + if (m->has_print_spacing) { + xmlSetProp(el, BAD_CAST "print-spacing", BAD_CAST mx_yes_no_to_string(m->print_spacing)); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxStaffDetailsChild *ch = &m->children[i]; + if (ch->staff_type) { + xmlNewTextChild(el, NULL, BAD_CAST "staff-type", BAD_CAST mx_staff_type_to_string((*ch->staff_type))); + } else if (ch->staff_lines) { + char *s = mx_format_int((*ch->staff_lines)); + xmlNewTextChild(el, NULL, BAD_CAST "staff-lines", BAD_CAST s); + free(s); + } else if (ch->staff_tuning) { + mx_staff_tuning_serialize(ch->staff_tuning, el, "staff-tuning"); + } else if (ch->capo) { + char *s = mx_format_int((*ch->capo)); + xmlNewTextChild(el, NULL, BAD_CAST "capo", BAD_CAST s); + free(s); + } else if (ch->staff_size) { + char *s = mx_non_negative_decimal_to_string((*ch->staff_size)); + xmlNewTextChild(el, NULL, BAD_CAST "staff-size", BAD_CAST s); + free(s); + } + } + return el; +} + +void mx_staff_details_free(MxStaffDetails *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxStaffDetailsChild *ch = &m->children[i]; + free(ch->staff_type); + free(ch->staff_lines); + if (ch->staff_tuning) + mx_staff_tuning_free(ch->staff_tuning); + free(ch->capo); + free(ch->staff_size); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_staff_details.h b/gen/test/c/mx/mx_staff_details.h new file mode 100644 index 000000000..acd0cbdce --- /dev/null +++ b/gen/test/c/mx/mx_staff_details.h @@ -0,0 +1,54 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_STAFF_DETAILS_H_INCLUDED +#define MX_STAFF_DETAILS_H_INCLUDED + +#include +#include +#include +#include "mx_non_negative_decimal.h" +#include "mx_show_frets.h" +#include "mx_staff_number.h" +#include "mx_staff_tuning.h" +#include "mx_staff_type.h" +#include "mx_yes_no.h" + +/* + * The staff-details element is used to indicate different types of staves. The optional number + * attribute specifies the staff number from top to bottom on the system, as with clef. The + * print-object attribute is used to indicate when a staff is not printed in a part, usually in + * large scores where empty parts are omitted. It is yes by default. If print-spacing is yes while + * print-object is no, the score is printed in cutaway format where vertical space is left for the + * empty part. + */ +/* One child element of MxStaffDetails: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxStaffType *staff_type; + long *staff_lines; + MxStaffTuning *staff_tuning; + long *capo; + MxNonNegativeDecimal *staff_size; +} MxStaffDetailsChild; + +typedef struct { + bool has_number; + MxStaffNumber number; /* attribute number */ + bool has_show_frets; + MxShowFrets show_frets; /* attribute show-frets */ + bool has_print_object; + MxYesNo print_object; /* attribute print-object */ + bool has_print_spacing; + MxYesNo print_spacing; /* attribute print-spacing */ + MxStaffDetailsChild *children; /* child elements in document order */ + size_t children_count; +} MxStaffDetails; + +/* NULL on error; the message is in mx_error(). */ +MxStaffDetails *mx_staff_details_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_staff_details_serialize(const MxStaffDetails *m, xmlNodePtr parent, const char *tag); +void mx_staff_details_free(MxStaffDetails *m); + +#endif /* MX_STAFF_DETAILS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_staff_divide.c b/gen/test/c/mx/mx_staff_divide.c new file mode 100644 index 000000000..e7758c4cf --- /dev/null +++ b/gen/test/c/mx/mx_staff_divide.c @@ -0,0 +1,152 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_staff_divide.h" + +#include "mx_runtime.h" +#include +#include + +MxStaffDivide *mx_staff_divide_parse(xmlNodePtr el) { + MxStaffDivide *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_staff_divide_symbol_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_staff_divide_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_staff_divide_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_staff_divide_serialize(const MxStaffDivide *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_staff_divide_symbol_to_string(m->type)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_staff_divide_free(MxStaffDivide *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + free(m); +} diff --git a/gen/test/c/mx/mx_staff_divide.h b/gen/test/c/mx/mx_staff_divide.h new file mode 100644 index 000000000..77cf74216 --- /dev/null +++ b/gen/test/c/mx/mx_staff_divide.h @@ -0,0 +1,58 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_STAFF_DIVIDE_H_INCLUDED +#define MX_STAFF_DIVIDE_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_center_right.h" +#include "mx_staff_divide_symbol.h" +#include "mx_tenths.h" +#include "mx_valign.h" + +/* + * The staff-divide element represents the staff division arrow symbols found at SMuFL code points + * U+E00B, U+E00C, and U+E00D. + */ +typedef struct { + bool has_type; + MxStaffDivideSymbol type; /* attribute type */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ + bool has_id; + char *id; /* attribute id */ +} MxStaffDivide; + +/* NULL on error; the message is in mx_error(). */ +MxStaffDivide *mx_staff_divide_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_staff_divide_serialize(const MxStaffDivide *m, xmlNodePtr parent, const char *tag); +void mx_staff_divide_free(MxStaffDivide *m); + +#endif /* MX_STAFF_DIVIDE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_staff_layout.c b/gen/test/c/mx/mx_staff_layout.c new file mode 100644 index 000000000..281d28fb8 --- /dev/null +++ b/gen/test/c/mx/mx_staff_layout.c @@ -0,0 +1,96 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_staff_layout.h" + +#include "mx_runtime.h" +#include +#include + +MxStaffLayout *mx_staff_layout_parse(xmlNodePtr el) { + MxStaffLayout *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_staff_number_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_staff_layout_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxStaffLayoutChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "staff-distance") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->staff_distance = malloc(sizeof(*ch->staff_distance)); + if (!ch->staff_distance) + abort(); + *ch->staff_distance = mx_tenths_parse(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_staff_layout_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_staff_layout_serialize(const MxStaffLayout *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_number) { + char *s = mx_staff_number_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxStaffLayoutChild *ch = &m->children[i]; + if (ch->staff_distance) { + char *s = mx_tenths_to_string((*ch->staff_distance)); + xmlNewTextChild(el, NULL, BAD_CAST "staff-distance", BAD_CAST s); + free(s); + } + } + return el; +} + +void mx_staff_layout_free(MxStaffLayout *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxStaffLayoutChild *ch = &m->children[i]; + free(ch->staff_distance); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_staff_layout.h b/gen/test/c/mx/mx_staff_layout.h new file mode 100644 index 000000000..ece64f227 --- /dev/null +++ b/gen/test/c/mx/mx_staff_layout.h @@ -0,0 +1,39 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_STAFF_LAYOUT_H_INCLUDED +#define MX_STAFF_LAYOUT_H_INCLUDED + +#include +#include +#include +#include "mx_staff_number.h" +#include "mx_tenths.h" + +/* + * Staff layout includes the vertical distance from the bottom line of the previous staff in this + * system to the top line of the staff specified by the number attribute. The optional number + * attribute refers to staff numbers within the part, from top to bottom on the system. A value of 1 + * is assumed if not present. When used in the defaults element, the values apply to all parts. This + * value is ignored for the first staff in a system. + */ +/* One child element of MxStaffLayout: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxTenths *staff_distance; +} MxStaffLayoutChild; + +typedef struct { + bool has_number; + MxStaffNumber number; /* attribute number */ + MxStaffLayoutChild *children; /* child elements in document order */ + size_t children_count; +} MxStaffLayout; + +/* NULL on error; the message is in mx_error(). */ +MxStaffLayout *mx_staff_layout_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_staff_layout_serialize(const MxStaffLayout *m, xmlNodePtr parent, const char *tag); +void mx_staff_layout_free(MxStaffLayout *m); + +#endif /* MX_STAFF_LAYOUT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_staff_tuning.c b/gen/test/c/mx/mx_staff_tuning.c new file mode 100644 index 000000000..18e9afdff --- /dev/null +++ b/gen/test/c/mx/mx_staff_tuning.c @@ -0,0 +1,120 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_staff_tuning.h" + +#include "mx_runtime.h" +#include +#include + +MxStaffTuning *mx_staff_tuning_parse(xmlNodePtr el) { + MxStaffTuning *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "line") == 0) { + m->has_line = true; + m->line = mx_staff_line_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_staff_tuning_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxStaffTuningChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "tuning-step") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->tuning_step = malloc(sizeof(*ch->tuning_step)); + if (!ch->tuning_step) + abort(); + *ch->tuning_step = mx_step_parse(s); + xmlFree(text); + } else if (strcmp(tag, "tuning-alter") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->tuning_alter = malloc(sizeof(*ch->tuning_alter)); + if (!ch->tuning_alter) + abort(); + *ch->tuning_alter = mx_semitones_parse(s); + xmlFree(text); + } else if (strcmp(tag, "tuning-octave") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->tuning_octave = malloc(sizeof(*ch->tuning_octave)); + if (!ch->tuning_octave) + abort(); + *ch->tuning_octave = mx_octave_parse(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_staff_tuning_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_staff_tuning_serialize(const MxStaffTuning *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_line) { + char *s = mx_staff_line_to_string(m->line); + xmlSetProp(el, BAD_CAST "line", BAD_CAST s); + free(s); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxStaffTuningChild *ch = &m->children[i]; + if (ch->tuning_step) { + xmlNewTextChild(el, NULL, BAD_CAST "tuning-step", BAD_CAST mx_step_to_string((*ch->tuning_step))); + } else if (ch->tuning_alter) { + char *s = mx_semitones_to_string((*ch->tuning_alter)); + xmlNewTextChild(el, NULL, BAD_CAST "tuning-alter", BAD_CAST s); + free(s); + } else if (ch->tuning_octave) { + char *s = mx_octave_to_string((*ch->tuning_octave)); + xmlNewTextChild(el, NULL, BAD_CAST "tuning-octave", BAD_CAST s); + free(s); + } + } + return el; +} + +void mx_staff_tuning_free(MxStaffTuning *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxStaffTuningChild *ch = &m->children[i]; + free(ch->tuning_step); + free(ch->tuning_alter); + free(ch->tuning_octave); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_staff_tuning.h b/gen/test/c/mx/mx_staff_tuning.h new file mode 100644 index 000000000..3b02ce60e --- /dev/null +++ b/gen/test/c/mx/mx_staff_tuning.h @@ -0,0 +1,39 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_STAFF_TUNING_H_INCLUDED +#define MX_STAFF_TUNING_H_INCLUDED + +#include +#include +#include +#include "mx_octave.h" +#include "mx_semitones.h" +#include "mx_staff_line.h" +#include "mx_step.h" + +/* + * The staff-tuning type specifies the open, non-capo tuning of the lines on a tablature staff. + */ +/* One child element of MxStaffTuning: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxStep *tuning_step; + MxSemitones *tuning_alter; + MxOctave *tuning_octave; +} MxStaffTuningChild; + +typedef struct { + bool has_line; + MxStaffLine line; /* attribute line */ + MxStaffTuningChild *children; /* child elements in document order */ + size_t children_count; +} MxStaffTuning; + +/* NULL on error; the message is in mx_error(). */ +MxStaffTuning *mx_staff_tuning_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_staff_tuning_serialize(const MxStaffTuning *m, xmlNodePtr parent, const char *tag); +void mx_staff_tuning_free(MxStaffTuning *m); + +#endif /* MX_STAFF_TUNING_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_stem.c b/gen/test/c/mx/mx_stem.c new file mode 100644 index 000000000..98caf789b --- /dev/null +++ b/gen/test/c/mx/mx_stem.c @@ -0,0 +1,103 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_stem.h" + +#include "mx_runtime.h" +#include +#include + +MxStem *mx_stem_parse(xmlNodePtr el) { + MxStem *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_stem_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_stem_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_stem_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_stem_serialize(const MxStem *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_stem_value_to_string(m->value))); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_stem_free(MxStem *m) { + if (!m) + return; + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_stem.h b/gen/test/c/mx/mx_stem.h new file mode 100644 index 000000000..b0cc013a5 --- /dev/null +++ b/gen/test/c/mx/mx_stem.h @@ -0,0 +1,40 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_STEM_H_INCLUDED +#define MX_STEM_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_stem_value.h" +#include "mx_tenths.h" + +/* + * Stems can be down, up, none, or double. For down and up stems, the position attributes can be + * used to specify stem length. The relative values specify the end of the stem relative to the + * program default. Default values specify an absolute end stem position. Negative values of + * relative-y that would flip a stem instead of shortening it are ignored. A stem element associated + * with a rest refers to a stemlet. + */ +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_color; + MxColor color; /* attribute color */ + MxStemValue value; /* text content */ +} MxStem; + +/* NULL on error; the message is in mx_error(). */ +MxStem *mx_stem_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_stem_serialize(const MxStem *m, xmlNodePtr parent, const char *tag); +void mx_stem_free(MxStem *m); + +#endif /* MX_STEM_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_stick.c b/gen/test/c/mx/mx_stick.c new file mode 100644 index 000000000..3912ccea6 --- /dev/null +++ b/gen/test/c/mx/mx_stick.c @@ -0,0 +1,115 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_stick.h" + +#include "mx_runtime.h" +#include +#include + +MxStick *mx_stick_parse(xmlNodePtr el) { + MxStick *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "tip") == 0) { + m->has_tip = true; + m->tip = mx_tip_direction_parse(s); + } else if (strcmp(aname, "parentheses") == 0) { + m->has_parentheses = true; + m->parentheses = mx_yes_no_parse(s); + } else if (strcmp(aname, "dashed-circle") == 0) { + m->has_dashed_circle = true; + m->dashed_circle = mx_yes_no_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_stick_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxStickChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "stick-type") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->stick_type = malloc(sizeof(*ch->stick_type)); + if (!ch->stick_type) + abort(); + *ch->stick_type = mx_stick_type_parse(s); + xmlFree(text); + } else if (strcmp(tag, "stick-material") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->stick_material = malloc(sizeof(*ch->stick_material)); + if (!ch->stick_material) + abort(); + *ch->stick_material = mx_stick_material_parse(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_stick_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_stick_serialize(const MxStick *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_tip) { + xmlSetProp(el, BAD_CAST "tip", BAD_CAST mx_tip_direction_to_string(m->tip)); + } + if (m->has_parentheses) { + xmlSetProp(el, BAD_CAST "parentheses", BAD_CAST mx_yes_no_to_string(m->parentheses)); + } + if (m->has_dashed_circle) { + xmlSetProp(el, BAD_CAST "dashed-circle", BAD_CAST mx_yes_no_to_string(m->dashed_circle)); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxStickChild *ch = &m->children[i]; + if (ch->stick_type) { + xmlNewTextChild(el, NULL, BAD_CAST "stick-type", BAD_CAST mx_stick_type_to_string((*ch->stick_type))); + } else if (ch->stick_material) { + xmlNewTextChild(el, NULL, BAD_CAST "stick-material", BAD_CAST mx_stick_material_to_string((*ch->stick_material))); + } + } + return el; +} + +void mx_stick_free(MxStick *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxStickChild *ch = &m->children[i]; + free(ch->stick_type); + free(ch->stick_material); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_stick.h b/gen/test/c/mx/mx_stick.h new file mode 100644 index 000000000..38ec5fdca --- /dev/null +++ b/gen/test/c/mx/mx_stick.h @@ -0,0 +1,44 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_STICK_H_INCLUDED +#define MX_STICK_H_INCLUDED + +#include +#include +#include +#include "mx_stick_material.h" +#include "mx_stick_type.h" +#include "mx_tip_direction.h" +#include "mx_yes_no.h" + +/* + * The stick type represents pictograms where the material of the stick, mallet, or beater is + * included.The parentheses and dashed-circle attributes indicate the presence of these marks around + * the round beater part of a pictogram. Values for these attributes are "no" if not present. + */ +/* One child element of MxStick: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxStickType *stick_type; + MxStickMaterial *stick_material; +} MxStickChild; + +typedef struct { + bool has_tip; + MxTipDirection tip; /* attribute tip */ + bool has_parentheses; + MxYesNo parentheses; /* attribute parentheses */ + bool has_dashed_circle; + MxYesNo dashed_circle; /* attribute dashed-circle */ + MxStickChild *children; /* child elements in document order */ + size_t children_count; +} MxStick; + +/* NULL on error; the message is in mx_error(). */ +MxStick *mx_stick_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_stick_serialize(const MxStick *m, xmlNodePtr parent, const char *tag); +void mx_stick_free(MxStick *m); + +#endif /* MX_STICK_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_string.c b/gen/test/c/mx/mx_string.c new file mode 100644 index 000000000..447ba6bd8 --- /dev/null +++ b/gen/test/c/mx/mx_string.c @@ -0,0 +1,143 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_string.h" + +#include "mx_runtime.h" +#include +#include + +MxString *mx_string_parse(xmlNodePtr el) { + MxString *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_string_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_string_number_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_string_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_string_serialize(const MxString *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + { + char *s = mx_string_number_to_string(m->value); + xmlAddChild(el, xmlNewText(BAD_CAST s)); + free(s); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + return el; +} + +void mx_string_free(MxString *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_string.h b/gen/test/c/mx/mx_string.h new file mode 100644 index 000000000..60caa53dd --- /dev/null +++ b/gen/test/c/mx/mx_string.h @@ -0,0 +1,52 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_STRING_H_INCLUDED +#define MX_STRING_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_string_number.h" +#include "mx_tenths.h" + +/* + * The string type is used with tablature notation, regular notation (where it is often circled), + * and chord diagrams. String numbers start with 1 for the highest pitched full-length string. + */ +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + MxStringNumber value; /* text content */ +} MxString; + +/* NULL on error; the message is in mx_error(). */ +MxString *mx_string_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_string_serialize(const MxString *m, xmlNodePtr parent, const char *tag); +void mx_string_free(MxString *m); + +#endif /* MX_STRING_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_string_mute.c b/gen/test/c/mx/mx_string_mute.c new file mode 100644 index 000000000..d88df87c5 --- /dev/null +++ b/gen/test/c/mx/mx_string_mute.c @@ -0,0 +1,152 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_string_mute.h" + +#include "mx_runtime.h" +#include +#include + +MxStringMute *mx_string_mute_parse(xmlNodePtr el) { + MxStringMute *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_on_off_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_string_mute_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_string_mute_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_string_mute_serialize(const MxStringMute *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_on_off_to_string(m->type)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_string_mute_free(MxStringMute *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + free(m); +} diff --git a/gen/test/c/mx/mx_string_mute.h b/gen/test/c/mx/mx_string_mute.h new file mode 100644 index 000000000..eb66d5890 --- /dev/null +++ b/gen/test/c/mx/mx_string_mute.h @@ -0,0 +1,57 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_STRING_MUTE_H_INCLUDED +#define MX_STRING_MUTE_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_left_center_right.h" +#include "mx_on_off.h" +#include "mx_tenths.h" +#include "mx_valign.h" + +/* + * The string-mute type represents string mute on and mute off symbols. + */ +typedef struct { + bool has_type; + MxOnOff type; /* attribute type */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ + bool has_id; + char *id; /* attribute id */ +} MxStringMute; + +/* NULL on error; the message is in mx_error(). */ +MxStringMute *mx_string_mute_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_string_mute_serialize(const MxStringMute *m, xmlNodePtr parent, const char *tag); +void mx_string_mute_free(MxStringMute *m); + +#endif /* MX_STRING_MUTE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_strong_accent.c b/gen/test/c/mx/mx_strong_accent.c new file mode 100644 index 000000000..2edd81cb6 --- /dev/null +++ b/gen/test/c/mx/mx_strong_accent.c @@ -0,0 +1,138 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_strong_accent.h" + +#include "mx_runtime.h" +#include +#include + +MxStrongAccent *mx_strong_accent_parse(xmlNodePtr el) { + MxStrongAccent *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_up_down_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_strong_accent_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_strong_accent_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_strong_accent_serialize(const MxStrongAccent *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_up_down_to_string(m->type)); + } + return el; +} + +void mx_strong_accent_free(MxStrongAccent *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_strong_accent.h b/gen/test/c/mx/mx_strong_accent.h new file mode 100644 index 000000000..9733f501d --- /dev/null +++ b/gen/test/c/mx/mx_strong_accent.h @@ -0,0 +1,54 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_STRONG_ACCENT_H_INCLUDED +#define MX_STRONG_ACCENT_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_empty_placement.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_tenths.h" +#include "mx_up_down.h" + +/* + * The strong-accent type indicates a vertical accent mark. The type attribute indicates if the + * point of the accent is down or up. + */ +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_type; + MxUpDown type; /* attribute type */ +} MxStrongAccent; + +/* NULL on error; the message is in mx_error(). */ +MxStrongAccent *mx_strong_accent_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_strong_accent_serialize(const MxStrongAccent *m, xmlNodePtr parent, const char *tag); +void mx_strong_accent_free(MxStrongAccent *m); + +#endif /* MX_STRONG_ACCENT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_style_text.c b/gen/test/c/mx/mx_style_text.c new file mode 100644 index 000000000..181fcf665 --- /dev/null +++ b/gen/test/c/mx/mx_style_text.c @@ -0,0 +1,134 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_style_text.h" + +#include "mx_runtime.h" +#include +#include + +MxStyleText *mx_style_text_parse(xmlNodePtr el) { + MxStyleText *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_style_text_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_style_text_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_style_text_serialize(const MxStyleText *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_style_text_free(MxStyleText *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_style_text.h b/gen/test/c/mx/mx_style_text.h new file mode 100644 index 000000000..393f40b26 --- /dev/null +++ b/gen/test/c/mx/mx_style_text.h @@ -0,0 +1,47 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_STYLE_TEXT_H_INCLUDED +#define MX_STYLE_TEXT_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_tenths.h" + +/* + * The style-text type represents a text element with a print-style attribute group. + */ +typedef struct { + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + char *value; /* text content */ +} MxStyleText; + +/* NULL on error; the message is in mx_error(). */ +MxStyleText *mx_style_text_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_style_text_serialize(const MxStyleText *m, xmlNodePtr parent, const char *tag); +void mx_style_text_free(MxStyleText *m); + +#endif /* MX_STYLE_TEXT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_supports.c b/gen/test/c/mx/mx_supports.c new file mode 100644 index 000000000..f68e19be0 --- /dev/null +++ b/gen/test/c/mx/mx_supports.c @@ -0,0 +1,86 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_supports.h" + +#include "mx_runtime.h" +#include +#include + +MxSupports *mx_supports_parse(xmlNodePtr el) { + MxSupports *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_yes_no_parse(s); + } else if (strcmp(aname, "element") == 0) { + m->has_element = true; + m->element = mx_strdup(s); + } else if (strcmp(aname, "attribute") == 0) { + m->has_attribute = true; + m->attribute = mx_strdup(s); + } else if (strcmp(aname, "value") == 0) { + m->has_value = true; + m->value = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_supports_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_supports_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_supports_serialize(const MxSupports *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_yes_no_to_string(m->type)); + } + if (m->has_element) { + xmlSetProp(el, BAD_CAST "element", BAD_CAST m->element); + } + if (m->has_attribute) { + xmlSetProp(el, BAD_CAST "attribute", BAD_CAST m->attribute); + } + if (m->has_value) { + xmlSetProp(el, BAD_CAST "value", BAD_CAST m->value); + } + return el; +} + +void mx_supports_free(MxSupports *m) { + if (!m) + return; + if (m->has_element) + free(m->element); + if (m->has_attribute) + free(m->attribute); + if (m->has_value) + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_supports.h b/gen/test/c/mx/mx_supports.h new file mode 100644 index 000000000..6dfb42d19 --- /dev/null +++ b/gen/test/c/mx/mx_supports.h @@ -0,0 +1,36 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SUPPORTS_H_INCLUDED +#define MX_SUPPORTS_H_INCLUDED + +#include +#include +#include +#include "mx_yes_no.h" + +/* + * The supports type indicates if a MusicXML encoding supports a particular MusicXML element. This + * is recommended for elements like beam, stem, and accidental, where the absence of an element is + * ambiguous if you do not know if the encoding supports that element. For Version 2.0, the supports + * element is expanded to allow programs to indicate support for particular attributes or particular + * values. This lets applications communicate, for example, that all system and/or page breaks are + * contained in the MusicXML file. + */ +typedef struct { + bool has_type; + MxYesNo type; /* attribute type */ + bool has_element; + char *element; /* attribute element */ + bool has_attribute; + char *attribute; /* attribute attribute */ + bool has_value; + char *value; /* attribute value */ +} MxSupports; + +/* NULL on error; the message is in mx_error(). */ +MxSupports *mx_supports_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_supports_serialize(const MxSupports *m, xmlNodePtr parent, const char *tag); +void mx_supports_free(MxSupports *m); + +#endif /* MX_SUPPORTS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_system_dividers.c b/gen/test/c/mx/mx_system_dividers.c new file mode 100644 index 000000000..efbab7aa1 --- /dev/null +++ b/gen/test/c/mx/mx_system_dividers.c @@ -0,0 +1,95 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_system_dividers.h" + +#include "mx_runtime.h" +#include +#include + +MxSystemDividers *mx_system_dividers_parse(xmlNodePtr el) { + MxSystemDividers *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_system_dividers_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxSystemDividersChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "left-divider") == 0) { + ch->left_divider = mx_empty_print_object_style_align_parse(c); + if (!ch->left_divider) { + mx_system_dividers_free(m); + return NULL; + } + } else if (strcmp(tag, "right-divider") == 0) { + ch->right_divider = mx_empty_print_object_style_align_parse(c); + if (!ch->right_divider) { + mx_system_dividers_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_system_dividers_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_system_dividers_serialize(const MxSystemDividers *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxSystemDividersChild *ch = &m->children[i]; + if (ch->left_divider) { + mx_empty_print_object_style_align_serialize(ch->left_divider, el, "left-divider"); + } else if (ch->right_divider) { + mx_empty_print_object_style_align_serialize(ch->right_divider, el, "right-divider"); + } + } + return el; +} + +void mx_system_dividers_free(MxSystemDividers *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxSystemDividersChild *ch = &m->children[i]; + if (ch->left_divider) + mx_empty_print_object_style_align_free(ch->left_divider); + if (ch->right_divider) + mx_empty_print_object_style_align_free(ch->right_divider); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_system_dividers.h b/gen/test/c/mx/mx_system_dividers.h new file mode 100644 index 000000000..0e149fe82 --- /dev/null +++ b/gen/test/c/mx/mx_system_dividers.h @@ -0,0 +1,39 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SYSTEM_DIVIDERS_H_INCLUDED +#define MX_SYSTEM_DIVIDERS_H_INCLUDED + +#include +#include +#include +#include "mx_empty_print_object_style_align.h" + +/* + * The system-dividers element indicates the presence or absence of system dividers (also known as + * system separation marks) between systems displayed on the same page. Dividers on the left and + * right side of the page are controlled by the left-divider and right-divider elements + * respectively. The default vertical position is half the system-distance value from the top of the + * system that is below the divider. The default horizontal position is the left and right system + * margin, respectively. When used in the print element, the system-dividers element affects the + * dividers that would appear between the current system and the previous system. + */ +/* One child element of MxSystemDividers: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxEmptyPrintObjectStyleAlign *left_divider; + MxEmptyPrintObjectStyleAlign *right_divider; +} MxSystemDividersChild; + +typedef struct { + MxSystemDividersChild *children; /* child elements in document order */ + size_t children_count; +} MxSystemDividers; + +/* NULL on error; the message is in mx_error(). */ +MxSystemDividers *mx_system_dividers_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_system_dividers_serialize(const MxSystemDividers *m, xmlNodePtr parent, const char *tag); +void mx_system_dividers_free(MxSystemDividers *m); + +#endif /* MX_SYSTEM_DIVIDERS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_system_layout.c b/gen/test/c/mx/mx_system_layout.c new file mode 100644 index 000000000..479e65286 --- /dev/null +++ b/gen/test/c/mx/mx_system_layout.c @@ -0,0 +1,121 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_system_layout.h" + +#include "mx_runtime.h" +#include +#include + +MxSystemLayout *mx_system_layout_parse(xmlNodePtr el) { + MxSystemLayout *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_system_layout_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxSystemLayoutChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "system-margins") == 0) { + ch->system_margins = mx_system_margins_parse(c); + if (!ch->system_margins) { + mx_system_layout_free(m); + return NULL; + } + } else if (strcmp(tag, "system-distance") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->system_distance = malloc(sizeof(*ch->system_distance)); + if (!ch->system_distance) + abort(); + *ch->system_distance = mx_tenths_parse(s); + xmlFree(text); + } else if (strcmp(tag, "top-system-distance") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->top_system_distance = malloc(sizeof(*ch->top_system_distance)); + if (!ch->top_system_distance) + abort(); + *ch->top_system_distance = mx_tenths_parse(s); + xmlFree(text); + } else if (strcmp(tag, "system-dividers") == 0) { + ch->system_dividers = mx_system_dividers_parse(c); + if (!ch->system_dividers) { + mx_system_layout_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_system_layout_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_system_layout_serialize(const MxSystemLayout *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxSystemLayoutChild *ch = &m->children[i]; + if (ch->system_margins) { + mx_system_margins_serialize(ch->system_margins, el, "system-margins"); + } else if (ch->system_distance) { + char *s = mx_tenths_to_string((*ch->system_distance)); + xmlNewTextChild(el, NULL, BAD_CAST "system-distance", BAD_CAST s); + free(s); + } else if (ch->top_system_distance) { + char *s = mx_tenths_to_string((*ch->top_system_distance)); + xmlNewTextChild(el, NULL, BAD_CAST "top-system-distance", BAD_CAST s); + free(s); + } else if (ch->system_dividers) { + mx_system_dividers_serialize(ch->system_dividers, el, "system-dividers"); + } + } + return el; +} + +void mx_system_layout_free(MxSystemLayout *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxSystemLayoutChild *ch = &m->children[i]; + if (ch->system_margins) + mx_system_margins_free(ch->system_margins); + free(ch->system_distance); + free(ch->top_system_distance); + if (ch->system_dividers) + mx_system_dividers_free(ch->system_dividers); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_system_layout.h b/gen/test/c/mx/mx_system_layout.h new file mode 100644 index 000000000..58948c808 --- /dev/null +++ b/gen/test/c/mx/mx_system_layout.h @@ -0,0 +1,46 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SYSTEM_LAYOUT_H_INCLUDED +#define MX_SYSTEM_LAYOUT_H_INCLUDED + +#include +#include +#include +#include "mx_system_dividers.h" +#include "mx_system_margins.h" +#include "mx_tenths.h" + +/* + * A system is a group of staves that are read and played simultaneously. System layout includes + * left and right margins and the vertical distance from the previous system. The system distance is + * measured from the bottom line of the previous system to the top line of the current system. It is + * ignored for the first system on a page. The top system distance is measured from the page's top + * margin to the top line of the first system. It is ignored for all but the first system on a page. + * Sometimes the sum of measure widths in a system may not equal the system width specified by the + * layout elements due to roundoff or other errors. The behavior when reading MusicXML files in + * these cases is application-dependent. For instance, applications may find that the system layout + * data is more reliable than the sum of the measure widths, and adjust the measure widths + * accordingly. + */ +/* One child element of MxSystemLayout: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxSystemMargins *system_margins; + MxTenths *system_distance; + MxTenths *top_system_distance; + MxSystemDividers *system_dividers; +} MxSystemLayoutChild; + +typedef struct { + MxSystemLayoutChild *children; /* child elements in document order */ + size_t children_count; +} MxSystemLayout; + +/* NULL on error; the message is in mx_error(). */ +MxSystemLayout *mx_system_layout_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_system_layout_serialize(const MxSystemLayout *m, xmlNodePtr parent, const char *tag); +void mx_system_layout_free(MxSystemLayout *m); + +#endif /* MX_SYSTEM_LAYOUT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_system_margins.c b/gen/test/c/mx/mx_system_margins.c new file mode 100644 index 000000000..f1529ffb3 --- /dev/null +++ b/gen/test/c/mx/mx_system_margins.c @@ -0,0 +1,101 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_system_margins.h" + +#include "mx_runtime.h" +#include +#include + +MxSystemMargins *mx_system_margins_parse(xmlNodePtr el) { + MxSystemMargins *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_system_margins_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxSystemMarginsChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "left-margin") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->left_margin = malloc(sizeof(*ch->left_margin)); + if (!ch->left_margin) + abort(); + *ch->left_margin = mx_tenths_parse(s); + xmlFree(text); + } else if (strcmp(tag, "right-margin") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->right_margin = malloc(sizeof(*ch->right_margin)); + if (!ch->right_margin) + abort(); + *ch->right_margin = mx_tenths_parse(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_system_margins_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_system_margins_serialize(const MxSystemMargins *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxSystemMarginsChild *ch = &m->children[i]; + if (ch->left_margin) { + char *s = mx_tenths_to_string((*ch->left_margin)); + xmlNewTextChild(el, NULL, BAD_CAST "left-margin", BAD_CAST s); + free(s); + } else if (ch->right_margin) { + char *s = mx_tenths_to_string((*ch->right_margin)); + xmlNewTextChild(el, NULL, BAD_CAST "right-margin", BAD_CAST s); + free(s); + } + } + return el; +} + +void mx_system_margins_free(MxSystemMargins *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxSystemMarginsChild *ch = &m->children[i]; + free(ch->left_margin); + free(ch->right_margin); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_system_margins.h b/gen/test/c/mx/mx_system_margins.h new file mode 100644 index 000000000..0fa1a20ec --- /dev/null +++ b/gen/test/c/mx/mx_system_margins.h @@ -0,0 +1,34 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_SYSTEM_MARGINS_H_INCLUDED +#define MX_SYSTEM_MARGINS_H_INCLUDED + +#include +#include +#include +#include "mx_tenths.h" + +/* + * System margins are relative to the page margins. Positive values indent and negative values + * reduce the margin size. + */ +/* One child element of MxSystemMargins: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxTenths *left_margin; + MxTenths *right_margin; +} MxSystemMarginsChild; + +typedef struct { + MxSystemMarginsChild *children; /* child elements in document order */ + size_t children_count; +} MxSystemMargins; + +/* NULL on error; the message is in mx_error(). */ +MxSystemMargins *mx_system_margins_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_system_margins_serialize(const MxSystemMargins *m, xmlNodePtr parent, const char *tag); +void mx_system_margins_free(MxSystemMargins *m); + +#endif /* MX_SYSTEM_MARGINS_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_tap.c b/gen/test/c/mx/mx_tap.c new file mode 100644 index 000000000..ee30f21ae --- /dev/null +++ b/gen/test/c/mx/mx_tap.c @@ -0,0 +1,146 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_tap.h" + +#include "mx_runtime.h" +#include +#include + +MxTap *mx_tap_parse(xmlNodePtr el) { + MxTap *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "hand") == 0) { + m->has_hand = true; + m->hand = mx_tap_hand_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_tap_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_tap_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_tap_serialize(const MxTap *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_hand) { + xmlSetProp(el, BAD_CAST "hand", BAD_CAST mx_tap_hand_to_string(m->hand)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + return el; +} + +void mx_tap_free(MxTap *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_tap.h b/gen/test/c/mx/mx_tap.h new file mode 100644 index 000000000..fde04ba3a --- /dev/null +++ b/gen/test/c/mx/mx_tap.h @@ -0,0 +1,57 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TAP_H_INCLUDED +#define MX_TAP_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_tap_hand.h" +#include "mx_tenths.h" + +/* + * The tap type indicates a tap on the fretboard. The text content allows specification of the + * notation; + and T are common choices. If the element is empty, the hand attribute is used to + * specify the symbol to use. The hand attribute is ignored if the tap glyph is already specified by + * the text content. If neither text content nor the hand attribute are present, the display is + * application-specific. + */ +typedef struct { + bool has_hand; + MxTapHand hand; /* attribute hand */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + char *value; /* text content */ +} MxTap; + +/* NULL on error; the message is in mx_error(). */ +MxTap *mx_tap_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_tap_serialize(const MxTap *m, xmlNodePtr parent, const char *tag); +void mx_tap_free(MxTap *m); + +#endif /* MX_TAP_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_technical.c b/gen/test/c/mx/mx_technical.c new file mode 100644 index 000000000..3079b5fdd --- /dev/null +++ b/gen/test/c/mx/mx_technical.c @@ -0,0 +1,393 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_technical.h" + +#include "mx_runtime.h" +#include +#include + +MxTechnical *mx_technical_parse(xmlNodePtr el) { + MxTechnical *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_technical_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxTechnicalChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "up-bow") == 0) { + ch->up_bow = mx_empty_placement_parse(c); + if (!ch->up_bow) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "down-bow") == 0) { + ch->down_bow = mx_empty_placement_parse(c); + if (!ch->down_bow) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "harmonic") == 0) { + ch->harmonic = mx_harmonic_parse(c); + if (!ch->harmonic) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "open-string") == 0) { + ch->open_string = mx_empty_placement_parse(c); + if (!ch->open_string) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "thumb-position") == 0) { + ch->thumb_position = mx_empty_placement_parse(c); + if (!ch->thumb_position) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "fingering") == 0) { + ch->fingering = mx_fingering_parse(c); + if (!ch->fingering) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "pluck") == 0) { + ch->pluck = mx_placement_text_parse(c); + if (!ch->pluck) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "double-tongue") == 0) { + ch->double_tongue = mx_empty_placement_parse(c); + if (!ch->double_tongue) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "triple-tongue") == 0) { + ch->triple_tongue = mx_empty_placement_parse(c); + if (!ch->triple_tongue) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "stopped") == 0) { + ch->stopped = mx_empty_placement_smufl_parse(c); + if (!ch->stopped) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "snap-pizzicato") == 0) { + ch->snap_pizzicato = mx_empty_placement_parse(c); + if (!ch->snap_pizzicato) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "fret") == 0) { + ch->fret = mx_fret_parse(c); + if (!ch->fret) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "string") == 0) { + ch->string = mx_string_parse(c); + if (!ch->string) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "hammer-on") == 0) { + ch->hammer_on = mx_hammer_on_pull_off_parse(c); + if (!ch->hammer_on) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "pull-off") == 0) { + ch->pull_off = mx_hammer_on_pull_off_parse(c); + if (!ch->pull_off) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "bend") == 0) { + ch->bend = mx_bend_parse(c); + if (!ch->bend) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "tap") == 0) { + ch->tap = mx_tap_parse(c); + if (!ch->tap) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "heel") == 0) { + ch->heel = mx_heel_toe_parse(c); + if (!ch->heel) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "toe") == 0) { + ch->toe = mx_heel_toe_parse(c); + if (!ch->toe) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "fingernails") == 0) { + ch->fingernails = mx_empty_placement_parse(c); + if (!ch->fingernails) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "hole") == 0) { + ch->hole = mx_hole_parse(c); + if (!ch->hole) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "arrow") == 0) { + ch->arrow = mx_arrow_parse(c); + if (!ch->arrow) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "handbell") == 0) { + ch->handbell = mx_handbell_parse(c); + if (!ch->handbell) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "brass-bend") == 0) { + ch->brass_bend = mx_empty_placement_parse(c); + if (!ch->brass_bend) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "flip") == 0) { + ch->flip = mx_empty_placement_parse(c); + if (!ch->flip) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "smear") == 0) { + ch->smear = mx_empty_placement_parse(c); + if (!ch->smear) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "open") == 0) { + ch->open = mx_empty_placement_smufl_parse(c); + if (!ch->open) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "half-muted") == 0) { + ch->half_muted = mx_empty_placement_smufl_parse(c); + if (!ch->half_muted) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "harmon-mute") == 0) { + ch->harmon_mute = mx_harmon_mute_parse(c); + if (!ch->harmon_mute) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "golpe") == 0) { + ch->golpe = mx_empty_placement_parse(c); + if (!ch->golpe) { + mx_technical_free(m); + return NULL; + } + } else if (strcmp(tag, "other-technical") == 0) { + ch->other_technical = mx_other_placement_text_parse(c); + if (!ch->other_technical) { + mx_technical_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_technical_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_technical_serialize(const MxTechnical *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxTechnicalChild *ch = &m->children[i]; + if (ch->up_bow) { + mx_empty_placement_serialize(ch->up_bow, el, "up-bow"); + } else if (ch->down_bow) { + mx_empty_placement_serialize(ch->down_bow, el, "down-bow"); + } else if (ch->harmonic) { + mx_harmonic_serialize(ch->harmonic, el, "harmonic"); + } else if (ch->open_string) { + mx_empty_placement_serialize(ch->open_string, el, "open-string"); + } else if (ch->thumb_position) { + mx_empty_placement_serialize(ch->thumb_position, el, "thumb-position"); + } else if (ch->fingering) { + mx_fingering_serialize(ch->fingering, el, "fingering"); + } else if (ch->pluck) { + mx_placement_text_serialize(ch->pluck, el, "pluck"); + } else if (ch->double_tongue) { + mx_empty_placement_serialize(ch->double_tongue, el, "double-tongue"); + } else if (ch->triple_tongue) { + mx_empty_placement_serialize(ch->triple_tongue, el, "triple-tongue"); + } else if (ch->stopped) { + mx_empty_placement_smufl_serialize(ch->stopped, el, "stopped"); + } else if (ch->snap_pizzicato) { + mx_empty_placement_serialize(ch->snap_pizzicato, el, "snap-pizzicato"); + } else if (ch->fret) { + mx_fret_serialize(ch->fret, el, "fret"); + } else if (ch->string) { + mx_string_serialize(ch->string, el, "string"); + } else if (ch->hammer_on) { + mx_hammer_on_pull_off_serialize(ch->hammer_on, el, "hammer-on"); + } else if (ch->pull_off) { + mx_hammer_on_pull_off_serialize(ch->pull_off, el, "pull-off"); + } else if (ch->bend) { + mx_bend_serialize(ch->bend, el, "bend"); + } else if (ch->tap) { + mx_tap_serialize(ch->tap, el, "tap"); + } else if (ch->heel) { + mx_heel_toe_serialize(ch->heel, el, "heel"); + } else if (ch->toe) { + mx_heel_toe_serialize(ch->toe, el, "toe"); + } else if (ch->fingernails) { + mx_empty_placement_serialize(ch->fingernails, el, "fingernails"); + } else if (ch->hole) { + mx_hole_serialize(ch->hole, el, "hole"); + } else if (ch->arrow) { + mx_arrow_serialize(ch->arrow, el, "arrow"); + } else if (ch->handbell) { + mx_handbell_serialize(ch->handbell, el, "handbell"); + } else if (ch->brass_bend) { + mx_empty_placement_serialize(ch->brass_bend, el, "brass-bend"); + } else if (ch->flip) { + mx_empty_placement_serialize(ch->flip, el, "flip"); + } else if (ch->smear) { + mx_empty_placement_serialize(ch->smear, el, "smear"); + } else if (ch->open) { + mx_empty_placement_smufl_serialize(ch->open, el, "open"); + } else if (ch->half_muted) { + mx_empty_placement_smufl_serialize(ch->half_muted, el, "half-muted"); + } else if (ch->harmon_mute) { + mx_harmon_mute_serialize(ch->harmon_mute, el, "harmon-mute"); + } else if (ch->golpe) { + mx_empty_placement_serialize(ch->golpe, el, "golpe"); + } else if (ch->other_technical) { + mx_other_placement_text_serialize(ch->other_technical, el, "other-technical"); + } + } + return el; +} + +void mx_technical_free(MxTechnical *m) { + if (!m) + return; + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxTechnicalChild *ch = &m->children[i]; + if (ch->up_bow) + mx_empty_placement_free(ch->up_bow); + if (ch->down_bow) + mx_empty_placement_free(ch->down_bow); + if (ch->harmonic) + mx_harmonic_free(ch->harmonic); + if (ch->open_string) + mx_empty_placement_free(ch->open_string); + if (ch->thumb_position) + mx_empty_placement_free(ch->thumb_position); + if (ch->fingering) + mx_fingering_free(ch->fingering); + if (ch->pluck) + mx_placement_text_free(ch->pluck); + if (ch->double_tongue) + mx_empty_placement_free(ch->double_tongue); + if (ch->triple_tongue) + mx_empty_placement_free(ch->triple_tongue); + if (ch->stopped) + mx_empty_placement_smufl_free(ch->stopped); + if (ch->snap_pizzicato) + mx_empty_placement_free(ch->snap_pizzicato); + if (ch->fret) + mx_fret_free(ch->fret); + if (ch->string) + mx_string_free(ch->string); + if (ch->hammer_on) + mx_hammer_on_pull_off_free(ch->hammer_on); + if (ch->pull_off) + mx_hammer_on_pull_off_free(ch->pull_off); + if (ch->bend) + mx_bend_free(ch->bend); + if (ch->tap) + mx_tap_free(ch->tap); + if (ch->heel) + mx_heel_toe_free(ch->heel); + if (ch->toe) + mx_heel_toe_free(ch->toe); + if (ch->fingernails) + mx_empty_placement_free(ch->fingernails); + if (ch->hole) + mx_hole_free(ch->hole); + if (ch->arrow) + mx_arrow_free(ch->arrow); + if (ch->handbell) + mx_handbell_free(ch->handbell); + if (ch->brass_bend) + mx_empty_placement_free(ch->brass_bend); + if (ch->flip) + mx_empty_placement_free(ch->flip); + if (ch->smear) + mx_empty_placement_free(ch->smear); + if (ch->open) + mx_empty_placement_smufl_free(ch->open); + if (ch->half_muted) + mx_empty_placement_smufl_free(ch->half_muted); + if (ch->harmon_mute) + mx_harmon_mute_free(ch->harmon_mute); + if (ch->golpe) + mx_empty_placement_free(ch->golpe); + if (ch->other_technical) + mx_other_placement_text_free(ch->other_technical); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_technical.h b/gen/test/c/mx/mx_technical.h new file mode 100644 index 000000000..eb3be8823 --- /dev/null +++ b/gen/test/c/mx/mx_technical.h @@ -0,0 +1,79 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TECHNICAL_H_INCLUDED +#define MX_TECHNICAL_H_INCLUDED + +#include +#include +#include +#include "mx_arrow.h" +#include "mx_bend.h" +#include "mx_empty_placement.h" +#include "mx_empty_placement_smufl.h" +#include "mx_fingering.h" +#include "mx_fret.h" +#include "mx_hammer_on_pull_off.h" +#include "mx_handbell.h" +#include "mx_harmon_mute.h" +#include "mx_harmonic.h" +#include "mx_heel_toe.h" +#include "mx_hole.h" +#include "mx_other_placement_text.h" +#include "mx_placement_text.h" +#include "mx_string.h" +#include "mx_tap.h" + +/* + * Technical indications give performance information for individual instruments. + */ +/* One child element of MxTechnical: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxEmptyPlacement *up_bow; + MxEmptyPlacement *down_bow; + MxHarmonic *harmonic; + MxEmptyPlacement *open_string; + MxEmptyPlacement *thumb_position; + MxFingering *fingering; + MxPlacementText *pluck; + MxEmptyPlacement *double_tongue; + MxEmptyPlacement *triple_tongue; + MxEmptyPlacementSMUFL *stopped; + MxEmptyPlacement *snap_pizzicato; + MxFret *fret; + MxString *string; + MxHammerOnPullOff *hammer_on; + MxHammerOnPullOff *pull_off; + MxBend *bend; + MxTap *tap; + MxHeelToe *heel; + MxHeelToe *toe; + MxEmptyPlacement *fingernails; + MxHole *hole; + MxArrow *arrow; + MxHandbell *handbell; + MxEmptyPlacement *brass_bend; + MxEmptyPlacement *flip; + MxEmptyPlacement *smear; + MxEmptyPlacementSMUFL *open; + MxEmptyPlacementSMUFL *half_muted; + MxHarmonMute *harmon_mute; + MxEmptyPlacement *golpe; + MxOtherPlacementText *other_technical; +} MxTechnicalChild; + +typedef struct { + bool has_id; + char *id; /* attribute id */ + MxTechnicalChild *children; /* child elements in document order */ + size_t children_count; +} MxTechnical; + +/* NULL on error; the message is in mx_error(). */ +MxTechnical *mx_technical_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_technical_serialize(const MxTechnical *m, xmlNodePtr parent, const char *tag); +void mx_technical_free(MxTechnical *m); + +#endif /* MX_TECHNICAL_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_text_element_data.c b/gen/test/c/mx/mx_text_element_data.c new file mode 100644 index 000000000..94880753a --- /dev/null +++ b/gen/test/c/mx/mx_text_element_data.c @@ -0,0 +1,158 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_text_element_data.h" + +#include "mx_runtime.h" +#include +#include + +MxTextElementData *mx_text_element_data_parse(xmlNodePtr el) { + MxTextElementData *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "xml:lang") == 0) { + m->has_xml_lang = true; + m->xml_lang = mx_strdup(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "underline") == 0) { + m->has_underline = true; + m->underline = mx_number_of_lines_parse(s); + } else if (strcmp(aname, "overline") == 0) { + m->has_overline = true; + m->overline = mx_number_of_lines_parse(s); + } else if (strcmp(aname, "line-through") == 0) { + m->has_line_through = true; + m->line_through = mx_number_of_lines_parse(s); + } else if (strcmp(aname, "rotation") == 0) { + m->has_rotation = true; + m->rotation = mx_rotation_degrees_parse(s); + } else if (strcmp(aname, "letter-spacing") == 0) { + m->has_letter_spacing = true; + m->letter_spacing = mx_number_or_normal_parse(s); + } else if (strcmp(aname, "dir") == 0) { + m->has_dir = true; + m->dir = mx_text_direction_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_text_element_data_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_text_element_data_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_text_element_data_serialize(const MxTextElementData *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_xml_lang) { + xmlSetProp(el, BAD_CAST "xml:lang", BAD_CAST m->xml_lang); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_underline) { + char *s = mx_number_of_lines_to_string(m->underline); + xmlSetProp(el, BAD_CAST "underline", BAD_CAST s); + free(s); + } + if (m->has_overline) { + char *s = mx_number_of_lines_to_string(m->overline); + xmlSetProp(el, BAD_CAST "overline", BAD_CAST s); + free(s); + } + if (m->has_line_through) { + char *s = mx_number_of_lines_to_string(m->line_through); + xmlSetProp(el, BAD_CAST "line-through", BAD_CAST s); + free(s); + } + if (m->has_rotation) { + char *s = mx_rotation_degrees_to_string(m->rotation); + xmlSetProp(el, BAD_CAST "rotation", BAD_CAST s); + free(s); + } + if (m->has_letter_spacing) { + char *s = mx_number_or_normal_to_string(m->letter_spacing); + xmlSetProp(el, BAD_CAST "letter-spacing", BAD_CAST s); + free(s); + } + if (m->has_dir) { + xmlSetProp(el, BAD_CAST "dir", BAD_CAST mx_text_direction_to_string(m->dir)); + } + return el; +} + +void mx_text_element_data_free(MxTextElementData *m) { + if (!m) + return; + if (m->has_xml_lang) + free(m->xml_lang); + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_letter_spacing) + mx_number_or_normal_free(&m->letter_spacing); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_text_element_data.h b/gen/test/c/mx/mx_text_element_data.h new file mode 100644 index 000000000..18dba52c8 --- /dev/null +++ b/gen/test/c/mx/mx_text_element_data.h @@ -0,0 +1,58 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TEXT_ELEMENT_DATA_H_INCLUDED +#define MX_TEXT_ELEMENT_DATA_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_number_of_lines.h" +#include "mx_number_or_normal.h" +#include "mx_rotation_degrees.h" +#include "mx_text_direction.h" + +/* + * The text-element-data type represents a syllable or portion of a syllable for lyric text + * underlay. A hyphen in the string content should only be used for an actual hyphenated word. + * Language names for text elements come from ISO 639, with optional country subcodes from ISO 3166. + */ +typedef struct { + bool has_xml_lang; + char *xml_lang; /* attribute xml:lang */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_underline; + MxNumberOfLines underline; /* attribute underline */ + bool has_overline; + MxNumberOfLines overline; /* attribute overline */ + bool has_line_through; + MxNumberOfLines line_through; /* attribute line-through */ + bool has_rotation; + MxRotationDegrees rotation; /* attribute rotation */ + bool has_letter_spacing; + MxNumberOrNormal letter_spacing; /* attribute letter-spacing */ + bool has_dir; + MxTextDirection dir; /* attribute dir */ + char *value; /* text content */ +} MxTextElementData; + +/* NULL on error; the message is in mx_error(). */ +MxTextElementData *mx_text_element_data_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_text_element_data_serialize(const MxTextElementData *m, xmlNodePtr parent, const char *tag); +void mx_text_element_data_free(MxTextElementData *m); + +#endif /* MX_TEXT_ELEMENT_DATA_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_tie.c b/gen/test/c/mx/mx_tie.c new file mode 100644 index 000000000..e47d60e09 --- /dev/null +++ b/gen/test/c/mx/mx_tie.c @@ -0,0 +1,70 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_tie.h" + +#include "mx_runtime.h" +#include +#include + +MxTie *mx_tie_parse(xmlNodePtr el) { + MxTie *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_parse(s); + } else if (strcmp(aname, "time-only") == 0) { + m->has_time_only = true; + m->time_only = mx_time_only_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_tie_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_tie_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_tie_serialize(const MxTie *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); + } + if (m->has_time_only) { + xmlSetProp(el, BAD_CAST "time-only", BAD_CAST m->time_only); + } + return el; +} + +void mx_tie_free(MxTie *m) { + if (!m) + return; + if (m->has_time_only) + free(m->time_only); + free(m); +} diff --git a/gen/test/c/mx/mx_tie.h b/gen/test/c/mx/mx_tie.h new file mode 100644 index 000000000..45588a605 --- /dev/null +++ b/gen/test/c/mx/mx_tie.h @@ -0,0 +1,30 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TIE_H_INCLUDED +#define MX_TIE_H_INCLUDED + +#include +#include +#include +#include "mx_start_stop.h" +#include "mx_time_only.h" + +/* + * The tie element indicates that a tie begins or ends with this note. If the tie element applies + * only particular times through a repeat, the time-only attribute indicates which times to apply + * it. The tie element indicates sound; the tied element indicates notation. + */ +typedef struct { + bool has_type; + MxStartStop type; /* attribute type */ + bool has_time_only; + MxTimeOnly time_only; /* attribute time-only */ +} MxTie; + +/* NULL on error; the message is in mx_error(). */ +MxTie *mx_tie_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_tie_serialize(const MxTie *m, xmlNodePtr parent, const char *tag); +void mx_tie_free(MxTie *m); + +#endif /* MX_TIE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_tied.c b/gen/test/c/mx/mx_tied.c new file mode 100644 index 000000000..05ac12dd2 --- /dev/null +++ b/gen/test/c/mx/mx_tied.c @@ -0,0 +1,200 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_tied.h" + +#include "mx_runtime.h" +#include +#include + +MxTied *mx_tied_parse(xmlNodePtr el) { + MxTied *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_tied_type_parse(s); + } else if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_number_level_parse(s); + } else if (strcmp(aname, "line-type") == 0) { + m->has_line_type = true; + m->line_type = mx_line_type_parse(s); + } else if (strcmp(aname, "dash-length") == 0) { + m->has_dash_length = true; + m->dash_length = mx_tenths_parse(s); + } else if (strcmp(aname, "space-length") == 0) { + m->has_space_length = true; + m->space_length = mx_tenths_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "orientation") == 0) { + m->has_orientation = true; + m->orientation = mx_over_under_parse(s); + } else if (strcmp(aname, "bezier-x") == 0) { + m->has_bezier_x = true; + m->bezier_x = mx_tenths_parse(s); + } else if (strcmp(aname, "bezier-y") == 0) { + m->has_bezier_y = true; + m->bezier_y = mx_tenths_parse(s); + } else if (strcmp(aname, "bezier-x2") == 0) { + m->has_bezier_x2 = true; + m->bezier_x2 = mx_tenths_parse(s); + } else if (strcmp(aname, "bezier-y2") == 0) { + m->has_bezier_y2 = true; + m->bezier_y2 = mx_tenths_parse(s); + } else if (strcmp(aname, "bezier-offset") == 0) { + m->has_bezier_offset = true; + m->bezier_offset = mx_divisions_parse(s); + } else if (strcmp(aname, "bezier-offset2") == 0) { + m->has_bezier_offset2 = true; + m->bezier_offset2 = mx_divisions_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_tied_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_tied_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_tied_serialize(const MxTied *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_tied_type_to_string(m->type)); + } + if (m->has_number) { + char *s = mx_number_level_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_line_type) { + xmlSetProp(el, BAD_CAST "line-type", BAD_CAST mx_line_type_to_string(m->line_type)); + } + if (m->has_dash_length) { + char *s = mx_tenths_to_string(m->dash_length); + xmlSetProp(el, BAD_CAST "dash-length", BAD_CAST s); + free(s); + } + if (m->has_space_length) { + char *s = mx_tenths_to_string(m->space_length); + xmlSetProp(el, BAD_CAST "space-length", BAD_CAST s); + free(s); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_orientation) { + xmlSetProp(el, BAD_CAST "orientation", BAD_CAST mx_over_under_to_string(m->orientation)); + } + if (m->has_bezier_x) { + char *s = mx_tenths_to_string(m->bezier_x); + xmlSetProp(el, BAD_CAST "bezier-x", BAD_CAST s); + free(s); + } + if (m->has_bezier_y) { + char *s = mx_tenths_to_string(m->bezier_y); + xmlSetProp(el, BAD_CAST "bezier-y", BAD_CAST s); + free(s); + } + if (m->has_bezier_x2) { + char *s = mx_tenths_to_string(m->bezier_x2); + xmlSetProp(el, BAD_CAST "bezier-x2", BAD_CAST s); + free(s); + } + if (m->has_bezier_y2) { + char *s = mx_tenths_to_string(m->bezier_y2); + xmlSetProp(el, BAD_CAST "bezier-y2", BAD_CAST s); + free(s); + } + if (m->has_bezier_offset) { + char *s = mx_divisions_to_string(m->bezier_offset); + xmlSetProp(el, BAD_CAST "bezier-offset", BAD_CAST s); + free(s); + } + if (m->has_bezier_offset2) { + char *s = mx_divisions_to_string(m->bezier_offset2); + xmlSetProp(el, BAD_CAST "bezier-offset2", BAD_CAST s); + free(s); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_tied_free(MxTied *m) { + if (!m) + return; + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + free(m); +} diff --git a/gen/test/c/mx/mx_tied.h b/gen/test/c/mx/mx_tied.h new file mode 100644 index 000000000..1f9bde8e3 --- /dev/null +++ b/gen/test/c/mx/mx_tied.h @@ -0,0 +1,79 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TIED_H_INCLUDED +#define MX_TIED_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_divisions.h" +#include "mx_line_type.h" +#include "mx_number_level.h" +#include "mx_over_under.h" +#include "mx_tenths.h" +#include "mx_tied_type.h" + +/* + * The tied element represents the notated tie. The tie element represents the tie sound. The number + * attribute is rarely needed to disambiguate ties, since note pitches will usually suffice. The + * attribute is implied rather than defaulting to 1 as with most elements. It is available for use + * in more complex tied notation situations. Ties that join two notes of the same pitch together + * should be represented with a tied element on the first note with type="start" and a tied element + * on the second note with type="stop". This can also be done if the two notes being tied are + * enharmonically equivalent, but have different step values. It is not recommended to use tied + * elements to join two notes with enharmonically inequivalent pitches. Ties that indicate that an + * instrument should be undamped are specified with a single tied element with type="let-ring". Ties + * that are visually attached to only one note, other than undamped ties, should be specified with + * two tied elements on the same note, first type="start" then type="stop". This can be used to + * represent ties into or out of repeated sections or codas. + */ +typedef struct { + bool has_type; + MxTiedType type; /* attribute type */ + bool has_number; + MxNumberLevel number; /* attribute number */ + bool has_line_type; + MxLineType line_type; /* attribute line-type */ + bool has_dash_length; + MxTenths dash_length; /* attribute dash-length */ + bool has_space_length; + MxTenths space_length; /* attribute space-length */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_orientation; + MxOverUnder orientation; /* attribute orientation */ + bool has_bezier_x; + MxTenths bezier_x; /* attribute bezier-x */ + bool has_bezier_y; + MxTenths bezier_y; /* attribute bezier-y */ + bool has_bezier_x2; + MxTenths bezier_x2; /* attribute bezier-x2 */ + bool has_bezier_y2; + MxTenths bezier_y2; /* attribute bezier-y2 */ + bool has_bezier_offset; + MxDivisions bezier_offset; /* attribute bezier-offset */ + bool has_bezier_offset2; + MxDivisions bezier_offset2; /* attribute bezier-offset2 */ + bool has_color; + MxColor color; /* attribute color */ + bool has_id; + char *id; /* attribute id */ +} MxTied; + +/* NULL on error; the message is in mx_error(). */ +MxTied *mx_tied_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_tied_serialize(const MxTied *m, xmlNodePtr parent, const char *tag); +void mx_tied_free(MxTied *m); + +#endif /* MX_TIED_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_time.c b/gen/test/c/mx/mx_time.c new file mode 100644 index 000000000..7418021de --- /dev/null +++ b/gen/test/c/mx/mx_time.c @@ -0,0 +1,225 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_time.h" + +#include "mx_runtime.h" +#include +#include + +MxTime *mx_time_parse(xmlNodePtr el) { + MxTime *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_staff_number_parse(s); + } else if (strcmp(aname, "symbol") == 0) { + m->has_symbol = true; + m->symbol = mx_time_symbol_parse(s); + } else if (strcmp(aname, "separator") == 0) { + m->has_separator = true; + m->separator = mx_time_separator_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "halign") == 0) { + m->has_halign = true; + m->halign = mx_left_center_right_parse(s); + } else if (strcmp(aname, "valign") == 0) { + m->has_valign = true; + m->valign = mx_valign_parse(s); + } else if (strcmp(aname, "print-object") == 0) { + m->has_print_object = true; + m->print_object = mx_yes_no_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_time_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxTimeChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "beats") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->beats = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "beat-type") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->beat_type = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "interchangeable") == 0) { + ch->interchangeable = mx_interchangeable_parse(c); + if (!ch->interchangeable) { + mx_time_free(m); + return NULL; + } + } else if (strcmp(tag, "senza-misura") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->senza_misura = mx_strdup(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_time_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_time_serialize(const MxTime *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_number) { + char *s = mx_staff_number_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_symbol) { + xmlSetProp(el, BAD_CAST "symbol", BAD_CAST mx_time_symbol_to_string(m->symbol)); + } + if (m->has_separator) { + xmlSetProp(el, BAD_CAST "separator", BAD_CAST mx_time_separator_to_string(m->separator)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_halign) { + xmlSetProp(el, BAD_CAST "halign", BAD_CAST mx_left_center_right_to_string(m->halign)); + } + if (m->has_valign) { + xmlSetProp(el, BAD_CAST "valign", BAD_CAST mx_valign_to_string(m->valign)); + } + if (m->has_print_object) { + xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxTimeChild *ch = &m->children[i]; + if (ch->beats) { + xmlNewTextChild(el, NULL, BAD_CAST "beats", BAD_CAST ch->beats); + } else if (ch->beat_type) { + xmlNewTextChild(el, NULL, BAD_CAST "beat-type", BAD_CAST ch->beat_type); + } else if (ch->interchangeable) { + mx_interchangeable_serialize(ch->interchangeable, el, "interchangeable"); + } else if (ch->senza_misura) { + xmlNewTextChild(el, NULL, BAD_CAST "senza-misura", BAD_CAST ch->senza_misura); + } + } + return el; +} + +void mx_time_free(MxTime *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxTimeChild *ch = &m->children[i]; + free(ch->beats); + free(ch->beat_type); + if (ch->interchangeable) + mx_interchangeable_free(ch->interchangeable); + free(ch->senza_misura); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_time.h b/gen/test/c/mx/mx_time.h new file mode 100644 index 000000000..76520d3fc --- /dev/null +++ b/gen/test/c/mx/mx_time.h @@ -0,0 +1,86 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TIME_H_INCLUDED +#define MX_TIME_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_interchangeable.h" +#include "mx_left_center_right.h" +#include "mx_staff_number.h" +#include "mx_tenths.h" +#include "mx_time_separator.h" +#include "mx_time_symbol.h" +#include "mx_valign.h" +#include "mx_yes_no.h" + +/* + * Time signatures are represented by the beats element for the numerator and the beat-type element + * for the denominator. The symbol attribute is used indicate common and cut time symbols as well as + * a single number display. Multiple pairs of beat and beat-type elements are used for composite + * time signatures with multiple denominators, such as 2/4 + 3/8. A composite such as 3+2/8 requires + * only one beat/beat-type pair. The print-object attribute allows a time signature to be specified + * but not printed, as is the case for excerpts from the middle of a score. The value is "yes" if + * not present. The optional number attribute refers to staff numbers within the part. If absent, + * the time signature applies to all staves in the part. + */ +/* One child element of MxTime: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + char *beats; + char *beat_type; + MxInterchangeable *interchangeable; + char *senza_misura; +} MxTimeChild; + +typedef struct { + bool has_number; + MxStaffNumber number; /* attribute number */ + bool has_symbol; + MxTimeSymbol symbol; /* attribute symbol */ + bool has_separator; + MxTimeSeparator separator; /* attribute separator */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_halign; + MxLeftCenterRight halign; /* attribute halign */ + bool has_valign; + MxValign valign; /* attribute valign */ + bool has_print_object; + MxYesNo print_object; /* attribute print-object */ + bool has_id; + char *id; /* attribute id */ + MxTimeChild *children; /* child elements in document order */ + size_t children_count; +} MxTime; + +/* NULL on error; the message is in mx_error(). */ +MxTime *mx_time_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_time_serialize(const MxTime *m, xmlNodePtr parent, const char *tag); +void mx_time_free(MxTime *m); + +#endif /* MX_TIME_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_time_modification.c b/gen/test/c/mx/mx_time_modification.c new file mode 100644 index 000000000..dc7b82700 --- /dev/null +++ b/gen/test/c/mx/mx_time_modification.c @@ -0,0 +1,122 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_time_modification.h" + +#include "mx_runtime.h" +#include +#include + +MxTimeModification *mx_time_modification_parse(xmlNodePtr el) { + MxTimeModification *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_time_modification_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxTimeModificationChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "actual-notes") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->actual_notes = malloc(sizeof(*ch->actual_notes)); + if (!ch->actual_notes) + abort(); + *ch->actual_notes = mx_parse_int(s); + xmlFree(text); + } else if (strcmp(tag, "normal-notes") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->normal_notes = malloc(sizeof(*ch->normal_notes)); + if (!ch->normal_notes) + abort(); + *ch->normal_notes = mx_parse_int(s); + xmlFree(text); + } else if (strcmp(tag, "normal-type") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->normal_type = malloc(sizeof(*ch->normal_type)); + if (!ch->normal_type) + abort(); + *ch->normal_type = mx_note_type_value_parse(s); + xmlFree(text); + } else if (strcmp(tag, "normal-dot") == 0) { + ch->normal_dot = mx_empty_parse(c); + if (!ch->normal_dot) { + mx_time_modification_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_time_modification_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_time_modification_serialize(const MxTimeModification *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxTimeModificationChild *ch = &m->children[i]; + if (ch->actual_notes) { + char *s = mx_format_int((*ch->actual_notes)); + xmlNewTextChild(el, NULL, BAD_CAST "actual-notes", BAD_CAST s); + free(s); + } else if (ch->normal_notes) { + char *s = mx_format_int((*ch->normal_notes)); + xmlNewTextChild(el, NULL, BAD_CAST "normal-notes", BAD_CAST s); + free(s); + } else if (ch->normal_type) { + xmlNewTextChild(el, NULL, BAD_CAST "normal-type", BAD_CAST mx_note_type_value_to_string((*ch->normal_type))); + } else if (ch->normal_dot) { + mx_empty_serialize(ch->normal_dot, el, "normal-dot"); + } + } + return el; +} + +void mx_time_modification_free(MxTimeModification *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxTimeModificationChild *ch = &m->children[i]; + free(ch->actual_notes); + free(ch->normal_notes); + free(ch->normal_type); + if (ch->normal_dot) + mx_empty_free(ch->normal_dot); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_time_modification.h b/gen/test/c/mx/mx_time_modification.h new file mode 100644 index 000000000..712af4f6c --- /dev/null +++ b/gen/test/c/mx/mx_time_modification.h @@ -0,0 +1,40 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TIME_MODIFICATION_H_INCLUDED +#define MX_TIME_MODIFICATION_H_INCLUDED + +#include +#include +#include +#include "mx_empty.h" +#include "mx_note_type_value.h" + +/* + * Time modification indicates tuplets, double-note tremolos, and other durational changes. A + * time-modification element shows how the cumulative, sounding effect of tuplets and double-note + * tremolos compare to the written note type represented by the type and dot elements. Nested + * tuplets and other notations that use more detailed information need both the time-modification + * and tuplet elements to be represented accurately. + */ +/* One child element of MxTimeModification: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + long *actual_notes; + long *normal_notes; + MxNoteTypeValue *normal_type; + MxEmpty *normal_dot; +} MxTimeModificationChild; + +typedef struct { + MxTimeModificationChild *children; /* child elements in document order */ + size_t children_count; +} MxTimeModification; + +/* NULL on error; the message is in mx_error(). */ +MxTimeModification *mx_time_modification_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_time_modification_serialize(const MxTimeModification *m, xmlNodePtr parent, const char *tag); +void mx_time_modification_free(MxTimeModification *m); + +#endif /* MX_TIME_MODIFICATION_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_timewise_measure.c b/gen/test/c/mx/mx_timewise_measure.c new file mode 100644 index 000000000..a2a419465 --- /dev/null +++ b/gen/test/c/mx/mx_timewise_measure.c @@ -0,0 +1,129 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_timewise_measure.h" + +#include "mx_runtime.h" +#include +#include + +MxTimewiseMeasure *mx_timewise_measure_parse(xmlNodePtr el) { + MxTimewiseMeasure *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_strdup(s); + } else if (strcmp(aname, "text") == 0) { + m->has_text = true; + m->text = mx_measure_text_parse(s); + } else if (strcmp(aname, "implicit") == 0) { + m->has_implicit = true; + m->implicit = mx_yes_no_parse(s); + } else if (strcmp(aname, "non-controlling") == 0) { + m->has_non_controlling = true; + m->non_controlling = mx_yes_no_parse(s); + } else if (strcmp(aname, "width") == 0) { + m->has_width = true; + m->width = mx_tenths_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_timewise_measure_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxTimewiseMeasureChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "part") == 0) { + ch->part = mx_timewise_part_parse(c); + if (!ch->part) { + mx_timewise_measure_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_timewise_measure_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_timewise_measure_serialize(const MxTimewiseMeasure *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_number) { + xmlSetProp(el, BAD_CAST "number", BAD_CAST m->number); + } + if (m->has_text) { + xmlSetProp(el, BAD_CAST "text", BAD_CAST m->text); + } + if (m->has_implicit) { + xmlSetProp(el, BAD_CAST "implicit", BAD_CAST mx_yes_no_to_string(m->implicit)); + } + if (m->has_non_controlling) { + xmlSetProp(el, BAD_CAST "non-controlling", BAD_CAST mx_yes_no_to_string(m->non_controlling)); + } + if (m->has_width) { + char *s = mx_tenths_to_string(m->width); + xmlSetProp(el, BAD_CAST "width", BAD_CAST s); + free(s); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxTimewiseMeasureChild *ch = &m->children[i]; + if (ch->part) { + mx_timewise_part_serialize(ch->part, el, "part"); + } + } + return el; +} + +void mx_timewise_measure_free(MxTimewiseMeasure *m) { + if (!m) + return; + if (m->has_number) + free(m->number); + if (m->has_text) + free(m->text); + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxTimewiseMeasureChild *ch = &m->children[i]; + if (ch->part) + mx_timewise_part_free(ch->part); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_timewise_measure.h b/gen/test/c/mx/mx_timewise_measure.h new file mode 100644 index 000000000..807f73cd8 --- /dev/null +++ b/gen/test/c/mx/mx_timewise_measure.h @@ -0,0 +1,44 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TIMEWISE_MEASURE_H_INCLUDED +#define MX_TIMEWISE_MEASURE_H_INCLUDED + +#include +#include +#include +#include "mx_measure_text.h" +#include "mx_tenths.h" +#include "mx_timewise_part.h" +#include "mx_yes_no.h" + +/* One child element of MxTimewiseMeasure: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxTimewisePart *part; +} MxTimewiseMeasureChild; + +typedef struct { + bool has_number; + char *number; /* attribute number */ + bool has_text; + MxMeasureText text; /* attribute text */ + bool has_implicit; + MxYesNo implicit; /* attribute implicit */ + bool has_non_controlling; + MxYesNo non_controlling; /* attribute non-controlling */ + bool has_width; + MxTenths width; /* attribute width */ + bool has_id; + char *id; /* attribute id */ + MxTimewiseMeasureChild *children; /* child elements in document order */ + size_t children_count; +} MxTimewiseMeasure; + +/* NULL on error; the message is in mx_error(). */ +MxTimewiseMeasure *mx_timewise_measure_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_timewise_measure_serialize(const MxTimewiseMeasure *m, xmlNodePtr parent, const char *tag); +void mx_timewise_measure_free(MxTimewiseMeasure *m); + +#endif /* MX_TIMEWISE_MEASURE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_timewise_part.c b/gen/test/c/mx/mx_timewise_part.c new file mode 100644 index 000000000..30058072e --- /dev/null +++ b/gen/test/c/mx/mx_timewise_part.c @@ -0,0 +1,213 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_timewise_part.h" + +#include "mx_runtime.h" +#include +#include + +MxTimewisePart *mx_timewise_part_parse(xmlNodePtr el) { + MxTimewisePart *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_timewise_part_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxTimewisePartChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "note") == 0) { + ch->note = mx_note_parse(c); + if (!ch->note) { + mx_timewise_part_free(m); + return NULL; + } + } else if (strcmp(tag, "backup") == 0) { + ch->backup = mx_backup_parse(c); + if (!ch->backup) { + mx_timewise_part_free(m); + return NULL; + } + } else if (strcmp(tag, "forward") == 0) { + ch->forward = mx_forward_parse(c); + if (!ch->forward) { + mx_timewise_part_free(m); + return NULL; + } + } else if (strcmp(tag, "direction") == 0) { + ch->direction = mx_direction_parse(c); + if (!ch->direction) { + mx_timewise_part_free(m); + return NULL; + } + } else if (strcmp(tag, "attributes") == 0) { + ch->attributes = mx_attributes_parse(c); + if (!ch->attributes) { + mx_timewise_part_free(m); + return NULL; + } + } else if (strcmp(tag, "harmony") == 0) { + ch->harmony = mx_harmony_parse(c); + if (!ch->harmony) { + mx_timewise_part_free(m); + return NULL; + } + } else if (strcmp(tag, "figured-bass") == 0) { + ch->figured_bass = mx_figured_bass_parse(c); + if (!ch->figured_bass) { + mx_timewise_part_free(m); + return NULL; + } + } else if (strcmp(tag, "print") == 0) { + ch->print = mx_print_parse(c); + if (!ch->print) { + mx_timewise_part_free(m); + return NULL; + } + } else if (strcmp(tag, "sound") == 0) { + ch->sound = mx_sound_parse(c); + if (!ch->sound) { + mx_timewise_part_free(m); + return NULL; + } + } else if (strcmp(tag, "barline") == 0) { + ch->barline = mx_barline_parse(c); + if (!ch->barline) { + mx_timewise_part_free(m); + return NULL; + } + } else if (strcmp(tag, "grouping") == 0) { + ch->grouping = mx_grouping_parse(c); + if (!ch->grouping) { + mx_timewise_part_free(m); + return NULL; + } + } else if (strcmp(tag, "link") == 0) { + ch->link = mx_link_parse(c); + if (!ch->link) { + mx_timewise_part_free(m); + return NULL; + } + } else if (strcmp(tag, "bookmark") == 0) { + ch->bookmark = mx_bookmark_parse(c); + if (!ch->bookmark) { + mx_timewise_part_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_timewise_part_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_timewise_part_serialize(const MxTimewisePart *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxTimewisePartChild *ch = &m->children[i]; + if (ch->note) { + mx_note_serialize(ch->note, el, "note"); + } else if (ch->backup) { + mx_backup_serialize(ch->backup, el, "backup"); + } else if (ch->forward) { + mx_forward_serialize(ch->forward, el, "forward"); + } else if (ch->direction) { + mx_direction_serialize(ch->direction, el, "direction"); + } else if (ch->attributes) { + mx_attributes_serialize(ch->attributes, el, "attributes"); + } else if (ch->harmony) { + mx_harmony_serialize(ch->harmony, el, "harmony"); + } else if (ch->figured_bass) { + mx_figured_bass_serialize(ch->figured_bass, el, "figured-bass"); + } else if (ch->print) { + mx_print_serialize(ch->print, el, "print"); + } else if (ch->sound) { + mx_sound_serialize(ch->sound, el, "sound"); + } else if (ch->barline) { + mx_barline_serialize(ch->barline, el, "barline"); + } else if (ch->grouping) { + mx_grouping_serialize(ch->grouping, el, "grouping"); + } else if (ch->link) { + mx_link_serialize(ch->link, el, "link"); + } else if (ch->bookmark) { + mx_bookmark_serialize(ch->bookmark, el, "bookmark"); + } + } + return el; +} + +void mx_timewise_part_free(MxTimewisePart *m) { + if (!m) + return; + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxTimewisePartChild *ch = &m->children[i]; + if (ch->note) + mx_note_free(ch->note); + if (ch->backup) + mx_backup_free(ch->backup); + if (ch->forward) + mx_forward_free(ch->forward); + if (ch->direction) + mx_direction_free(ch->direction); + if (ch->attributes) + mx_attributes_free(ch->attributes); + if (ch->harmony) + mx_harmony_free(ch->harmony); + if (ch->figured_bass) + mx_figured_bass_free(ch->figured_bass); + if (ch->print) + mx_print_free(ch->print); + if (ch->sound) + mx_sound_free(ch->sound); + if (ch->barline) + mx_barline_free(ch->barline); + if (ch->grouping) + mx_grouping_free(ch->grouping); + if (ch->link) + mx_link_free(ch->link); + if (ch->bookmark) + mx_bookmark_free(ch->bookmark); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_timewise_part.h b/gen/test/c/mx/mx_timewise_part.h new file mode 100644 index 000000000..f41f85ab0 --- /dev/null +++ b/gen/test/c/mx/mx_timewise_part.h @@ -0,0 +1,55 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TIMEWISE_PART_H_INCLUDED +#define MX_TIMEWISE_PART_H_INCLUDED + +#include +#include +#include +#include "mx_attributes.h" +#include "mx_backup.h" +#include "mx_barline.h" +#include "mx_bookmark.h" +#include "mx_direction.h" +#include "mx_figured_bass.h" +#include "mx_forward.h" +#include "mx_grouping.h" +#include "mx_harmony.h" +#include "mx_link.h" +#include "mx_note.h" +#include "mx_print.h" +#include "mx_sound.h" + +/* One child element of MxTimewisePart: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxNote *note; + MxBackup *backup; + MxForward *forward; + MxDirection *direction; + MxAttributes *attributes; + MxHarmony *harmony; + MxFiguredBass *figured_bass; + MxPrint *print; + MxSound *sound; + MxBarline *barline; + MxGrouping *grouping; + MxLink *link; + MxBookmark *bookmark; +} MxTimewisePartChild; + +typedef struct { + bool has_id; + char *id; /* attribute id */ + MxTimewisePartChild *children; /* child elements in document order */ + size_t children_count; +} MxTimewisePart; + +/* NULL on error; the message is in mx_error(). */ +MxTimewisePart *mx_timewise_part_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_timewise_part_serialize(const MxTimewisePart *m, xmlNodePtr parent, const char *tag); +void mx_timewise_part_free(MxTimewisePart *m); + +#endif /* MX_TIMEWISE_PART_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_transpose.c b/gen/test/c/mx/mx_transpose.c new file mode 100644 index 000000000..5df17ded8 --- /dev/null +++ b/gen/test/c/mx/mx_transpose.c @@ -0,0 +1,140 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_transpose.h" + +#include "mx_runtime.h" +#include +#include + +MxTranspose *mx_transpose_parse(xmlNodePtr el) { + MxTranspose *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_staff_number_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_transpose_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxTransposeChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "diatonic") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->diatonic = malloc(sizeof(*ch->diatonic)); + if (!ch->diatonic) + abort(); + *ch->diatonic = mx_parse_int(s); + xmlFree(text); + } else if (strcmp(tag, "chromatic") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->chromatic = malloc(sizeof(*ch->chromatic)); + if (!ch->chromatic) + abort(); + *ch->chromatic = mx_semitones_parse(s); + xmlFree(text); + } else if (strcmp(tag, "octave-change") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->octave_change = malloc(sizeof(*ch->octave_change)); + if (!ch->octave_change) + abort(); + *ch->octave_change = mx_parse_int(s); + xmlFree(text); + } else if (strcmp(tag, "double") == 0) { + ch->double_ = mx_empty_parse(c); + if (!ch->double_) { + mx_transpose_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_transpose_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_transpose_serialize(const MxTranspose *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_number) { + char *s = mx_staff_number_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxTransposeChild *ch = &m->children[i]; + if (ch->diatonic) { + char *s = mx_format_int((*ch->diatonic)); + xmlNewTextChild(el, NULL, BAD_CAST "diatonic", BAD_CAST s); + free(s); + } else if (ch->chromatic) { + char *s = mx_semitones_to_string((*ch->chromatic)); + xmlNewTextChild(el, NULL, BAD_CAST "chromatic", BAD_CAST s); + free(s); + } else if (ch->octave_change) { + char *s = mx_format_int((*ch->octave_change)); + xmlNewTextChild(el, NULL, BAD_CAST "octave-change", BAD_CAST s); + free(s); + } else if (ch->double_) { + mx_empty_serialize(ch->double_, el, "double"); + } + } + return el; +} + +void mx_transpose_free(MxTranspose *m) { + if (!m) + return; + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxTransposeChild *ch = &m->children[i]; + free(ch->diatonic); + free(ch->chromatic); + free(ch->octave_change); + if (ch->double_) + mx_empty_free(ch->double_); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_transpose.h b/gen/test/c/mx/mx_transpose.h new file mode 100644 index 000000000..7fcda6beb --- /dev/null +++ b/gen/test/c/mx/mx_transpose.h @@ -0,0 +1,44 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TRANSPOSE_H_INCLUDED +#define MX_TRANSPOSE_H_INCLUDED + +#include +#include +#include +#include "mx_empty.h" +#include "mx_semitones.h" +#include "mx_staff_number.h" + +/* + * The transpose type represents what must be added to a written pitch to get a correct sounding + * pitch. The optional number attribute refers to staff numbers, from top to bottom on the system. + * If absent, the transposition applies to all staves in the part. Per-staff transposition is most + * often used in parts that represent multiple instruments. + */ +/* One child element of MxTranspose: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + long *diatonic; + MxSemitones *chromatic; + long *octave_change; + MxEmpty *double_; +} MxTransposeChild; + +typedef struct { + bool has_number; + MxStaffNumber number; /* attribute number */ + bool has_id; + char *id; /* attribute id */ + MxTransposeChild *children; /* child elements in document order */ + size_t children_count; +} MxTranspose; + +/* NULL on error; the message is in mx_error(). */ +MxTranspose *mx_transpose_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_transpose_serialize(const MxTranspose *m, xmlNodePtr parent, const char *tag); +void mx_transpose_free(MxTranspose *m); + +#endif /* MX_TRANSPOSE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_tremolo.c b/gen/test/c/mx/mx_tremolo.c new file mode 100644 index 000000000..2d9222c16 --- /dev/null +++ b/gen/test/c/mx/mx_tremolo.c @@ -0,0 +1,157 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_tremolo.h" + +#include "mx_runtime.h" +#include +#include + +MxTremolo *mx_tremolo_parse(xmlNodePtr el) { + MxTremolo *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_tremolo_type_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "smufl") == 0) { + m->has_smufl = true; + m->smufl = mx_smufl_glyph_name_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_tremolo_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_tremolo_marks_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_tremolo_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_tremolo_serialize(const MxTremolo *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + { + char *s = mx_tremolo_marks_to_string(m->value); + xmlAddChild(el, xmlNewText(BAD_CAST s)); + free(s); + } + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_tremolo_type_to_string(m->type)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_smufl) { + xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); + } + return el; +} + +void mx_tremolo_free(MxTremolo *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + if (m->has_smufl) + free(m->smufl); + free(m); +} diff --git a/gen/test/c/mx/mx_tremolo.h b/gen/test/c/mx/mx_tremolo.h new file mode 100644 index 000000000..2be435788 --- /dev/null +++ b/gen/test/c/mx/mx_tremolo.h @@ -0,0 +1,69 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TREMOLO_H_INCLUDED +#define MX_TREMOLO_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_smufl_glyph_name.h" +#include "mx_tenths.h" +#include "mx_tremolo_marks.h" +#include "mx_tremolo_type.h" + +/* + * The tremolo ornament can be used to indicate single-note, double-note, or unmeasured tremolos. + * Single-note tremolos use the single type, double-note tremolos use the start and stop types, and + * unmeasured tremolos use the unmeasured type. The default is "single" for compatibility with + * Version 1.1. The text of the element indicates the number of tremolo marks and is an integer from + * 0 to 8. Note that the number of attached beams is not included in this value, but is represented + * separately using the beam element. The value should be 0 for unmeasured tremolos. When using + * double-note tremolos, the duration of each note in the tremolo should correspond to half of the + * notated type value. A time-modification element should also be added with an actual-notes value + * of 2 and a normal-notes value of 1. If used within a tuplet, this 2/1 ratio should be multiplied + * by the existing tuplet ratio. The smufl attribute specifies the glyph to use from the SMuFL + * tremolos range for an unmeasured tremolo. It is ignored for other tremolo types. The SMuFL + * buzzRoll glyph is used by default if the attribute is missing. Using repeater beams for + * indicating tremolos is deprecated as of MusicXML 3.0. + */ +typedef struct { + bool has_type; + MxTremoloType type; /* attribute type */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_smufl; + MxSMUFLGlyphName smufl; /* attribute smufl */ + MxTremoloMarks value; /* text content */ +} MxTremolo; + +/* NULL on error; the message is in mx_error(). */ +MxTremolo *mx_tremolo_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_tremolo_serialize(const MxTremolo *m, xmlNodePtr parent, const char *tag); +void mx_tremolo_free(MxTremolo *m); + +#endif /* MX_TREMOLO_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_tuplet.c b/gen/test/c/mx/mx_tuplet.c new file mode 100644 index 000000000..47af9d269 --- /dev/null +++ b/gen/test/c/mx/mx_tuplet.c @@ -0,0 +1,179 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_tuplet.h" + +#include "mx_runtime.h" +#include +#include + +MxTuplet *mx_tuplet_parse(xmlNodePtr el) { + MxTuplet *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_parse(s); + } else if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_number_level_parse(s); + } else if (strcmp(aname, "bracket") == 0) { + m->has_bracket = true; + m->bracket = mx_yes_no_parse(s); + } else if (strcmp(aname, "show-number") == 0) { + m->has_show_number = true; + m->show_number = mx_show_tuplet_parse(s); + } else if (strcmp(aname, "show-type") == 0) { + m->has_show_type = true; + m->show_type = mx_show_tuplet_parse(s); + } else if (strcmp(aname, "line-shape") == 0) { + m->has_line_shape = true; + m->line_shape = mx_line_shape_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_tuplet_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxTupletChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "tuplet-actual") == 0) { + ch->tuplet_actual = mx_tuplet_portion_parse(c); + if (!ch->tuplet_actual) { + mx_tuplet_free(m); + return NULL; + } + } else if (strcmp(tag, "tuplet-normal") == 0) { + ch->tuplet_normal = mx_tuplet_portion_parse(c); + if (!ch->tuplet_normal) { + mx_tuplet_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_tuplet_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_tuplet_serialize(const MxTuplet *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); + } + if (m->has_number) { + char *s = mx_number_level_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_bracket) { + xmlSetProp(el, BAD_CAST "bracket", BAD_CAST mx_yes_no_to_string(m->bracket)); + } + if (m->has_show_number) { + xmlSetProp(el, BAD_CAST "show-number", BAD_CAST mx_show_tuplet_to_string(m->show_number)); + } + if (m->has_show_type) { + xmlSetProp(el, BAD_CAST "show-type", BAD_CAST mx_show_tuplet_to_string(m->show_type)); + } + if (m->has_line_shape) { + xmlSetProp(el, BAD_CAST "line-shape", BAD_CAST mx_line_shape_to_string(m->line_shape)); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + for (size_t i = 0; i < m->children_count; i++) { + const MxTupletChild *ch = &m->children[i]; + if (ch->tuplet_actual) { + mx_tuplet_portion_serialize(ch->tuplet_actual, el, "tuplet-actual"); + } else if (ch->tuplet_normal) { + mx_tuplet_portion_serialize(ch->tuplet_normal, el, "tuplet-normal"); + } + } + return el; +} + +void mx_tuplet_free(MxTuplet *m) { + if (!m) + return; + if (m->has_id) + free(m->id); + for (size_t i = 0; i < m->children_count; i++) { + MxTupletChild *ch = &m->children[i]; + if (ch->tuplet_actual) + mx_tuplet_portion_free(ch->tuplet_actual); + if (ch->tuplet_normal) + mx_tuplet_portion_free(ch->tuplet_normal); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_tuplet.h b/gen/test/c/mx/mx_tuplet.h new file mode 100644 index 000000000..5b5e3b2f3 --- /dev/null +++ b/gen/test/c/mx/mx_tuplet.h @@ -0,0 +1,76 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TUPLET_H_INCLUDED +#define MX_TUPLET_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_line_shape.h" +#include "mx_number_level.h" +#include "mx_show_tuplet.h" +#include "mx_start_stop.h" +#include "mx_tenths.h" +#include "mx_tuplet_portion.h" +#include "mx_yes_no.h" + +/* + * A tuplet element is present when a tuplet is to be displayed graphically, in addition to the + * sound data provided by the time-modification elements. The number attribute is used to + * distinguish nested tuplets. The bracket attribute is used to indicate the presence of a bracket. + * If unspecified, the results are implementation-dependent. The line-shape attribute is used to + * specify whether the bracket is straight or in the older curved or slurred style. It is straight + * by default. Whereas a time-modification element shows how the cumulative, sounding effect of + * tuplets and double-note tremolos compare to the written note type, the tuplet element describes + * how this is displayed. The tuplet element also provides more detailed representation information + * than the time-modification element, and is needed to represent nested tuplets and other complex + * tuplets accurately. The show-number attribute is used to display either the number of actual + * notes, the number of both actual and normal notes, or neither. It is actual by default. The + * show-type attribute is used to display either the actual type, both the actual and normal types, + * or neither. It is none by default. + */ +/* One child element of MxTuplet: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxTupletPortion *tuplet_actual; + MxTupletPortion *tuplet_normal; +} MxTupletChild; + +typedef struct { + bool has_type; + MxStartStop type; /* attribute type */ + bool has_number; + MxNumberLevel number; /* attribute number */ + bool has_bracket; + MxYesNo bracket; /* attribute bracket */ + bool has_show_number; + MxShowTuplet show_number; /* attribute show-number */ + bool has_show_type; + MxShowTuplet show_type; /* attribute show-type */ + bool has_line_shape; + MxLineShape line_shape; /* attribute line-shape */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_id; + char *id; /* attribute id */ + MxTupletChild *children; /* child elements in document order */ + size_t children_count; +} MxTuplet; + +/* NULL on error; the message is in mx_error(). */ +MxTuplet *mx_tuplet_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_tuplet_serialize(const MxTuplet *m, xmlNodePtr parent, const char *tag); +void mx_tuplet_free(MxTuplet *m); + +#endif /* MX_TUPLET_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_tuplet_dot.c b/gen/test/c/mx/mx_tuplet_dot.c new file mode 100644 index 000000000..1bb0a5d4f --- /dev/null +++ b/gen/test/c/mx/mx_tuplet_dot.c @@ -0,0 +1,94 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_tuplet_dot.h" + +#include "mx_runtime.h" +#include +#include + +MxTupletDot *mx_tuplet_dot_parse(xmlNodePtr el) { + MxTupletDot *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_tuplet_dot_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_tuplet_dot_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_tuplet_dot_serialize(const MxTupletDot *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_tuplet_dot_free(MxTupletDot *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_tuplet_dot.h b/gen/test/c/mx/mx_tuplet_dot.h new file mode 100644 index 000000000..3aa309174 --- /dev/null +++ b/gen/test/c/mx/mx_tuplet_dot.h @@ -0,0 +1,37 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TUPLET_DOT_H_INCLUDED +#define MX_TUPLET_DOT_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" + +/* + * The tuplet-dot type is used to specify dotted normal tuplet types. + */ +typedef struct { + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ +} MxTupletDot; + +/* NULL on error; the message is in mx_error(). */ +MxTupletDot *mx_tuplet_dot_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_tuplet_dot_serialize(const MxTupletDot *m, xmlNodePtr parent, const char *tag); +void mx_tuplet_dot_free(MxTupletDot *m); + +#endif /* MX_TUPLET_DOT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_tuplet_number.c b/gen/test/c/mx/mx_tuplet_number.c new file mode 100644 index 000000000..851c8e000 --- /dev/null +++ b/gen/test/c/mx/mx_tuplet_number.c @@ -0,0 +1,105 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_tuplet_number.h" + +#include "mx_runtime.h" +#include +#include + +MxTupletNumber *mx_tuplet_number_parse(xmlNodePtr el) { + MxTupletNumber *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_tuplet_number_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_parse_int(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_tuplet_number_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_tuplet_number_serialize(const MxTupletNumber *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + { + char *s = mx_format_int(m->value); + xmlAddChild(el, xmlNewText(BAD_CAST s)); + free(s); + } + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_tuplet_number_free(MxTupletNumber *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_tuplet_number.h b/gen/test/c/mx/mx_tuplet_number.h new file mode 100644 index 000000000..4cfe76d9e --- /dev/null +++ b/gen/test/c/mx/mx_tuplet_number.h @@ -0,0 +1,38 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TUPLET_NUMBER_H_INCLUDED +#define MX_TUPLET_NUMBER_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" + +/* + * The tuplet-number type indicates the number of notes for this portion of the tuplet. + */ +typedef struct { + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + long value; /* text content */ +} MxTupletNumber; + +/* NULL on error; the message is in mx_error(). */ +MxTupletNumber *mx_tuplet_number_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_tuplet_number_serialize(const MxTupletNumber *m, xmlNodePtr parent, const char *tag); +void mx_tuplet_number_free(MxTupletNumber *m); + +#endif /* MX_TUPLET_NUMBER_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_tuplet_portion.c b/gen/test/c/mx/mx_tuplet_portion.c new file mode 100644 index 000000000..33e54f445 --- /dev/null +++ b/gen/test/c/mx/mx_tuplet_portion.c @@ -0,0 +1,105 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_tuplet_portion.h" + +#include "mx_runtime.h" +#include +#include + +MxTupletPortion *mx_tuplet_portion_parse(xmlNodePtr el) { + MxTupletPortion *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_tuplet_portion_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxTupletPortionChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "tuplet-number") == 0) { + ch->tuplet_number = mx_tuplet_number_parse(c); + if (!ch->tuplet_number) { + mx_tuplet_portion_free(m); + return NULL; + } + } else if (strcmp(tag, "tuplet-type") == 0) { + ch->tuplet_type = mx_tuplet_type_parse(c); + if (!ch->tuplet_type) { + mx_tuplet_portion_free(m); + return NULL; + } + } else if (strcmp(tag, "tuplet-dot") == 0) { + ch->tuplet_dot = mx_tuplet_dot_parse(c); + if (!ch->tuplet_dot) { + mx_tuplet_portion_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_tuplet_portion_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_tuplet_portion_serialize(const MxTupletPortion *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxTupletPortionChild *ch = &m->children[i]; + if (ch->tuplet_number) { + mx_tuplet_number_serialize(ch->tuplet_number, el, "tuplet-number"); + } else if (ch->tuplet_type) { + mx_tuplet_type_serialize(ch->tuplet_type, el, "tuplet-type"); + } else if (ch->tuplet_dot) { + mx_tuplet_dot_serialize(ch->tuplet_dot, el, "tuplet-dot"); + } + } + return el; +} + +void mx_tuplet_portion_free(MxTupletPortion *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxTupletPortionChild *ch = &m->children[i]; + if (ch->tuplet_number) + mx_tuplet_number_free(ch->tuplet_number); + if (ch->tuplet_type) + mx_tuplet_type_free(ch->tuplet_type); + if (ch->tuplet_dot) + mx_tuplet_dot_free(ch->tuplet_dot); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_tuplet_portion.h b/gen/test/c/mx/mx_tuplet_portion.h new file mode 100644 index 000000000..f78e71ba8 --- /dev/null +++ b/gen/test/c/mx/mx_tuplet_portion.h @@ -0,0 +1,39 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TUPLET_PORTION_H_INCLUDED +#define MX_TUPLET_PORTION_H_INCLUDED + +#include +#include +#include +#include "mx_tuplet_dot.h" +#include "mx_tuplet_number.h" +#include "mx_tuplet_type.h" + +/* + * The tuplet-portion type provides optional full control over tuplet specifications. It allows the + * number and note type (including dots) to be set for the actual and normal portions of a single + * tuplet. If any of these elements are absent, their values are based on the time-modification + * element. + */ +/* One child element of MxTupletPortion: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxTupletNumber *tuplet_number; + MxTupletType *tuplet_type; + MxTupletDot *tuplet_dot; +} MxTupletPortionChild; + +typedef struct { + MxTupletPortionChild *children; /* child elements in document order */ + size_t children_count; +} MxTupletPortion; + +/* NULL on error; the message is in mx_error(). */ +MxTupletPortion *mx_tuplet_portion_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_tuplet_portion_serialize(const MxTupletPortion *m, xmlNodePtr parent, const char *tag); +void mx_tuplet_portion_free(MxTupletPortion *m); + +#endif /* MX_TUPLET_PORTION_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_tuplet_type.c b/gen/test/c/mx/mx_tuplet_type.c new file mode 100644 index 000000000..e14b400fc --- /dev/null +++ b/gen/test/c/mx/mx_tuplet_type.c @@ -0,0 +1,101 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_tuplet_type.h" + +#include "mx_runtime.h" +#include +#include + +MxTupletType *mx_tuplet_type_parse(xmlNodePtr el) { + MxTupletType *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "font-family") == 0) { + m->has_font_family = true; + m->font_family = mx_comma_separated_text_parse(s); + } else if (strcmp(aname, "font-style") == 0) { + m->has_font_style = true; + m->font_style = mx_font_style_parse(s); + } else if (strcmp(aname, "font-size") == 0) { + m->has_font_size = true; + m->font_size = mx_font_size_parse(s); + } else if (strcmp(aname, "font-weight") == 0) { + m->has_font_weight = true; + m->font_weight = mx_font_weight_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_tuplet_type_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_note_type_value_parse(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_tuplet_type_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_tuplet_type_serialize(const MxTupletType *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST mx_note_type_value_to_string(m->value))); + if (m->has_font_family) { + xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); + } + if (m->has_font_style) { + xmlSetProp(el, BAD_CAST "font-style", BAD_CAST mx_font_style_to_string(m->font_style)); + } + if (m->has_font_size) { + char *s = mx_font_size_to_string(m->font_size); + xmlSetProp(el, BAD_CAST "font-size", BAD_CAST s); + free(s); + } + if (m->has_font_weight) { + xmlSetProp(el, BAD_CAST "font-weight", BAD_CAST mx_font_weight_to_string(m->font_weight)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + return el; +} + +void mx_tuplet_type_free(MxTupletType *m) { + if (!m) + return; + if (m->has_font_family) + free(m->font_family); + if (m->has_font_size) + mx_font_size_free(&m->font_size); + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_tuplet_type.h b/gen/test/c/mx/mx_tuplet_type.h new file mode 100644 index 000000000..44ff1e26f --- /dev/null +++ b/gen/test/c/mx/mx_tuplet_type.h @@ -0,0 +1,40 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TUPLET_TYPE_H_INCLUDED +#define MX_TUPLET_TYPE_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_comma_separated_text.h" +#include "mx_font_size.h" +#include "mx_font_style.h" +#include "mx_font_weight.h" +#include "mx_note_type_value.h" + +/* + * The tuplet-type type indicates the graphical note type of the notes for this portion of the + * tuplet. + */ +typedef struct { + bool has_font_family; + MxCommaSeparatedText font_family; /* attribute font-family */ + bool has_font_style; + MxFontStyle font_style; /* attribute font-style */ + bool has_font_size; + MxFontSize font_size; /* attribute font-size */ + bool has_font_weight; + MxFontWeight font_weight; /* attribute font-weight */ + bool has_color; + MxColor color; /* attribute color */ + MxNoteTypeValue value; /* text content */ +} MxTupletType; + +/* NULL on error; the message is in mx_error(). */ +MxTupletType *mx_tuplet_type_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_tuplet_type_serialize(const MxTupletType *m, xmlNodePtr parent, const char *tag); +void mx_tuplet_type_free(MxTupletType *m); + +#endif /* MX_TUPLET_TYPE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_typed_text.c b/gen/test/c/mx/mx_typed_text.c new file mode 100644 index 000000000..78e4d4ebe --- /dev/null +++ b/gen/test/c/mx/mx_typed_text.c @@ -0,0 +1,72 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_typed_text.h" + +#include "mx_runtime.h" +#include +#include + +MxTypedText *mx_typed_text_parse(xmlNodePtr el) { + MxTypedText *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_typed_text_free(m); + return NULL; + } + xmlFree(avalue); + } + { + xmlChar *text = xmlNodeGetContent(el); + const char *s = text ? (const char *)text : ""; + m->value = mx_strdup(s); + xmlFree(text); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_typed_text_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_typed_text_serialize(const MxTypedText *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + xmlAddChild(el, xmlNewText(BAD_CAST m->value)); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST m->type); + } + return el; +} + +void mx_typed_text_free(MxTypedText *m) { + if (!m) + return; + if (m->has_type) + free(m->type); + free(m->value); + free(m); +} diff --git a/gen/test/c/mx/mx_typed_text.h b/gen/test/c/mx/mx_typed_text.h new file mode 100644 index 000000000..fd6eb25b1 --- /dev/null +++ b/gen/test/c/mx/mx_typed_text.h @@ -0,0 +1,25 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_TYPED_TEXT_H_INCLUDED +#define MX_TYPED_TEXT_H_INCLUDED + +#include +#include +#include + +/* + * The typed-text type represents a text element with a type attributes. + */ +typedef struct { + bool has_type; + char *type; /* attribute type */ + char *value; /* text content */ +} MxTypedText; + +/* NULL on error; the message is in mx_error(). */ +MxTypedText *mx_typed_text_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_typed_text_serialize(const MxTypedText *m, xmlNodePtr parent, const char *tag); +void mx_typed_text_free(MxTypedText *m); + +#endif /* MX_TYPED_TEXT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_unpitched.c b/gen/test/c/mx/mx_unpitched.c new file mode 100644 index 000000000..81e5b8661 --- /dev/null +++ b/gen/test/c/mx/mx_unpitched.c @@ -0,0 +1,99 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_unpitched.h" + +#include "mx_runtime.h" +#include +#include + +MxUnpitched *mx_unpitched_parse(xmlNodePtr el) { + MxUnpitched *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_unpitched_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxUnpitchedChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "display-step") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->display_step = malloc(sizeof(*ch->display_step)); + if (!ch->display_step) + abort(); + *ch->display_step = mx_step_parse(s); + xmlFree(text); + } else if (strcmp(tag, "display-octave") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->display_octave = malloc(sizeof(*ch->display_octave)); + if (!ch->display_octave) + abort(); + *ch->display_octave = mx_octave_parse(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_unpitched_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_unpitched_serialize(const MxUnpitched *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxUnpitchedChild *ch = &m->children[i]; + if (ch->display_step) { + xmlNewTextChild(el, NULL, BAD_CAST "display-step", BAD_CAST mx_step_to_string((*ch->display_step))); + } else if (ch->display_octave) { + char *s = mx_octave_to_string((*ch->display_octave)); + xmlNewTextChild(el, NULL, BAD_CAST "display-octave", BAD_CAST s); + free(s); + } + } + return el; +} + +void mx_unpitched_free(MxUnpitched *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxUnpitchedChild *ch = &m->children[i]; + free(ch->display_step); + free(ch->display_octave); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_unpitched.h b/gen/test/c/mx/mx_unpitched.h new file mode 100644 index 000000000..ef2294185 --- /dev/null +++ b/gen/test/c/mx/mx_unpitched.h @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_UNPITCHED_H_INCLUDED +#define MX_UNPITCHED_H_INCLUDED + +#include +#include +#include +#include "mx_octave.h" +#include "mx_step.h" + +/* + * The unpitched type represents musical elements that are notated on the staff but lack definite + * pitch, such as unpitched percussion and speaking voice. + */ +/* One child element of MxUnpitched: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + MxStep *display_step; + MxOctave *display_octave; +} MxUnpitchedChild; + +typedef struct { + MxUnpitchedChild *children; /* child elements in document order */ + size_t children_count; +} MxUnpitched; + +/* NULL on error; the message is in mx_error(). */ +MxUnpitched *mx_unpitched_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_unpitched_serialize(const MxUnpitched *m, xmlNodePtr parent, const char *tag); +void mx_unpitched_free(MxUnpitched *m); + +#endif /* MX_UNPITCHED_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_virtual_instrument.c b/gen/test/c/mx/mx_virtual_instrument.c new file mode 100644 index 000000000..1f3804be3 --- /dev/null +++ b/gen/test/c/mx/mx_virtual_instrument.c @@ -0,0 +1,91 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_virtual_instrument.h" + +#include "mx_runtime.h" +#include +#include + +MxVirtualInstrument *mx_virtual_instrument_parse(xmlNodePtr el) { + MxVirtualInstrument *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_virtual_instrument_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxVirtualInstrumentChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "virtual-library") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->virtual_library = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "virtual-name") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->virtual_name = mx_strdup(s); + xmlFree(text); + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_virtual_instrument_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_virtual_instrument_serialize(const MxVirtualInstrument *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxVirtualInstrumentChild *ch = &m->children[i]; + if (ch->virtual_library) { + xmlNewTextChild(el, NULL, BAD_CAST "virtual-library", BAD_CAST ch->virtual_library); + } else if (ch->virtual_name) { + xmlNewTextChild(el, NULL, BAD_CAST "virtual-name", BAD_CAST ch->virtual_name); + } + } + return el; +} + +void mx_virtual_instrument_free(MxVirtualInstrument *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxVirtualInstrumentChild *ch = &m->children[i]; + free(ch->virtual_library); + free(ch->virtual_name); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_virtual_instrument.h b/gen/test/c/mx/mx_virtual_instrument.h new file mode 100644 index 000000000..1f3d0467a --- /dev/null +++ b/gen/test/c/mx/mx_virtual_instrument.h @@ -0,0 +1,33 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_VIRTUAL_INSTRUMENT_H_INCLUDED +#define MX_VIRTUAL_INSTRUMENT_H_INCLUDED + +#include +#include +#include + +/* + * The virtual-instrument element defines a specific virtual instrument used for an instrument + * sound. + */ +/* One child element of MxVirtualInstrument: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + char *virtual_library; + char *virtual_name; +} MxVirtualInstrumentChild; + +typedef struct { + MxVirtualInstrumentChild *children; /* child elements in document order */ + size_t children_count; +} MxVirtualInstrument; + +/* NULL on error; the message is in mx_error(). */ +MxVirtualInstrument *mx_virtual_instrument_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_virtual_instrument_serialize(const MxVirtualInstrument *m, xmlNodePtr parent, const char *tag); +void mx_virtual_instrument_free(MxVirtualInstrument *m); + +#endif /* MX_VIRTUAL_INSTRUMENT_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_wavy_line.c b/gen/test/c/mx/mx_wavy_line.c new file mode 100644 index 000000000..d4fe29fc6 --- /dev/null +++ b/gen/test/c/mx/mx_wavy_line.c @@ -0,0 +1,164 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_wavy_line.h" + +#include "mx_runtime.h" +#include +#include + +MxWavyLine *mx_wavy_line_parse(xmlNodePtr el) { + MxWavyLine *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_start_stop_continue_parse(s); + } else if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_number_level_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "placement") == 0) { + m->has_placement = true; + m->placement = mx_above_below_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "start-note") == 0) { + m->has_start_note = true; + m->start_note = mx_start_note_parse(s); + } else if (strcmp(aname, "trill-step") == 0) { + m->has_trill_step = true; + m->trill_step = mx_trill_step_parse(s); + } else if (strcmp(aname, "two-note-turn") == 0) { + m->has_two_note_turn = true; + m->two_note_turn = mx_two_note_turn_parse(s); + } else if (strcmp(aname, "accelerate") == 0) { + m->has_accelerate = true; + m->accelerate = mx_yes_no_parse(s); + } else if (strcmp(aname, "beats") == 0) { + m->has_beats = true; + m->beats = mx_trill_beats_parse(s); + } else if (strcmp(aname, "second-beat") == 0) { + m->has_second_beat = true; + m->second_beat = mx_percent_parse(s); + } else if (strcmp(aname, "last-beat") == 0) { + m->has_last_beat = true; + m->last_beat = mx_percent_parse(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_wavy_line_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_wavy_line_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_wavy_line_serialize(const MxWavyLine *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_continue_to_string(m->type)); + } + if (m->has_number) { + char *s = mx_number_level_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_placement) { + xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_start_note) { + xmlSetProp(el, BAD_CAST "start-note", BAD_CAST mx_start_note_to_string(m->start_note)); + } + if (m->has_trill_step) { + xmlSetProp(el, BAD_CAST "trill-step", BAD_CAST mx_trill_step_to_string(m->trill_step)); + } + if (m->has_two_note_turn) { + xmlSetProp(el, BAD_CAST "two-note-turn", BAD_CAST mx_two_note_turn_to_string(m->two_note_turn)); + } + if (m->has_accelerate) { + xmlSetProp(el, BAD_CAST "accelerate", BAD_CAST mx_yes_no_to_string(m->accelerate)); + } + if (m->has_beats) { + char *s = mx_trill_beats_to_string(m->beats); + xmlSetProp(el, BAD_CAST "beats", BAD_CAST s); + free(s); + } + if (m->has_second_beat) { + char *s = mx_percent_to_string(m->second_beat); + xmlSetProp(el, BAD_CAST "second-beat", BAD_CAST s); + free(s); + } + if (m->has_last_beat) { + char *s = mx_percent_to_string(m->last_beat); + xmlSetProp(el, BAD_CAST "last-beat", BAD_CAST s); + free(s); + } + return el; +} + +void mx_wavy_line_free(MxWavyLine *m) { + if (!m) + return; + if (m->has_color) + free(m->color); + free(m); +} diff --git a/gen/test/c/mx/mx_wavy_line.h b/gen/test/c/mx/mx_wavy_line.h new file mode 100644 index 000000000..99abd8381 --- /dev/null +++ b/gen/test/c/mx/mx_wavy_line.h @@ -0,0 +1,64 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_WAVY_LINE_H_INCLUDED +#define MX_WAVY_LINE_H_INCLUDED + +#include +#include +#include +#include "mx_above_below.h" +#include "mx_color.h" +#include "mx_number_level.h" +#include "mx_percent.h" +#include "mx_start_note.h" +#include "mx_start_stop_continue.h" +#include "mx_tenths.h" +#include "mx_trill_beats.h" +#include "mx_trill_step.h" +#include "mx_two_note_turn.h" +#include "mx_yes_no.h" + +/* + * Wavy lines are one way to indicate trills. When used with a barline element, they should always + * have type="continue" set. + */ +typedef struct { + bool has_type; + MxStartStopContinue type; /* attribute type */ + bool has_number; + MxNumberLevel number; /* attribute number */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_placement; + MxAboveBelow placement; /* attribute placement */ + bool has_color; + MxColor color; /* attribute color */ + bool has_start_note; + MxStartNote start_note; /* attribute start-note */ + bool has_trill_step; + MxTrillStep trill_step; /* attribute trill-step */ + bool has_two_note_turn; + MxTwoNoteTurn two_note_turn; /* attribute two-note-turn */ + bool has_accelerate; + MxYesNo accelerate; /* attribute accelerate */ + bool has_beats; + MxTrillBeats beats; /* attribute beats */ + bool has_second_beat; + MxPercent second_beat; /* attribute second-beat */ + bool has_last_beat; + MxPercent last_beat; /* attribute last-beat */ +} MxWavyLine; + +/* NULL on error; the message is in mx_error(). */ +MxWavyLine *mx_wavy_line_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_wavy_line_serialize(const MxWavyLine *m, xmlNodePtr parent, const char *tag); +void mx_wavy_line_free(MxWavyLine *m); + +#endif /* MX_WAVY_LINE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_wedge.c b/gen/test/c/mx/mx_wedge.c new file mode 100644 index 000000000..4cf442fed --- /dev/null +++ b/gen/test/c/mx/mx_wedge.c @@ -0,0 +1,154 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_wedge.h" + +#include "mx_runtime.h" +#include +#include + +MxWedge *mx_wedge_parse(xmlNodePtr el) { + MxWedge *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + if (strcmp(aname, "type") == 0) { + m->has_type = true; + m->type = mx_wedge_type_parse(s); + } else if (strcmp(aname, "number") == 0) { + m->has_number = true; + m->number = mx_number_level_parse(s); + } else if (strcmp(aname, "spread") == 0) { + m->has_spread = true; + m->spread = mx_tenths_parse(s); + } else if (strcmp(aname, "niente") == 0) { + m->has_niente = true; + m->niente = mx_yes_no_parse(s); + } else if (strcmp(aname, "line-type") == 0) { + m->has_line_type = true; + m->line_type = mx_line_type_parse(s); + } else if (strcmp(aname, "dash-length") == 0) { + m->has_dash_length = true; + m->dash_length = mx_tenths_parse(s); + } else if (strcmp(aname, "space-length") == 0) { + m->has_space_length = true; + m->space_length = mx_tenths_parse(s); + } else if (strcmp(aname, "default-x") == 0) { + m->has_default_x = true; + m->default_x = mx_tenths_parse(s); + } else if (strcmp(aname, "default-y") == 0) { + m->has_default_y = true; + m->default_y = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-x") == 0) { + m->has_relative_x = true; + m->relative_x = mx_tenths_parse(s); + } else if (strcmp(aname, "relative-y") == 0) { + m->has_relative_y = true; + m->relative_y = mx_tenths_parse(s); + } else if (strcmp(aname, "color") == 0) { + m->has_color = true; + m->color = mx_color_parse(s); + } else if (strcmp(aname, "id") == 0) { + m->has_id = true; + m->id = mx_strdup(s); + } else { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_wedge_free(m); + return NULL; + } + xmlFree(avalue); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_wedge_free(m); + return NULL; + } + } + return m; +} + +xmlNodePtr mx_wedge_serialize(const MxWedge *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + if (m->has_type) { + xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_wedge_type_to_string(m->type)); + } + if (m->has_number) { + char *s = mx_number_level_to_string(m->number); + xmlSetProp(el, BAD_CAST "number", BAD_CAST s); + free(s); + } + if (m->has_spread) { + char *s = mx_tenths_to_string(m->spread); + xmlSetProp(el, BAD_CAST "spread", BAD_CAST s); + free(s); + } + if (m->has_niente) { + xmlSetProp(el, BAD_CAST "niente", BAD_CAST mx_yes_no_to_string(m->niente)); + } + if (m->has_line_type) { + xmlSetProp(el, BAD_CAST "line-type", BAD_CAST mx_line_type_to_string(m->line_type)); + } + if (m->has_dash_length) { + char *s = mx_tenths_to_string(m->dash_length); + xmlSetProp(el, BAD_CAST "dash-length", BAD_CAST s); + free(s); + } + if (m->has_space_length) { + char *s = mx_tenths_to_string(m->space_length); + xmlSetProp(el, BAD_CAST "space-length", BAD_CAST s); + free(s); + } + if (m->has_default_x) { + char *s = mx_tenths_to_string(m->default_x); + xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); + free(s); + } + if (m->has_default_y) { + char *s = mx_tenths_to_string(m->default_y); + xmlSetProp(el, BAD_CAST "default-y", BAD_CAST s); + free(s); + } + if (m->has_relative_x) { + char *s = mx_tenths_to_string(m->relative_x); + xmlSetProp(el, BAD_CAST "relative-x", BAD_CAST s); + free(s); + } + if (m->has_relative_y) { + char *s = mx_tenths_to_string(m->relative_y); + xmlSetProp(el, BAD_CAST "relative-y", BAD_CAST s); + free(s); + } + if (m->has_color) { + xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); + } + if (m->has_id) { + xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); + } + return el; +} + +void mx_wedge_free(MxWedge *m) { + if (!m) + return; + if (m->has_color) + free(m->color); + if (m->has_id) + free(m->id); + free(m); +} diff --git a/gen/test/c/mx/mx_wedge.h b/gen/test/c/mx/mx_wedge.h new file mode 100644 index 000000000..a5c9e5bc1 --- /dev/null +++ b/gen/test/c/mx/mx_wedge.h @@ -0,0 +1,60 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_WEDGE_H_INCLUDED +#define MX_WEDGE_H_INCLUDED + +#include +#include +#include +#include "mx_color.h" +#include "mx_line_type.h" +#include "mx_number_level.h" +#include "mx_tenths.h" +#include "mx_wedge_type.h" +#include "mx_yes_no.h" + +/* + * The wedge type represents crescendo and diminuendo wedge symbols. The type attribute is crescendo + * for the start of a wedge that is closed at the left side, and diminuendo for the start of a wedge + * that is closed on the right side. Spread values are measured in tenths; those at the start of a + * crescendo wedge or end of a diminuendo wedge are ignored. The niente attribute is yes if a circle + * appears at the point of the wedge, indicating a crescendo from nothing or diminuendo to nothing. + * It is no by default, and used only when the type is crescendo, or the type is stop for a wedge + * that began with a diminuendo type. The line-type is solid by default. + */ +typedef struct { + bool has_type; + MxWedgeType type; /* attribute type */ + bool has_number; + MxNumberLevel number; /* attribute number */ + bool has_spread; + MxTenths spread; /* attribute spread */ + bool has_niente; + MxYesNo niente; /* attribute niente */ + bool has_line_type; + MxLineType line_type; /* attribute line-type */ + bool has_dash_length; + MxTenths dash_length; /* attribute dash-length */ + bool has_space_length; + MxTenths space_length; /* attribute space-length */ + bool has_default_x; + MxTenths default_x; /* attribute default-x */ + bool has_default_y; + MxTenths default_y; /* attribute default-y */ + bool has_relative_x; + MxTenths relative_x; /* attribute relative-x */ + bool has_relative_y; + MxTenths relative_y; /* attribute relative-y */ + bool has_color; + MxColor color; /* attribute color */ + bool has_id; + char *id; /* attribute id */ +} MxWedge; + +/* NULL on error; the message is in mx_error(). */ +MxWedge *mx_wedge_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_wedge_serialize(const MxWedge *m, xmlNodePtr parent, const char *tag); +void mx_wedge_free(MxWedge *m); + +#endif /* MX_WEDGE_H_INCLUDED */ diff --git a/gen/test/c/mx/mx_work.c b/gen/test/c/mx/mx_work.c new file mode 100644 index 000000000..b6bee4150 --- /dev/null +++ b/gen/test/c/mx/mx_work.c @@ -0,0 +1,101 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#include "mx_work.h" + +#include "mx_runtime.h" +#include +#include + +MxWork *mx_work_parse(xmlNodePtr el) { + MxWork *m = calloc(1, sizeof(*m)); + if (!m) + abort(); + for (xmlAttrPtr a = el->properties; a; a = a->next) { + char aname[128]; + if (a->ns && a->ns->prefix) + snprintf(aname, sizeof(aname), "%s:%s", + (const char *)a->ns->prefix, (const char *)a->name); + else + snprintf(aname, sizeof(aname), "%s", (const char *)a->name); + xmlChar *avalue = xmlNodeListGetString(el->doc, a->children, 1); + const char *s = avalue ? (const char *)avalue : ""; + (void)s; + { + mx_error_set("unknown attribute \"%s\" on <%s>", aname, + (const char *)el->name); + xmlFree(avalue); + mx_work_free(m); + return NULL; + } + xmlFree(avalue); + } + size_t n = 0; + for (xmlNodePtr c = el->children; c; c = c->next) + if (c->type == XML_ELEMENT_NODE) + n++; + if (n) { + m->children = calloc(n, sizeof(*m->children)); + if (!m->children) + abort(); + } + for (xmlNodePtr c = el->children; c; c = c->next) { + if (c->type != XML_ELEMENT_NODE) + continue; + const char *tag = (const char *)c->name; + MxWorkChild *ch = &m->children[m->children_count]; + if (strcmp(tag, "work-number") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->work_number = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "work-title") == 0) { + xmlChar *text = xmlNodeGetContent(c); + const char *s = text ? (const char *)text : ""; + ch->work_title = mx_strdup(s); + xmlFree(text); + } else if (strcmp(tag, "opus") == 0) { + ch->opus = mx_opus_parse(c); + if (!ch->opus) { + mx_work_free(m); + return NULL; + } + } else { + mx_error_set("unknown element <%s> in <%s>", tag, + (const char *)el->name); + mx_work_free(m); + return NULL; + } + m->children_count++; + } + return m; +} + +xmlNodePtr mx_work_serialize(const MxWork *m, xmlNodePtr parent, const char *tag) { + xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) + : xmlNewNode(NULL, BAD_CAST tag); + for (size_t i = 0; i < m->children_count; i++) { + const MxWorkChild *ch = &m->children[i]; + if (ch->work_number) { + xmlNewTextChild(el, NULL, BAD_CAST "work-number", BAD_CAST ch->work_number); + } else if (ch->work_title) { + xmlNewTextChild(el, NULL, BAD_CAST "work-title", BAD_CAST ch->work_title); + } else if (ch->opus) { + mx_opus_serialize(ch->opus, el, "opus"); + } + } + return el; +} + +void mx_work_free(MxWork *m) { + if (!m) + return; + for (size_t i = 0; i < m->children_count; i++) { + MxWorkChild *ch = &m->children[i]; + free(ch->work_number); + free(ch->work_title); + if (ch->opus) + mx_opus_free(ch->opus); + } + free(m->children); + free(m); +} diff --git a/gen/test/c/mx/mx_work.h b/gen/test/c/mx/mx_work.h new file mode 100644 index 000000000..8fbada532 --- /dev/null +++ b/gen/test/c/mx/mx_work.h @@ -0,0 +1,35 @@ +/* Code generated by gen from musicxml-3.1-8bbe8e5. DO NOT EDIT. */ + +#ifndef MX_WORK_H_INCLUDED +#define MX_WORK_H_INCLUDED + +#include +#include +#include +#include "mx_opus.h" + +/* + * Works are optionally identified by number and title. The work type also may indicate a link to + * the opus document that composes multiple scores into a collection. + */ +/* One child element of MxWork: exactly one field is non-NULL, + and that pointer says which element this is. Document order is + the array order on the owning struct. */ +typedef struct { + char *work_number; + char *work_title; + MxOpus *opus; +} MxWorkChild; + +typedef struct { + MxWorkChild *children; /* child elements in document order */ + size_t children_count; +} MxWork; + +/* NULL on error; the message is in mx_error(). */ +MxWork *mx_work_parse(xmlNodePtr el); +/* Appends under parent, or creates a free node when parent is NULL. */ +xmlNodePtr mx_work_serialize(const MxWork *m, xmlNodePtr parent, const char *tag); +void mx_work_free(MxWork *m); + +#endif /* MX_WORK_H_INCLUDED */ diff --git a/gen/test/c/mx/sources.cmake b/gen/test/c/mx/sources.cmake index 8827aad5e..be1d3dc40 100644 --- a/gen/test/c/mx/sources.cmake +++ b/gen/test/c/mx/sources.cmake @@ -2,136 +2,340 @@ set(MX_GENERATED_SOURCES ${CMAKE_CURRENT_LIST_DIR}/mx_above_below.c + ${CMAKE_CURRENT_LIST_DIR}/mx_accidental.c + ${CMAKE_CURRENT_LIST_DIR}/mx_accidental_mark.c + ${CMAKE_CURRENT_LIST_DIR}/mx_accidental_text.c ${CMAKE_CURRENT_LIST_DIR}/mx_accidental_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_accord.c ${CMAKE_CURRENT_LIST_DIR}/mx_accordion_middle.c + ${CMAKE_CURRENT_LIST_DIR}/mx_accordion_registration.c + ${CMAKE_CURRENT_LIST_DIR}/mx_appearance.c + ${CMAKE_CURRENT_LIST_DIR}/mx_arpeggiate.c + ${CMAKE_CURRENT_LIST_DIR}/mx_arrow.c ${CMAKE_CURRENT_LIST_DIR}/mx_arrow_direction.c ${CMAKE_CURRENT_LIST_DIR}/mx_arrow_style.c + ${CMAKE_CURRENT_LIST_DIR}/mx_articulations.c + ${CMAKE_CURRENT_LIST_DIR}/mx_attributes.c + ${CMAKE_CURRENT_LIST_DIR}/mx_backup.c ${CMAKE_CURRENT_LIST_DIR}/mx_backward_forward.c ${CMAKE_CURRENT_LIST_DIR}/mx_bar_style.c + ${CMAKE_CURRENT_LIST_DIR}/mx_bar_style_color.c + ${CMAKE_CURRENT_LIST_DIR}/mx_barline.c + ${CMAKE_CURRENT_LIST_DIR}/mx_barre.c + ${CMAKE_CURRENT_LIST_DIR}/mx_bass.c + ${CMAKE_CURRENT_LIST_DIR}/mx_bass_alter.c + ${CMAKE_CURRENT_LIST_DIR}/mx_bass_step.c + ${CMAKE_CURRENT_LIST_DIR}/mx_beam.c ${CMAKE_CURRENT_LIST_DIR}/mx_beam_level.c ${CMAKE_CURRENT_LIST_DIR}/mx_beam_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_beat_repeat.c + ${CMAKE_CURRENT_LIST_DIR}/mx_beat_unit_tied.c + ${CMAKE_CURRENT_LIST_DIR}/mx_beater.c ${CMAKE_CURRENT_LIST_DIR}/mx_beater_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_bend.c + ${CMAKE_CURRENT_LIST_DIR}/mx_bookmark.c + ${CMAKE_CURRENT_LIST_DIR}/mx_bracket.c + ${CMAKE_CURRENT_LIST_DIR}/mx_breath_mark.c ${CMAKE_CURRENT_LIST_DIR}/mx_breath_mark_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_caesura.c ${CMAKE_CURRENT_LIST_DIR}/mx_caesura_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_cancel.c ${CMAKE_CURRENT_LIST_DIR}/mx_cancel_location.c ${CMAKE_CURRENT_LIST_DIR}/mx_circular_arrow.c + ${CMAKE_CURRENT_LIST_DIR}/mx_clef.c ${CMAKE_CURRENT_LIST_DIR}/mx_clef_sign.c + ${CMAKE_CURRENT_LIST_DIR}/mx_coda.c ${CMAKE_CURRENT_LIST_DIR}/mx_color.c ${CMAKE_CURRENT_LIST_DIR}/mx_comma_separated_text.c + ${CMAKE_CURRENT_LIST_DIR}/mx_credit.c ${CMAKE_CURRENT_LIST_DIR}/mx_css_font_size.c + ${CMAKE_CURRENT_LIST_DIR}/mx_dashes.c + ${CMAKE_CURRENT_LIST_DIR}/mx_defaults.c + ${CMAKE_CURRENT_LIST_DIR}/mx_degree.c + ${CMAKE_CURRENT_LIST_DIR}/mx_degree_alter.c ${CMAKE_CURRENT_LIST_DIR}/mx_degree_symbol_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_degree_type.c ${CMAKE_CURRENT_LIST_DIR}/mx_degree_type_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_degree_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_direction.c + ${CMAKE_CURRENT_LIST_DIR}/mx_direction_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_directive.c + ${CMAKE_CURRENT_LIST_DIR}/mx_distance.c ${CMAKE_CURRENT_LIST_DIR}/mx_distance_type.c ${CMAKE_CURRENT_LIST_DIR}/mx_divisions.c + ${CMAKE_CURRENT_LIST_DIR}/mx_document.c + ${CMAKE_CURRENT_LIST_DIR}/mx_dynamics.c ${CMAKE_CURRENT_LIST_DIR}/mx_effect.c + ${CMAKE_CURRENT_LIST_DIR}/mx_elision.c + ${CMAKE_CURRENT_LIST_DIR}/mx_empty.c + ${CMAKE_CURRENT_LIST_DIR}/mx_empty_font.c + ${CMAKE_CURRENT_LIST_DIR}/mx_empty_line.c + ${CMAKE_CURRENT_LIST_DIR}/mx_empty_placement.c + ${CMAKE_CURRENT_LIST_DIR}/mx_empty_placement_smufl.c + ${CMAKE_CURRENT_LIST_DIR}/mx_empty_print_object_style_align.c + ${CMAKE_CURRENT_LIST_DIR}/mx_empty_print_style_align_id.c + ${CMAKE_CURRENT_LIST_DIR}/mx_empty_trill_sound.c ${CMAKE_CURRENT_LIST_DIR}/mx_enclosure_shape.c + ${CMAKE_CURRENT_LIST_DIR}/mx_encoding.c + ${CMAKE_CURRENT_LIST_DIR}/mx_ending.c ${CMAKE_CURRENT_LIST_DIR}/mx_ending_number.c + ${CMAKE_CURRENT_LIST_DIR}/mx_extend.c ${CMAKE_CURRENT_LIST_DIR}/mx_fan.c + ${CMAKE_CURRENT_LIST_DIR}/mx_feature.c + ${CMAKE_CURRENT_LIST_DIR}/mx_fermata.c ${CMAKE_CURRENT_LIST_DIR}/mx_fermata_shape.c ${CMAKE_CURRENT_LIST_DIR}/mx_fifths.c + ${CMAKE_CURRENT_LIST_DIR}/mx_figure.c + ${CMAKE_CURRENT_LIST_DIR}/mx_figured_bass.c + ${CMAKE_CURRENT_LIST_DIR}/mx_fingering.c + ${CMAKE_CURRENT_LIST_DIR}/mx_first_fret.c ${CMAKE_CURRENT_LIST_DIR}/mx_font_size.c ${CMAKE_CURRENT_LIST_DIR}/mx_font_style.c ${CMAKE_CURRENT_LIST_DIR}/mx_font_weight.c + ${CMAKE_CURRENT_LIST_DIR}/mx_formatted_symbol_id.c + ${CMAKE_CURRENT_LIST_DIR}/mx_formatted_text.c + ${CMAKE_CURRENT_LIST_DIR}/mx_formatted_text_id.c + ${CMAKE_CURRENT_LIST_DIR}/mx_forward.c + ${CMAKE_CURRENT_LIST_DIR}/mx_frame.c + ${CMAKE_CURRENT_LIST_DIR}/mx_frame_note.c + ${CMAKE_CURRENT_LIST_DIR}/mx_fret.c + ${CMAKE_CURRENT_LIST_DIR}/mx_glass.c ${CMAKE_CURRENT_LIST_DIR}/mx_glass_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_glissando.c + ${CMAKE_CURRENT_LIST_DIR}/mx_glyph.c ${CMAKE_CURRENT_LIST_DIR}/mx_glyph_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_grace.c + ${CMAKE_CURRENT_LIST_DIR}/mx_group_barline.c ${CMAKE_CURRENT_LIST_DIR}/mx_group_barline_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_group_name.c + ${CMAKE_CURRENT_LIST_DIR}/mx_group_symbol.c ${CMAKE_CURRENT_LIST_DIR}/mx_group_symbol_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_grouping.c + ${CMAKE_CURRENT_LIST_DIR}/mx_hammer_on_pull_off.c + ${CMAKE_CURRENT_LIST_DIR}/mx_handbell.c ${CMAKE_CURRENT_LIST_DIR}/mx_handbell_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_harmon_closed.c ${CMAKE_CURRENT_LIST_DIR}/mx_harmon_closed_location.c ${CMAKE_CURRENT_LIST_DIR}/mx_harmon_closed_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_harmon_mute.c + ${CMAKE_CURRENT_LIST_DIR}/mx_harmonic.c + ${CMAKE_CURRENT_LIST_DIR}/mx_harmony.c ${CMAKE_CURRENT_LIST_DIR}/mx_harmony_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_harp_pedals.c + ${CMAKE_CURRENT_LIST_DIR}/mx_heel_toe.c + ${CMAKE_CURRENT_LIST_DIR}/mx_hole.c + ${CMAKE_CURRENT_LIST_DIR}/mx_hole_closed.c ${CMAKE_CURRENT_LIST_DIR}/mx_hole_closed_location.c ${CMAKE_CURRENT_LIST_DIR}/mx_hole_closed_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_horizontal_turn.c + ${CMAKE_CURRENT_LIST_DIR}/mx_identification.c + ${CMAKE_CURRENT_LIST_DIR}/mx_image.c + ${CMAKE_CURRENT_LIST_DIR}/mx_instrument.c ${CMAKE_CURRENT_LIST_DIR}/mx_instrument_sound.c + ${CMAKE_CURRENT_LIST_DIR}/mx_interchangeable.c + ${CMAKE_CURRENT_LIST_DIR}/mx_inversion.c + ${CMAKE_CURRENT_LIST_DIR}/mx_key.c + ${CMAKE_CURRENT_LIST_DIR}/mx_key_accidental.c + ${CMAKE_CURRENT_LIST_DIR}/mx_key_octave.c + ${CMAKE_CURRENT_LIST_DIR}/mx_kind.c ${CMAKE_CURRENT_LIST_DIR}/mx_kind_value.c ${CMAKE_CURRENT_LIST_DIR}/mx_left_center_right.c ${CMAKE_CURRENT_LIST_DIR}/mx_left_right.c + ${CMAKE_CURRENT_LIST_DIR}/mx_level.c ${CMAKE_CURRENT_LIST_DIR}/mx_line_end.c ${CMAKE_CURRENT_LIST_DIR}/mx_line_length.c ${CMAKE_CURRENT_LIST_DIR}/mx_line_shape.c ${CMAKE_CURRENT_LIST_DIR}/mx_line_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_line_width.c ${CMAKE_CURRENT_LIST_DIR}/mx_line_width_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_link.c + ${CMAKE_CURRENT_LIST_DIR}/mx_lyric.c + ${CMAKE_CURRENT_LIST_DIR}/mx_lyric_font.c + ${CMAKE_CURRENT_LIST_DIR}/mx_lyric_language.c ${CMAKE_CURRENT_LIST_DIR}/mx_margin_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_measure_layout.c + ${CMAKE_CURRENT_LIST_DIR}/mx_measure_numbering.c ${CMAKE_CURRENT_LIST_DIR}/mx_measure_numbering_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_measure_repeat.c + ${CMAKE_CURRENT_LIST_DIR}/mx_measure_style.c ${CMAKE_CURRENT_LIST_DIR}/mx_measure_text.c ${CMAKE_CURRENT_LIST_DIR}/mx_membrane.c ${CMAKE_CURRENT_LIST_DIR}/mx_metal.c + ${CMAKE_CURRENT_LIST_DIR}/mx_metronome.c + ${CMAKE_CURRENT_LIST_DIR}/mx_metronome_beam.c + ${CMAKE_CURRENT_LIST_DIR}/mx_metronome_note.c + ${CMAKE_CURRENT_LIST_DIR}/mx_metronome_tied.c + ${CMAKE_CURRENT_LIST_DIR}/mx_metronome_tuplet.c ${CMAKE_CURRENT_LIST_DIR}/mx_midi_128.c ${CMAKE_CURRENT_LIST_DIR}/mx_midi_16.c ${CMAKE_CURRENT_LIST_DIR}/mx_midi_16384.c + ${CMAKE_CURRENT_LIST_DIR}/mx_midi_device.c + ${CMAKE_CURRENT_LIST_DIR}/mx_midi_instrument.c ${CMAKE_CURRENT_LIST_DIR}/mx_millimeters.c + ${CMAKE_CURRENT_LIST_DIR}/mx_miscellaneous.c + ${CMAKE_CURRENT_LIST_DIR}/mx_miscellaneous_field.c ${CMAKE_CURRENT_LIST_DIR}/mx_mode.c + ${CMAKE_CURRENT_LIST_DIR}/mx_mordent.c + ${CMAKE_CURRENT_LIST_DIR}/mx_multiple_rest.c ${CMAKE_CURRENT_LIST_DIR}/mx_mute.c + ${CMAKE_CURRENT_LIST_DIR}/mx_name_display.c + ${CMAKE_CURRENT_LIST_DIR}/mx_non_arpeggiate.c ${CMAKE_CURRENT_LIST_DIR}/mx_non_negative_decimal.c + ${CMAKE_CURRENT_LIST_DIR}/mx_notations.c + ${CMAKE_CURRENT_LIST_DIR}/mx_note.c + ${CMAKE_CURRENT_LIST_DIR}/mx_note_size.c ${CMAKE_CURRENT_LIST_DIR}/mx_note_size_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_note_type.c ${CMAKE_CURRENT_LIST_DIR}/mx_note_type_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_notehead.c + ${CMAKE_CURRENT_LIST_DIR}/mx_notehead_text.c ${CMAKE_CURRENT_LIST_DIR}/mx_notehead_value.c ${CMAKE_CURRENT_LIST_DIR}/mx_number_level.c ${CMAKE_CURRENT_LIST_DIR}/mx_number_of_lines.c ${CMAKE_CURRENT_LIST_DIR}/mx_number_or_normal.c ${CMAKE_CURRENT_LIST_DIR}/mx_octave.c + ${CMAKE_CURRENT_LIST_DIR}/mx_octave_shift.c + ${CMAKE_CURRENT_LIST_DIR}/mx_offset.c ${CMAKE_CURRENT_LIST_DIR}/mx_on_off.c + ${CMAKE_CURRENT_LIST_DIR}/mx_opus.c + ${CMAKE_CURRENT_LIST_DIR}/mx_ornaments.c + ${CMAKE_CURRENT_LIST_DIR}/mx_other_appearance.c + ${CMAKE_CURRENT_LIST_DIR}/mx_other_direction.c + ${CMAKE_CURRENT_LIST_DIR}/mx_other_notation.c + ${CMAKE_CURRENT_LIST_DIR}/mx_other_placement_text.c + ${CMAKE_CURRENT_LIST_DIR}/mx_other_play.c + ${CMAKE_CURRENT_LIST_DIR}/mx_other_text.c ${CMAKE_CURRENT_LIST_DIR}/mx_over_under.c + ${CMAKE_CURRENT_LIST_DIR}/mx_page_layout.c + ${CMAKE_CURRENT_LIST_DIR}/mx_page_margins.c + ${CMAKE_CURRENT_LIST_DIR}/mx_part_group.c + ${CMAKE_CURRENT_LIST_DIR}/mx_part_list.c + ${CMAKE_CURRENT_LIST_DIR}/mx_part_name.c + ${CMAKE_CURRENT_LIST_DIR}/mx_part_symbol.c + ${CMAKE_CURRENT_LIST_DIR}/mx_partwise_measure.c + ${CMAKE_CURRENT_LIST_DIR}/mx_partwise_part.c + ${CMAKE_CURRENT_LIST_DIR}/mx_pedal.c + ${CMAKE_CURRENT_LIST_DIR}/mx_pedal_tuning.c ${CMAKE_CURRENT_LIST_DIR}/mx_pedal_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_per_minute.c ${CMAKE_CURRENT_LIST_DIR}/mx_percent.c + ${CMAKE_CURRENT_LIST_DIR}/mx_percussion.c + ${CMAKE_CURRENT_LIST_DIR}/mx_pitch.c + ${CMAKE_CURRENT_LIST_DIR}/mx_pitched.c ${CMAKE_CURRENT_LIST_DIR}/mx_pitched_value.c + ${CMAKE_CURRENT_LIST_DIR}/mx_placement_text.c + ${CMAKE_CURRENT_LIST_DIR}/mx_play.c ${CMAKE_CURRENT_LIST_DIR}/mx_positive_divisions.c ${CMAKE_CURRENT_LIST_DIR}/mx_positive_integer_or_empty.c + ${CMAKE_CURRENT_LIST_DIR}/mx_principal_voice.c ${CMAKE_CURRENT_LIST_DIR}/mx_principal_voice_symbol.c + ${CMAKE_CURRENT_LIST_DIR}/mx_print.c + ${CMAKE_CURRENT_LIST_DIR}/mx_repeat.c + ${CMAKE_CURRENT_LIST_DIR}/mx_rest.c ${CMAKE_CURRENT_LIST_DIR}/mx_right_left_middle.c + ${CMAKE_CURRENT_LIST_DIR}/mx_root.c + ${CMAKE_CURRENT_LIST_DIR}/mx_root_alter.c + ${CMAKE_CURRENT_LIST_DIR}/mx_root_step.c ${CMAKE_CURRENT_LIST_DIR}/mx_rotation_degrees.c ${CMAKE_CURRENT_LIST_DIR}/mx_runtime.c + ${CMAKE_CURRENT_LIST_DIR}/mx_scaling.c + ${CMAKE_CURRENT_LIST_DIR}/mx_scordatura.c + ${CMAKE_CURRENT_LIST_DIR}/mx_score_instrument.c + ${CMAKE_CURRENT_LIST_DIR}/mx_score_part.c + ${CMAKE_CURRENT_LIST_DIR}/mx_score_partwise.c + ${CMAKE_CURRENT_LIST_DIR}/mx_score_timewise.c + ${CMAKE_CURRENT_LIST_DIR}/mx_segno.c ${CMAKE_CURRENT_LIST_DIR}/mx_semi_pitched.c ${CMAKE_CURRENT_LIST_DIR}/mx_semitones.c ${CMAKE_CURRENT_LIST_DIR}/mx_show_frets.c ${CMAKE_CURRENT_LIST_DIR}/mx_show_tuplet.c + ${CMAKE_CURRENT_LIST_DIR}/mx_slash.c + ${CMAKE_CURRENT_LIST_DIR}/mx_slide.c + ${CMAKE_CURRENT_LIST_DIR}/mx_slur.c ${CMAKE_CURRENT_LIST_DIR}/mx_smufl_accidental_glyph_name.c ${CMAKE_CURRENT_LIST_DIR}/mx_smufl_coda_glyph_name.c ${CMAKE_CURRENT_LIST_DIR}/mx_smufl_glyph_name.c ${CMAKE_CURRENT_LIST_DIR}/mx_smufl_lyrics_glyph_name.c ${CMAKE_CURRENT_LIST_DIR}/mx_smufl_pictogram_glyph_name.c ${CMAKE_CURRENT_LIST_DIR}/mx_smufl_segno_glyph_name.c + ${CMAKE_CURRENT_LIST_DIR}/mx_sound.c ${CMAKE_CURRENT_LIST_DIR}/mx_sound_id.c + ${CMAKE_CURRENT_LIST_DIR}/mx_staff_details.c + ${CMAKE_CURRENT_LIST_DIR}/mx_staff_divide.c ${CMAKE_CURRENT_LIST_DIR}/mx_staff_divide_symbol.c + ${CMAKE_CURRENT_LIST_DIR}/mx_staff_layout.c ${CMAKE_CURRENT_LIST_DIR}/mx_staff_line.c ${CMAKE_CURRENT_LIST_DIR}/mx_staff_number.c + ${CMAKE_CURRENT_LIST_DIR}/mx_staff_tuning.c ${CMAKE_CURRENT_LIST_DIR}/mx_staff_type.c ${CMAKE_CURRENT_LIST_DIR}/mx_start_note.c ${CMAKE_CURRENT_LIST_DIR}/mx_start_stop.c ${CMAKE_CURRENT_LIST_DIR}/mx_start_stop_continue.c ${CMAKE_CURRENT_LIST_DIR}/mx_start_stop_discontinue.c ${CMAKE_CURRENT_LIST_DIR}/mx_start_stop_single.c + ${CMAKE_CURRENT_LIST_DIR}/mx_stem.c ${CMAKE_CURRENT_LIST_DIR}/mx_stem_value.c ${CMAKE_CURRENT_LIST_DIR}/mx_step.c + ${CMAKE_CURRENT_LIST_DIR}/mx_stick.c ${CMAKE_CURRENT_LIST_DIR}/mx_stick_location.c ${CMAKE_CURRENT_LIST_DIR}/mx_stick_material.c ${CMAKE_CURRENT_LIST_DIR}/mx_stick_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_string.c + ${CMAKE_CURRENT_LIST_DIR}/mx_string_mute.c ${CMAKE_CURRENT_LIST_DIR}/mx_string_number.c + ${CMAKE_CURRENT_LIST_DIR}/mx_strong_accent.c + ${CMAKE_CURRENT_LIST_DIR}/mx_style_text.c + ${CMAKE_CURRENT_LIST_DIR}/mx_supports.c ${CMAKE_CURRENT_LIST_DIR}/mx_syllabic.c ${CMAKE_CURRENT_LIST_DIR}/mx_symbol_size.c + ${CMAKE_CURRENT_LIST_DIR}/mx_system_dividers.c + ${CMAKE_CURRENT_LIST_DIR}/mx_system_layout.c + ${CMAKE_CURRENT_LIST_DIR}/mx_system_margins.c + ${CMAKE_CURRENT_LIST_DIR}/mx_tap.c ${CMAKE_CURRENT_LIST_DIR}/mx_tap_hand.c + ${CMAKE_CURRENT_LIST_DIR}/mx_technical.c ${CMAKE_CURRENT_LIST_DIR}/mx_tenths.c ${CMAKE_CURRENT_LIST_DIR}/mx_text_direction.c + ${CMAKE_CURRENT_LIST_DIR}/mx_text_element_data.c + ${CMAKE_CURRENT_LIST_DIR}/mx_tie.c + ${CMAKE_CURRENT_LIST_DIR}/mx_tied.c ${CMAKE_CURRENT_LIST_DIR}/mx_tied_type.c + ${CMAKE_CURRENT_LIST_DIR}/mx_time.c + ${CMAKE_CURRENT_LIST_DIR}/mx_time_modification.c ${CMAKE_CURRENT_LIST_DIR}/mx_time_only.c ${CMAKE_CURRENT_LIST_DIR}/mx_time_relation.c ${CMAKE_CURRENT_LIST_DIR}/mx_time_separator.c ${CMAKE_CURRENT_LIST_DIR}/mx_time_symbol.c + ${CMAKE_CURRENT_LIST_DIR}/mx_timewise_measure.c + ${CMAKE_CURRENT_LIST_DIR}/mx_timewise_part.c ${CMAKE_CURRENT_LIST_DIR}/mx_tip_direction.c ${CMAKE_CURRENT_LIST_DIR}/mx_top_bottom.c + ${CMAKE_CURRENT_LIST_DIR}/mx_transpose.c + ${CMAKE_CURRENT_LIST_DIR}/mx_tremolo.c ${CMAKE_CURRENT_LIST_DIR}/mx_tremolo_marks.c ${CMAKE_CURRENT_LIST_DIR}/mx_tremolo_type.c ${CMAKE_CURRENT_LIST_DIR}/mx_trill_beats.c ${CMAKE_CURRENT_LIST_DIR}/mx_trill_step.c + ${CMAKE_CURRENT_LIST_DIR}/mx_tuplet.c + ${CMAKE_CURRENT_LIST_DIR}/mx_tuplet_dot.c + ${CMAKE_CURRENT_LIST_DIR}/mx_tuplet_number.c + ${CMAKE_CURRENT_LIST_DIR}/mx_tuplet_portion.c + ${CMAKE_CURRENT_LIST_DIR}/mx_tuplet_type.c ${CMAKE_CURRENT_LIST_DIR}/mx_two_note_turn.c + ${CMAKE_CURRENT_LIST_DIR}/mx_typed_text.c + ${CMAKE_CURRENT_LIST_DIR}/mx_unpitched.c ${CMAKE_CURRENT_LIST_DIR}/mx_up_down.c ${CMAKE_CURRENT_LIST_DIR}/mx_up_down_stop_continue.c ${CMAKE_CURRENT_LIST_DIR}/mx_upright_inverted.c ${CMAKE_CURRENT_LIST_DIR}/mx_valign.c ${CMAKE_CURRENT_LIST_DIR}/mx_valign_image.c + ${CMAKE_CURRENT_LIST_DIR}/mx_virtual_instrument.c + ${CMAKE_CURRENT_LIST_DIR}/mx_wavy_line.c + ${CMAKE_CURRENT_LIST_DIR}/mx_wedge.c ${CMAKE_CURRENT_LIST_DIR}/mx_wedge_type.c ${CMAKE_CURRENT_LIST_DIR}/mx_winged.c ${CMAKE_CURRENT_LIST_DIR}/mx_wood.c + ${CMAKE_CURRENT_LIST_DIR}/mx_work.c ${CMAKE_CURRENT_LIST_DIR}/mx_yes_no.c ${CMAKE_CURRENT_LIST_DIR}/mx_yes_no_number.c ${CMAKE_CURRENT_LIST_DIR}/mx_yyyy_mm_dd.c diff --git a/gen/test/c/src/compare.c b/gen/test/c/src/compare.c index 94ab4ec3e..78ddbbc73 100644 --- a/gen/test/c/src/compare.c +++ b/gen/test/c/src/compare.c @@ -26,14 +26,36 @@ static int is_equivalent(const char *a, const char *b) { return 0; } +/* The element's DIRECT text content only (not the subtree concatenation + xmlNodeGetContent computes): subtree text would re-compare every leaf at + every ancestor, and a numerically-equivalent leaf reformat ("1.0" -> "1") + would fail the ancestors' exact comparison. */ static char *node_text(xmlNodePtr node) { - xmlChar *c = xmlNodeGetContent(node); - if (!c) return strdup(""); - char *s = strdup((const char *)c); - xmlFree(c); + size_t len = 0; + for (xmlNodePtr c = node->children; c; c = c->next) + if (c->type == XML_TEXT_NODE || c->type == XML_CDATA_SECTION_NODE) + len += c->content ? strlen((const char *)c->content) : 0; + char *s = malloc(len + 1); + if (!s) abort(); + s[0] = '\0'; + for (xmlNodePtr c = node->children; c; c = c->next) + if (c->type == XML_TEXT_NODE || c->type == XML_CDATA_SECTION_NODE) + if (c->content) + strcat(s, (const char *)c->content); return s; } +/* The attribute's qualified name: a parsed document carries xlink:href as + (ns prefix "xlink", name "href") while a serialized one may carry the + literal name "xlink:href"; both must compare equal. */ +static void attr_qname(xmlAttrPtr a, char *buf, size_t cap) { + if (a->ns && a->ns->prefix) + snprintf(buf, cap, "%s:%s", (const char *)a->ns->prefix, + (const char *)a->name); + else + snprintf(buf, cap, "%s", (const char *)a->name); +} + static CompareResult do_compare(xmlNodePtr expected, xmlNodePtr actual, const char *path) { CompareResult r = {0, ""}; @@ -78,14 +100,17 @@ static CompareResult do_compare(xmlNodePtr expected, xmlNodePtr actual, xmlAttrPtr ea = expected->properties; xmlAttrPtr aa = actual->properties; while (ea && aa) { - xmlChar *ev = xmlGetProp(expected, ea->name); - xmlChar *av = xmlGetProp(actual, aa->name); - int name_eq = strcmp((const char *)ea->name, (const char *)aa->name) == 0; + char ename[128], aname[128]; + attr_qname(ea, ename, sizeof(ename)); + attr_qname(aa, aname, sizeof(aname)); + xmlChar *ev = xmlNodeListGetString(expected->doc, ea->children, 1); + xmlChar *av = xmlNodeListGetString(actual->doc, aa->children, 1); + int name_eq = strcmp(ename, aname) == 0; int val_eq = is_equivalent((const char *)ev, (const char *)av); if (!name_eq || !val_eq) { r.failed = 1; snprintf(r.detail, sizeof(r.detail), - "attribute mismatch at %s[@%s]", path, ea->name); + "attribute mismatch at %s[@%s]", path, ename); xmlFree(ev); xmlFree(av); return r; diff --git a/gen/test/c/src/main.c b/gen/test/c/src/main.c index d176aa489..14ab90328 100644 --- a/gen/test/c/src/main.c +++ b/gen/test/c/src/main.c @@ -25,11 +25,13 @@ int main(int argc, char **argv) { return 1; } - int pass = 0, fail = 0; + int pass = 0, fail = 0, skip = 0; for (int i = 0; i < files.count; i++) { char *name = to_test_name(files.paths[i], data_root); RoundtripResult result = run_core_roundtrip(files.paths[i]); - if (result.ok) { + if (result.skipped) { + skip++; + } else if (result.ok) { pass++; } else { fail++; @@ -38,7 +40,8 @@ int main(int argc, char **argv) { free(name); } - printf("\n%d tests: %d passed, %d failed\n", pass + fail, pass, fail); + printf("\n%d tests: %d passed, %d failed, %d skipped\n", + pass + fail + skip, pass, fail, skip); file_list_free(&files); xmlCleanupParser(); return fail > 0 ? 1 : 0; diff --git a/gen/test/c/src/normalize.c b/gen/test/c/src/normalize.c index f9e80b28f..d94e0a5e3 100644 --- a/gen/test/c/src/normalize.c +++ b/gen/test/c/src/normalize.c @@ -77,10 +77,21 @@ static void strip_decimal_zeros(xmlNodePtr node) { strip_decimal_zeros(child); } +static void sort_qname(xmlAttrPtr a, char *buf, size_t cap) { + if (a->ns && a->ns->prefix) + snprintf(buf, cap, "%s:%s", (const char *)a->ns->prefix, + (const char *)a->name); + else + snprintf(buf, cap, "%s", (const char *)a->name); +} + static int attr_cmp(const void *a, const void *b) { const xmlAttrPtr *aa = a; const xmlAttrPtr *bb = b; - return strcmp((const char *)(*aa)->name, (const char *)(*bb)->name); + char an[128], bn[128]; + sort_qname(*aa, an, sizeof(an)); + sort_qname(*bb, bn, sizeof(bn)); + return strcmp(an, bn); } static void sort_attributes(xmlNodePtr node) { @@ -110,16 +121,52 @@ static void sort_attributes(xmlNodePtr node) { sort_attributes(child); } -static void set_root_version(xmlNodePtr root) { +void set_root_version(xmlNodePtr root) { if (!root) return; xmlSetProp(root, (const xmlChar *)"version", (const xmlChar *)MUSICXML_VERSION); } +/* Remove whitespace-only text nodes from every element: pretty-printing + indentation in containers, including containers whose optional children + are all absent. MusicXML has no mixed content, and the rule applies to + expected and actual alike, so leaf values with real content are never + touched and the comparison stays symmetric. */ +static int is_blank_text(xmlNodePtr node) { + if (node->type != XML_TEXT_NODE && node->type != XML_CDATA_SECTION_NODE) + return 0; + xmlChar *content = xmlNodeGetContent(node); + int blank = 1; + for (const xmlChar *p = content; p && *p; p++) { + if (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\r') { + blank = 0; + break; + } + } + xmlFree(content); + return blank; +} + +static void strip_inter_element_whitespace(xmlNodePtr node) { + if (!node || node->type != XML_ELEMENT_NODE) return; + xmlNodePtr child = node->children; + while (child) { + xmlNodePtr next = child->next; + if (is_blank_text(child)) { + xmlUnlinkNode(child); + xmlFreeNode(child); + } else if (child->type == XML_ELEMENT_NODE) { + strip_inter_element_whitespace(child); + } + child = next; + } +} + void normalize(xmlDocPtr doc) { if (!doc) return; xmlNodePtr root = xmlDocGetRootElement(doc); set_root_version(root); + strip_inter_element_whitespace(root); strip_decimal_zeros(root); sort_attributes(root); } diff --git a/gen/test/c/src/normalize.h b/gen/test/c/src/normalize.h index 95743ef07..72a9e5f9f 100644 --- a/gen/test/c/src/normalize.h +++ b/gen/test/c/src/normalize.h @@ -5,4 +5,7 @@ void normalize(xmlDocPtr doc); +/* Pin the root version attribute to the corert baseline ("3.0"). */ +void set_root_version(xmlNodePtr root); + #endif diff --git a/gen/test/c/src/roundtrip.c b/gen/test/c/src/roundtrip.c index e7a0ce371..f9ff23902 100644 --- a/gen/test/c/src/roundtrip.c +++ b/gen/test/c/src/roundtrip.c @@ -3,14 +3,39 @@ #include "compare.h" #include "fixer.h" #include "normalize.h" -#include "stub.h" + +#include "mx_document.h" +#include "mx_runtime.h" #include #include +#include #include +/* The MusicXML version the generated model supports (the schema pinned in + this target's config.toml). Documents declaring a NEWER version may use + elements the model has no types for; MusicXML is backward compatible, so + older documents are fine. */ +#define MAX_SUPPORTED_MAJOR 3 +#define MAX_SUPPORTED_MINOR 1 + +/* Reports whether the root's version attribute declares a version newer + than the supported one. An absent attribute means MusicXML 1.0. */ +static int declared_version_exceeds(xmlDocPtr doc) { + xmlNodePtr root = xmlDocGetRootElement(doc); + if (!root) + return 0; + xmlChar *version = xmlGetProp(root, (const xmlChar *)"version"); + int major = 1, minor = 0; + if (version && version[0]) + sscanf((const char *)version, "%d.%d", &major, &minor); + xmlFree(version); + return major > MAX_SUPPORTED_MAJOR || + (major == MAX_SUPPORTED_MAJOR && minor > MAX_SUPPORTED_MINOR); +} + RoundtripResult run_core_roundtrip(const char *abs_input_path) { - RoundtripResult r = {0, ""}; + RoundtripResult r = {0, 0, ""}; xmlDocPtr input_doc = xmlReadFile(abs_input_path, NULL, XML_PARSE_NONET); if (!input_doc) { @@ -19,22 +44,34 @@ RoundtripResult run_core_roundtrip(const char *abs_input_path) { return r; } - void *model = NULL; - if (mx_from_xdoc(input_doc, &model) != 0) { + if (declared_version_exceeds(input_doc)) { snprintf(r.message, sizeof(r.message), - "from_xdoc: generated parser not implemented"); + "declares MusicXML > %d.%d; this target generates from the " + "%d.%d schema", + MAX_SUPPORTED_MAJOR, MAX_SUPPORTED_MINOR, + MAX_SUPPORTED_MAJOR, MAX_SUPPORTED_MINOR); + r.skipped = 1; + xmlFreeDoc(input_doc); + return r; + } + + set_root_version(xmlDocGetRootElement(input_doc)); + + MxDocument *model = mx_document_from_xdoc(input_doc); + if (!model) { + snprintf(r.message, sizeof(r.message), "from_xdoc: %s", mx_error()); xmlFreeDoc(input_doc); return r; } xmlDocPtr actual_doc = NULL; - if (mx_to_xdoc(model, &actual_doc) != 0) { - snprintf(r.message, sizeof(r.message), "to_xdoc failed"); - mx_model_free(model); + if (mx_document_to_xdoc(model, &actual_doc) != 0) { + snprintf(r.message, sizeof(r.message), "to_xdoc: %s", mx_error()); + mx_document_free(model); xmlFreeDoc(input_doc); return r; } - mx_model_free(model); + mx_document_free(model); normalize(actual_doc); xmlDocPtr expected_doc = xmlReadFile(abs_input_path, NULL, XML_PARSE_NONET); diff --git a/gen/test/c/src/roundtrip.h b/gen/test/c/src/roundtrip.h index 6ec4a7d58..1e260247a 100644 --- a/gen/test/c/src/roundtrip.h +++ b/gen/test/c/src/roundtrip.h @@ -3,6 +3,7 @@ typedef struct { int ok; + int skipped; /* document declares a newer MusicXML than this target */ char message[1024]; } RoundtripResult; diff --git a/gen/test/c/src/stub.c b/gen/test/c/src/stub.c deleted file mode 100644 index 073e382b2..000000000 --- a/gen/test/c/src/stub.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "stub.h" - -int mx_from_xdoc(xmlDocPtr input, void **model_out) { - (void)input; - *model_out = NULL; - return -1; /* not implemented */ -} - -int mx_to_xdoc(void *model, xmlDocPtr *output) { - (void)model; - *output = NULL; - return -1; /* not implemented */ -} - -void mx_model_free(void *model) { - (void)model; -} diff --git a/gen/test/c/src/stub.h b/gen/test/c/src/stub.h deleted file mode 100644 index 552db1b91..000000000 --- a/gen/test/c/src/stub.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef MX_CORERT_STUB_H -#define MX_CORERT_STUB_H - -#include - -// Stub for the generated parser. Returns non-zero (failure) until the -// generator emits the C typed model. -int mx_from_xdoc(xmlDocPtr input, void **model_out); -int mx_to_xdoc(void *model, xmlDocPtr *output); -void mx_model_free(void *model); - -#endif From 60ec9abaa56a8eb1be791f61f0dbfcc1eebdbc85 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 19:27:30 +0000 Subject: [PATCH 24/45] gen: incorporate complex-type architecture review; land the decision record The architecture review of the complex-type milestones accepted findings: The decision record (the review's top insistence): plates.md section 11 gains the round-3 entry -- the ordered-children representation and why the original per-member-field sketch cannot round-trip MusicXML (interleaved music-data, metronome's repeated beat-unit), the no-discriminator rationale (harmony's ), presence-tracked required attributes, the strict-names/lenient-structure/lenient-values contract (the generated packages are order-faithful typed DOMs, not validating bindings; content and cardinality stay on the plates for the C++ backend and the JSON Schema forcing function), and an explicit instruction that C++ should use a real sum type rather than copying this encoding. Sections 8.1/8.7 now point at it instead of contradicting it. AGENTS.md's normalization-pipeline section documents the whitespace stripping, qualified-name attribute handling, direct-text comparison, and encoding transcoding the C++ harness will need. Single-sourced facts: - Plates.schema_version (parsed from the source stem) is emitted into each runtime (SupportedMusicXMLVersion, MX_SUPPORTED_MUSICXML_VERSION) and the corert harnesses read it: retargeting a schema cannot leave a stale version gate. The hand-kept 3.1 constants are gone. - The duplicated shape queries moved beside the data: attribute_members / element_members / value_member, ComplexPlate.members_view() (the strategy-resolved member list), and Plates.children_owner() (base-chain walking was schema reasoning inside a template) live in gen/plates/model; both backends consume them, and a third backend will too. Identifier guards: the few names the backends still compose (per-type Child structs, Children/has_/children_count fields, the document support types) are now guarded at render time -- a schema name landing on one fails loud with a rename suggestion instead of surfacing as a compile error in generated code. Serializing a child with zero or multiple fields set is documented as undefined on the Child types. Also: status docs refreshed (both corert suites green; generated models committed; C++ backend the remaining gap). Verified after regeneration: 83 unit tests, gofmt-clean Go build/vet, zero-warning C build, both smoke binaries, and both corert suites green (776/0/52). Valgrind over the entire C corert run: 52.9M allocations, zero leaks, zero errors. --- AGENTS.md | 52 +++++++++++++++-------- docs/ai/design/plates.md | 47 ++++++++++++++++++-- gen/README.md | 15 ++++--- gen/emit/c/__init__.py | 47 +++++++++++++++++--- gen/emit/c/complexes.py | 34 ++++++--------- gen/emit/c/runtime.py | 12 +++++- gen/emit/go/__init__.py | 41 +++++++++++++++--- gen/emit/go/complexes.py | 38 ++++++----------- gen/emit/go/runtime.py | 10 ++++- gen/plates/build.py | 4 ++ gen/plates/model.py | 36 ++++++++++++++++ gen/test/c/mx/mx_accord.h | 4 +- gen/test/c/mx/mx_accordion_registration.h | 4 +- gen/test/c/mx/mx_appearance.h | 4 +- gen/test/c/mx/mx_arrow.h | 4 +- gen/test/c/mx/mx_articulations.h | 4 +- gen/test/c/mx/mx_attributes.h | 4 +- gen/test/c/mx/mx_backup.h | 4 +- gen/test/c/mx/mx_barline.h | 4 +- gen/test/c/mx/mx_bass.h | 4 +- gen/test/c/mx/mx_beat_repeat.h | 4 +- gen/test/c/mx/mx_beat_unit_tied.h | 4 +- gen/test/c/mx/mx_bend.h | 4 +- gen/test/c/mx/mx_clef.h | 4 +- gen/test/c/mx/mx_credit.h | 4 +- gen/test/c/mx/mx_defaults.h | 4 +- gen/test/c/mx/mx_degree.h | 4 +- gen/test/c/mx/mx_direction.h | 4 +- gen/test/c/mx/mx_direction_type.h | 4 +- gen/test/c/mx/mx_dynamics.h | 4 +- gen/test/c/mx/mx_encoding.h | 4 +- gen/test/c/mx/mx_figure.h | 4 +- gen/test/c/mx/mx_figured_bass.h | 4 +- gen/test/c/mx/mx_forward.h | 4 +- gen/test/c/mx/mx_frame.h | 4 +- gen/test/c/mx/mx_frame_note.h | 4 +- gen/test/c/mx/mx_grouping.h | 4 +- gen/test/c/mx/mx_harmon_mute.h | 4 +- gen/test/c/mx/mx_harmonic.h | 4 +- gen/test/c/mx/mx_harmony.h | 4 +- gen/test/c/mx/mx_harp_pedals.h | 4 +- gen/test/c/mx/mx_hole.h | 4 +- gen/test/c/mx/mx_identification.h | 4 +- gen/test/c/mx/mx_interchangeable.h | 4 +- gen/test/c/mx/mx_key.h | 4 +- gen/test/c/mx/mx_lyric.h | 4 +- gen/test/c/mx/mx_measure_layout.h | 4 +- gen/test/c/mx/mx_measure_style.h | 4 +- gen/test/c/mx/mx_metronome.h | 4 +- gen/test/c/mx/mx_metronome_note.h | 4 +- gen/test/c/mx/mx_metronome_tuplet.h | 4 +- gen/test/c/mx/mx_midi_instrument.h | 4 +- gen/test/c/mx/mx_miscellaneous.h | 4 +- gen/test/c/mx/mx_name_display.h | 4 +- gen/test/c/mx/mx_notations.h | 4 +- gen/test/c/mx/mx_note.h | 4 +- gen/test/c/mx/mx_notehead_text.h | 4 +- gen/test/c/mx/mx_ornaments.h | 4 +- gen/test/c/mx/mx_page_layout.h | 4 +- gen/test/c/mx/mx_page_margins.h | 4 +- gen/test/c/mx/mx_part_group.h | 4 +- gen/test/c/mx/mx_part_list.h | 4 +- gen/test/c/mx/mx_partwise_measure.h | 4 +- gen/test/c/mx/mx_partwise_part.h | 4 +- gen/test/c/mx/mx_pedal_tuning.h | 4 +- gen/test/c/mx/mx_percussion.h | 4 +- gen/test/c/mx/mx_pitch.h | 4 +- gen/test/c/mx/mx_play.h | 4 +- gen/test/c/mx/mx_print.h | 4 +- gen/test/c/mx/mx_rest.h | 4 +- gen/test/c/mx/mx_root.h | 4 +- gen/test/c/mx/mx_runtime.h | 5 +++ gen/test/c/mx/mx_scaling.h | 4 +- gen/test/c/mx/mx_scordatura.h | 4 +- gen/test/c/mx/mx_score_instrument.h | 4 +- gen/test/c/mx/mx_score_part.h | 4 +- gen/test/c/mx/mx_score_partwise.h | 4 +- gen/test/c/mx/mx_score_timewise.h | 4 +- gen/test/c/mx/mx_slash.h | 4 +- gen/test/c/mx/mx_sound.h | 4 +- gen/test/c/mx/mx_staff_details.h | 4 +- gen/test/c/mx/mx_staff_layout.h | 4 +- gen/test/c/mx/mx_staff_tuning.h | 4 +- gen/test/c/mx/mx_stick.h | 4 +- gen/test/c/mx/mx_system_dividers.h | 4 +- gen/test/c/mx/mx_system_layout.h | 4 +- gen/test/c/mx/mx_system_margins.h | 4 +- gen/test/c/mx/mx_technical.h | 4 +- gen/test/c/mx/mx_time.h | 4 +- gen/test/c/mx/mx_time_modification.h | 4 +- gen/test/c/mx/mx_timewise_measure.h | 4 +- gen/test/c/mx/mx_timewise_part.h | 4 +- gen/test/c/mx/mx_transpose.h | 4 +- gen/test/c/mx/mx_tuplet.h | 4 +- gen/test/c/mx/mx_tuplet_portion.h | 4 +- gen/test/c/mx/mx_unpitched.h | 4 +- gen/test/c/mx/mx_virtual_instrument.h | 4 +- gen/test/c/mx/mx_work.h | 4 +- gen/test/c/src/roundtrip.c | 27 ++++++------ gen/test/go/corert/roundtrip.go | 11 ++--- gen/test/go/mx/accord.go | 4 +- gen/test/go/mx/accordion_registration.go | 4 +- gen/test/go/mx/appearance.go | 4 +- gen/test/go/mx/arrow.go | 4 +- gen/test/go/mx/articulations.go | 4 +- gen/test/go/mx/attributes.go | 4 +- gen/test/go/mx/backup.go | 4 +- gen/test/go/mx/barline.go | 4 +- gen/test/go/mx/bass.go | 4 +- gen/test/go/mx/beat_repeat.go | 4 +- gen/test/go/mx/beat_unit_tied.go | 4 +- gen/test/go/mx/bend.go | 4 +- gen/test/go/mx/clef.go | 4 +- gen/test/go/mx/credit.go | 4 +- gen/test/go/mx/defaults.go | 4 +- gen/test/go/mx/degree.go | 4 +- gen/test/go/mx/direction.go | 4 +- gen/test/go/mx/direction_type.go | 4 +- gen/test/go/mx/dynamics.go | 4 +- gen/test/go/mx/encoding.go | 4 +- gen/test/go/mx/figure.go | 4 +- gen/test/go/mx/figured_bass.go | 4 +- gen/test/go/mx/forward.go | 4 +- gen/test/go/mx/frame.go | 4 +- gen/test/go/mx/frame_note.go | 4 +- gen/test/go/mx/grouping.go | 4 +- gen/test/go/mx/harmon_mute.go | 4 +- gen/test/go/mx/harmonic.go | 4 +- gen/test/go/mx/harmony.go | 4 +- gen/test/go/mx/harp_pedals.go | 4 +- gen/test/go/mx/hole.go | 4 +- gen/test/go/mx/identification.go | 4 +- gen/test/go/mx/interchangeable.go | 4 +- gen/test/go/mx/key.go | 4 +- gen/test/go/mx/lyric.go | 4 +- gen/test/go/mx/measure_layout.go | 4 +- gen/test/go/mx/measure_style.go | 4 +- gen/test/go/mx/metronome.go | 4 +- gen/test/go/mx/metronome_note.go | 4 +- gen/test/go/mx/midi_instrument.go | 4 +- gen/test/go/mx/miscellaneous.go | 4 +- gen/test/go/mx/name_display.go | 4 +- gen/test/go/mx/notations.go | 4 +- gen/test/go/mx/note.go | 4 +- gen/test/go/mx/notehead_text.go | 4 +- gen/test/go/mx/ornaments.go | 4 +- gen/test/go/mx/page_layout.go | 4 +- gen/test/go/mx/page_margins.go | 4 +- gen/test/go/mx/part_group.go | 4 +- gen/test/go/mx/part_list.go | 4 +- gen/test/go/mx/partwise_measure.go | 4 +- gen/test/go/mx/partwise_part.go | 4 +- gen/test/go/mx/pedal_tuning.go | 4 +- gen/test/go/mx/percussion.go | 4 +- gen/test/go/mx/pitch.go | 4 +- gen/test/go/mx/play.go | 4 +- gen/test/go/mx/print.go | 4 +- gen/test/go/mx/rest.go | 4 +- gen/test/go/mx/root.go | 4 +- gen/test/go/mx/runtime.go | 5 +++ gen/test/go/mx/scaling.go | 4 +- gen/test/go/mx/scordatura.go | 4 +- gen/test/go/mx/score_instrument.go | 4 +- gen/test/go/mx/score_part.go | 4 +- gen/test/go/mx/score_partwise.go | 4 +- gen/test/go/mx/score_timewise.go | 4 +- gen/test/go/mx/slash.go | 4 +- gen/test/go/mx/sound.go | 4 +- gen/test/go/mx/staff_details.go | 4 +- gen/test/go/mx/staff_layout.go | 4 +- gen/test/go/mx/staff_tuning.go | 4 +- gen/test/go/mx/stick.go | 4 +- gen/test/go/mx/system_dividers.go | 4 +- gen/test/go/mx/system_layout.go | 4 +- gen/test/go/mx/system_margins.go | 4 +- gen/test/go/mx/technical.go | 4 +- gen/test/go/mx/time.go | 4 +- gen/test/go/mx/time_modification.go | 4 +- gen/test/go/mx/timewise_measure.go | 4 +- gen/test/go/mx/timewise_part.go | 4 +- gen/test/go/mx/transpose.go | 4 +- gen/test/go/mx/tuplet.go | 4 +- gen/test/go/mx/tuplet_portion.go | 4 +- gen/test/go/mx/unpitched.go | 4 +- gen/test/go/mx/virtual_instrument.go | 4 +- gen/test/go/mx/work.go | 4 +- 186 files changed, 791 insertions(+), 277 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 434fdaebf..7daf456b5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,7 +26,7 @@ mx/ mxtest/file/ <- PathRoot.h (CMake-generated, gitignored) cpul/ <- vendored Catch2 test runner gen/ <- code generator system (see gen/README.md) - __main__.py <- CLI: analyze | ir | + __main__.py <- CLI: analyze | ir | plates | README.md <- architecture, IR glossary, XSD analysis xsd/ <- XSD parser + structural analysis ir/ <- resolved intermediate representation (IR) @@ -36,11 +36,12 @@ mx/ go.mod, go.sum <- Go module (etree dependency, vendored) vendor/ <- vendored Go deps corert/ <- test package (discover, fixer, normalize, roundtrip, test) - stub/ <- placeholder parser stubs (always return error) + mx/ <- the GENERATED Go model (committed; do not edit) test/c/ <- C corert test target config.toml <- C target configuration CMakeLists.txt <- CMake project using libxml2 - src/ <- C source (main, discover, fixer, normalize, compare, roundtrip, stub) + src/ <- C source (main, discover, fixer, normalize, compare, roundtrip) + mx/ <- the GENERATED C model (committed; do not edit) ``` ## Build system @@ -85,9 +86,11 @@ baseline. ### Current state -All three test targets (C++, Go, C) discover ~829 files and produce 100% failures because the -generated parser stubs always return "not implemented". This is the expected state until the -generator emits code. +The Go and C suites are GREEN: 776 files pass, 0 fail, and 52 skip (they declare MusicXML 4.0; +those targets generate from the 3.1 schema, and while MusicXML is backward compatible, a newer +document may use types an older model cannot represent -- the harnesses gate on the root's +declared version). The C++ target still has no generated `mx/core`, so corert C++ does not +compile yet; that is the remaining expected gap. ### Data directory conventions @@ -114,8 +117,20 @@ Applied to both expected and actual documents before comparison: 1. Set XML declaration: ``. 2. Set DOCTYPE based on root element name (`score-timewise` vs `score-partwise`). 3. Set root `version` attribute to `"3.0"`. -4. Strip trailing zeros from decimal fields (the list lives in `DecimalFields.h`). -5. Sort attributes alphabetically (must be last). +4. Strip whitespace-only text nodes from every element (pretty-printing indentation is not + content; MusicXML has no mixed content, and the rule is applied to both sides, so it stays + symmetric). +5. Strip trailing zeros from decimal fields (the list lives in `DecimalFields.h`). +6. Sort attributes alphabetically by QUALIFIED name (`xlink:href`, not `href`; must be last). + +Comparison details that took debugging to get right (the C++ harness will need the same when its +generated core lands): compare each element's DIRECT text only, never the subtree concatenation +(a numerically-equivalent leaf reformat would otherwise fail at every ancestor); compare +attributes by qualified name with entity-resolved values (a parsed `xlink:href` is (ns, href) +while a serialized one may be the literal name); the Go loader transcodes UTF-16 and ISO-8859-1 +to UTF-8 (libxml2 and pugixml auto-detect these; Go's encoding/xml does not). Documents whose +root declares a version newer than the target's generated `SupportedMusicXMLVersion` / +`MX_SUPPORTED_MUSICXML_VERSION` constant are skipped, not failed. ### Numeric equivalence @@ -126,8 +141,8 @@ their values instead of their string representations. Float comparison uses epsi The generator (`gen/`) is a Python program structured as a pipeline: parse the MusicXML XSD into a model (`gen/xsd/`), lower that into a resolved intermediate representation (`gen/ir/`), project the -IR onto a target as the Plates (`gen/plates/`, designed but not yet implemented), then emit code -from the plates via per-language templates. The IR data model preserves the schema's named structure +IR onto a target as the Plates (`gen/plates/`), then emit code from the plates via per-language +backends (`gen/emit/`). The IR data model preserves the schema's named structure (model groups, attribute groups, inheritance edges); `gen/ir/resolve.py` collapses it on demand into the flattened view an emitter consumes (attribute groups expanded, group refs spliced into content), so that splicing-and-deduping reasoning lives once rather than once per language. See `gen/README.md` @@ -164,10 +179,10 @@ the only place the IR depends on an input beyond the XSD; it is opt-in per targe stays a pure function of the schema. **Status.** The parse, IR, analysis, Plates, and emit stages exist (`python3 -m gen plates ---config C [--check]` dumps or gates the projection; `python3 -m gen ` emits). -Backends are landing bottom-up: the Go backend renders the four value shapes (the leaf types) -into `gen/test/go/mx/`; complex types, the document entry points, and the C backend are next. -Generated output is committed. +--config C [--check]` dumps or gates the projection; `python3 -m gen ` emits). The +Go and C backends are complete -- all value and complex shapes plus the document entry points -- +and both corert suites are green. Generated output is committed (`gen/test/go/mx/`, +`gen/test/c/mx/`). The C++ backend is not yet implemented. ## Language targets @@ -180,14 +195,15 @@ generated code builds on. ### Go (test target, `gen/test/go/`) MusicXML 3.1 *without* the sounds companion. Uses `github.com/beevik/etree` (vendored) for DOM-style -XML. The generated code will land in `gen/test/go/mx/`. Test runner uses Go's `testing` package with -subtests. +XML. The generated code lives in `gen/test/go/mx/` (committed). Test runner uses Go's `testing` +package with subtests; documents declaring a newer MusicXML than 3.1 are skipped. ### C (test target, `gen/test/c/`) MusicXML 3.1 *with* the sounds companion -- same schema as Go, so the two outputs differ only by the -fold. Uses libxml2 (apt package in Docker). The generated code will land in `gen/test/c/mx/`. Test -runner is a simple `main()` that prints pass/fail per file and a summary. +fold. Uses libxml2 (apt package in Docker). The generated code lives in `gen/test/c/mx/` +(committed), built as the `mx-c` static library plus a `values-smoke` test binary. Test runner is a +simple `main()` that prints pass/fail/skip per file and a summary. ## Key files to understand diff --git a/docs/ai/design/plates.md b/docs/ai/design/plates.md index a039121a6..261bf4637 100644 --- a/docs/ai/design/plates.md +++ b/docs/ai/design/plates.md @@ -477,7 +477,7 @@ re-derives the shape. The eight shapes and their default strategies: | value: string | `string-wrapper` | wrapper over the target string type, optional pattern | | value: union | `tagged-variant` | a small tagged variant over the member types | | complex: value | `value-class` | class with a `value` field (typed by `value_type`) + attrs | -| complex: composite | `composite-class` | class, one member per child element + attrs, order kept | +| complex: composite | `composite-class` | class with attrs + ordered children (see section 11, round 3) | | complex: empty | `flag` or `attrs-class` | bool if `presence_only`, else an attributes-only class | | complex: derived | `inherit` or `flatten` | base-class inheritance, or a flattened copy (8.4) | @@ -543,8 +543,10 @@ Both are exposed, because emitters need different views: - `ComplexPlate.content` is the resolved sequence/choice tree (from `Resolver.content`, groups spliced), for a target that cares about order and choice structure (a schema emitter). - `ComplexPlate.members` is the flat, deduped, cardinality-tagged member list (attributes from - `Resolver.attributes`/`all_attributes` + elements from `Resolver.elements`), for a code target - that emits one field per member. + `Resolver.attributes`/`all_attributes` + elements from `Resolver.flat_elements`), for a code + target's field list. Note that "one field per child element member" turned out to be + insufficient for round-trip fidelity -- see the ordered-children decision in section 11, + round 3: code targets emit attributes as fields and child elements as ONE ordered collection. ### 8.8 File / layout partitioning (optional) @@ -736,3 +738,42 @@ Revised after the second review round (the emit stage and its first two backends comments mention clamping only when clamp steps exist. - **An open string union member must be last** (it matches anything); both backends fail loud if a schema ever orders one earlier rather than silently emitting unreachable members. + +Revised after the third review round (the complex-type templates; both corert suites green): + +- **Ordered children, not one-field-per-member.** This document's original composite sketch (a + class with one member per child element, section 8.1/8.7) cannot round-trip MusicXML: a + measure's music-data interleaves note/backup/direction in document order, and metronome's + beat-unit legally appears twice in one instance -- per-member fields lose the interleaving. + The Go and C backends therefore emit attributes as presence-tracked fields (a pointer in Go; + `bool has_x` + value in C -- required attributes included, because the corert contract is + "write back exactly what was parsed" and corpus files do omit required attributes) and child + elements as ONE ordered collection of per-type Child structs whose typed pointers discriminate + by non-nil/non-NULL. No kind discriminator: harmony has a child element literally named `kind`, + so any synthetic field can collide in those languages. **The C++ backend should not copy this + encoding**: with real sum types the collision argument evaporates + (`std::vector>` or generated choice classes give document order and + exactly-one by construction), and a hybrid -- plain fields for pure-sequence composites, + ordered variants only for choice-bearing content -- is still generate-by-shape, derivable from + `content`. +- **The generated packages are order-faithful typed DOMs, not validating bindings.** Parsing is + strict about NAMES (an unknown attribute or element is an error; the version gate keeps newer + documents out, so an unknown name is a generator gap, not data) and lenient about STRUCTURE and + VALUES (`pitch` accepts its children in any order or multiplicity; values degrade per the clamp + policy). `Member.cardinality` and `ComplexPlate.content` are therefore unread by these two + backends -- they stay on the plates for the C++ backend and the JSON Schema forcing function, + which want the structural facts. +- **Version gating is generated, not hand-kept.** `Plates.schema_version` (parsed from the source + stem) is emitted into each runtime (`SupportedMusicXMLVersion`, + `MX_SUPPORTED_MUSICXML_VERSION`) and the corert harnesses read it, so retargeting a schema + cannot leave a stale gate. +- **Shape queries live beside the data.** `attribute_members`/`element_members`/`value_member`, + `ComplexPlate.members_view()` (the strategy-resolved member list), and + `Plates.children_owner()` (the base-chain plate whose child struct holds an inheriting type's + children) moved out of the templates into `gen/plates/model.py`, so a third backend consumes + decisions instead of copying them. +- **Backend-composed identifiers are guarded.** A few names are still composed in templates (the + per-type `Child` struct, the children/presence fields, the document support types). Each + backend fails loud at render time if a projected identifier lands on one, so the collision + story stays airtight even where the gate cannot see; serializing a child with zero or multiple + fields set is documented as undefined (first non-nil wins; all-nil writes nothing). diff --git a/gen/README.md b/gen/README.md index 094577605..c241a795a 100644 --- a/gen/README.md +++ b/gen/README.md @@ -9,8 +9,8 @@ build system, Docker toolchain, and corert test, see [`../AGENTS.md`](../AGENTS. ### Pipeline ``` -XSD file --parse--> XSD model --lower--> IR --project--> Plates --emit--> C++ / Go / C - (gen.xsd) (gen.ir) (gen.plates, designed) (templates, not yet built) +XSD file --parse--> XSD model --lower--> IR --project--> Plates --emit--> Go / C (/ C++) + (gen.xsd) (gen.ir) (gen.plates) (gen.emit) ``` 1. Parse (`gen/xsd/`) reads the XSD into a model mirroring it 1:1, still speaking XSD: restriction @@ -24,8 +24,9 @@ XSD file --parse--> XSD model --lower--> IR --project--> Plates --emit--> **Plates**. Designed in [`../docs/ai/design/plates.md`](../docs/ai/design/plates.md). 4. Emit (`gen/emit/`) renders each plate through a per-language backend ("dumb renderers": walk the plate, print text, no naming logic; see `gen/emit/__init__.py` for the backend contract). - The Go backend currently renders the four value shapes; complex types and the C backend are - landing bottom-up. + The Go and C backends are complete (all value and complex shapes plus the document entry + points); both corert suites round-trip the corpus green. The C++ backend is not yet + implemented. ### Layout @@ -49,6 +50,10 @@ gen/ languages.py per-language defaults (type maps, reserved words, doc styles) build.py the projection: IR + config -> Plates check.py post-projection collision detection + emit/ + writer.py deterministic output (write-if-changed, marker-gated pruning) + go/ Go backend: values, complexes, document, runtime, gofmt pass + c/ C backend: api (calling convention), values, complexes, document, runtime cpp/, test/go/, test/c/ per-target config and corert test harnesses ``` @@ -91,7 +96,7 @@ type-shaping knob: it selects an *input*, not how a type is emitted. python3 -m gen analyze [xsd] # structural analysis report (text) python3 -m gen ir [--type NAME] [--resolve] [--config C] [xsd] # lower to IR, print as JSON python3 -m gen plates --config C [--type NAME] [--check] # project the IR onto a target, print as JSON -python3 -m gen # emit code for a target (not yet implemented) +python3 -m gen # emit code for the target the config describes ``` `--resolve` prints the *collapsed* view of complex types (attribute groups flattened, model-group diff --git a/gen/emit/c/__init__.py b/gen/emit/c/__init__.py index 81063731a..c547dc439 100644 --- a/gen/emit/c/__init__.py +++ b/gen/emit/c/__init__.py @@ -24,16 +24,53 @@ from gen.plates.model import Plates -def render(plates: Plates) -> dict[str, str]: - rt = runtime_stem(plates) - doc_stem = document_stem(plates) - reserved = (rt, doc_stem, "sources") +def _guard_identifiers(plates: Plates, reserved_stems: tuple[str, ...]) -> None: + """The plates' collision gate certifies projected identifiers; this + backend also COMPOSES a few (the per-type Child struct, the has_/children + fields, its support types). A schema name landing on one of those must + fail loud here, not as a confusing compile error in generated code.""" + from gen.plates.model import element_members + + prefix = plates.target.prefix + reserved_idents = {f"{prefix}Document", f"{prefix}Namespace"} + type_idents = {p.ident for p in list(plates.value_types) + list(plates.complex_types)} for plate in list(plates.value_types) + list(plates.complex_types): - if plate.file in reserved: + if plate.file in reserved_stems: raise ValueError( f"type '{plate.name.wire}' projects to the reserved file stem " f"'{plate.file}'; rename it in config.toml" ) + if plate.ident in reserved_idents: + raise ValueError( + f"type '{plate.name.wire}' projects to '{plate.ident}', which this " + f"backend reserves; rename it in config.toml" + ) + for plate in plates.complex_types: + members = plate.members_view() + elements = element_members(members) + if elements and f"{plate.ident}Child" in type_idents: + raise ValueError( + f"type '{plate.ident}Child' collides with '{plate.name.wire}'s " + f"child struct; rename one in config.toml" + ) + idents = {m.ident for m in members} + if elements and idents & {"children", "children_count"}: + raise ValueError( + f"'{plate.name.wire}' has a member projecting to a reserved " + f"children field; rename it in config.toml" + ) + for m in members: + if m.kind == "attribute" and f"has_{m.ident}" in idents: + raise ValueError( + f"'{plate.name.wire}': member 'has_{m.ident}' collides with " + f"the presence flag of attribute '{m.name.wire}'; rename one" + ) + + +def render(plates: Plates) -> dict[str, str]: + rt = runtime_stem(plates) + doc_stem = document_stem(plates) + _guard_identifiers(plates, (rt, doc_stem, "sources")) includes_of = { spec.file: spec.includes for spec in (plates.files or []) } diff --git a/gen/emit/c/complexes.py b/gen/emit/c/complexes.py index 5bfcb3357..d31e7a092 100644 --- a/gen/emit/c/complexes.py +++ b/gen/emit/c/complexes.py @@ -25,26 +25,14 @@ from gen.emit.c.api import CValue, value_api from gen.emit.c.common import c_string, doc_comment, fn_name, fn_prefix, header_file, impl_file -from gen.plates.model import ComplexPlate, Member, Plates - - -def _attr_members(members: list[Member]) -> list[Member]: - return [m for m in members if m.kind == "attribute"] - - -def _element_members(members: list[Member]) -> list[Member]: - return [m for m in members if m.kind == "element"] - - -def _value_member(members: list[Member]) -> Member | None: - return next((m for m in members if m.kind == "value"), None) - - -def _members_view(plate: ComplexPlate) -> list[Member]: - """C flattens derived types: the merged base-chain view when present.""" - if plate.strategy == "flatten" and plate.all_members is not None: - return plate.all_members - return plate.members +from gen.plates.model import ( + ComplexPlate, + Member, + Plates, + attribute_members as _attr_members, + element_members as _element_members, + value_member as _value_member, +) def _child_field(plates: Plates, member: Member) -> tuple[str, bool]: @@ -60,7 +48,7 @@ def _child_field(plates: Plates, member: Member) -> tuple[str, bool]: def complex_files(plates: Plates, plate: ComplexPlate, includes: list[str], rt: str): - members = _members_view(plate) + members = plate.members_view() attrs = _attr_members(members) elements = _element_members(members) value = _value_member(members) @@ -78,7 +66,9 @@ def complex_files(plates: Plates, plate: ComplexPlate, includes: list[str], rt: decl += [ f"/* One child element of {ident}: exactly one field is non-NULL,", " and that pointer says which element this is. Document order is", - " the array order on the owning struct. */", + " the array order on the owning struct. Zero or multiple fields", + " set is undefined: serialization writes the first non-NULL field", + " in schema order and nothing when all are NULL. */", "typedef struct {", ] for m in elements: diff --git a/gen/emit/c/runtime.py b/gen/emit/c/runtime.py index 7619b3434..9cd2c8b4f 100644 --- a/gen/emit/c/runtime.py +++ b/gen/emit/c/runtime.py @@ -159,7 +159,17 @@ def runtime_stem(plates: Plates) -> str: def runtime_header(plates: Plates, stem: str) -> str: - return header_file(plates, stem, _substitute(_HEADER_BODY, plates), [""]) + prefix = plates.target.prefix.upper() + "_" if plates.target.prefix else "" + version = [ + "/* The MusicXML version of the schema this model was generated from.", + " Documents declaring a newer version may use types this model cannot", + " represent; harnesses gate on it. */", + f'#define {prefix}SUPPORTED_MUSICXML_VERSION "{plates.schema_version}"', + "", + ] + return header_file( + plates, stem, version + _substitute(_HEADER_BODY, plates), [""] + ) def runtime_impl(plates: Plates, stem: str) -> str: diff --git a/gen/emit/go/__init__.py b/gen/emit/go/__init__.py index 84a7df5f4..03ef6bb67 100644 --- a/gen/emit/go/__init__.py +++ b/gen/emit/go/__init__.py @@ -27,18 +27,49 @@ # landing on one would silently overwrite it in the manifest. _RESERVED_STEMS = ("runtime", "document") +# Type identifiers this backend declares itself (document.go). +_RESERVED_IDENTS = ("Document", "ExtraAttr") -def render(plates: Plates) -> dict[str, str]: - files: dict[str, str] = { - "runtime.go": runtime_file(plates), - "document.go": document_file(plates), - } + +def _guard_identifiers(plates: Plates) -> None: + """The plates' collision gate certifies projected identifiers; this + backend also COMPOSES a few (the per-type Child struct, the Children + field, its support types). A schema name landing on one of those must + fail loud here, not as a confusing compile error in generated code.""" + from gen.plates.model import element_members + + type_idents = {p.ident for p in list(plates.value_types) + list(plates.complex_types)} for plate in list(plates.value_types) + list(plates.complex_types): if plate.file in _RESERVED_STEMS: raise ValueError( f"type '{plate.name.wire}' projects to the reserved file stem " f"'{plate.file}'; rename it in config.toml" ) + if plate.ident in _RESERVED_IDENTS: + raise ValueError( + f"type '{plate.name.wire}' projects to '{plate.ident}', which this " + f"backend reserves; rename it in config.toml" + ) + for plate in plates.complex_types: + elements = element_members(plate.members) + if elements and f"{plate.ident}Child" in type_idents: + raise ValueError( + f"type '{plate.ident}Child' collides with '{plate.name.wire}'s " + f"child struct; rename one in config.toml" + ) + if elements and any(m.ident == "Children" for m in plate.members): + raise ValueError( + f"'{plate.name.wire}' has a member projecting to 'Children', " + f"which this backend reserves; rename it in config.toml" + ) + + +def render(plates: Plates) -> dict[str, str]: + _guard_identifiers(plates) + files: dict[str, str] = { + "runtime.go": runtime_file(plates), + "document.go": document_file(plates), + } for plate in plates.value_types: files[plate.file + ".go"] = value_file(plates, plate) for plate in plates.complex_types: diff --git a/gen/emit/go/complexes.py b/gen/emit/go/complexes.py index 613b67343..02b393686 100644 --- a/gen/emit/go/complexes.py +++ b/gen/emit/go/complexes.py @@ -29,7 +29,14 @@ from __future__ import annotations from gen.emit.go.common import doc_comment, file_frame, go_string -from gen.plates.model import ComplexPlate, Member, Plates +from gen.plates.model import ( + ComplexPlate, + Member, + Plates, + attribute_members as _attr_members, + element_members as _element_members, + value_member as _value_member, +) # IR primitive -> (Go type, parse expr template, to-string expr template). _PRIM = { @@ -77,18 +84,6 @@ def complex_file(plates: Plates, plate: ComplexPlate) -> str: # --------------------------------------------------------------------------- # -def _attr_members(members: list[Member]) -> list[Member]: - return [m for m in members if m.kind == "attribute"] - - -def _element_members(members: list[Member]) -> list[Member]: - return [m for m in members if m.kind == "element"] - - -def _value_member(members: list[Member]) -> Member | None: - return next((m for m in members if m.kind == "value"), None) - - def _struct_lines(plates: Plates, plate: ComplexPlate, members: list[Member], embed: str | None, children_of: str | None) -> list[str]: wrap = plates.target.doc_style.wrap @@ -114,7 +109,9 @@ def _child_struct_lines(plates: Plates, plate: ComplexPlate) -> list[str]: f"// {ident}Child is one child element of {ident}: exactly one field", "// is non-nil, and that pointer says which element this is. (No kind", "// discriminator: schema element names like harmony's would", - "// collide with a synthetic field.)", + "// collide with a synthetic field.) Constructing a child with zero or", + "// multiple fields set is undefined: serialization writes the first", + "// non-nil field in schema order and nothing when all are nil.", f"type {ident}Child struct {{", ] for m in elements: @@ -265,21 +262,10 @@ def _class_body(plates: Plates, plate: ComplexPlate) -> list[str]: # --------------------------------------------------------------------------- # -def _children_owner(plates: Plates, plate: ComplexPlate) -> ComplexPlate | None: - """The base-chain plate whose Child struct holds this type's children: - the nearest ancestor (or self) with element members.""" - cur = plate - while cur is not None: - if _element_members(cur.members): - return cur - cur = plates.plate(cur.base.wire) if cur.base is not None else None - return None - - def _inherit_body(plates: Plates, plate: ComplexPlate) -> list[str]: ident = plate.ident members = plate.all_members or plate.members - owner = _children_owner(plates, plate) + owner = plates.children_owner(plate) lines = _struct_lines( plates, plate, plate.members, # own attrs only: the base is embedded diff --git a/gen/emit/go/runtime.py b/gen/emit/go/runtime.py index a9cec425f..517f05d00 100644 --- a/gen/emit/go/runtime.py +++ b/gen/emit/go/runtime.py @@ -11,6 +11,13 @@ from gen.emit.go.common import file_frame from gen.plates.model import Plates +_VERSION = '''\ +// SupportedMusicXMLVersion is the MusicXML version of the schema this +// package was generated from. Documents declaring a newer version may use +// types this model cannot represent; harnesses gate on it. +const SupportedMusicXMLVersion = "{0}" +''' + _BODY = '''\ // tryParseDecimal parses s strictly as a decimal number. func tryParseDecimal(s string) (float64, bool) { @@ -66,4 +73,5 @@ def runtime_file(plates: Plates) -> str: - return file_frame(plates, _BODY.split("\n"), imports=["math", "strconv", "strings"]) + body = _VERSION.format(plates.schema_version).split("\n") + _BODY.split("\n") + return file_frame(plates, body, imports=["math", "strconv", "strings"]) diff --git a/gen/plates/build.py b/gen/plates/build.py index a065a20e7..461c26ba5 100644 --- a/gen/plates/build.py +++ b/gen/plates/build.py @@ -17,6 +17,8 @@ from __future__ import annotations +import re + from gen.config import Config from gen.ir import model as ir from gen.ir.build import PRIMITIVES @@ -158,8 +160,10 @@ def build(self) -> Plates: if errors: raise PlatesError(errors) + version = re.search(r"musicxml-(\d+\.\d+)", self.m.source) plates = Plates( source=self.m.source, + schema_version=version.group(1) if version else "", target=self._target_info(), value_types=[self._value_plate(v) for v in self.m.value_types], complex_types=[self._complex_plate(c) for c in self.m.complex_types], diff --git a/gen/plates/model.py b/gen/plates/model.py index 620857f35..0f7eeccd5 100644 --- a/gen/plates/model.py +++ b/gen/plates/model.py @@ -269,12 +269,35 @@ def member(self, wire: str, kind: str | None = None) -> Member: return m raise KeyError(f"{self.name.wire}: no member {wire!r} (kind={kind})") + def members_view(self) -> list[Member]: + """The member list this plate's strategy renders: the merged + base-chain view when flattening a derived type, own members + otherwise. Backends render this; they never re-derive it.""" + if self.strategy == "flatten" and self.all_members is not None: + return self.all_members + return self.members + # --------------------------------------------------------------------------- # # The whole projected target # --------------------------------------------------------------------------- # +def attribute_members(members: list[Member]) -> list[Member]: + """The shape queries backends partition a member list with. They live + here, beside the data, so every backend asks the same question the same + way instead of filtering inline.""" + return [m for m in members if m.kind == "attribute"] + + +def element_members(members: list[Member]) -> list[Member]: + return [m for m in members if m.kind == "element"] + + +def value_member(members: list[Member]) -> Member | None: + return next((m for m in members if m.kind == "value"), None) + + @dataclass class FileSpec: """One output file when partitioning: its stem (extension is the @@ -295,6 +318,7 @@ class Plates: source: str # provenance: the XSD stem the IR was lowered from target: TargetInfo + schema_version: str = "" # the MusicXML version in the source stem ("3.1") value_types: list[ValuePlate] = field(default_factory=list) complex_types: list[ComplexPlate] = field(default_factory=list) roots: list[PlateRef] = field(default_factory=list) @@ -312,3 +336,15 @@ def plate(self, wire: str) -> ValuePlate | ComplexPlate: def has_plate(self, wire: str) -> bool: return wire in self._index + + def children_owner(self, plate: ComplexPlate) -> ComplexPlate | None: + """For an inheriting target: the base-chain plate whose child struct + holds this type's children -- the nearest ancestor (or self) with + element members. Schema reasoning, so it lives here, not in a + template.""" + cur: ComplexPlate | None = plate + while cur is not None: + if element_members(cur.members): + return cur + cur = self.plate(cur.base.wire) if cur.base is not None else None + return None diff --git a/gen/test/c/mx/mx_accord.h b/gen/test/c/mx/mx_accord.h index fc22e47fc..5a169df70 100644 --- a/gen/test/c/mx/mx_accord.h +++ b/gen/test/c/mx/mx_accord.h @@ -17,7 +17,9 @@ */ /* One child element of MxAccord: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxStep *tuning_step; MxSemitones *tuning_alter; diff --git a/gen/test/c/mx/mx_accordion_registration.h b/gen/test/c/mx/mx_accordion_registration.h index 66d3bebca..54375e58d 100644 --- a/gen/test/c/mx/mx_accordion_registration.h +++ b/gen/test/c/mx/mx_accordion_registration.h @@ -26,7 +26,9 @@ */ /* One child element of MxAccordionRegistration: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxEmpty *accordion_high; MxAccordionMiddle *accordion_middle; diff --git a/gen/test/c/mx/mx_appearance.h b/gen/test/c/mx/mx_appearance.h index 7cf8d0fbd..4a169fa4d 100644 --- a/gen/test/c/mx/mx_appearance.h +++ b/gen/test/c/mx/mx_appearance.h @@ -20,7 +20,9 @@ */ /* One child element of MxAppearance: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxLineWidth *line_width; MxNoteSize *note_size; diff --git a/gen/test/c/mx/mx_arrow.h b/gen/test/c/mx/mx_arrow.h index e77387ca4..c107de7bd 100644 --- a/gen/test/c/mx/mx_arrow.h +++ b/gen/test/c/mx/mx_arrow.h @@ -28,7 +28,9 @@ */ /* One child element of MxArrow: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxArrowDirection *arrow_direction; MxArrowStyle *arrow_style; diff --git a/gen/test/c/mx/mx_articulations.h b/gen/test/c/mx/mx_articulations.h index e49e14934..54fd5fe53 100644 --- a/gen/test/c/mx/mx_articulations.h +++ b/gen/test/c/mx/mx_articulations.h @@ -18,7 +18,9 @@ */ /* One child element of MxArticulations: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxEmptyPlacement *accent; MxStrongAccent *strong_accent; diff --git a/gen/test/c/mx/mx_attributes.h b/gen/test/c/mx/mx_attributes.h index ef13977a6..572149887 100644 --- a/gen/test/c/mx/mx_attributes.h +++ b/gen/test/c/mx/mx_attributes.h @@ -25,7 +25,9 @@ */ /* One child element of MxAttributes: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxFormattedText *footnote; MxLevel *level; diff --git a/gen/test/c/mx/mx_backup.h b/gen/test/c/mx/mx_backup.h index e578043e8..638b28236 100644 --- a/gen/test/c/mx/mx_backup.h +++ b/gen/test/c/mx/mx_backup.h @@ -19,7 +19,9 @@ */ /* One child element of MxBackup: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxPositiveDivisions *duration; MxFormattedText *footnote; diff --git a/gen/test/c/mx/mx_barline.h b/gen/test/c/mx/mx_barline.h index 781788122..2c8299a3c 100644 --- a/gen/test/c/mx/mx_barline.h +++ b/gen/test/c/mx/mx_barline.h @@ -37,7 +37,9 @@ */ /* One child element of MxBarline: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxBarStyleColor *bar_style; MxFormattedText *footnote; diff --git a/gen/test/c/mx/mx_bass.h b/gen/test/c/mx/mx_bass.h index b656fafd6..da67ccc57 100644 --- a/gen/test/c/mx/mx_bass.h +++ b/gen/test/c/mx/mx_bass.h @@ -16,7 +16,9 @@ */ /* One child element of MxBass: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxBassStep *bass_step; MxBassAlter *bass_alter; diff --git a/gen/test/c/mx/mx_beat_repeat.h b/gen/test/c/mx/mx_beat_repeat.h index c402f7f49..70888d334 100644 --- a/gen/test/c/mx/mx_beat_repeat.h +++ b/gen/test/c/mx/mx_beat_repeat.h @@ -22,7 +22,9 @@ */ /* One child element of MxBeatRepeat: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxNoteTypeValue *slash_type; MxEmpty *slash_dot; diff --git a/gen/test/c/mx/mx_beat_unit_tied.h b/gen/test/c/mx/mx_beat_unit_tied.h index 320bd628f..3e8bfe2c4 100644 --- a/gen/test/c/mx/mx_beat_unit_tied.h +++ b/gen/test/c/mx/mx_beat_unit_tied.h @@ -17,7 +17,9 @@ */ /* One child element of MxBeatUnitTied: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxNoteTypeValue *beat_unit; MxEmpty *beat_unit_dot; diff --git a/gen/test/c/mx/mx_bend.h b/gen/test/c/mx/mx_bend.h index 5e3bb67a2..e04ac6bf5 100644 --- a/gen/test/c/mx/mx_bend.h +++ b/gen/test/c/mx/mx_bend.h @@ -29,7 +29,9 @@ */ /* One child element of MxBend: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxSemitones *bend_alter; MxEmpty *pre_bend; diff --git a/gen/test/c/mx/mx_clef.h b/gen/test/c/mx/mx_clef.h index 97dbca3b5..c676acc8e 100644 --- a/gen/test/c/mx/mx_clef.h +++ b/gen/test/c/mx/mx_clef.h @@ -33,7 +33,9 @@ */ /* One child element of MxClef: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxClefSign *sign; MxStaffLine *line; diff --git a/gen/test/c/mx/mx_credit.h b/gen/test/c/mx/mx_credit.h index 06e7e3ae9..bdad09c5e 100644 --- a/gen/test/c/mx/mx_credit.h +++ b/gen/test/c/mx/mx_credit.h @@ -31,7 +31,9 @@ */ /* One child element of MxCredit: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { char *credit_type; MxLink *link; diff --git a/gen/test/c/mx/mx_defaults.h b/gen/test/c/mx/mx_defaults.h index ef8eb1601..5fc751262 100644 --- a/gen/test/c/mx/mx_defaults.h +++ b/gen/test/c/mx/mx_defaults.h @@ -20,7 +20,9 @@ */ /* One child element of MxDefaults: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxScaling *scaling; MxPageLayout *page_layout; diff --git a/gen/test/c/mx/mx_degree.h b/gen/test/c/mx/mx_degree.h index b7fe85341..c2cdc0e61 100644 --- a/gen/test/c/mx/mx_degree.h +++ b/gen/test/c/mx/mx_degree.h @@ -21,7 +21,9 @@ */ /* One child element of MxDegree: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxDegreeValue *degree_value; MxDegreeAlter *degree_alter; diff --git a/gen/test/c/mx/mx_direction.h b/gen/test/c/mx/mx_direction.h index 0c0531cbf..b5df92fb8 100644 --- a/gen/test/c/mx/mx_direction.h +++ b/gen/test/c/mx/mx_direction.h @@ -26,7 +26,9 @@ */ /* One child element of MxDirection: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxDirectionType *direction_type; MxOffset *offset; diff --git a/gen/test/c/mx/mx_direction_type.h b/gen/test/c/mx/mx_direction_type.h index d50d5d2a1..bab178fd8 100644 --- a/gen/test/c/mx/mx_direction_type.h +++ b/gen/test/c/mx/mx_direction_type.h @@ -35,7 +35,9 @@ */ /* One child element of MxDirectionType: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxFormattedTextID *rehearsal; MxSegno *segno; diff --git a/gen/test/c/mx/mx_dynamics.h b/gen/test/c/mx/mx_dynamics.h index f3ac316f0..dcb73ad63 100644 --- a/gen/test/c/mx/mx_dynamics.h +++ b/gen/test/c/mx/mx_dynamics.h @@ -36,7 +36,9 @@ */ /* One child element of MxDynamics: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxEmpty *p; MxEmpty *pp; diff --git a/gen/test/c/mx/mx_encoding.h b/gen/test/c/mx/mx_encoding.h index adaa5942f..3af2c6dac 100644 --- a/gen/test/c/mx/mx_encoding.h +++ b/gen/test/c/mx/mx_encoding.h @@ -18,7 +18,9 @@ */ /* One child element of MxEncoding: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxYyyyMmDd *encoding_date; MxTypedText *encoder; diff --git a/gen/test/c/mx/mx_figure.h b/gen/test/c/mx/mx_figure.h index 59950e7f9..243baca41 100644 --- a/gen/test/c/mx/mx_figure.h +++ b/gen/test/c/mx/mx_figure.h @@ -16,7 +16,9 @@ */ /* One child element of MxFigure: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxStyleText *prefix; MxStyleText *figure_number; diff --git a/gen/test/c/mx/mx_figured_bass.h b/gen/test/c/mx/mx_figured_bass.h index c3fab1609..2f3eba36e 100644 --- a/gen/test/c/mx/mx_figured_bass.h +++ b/gen/test/c/mx/mx_figured_bass.h @@ -26,7 +26,9 @@ */ /* One child element of MxFiguredBass: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxFigure *figure; MxPositiveDivisions *duration; diff --git a/gen/test/c/mx/mx_forward.h b/gen/test/c/mx/mx_forward.h index 36afe321f..0bc102ab4 100644 --- a/gen/test/c/mx/mx_forward.h +++ b/gen/test/c/mx/mx_forward.h @@ -18,7 +18,9 @@ */ /* One child element of MxForward: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxPositiveDivisions *duration; MxFormattedText *footnote; diff --git a/gen/test/c/mx/mx_frame.h b/gen/test/c/mx/mx_frame.h index 499e74bd5..4ed78a4d0 100644 --- a/gen/test/c/mx/mx_frame.h +++ b/gen/test/c/mx/mx_frame.h @@ -22,7 +22,9 @@ */ /* One child element of MxFrame: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { long *frame_strings; long *frame_frets; diff --git a/gen/test/c/mx/mx_frame_note.h b/gen/test/c/mx/mx_frame_note.h index 14067c2de..2ed425cfb 100644 --- a/gen/test/c/mx/mx_frame_note.h +++ b/gen/test/c/mx/mx_frame_note.h @@ -17,7 +17,9 @@ */ /* One child element of MxFrameNote: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxString *string; MxFret *fret; diff --git a/gen/test/c/mx/mx_grouping.h b/gen/test/c/mx/mx_grouping.h index 9a6f9674e..5cb0d678a 100644 --- a/gen/test/c/mx/mx_grouping.h +++ b/gen/test/c/mx/mx_grouping.h @@ -20,7 +20,9 @@ */ /* One child element of MxGrouping: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxFeature *feature; } MxGroupingChild; diff --git a/gen/test/c/mx/mx_harmon_mute.h b/gen/test/c/mx/mx_harmon_mute.h index 297f80756..b148fdcb5 100644 --- a/gen/test/c/mx/mx_harmon_mute.h +++ b/gen/test/c/mx/mx_harmon_mute.h @@ -20,7 +20,9 @@ */ /* One child element of MxHarmonMute: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxHarmonClosed *harmon_closed; } MxHarmonMuteChild; diff --git a/gen/test/c/mx/mx_harmonic.h b/gen/test/c/mx/mx_harmonic.h index 8af46de5b..c164c05a2 100644 --- a/gen/test/c/mx/mx_harmonic.h +++ b/gen/test/c/mx/mx_harmonic.h @@ -26,7 +26,9 @@ */ /* One child element of MxHarmonic: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxEmpty *natural; MxEmpty *artificial; diff --git a/gen/test/c/mx/mx_harmony.h b/gen/test/c/mx/mx_harmony.h index da5486747..29cd44b97 100644 --- a/gen/test/c/mx/mx_harmony.h +++ b/gen/test/c/mx/mx_harmony.h @@ -39,7 +39,9 @@ */ /* One child element of MxHarmony: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxRoot *root; MxStyleText *function; diff --git a/gen/test/c/mx/mx_harp_pedals.h b/gen/test/c/mx/mx_harp_pedals.h index b8c9d6b51..d0688dd92 100644 --- a/gen/test/c/mx/mx_harp_pedals.h +++ b/gen/test/c/mx/mx_harp_pedals.h @@ -24,7 +24,9 @@ */ /* One child element of MxHarpPedals: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxPedalTuning *pedal_tuning; } MxHarpPedalsChild; diff --git a/gen/test/c/mx/mx_hole.h b/gen/test/c/mx/mx_hole.h index 7744fcbff..edf1e15cc 100644 --- a/gen/test/c/mx/mx_hole.h +++ b/gen/test/c/mx/mx_hole.h @@ -21,7 +21,9 @@ */ /* One child element of MxHole: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { char *hole_type; MxHoleClosed *hole_closed; diff --git a/gen/test/c/mx/mx_identification.h b/gen/test/c/mx/mx_identification.h index 7ffe9d82c..aaaff4e45 100644 --- a/gen/test/c/mx/mx_identification.h +++ b/gen/test/c/mx/mx_identification.h @@ -17,7 +17,9 @@ */ /* One child element of MxIdentification: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxTypedText *creator; MxTypedText *rights; diff --git a/gen/test/c/mx/mx_interchangeable.h b/gen/test/c/mx/mx_interchangeable.h index cf5f889f5..b4aa779eb 100644 --- a/gen/test/c/mx/mx_interchangeable.h +++ b/gen/test/c/mx/mx_interchangeable.h @@ -17,7 +17,9 @@ */ /* One child element of MxInterchangeable: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxTimeRelation *time_relation; char *beats; diff --git a/gen/test/c/mx/mx_key.h b/gen/test/c/mx/mx_key.h index 88d70436a..d6d4ab6d9 100644 --- a/gen/test/c/mx/mx_key.h +++ b/gen/test/c/mx/mx_key.h @@ -30,7 +30,9 @@ */ /* One child element of MxKey: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxCancel *cancel; MxFifths *fifths; diff --git a/gen/test/c/mx/mx_lyric.h b/gen/test/c/mx/mx_lyric.h index 33af53a7d..3486430e0 100644 --- a/gen/test/c/mx/mx_lyric.h +++ b/gen/test/c/mx/mx_lyric.h @@ -34,7 +34,9 @@ */ /* One child element of MxLyric: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxSyllabic *syllabic; MxTextElementData *text; diff --git a/gen/test/c/mx/mx_measure_layout.h b/gen/test/c/mx/mx_measure_layout.h index d3c7ef50b..6fd3b750b 100644 --- a/gen/test/c/mx/mx_measure_layout.h +++ b/gen/test/c/mx/mx_measure_layout.h @@ -13,7 +13,9 @@ */ /* One child element of MxMeasureLayout: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxTenths *measure_distance; } MxMeasureLayoutChild; diff --git a/gen/test/c/mx/mx_measure_style.h b/gen/test/c/mx/mx_measure_style.h index 5e902f1b6..b3cb2af43 100644 --- a/gen/test/c/mx/mx_measure_style.h +++ b/gen/test/c/mx/mx_measure_style.h @@ -28,7 +28,9 @@ */ /* One child element of MxMeasureStyle: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxMultipleRest *multiple_rest; MxMeasureRepeat *measure_repeat; diff --git a/gen/test/c/mx/mx_metronome.h b/gen/test/c/mx/mx_metronome.h index 55f414e4b..7927348f4 100644 --- a/gen/test/c/mx/mx_metronome.h +++ b/gen/test/c/mx/mx_metronome.h @@ -32,7 +32,9 @@ */ /* One child element of MxMetronome: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxNoteTypeValue *beat_unit; MxEmpty *beat_unit_dot; diff --git a/gen/test/c/mx/mx_metronome_note.h b/gen/test/c/mx/mx_metronome_note.h index eab03ef6a..0926eef34 100644 --- a/gen/test/c/mx/mx_metronome_note.h +++ b/gen/test/c/mx/mx_metronome_note.h @@ -17,7 +17,9 @@ */ /* One child element of MxMetronomeNote: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxNoteTypeValue *metronome_type; MxEmpty *metronome_dot; diff --git a/gen/test/c/mx/mx_metronome_tuplet.h b/gen/test/c/mx/mx_metronome_tuplet.h index 06f42cc12..bc4e84492 100644 --- a/gen/test/c/mx/mx_metronome_tuplet.h +++ b/gen/test/c/mx/mx_metronome_tuplet.h @@ -19,7 +19,9 @@ */ /* One child element of MxMetronomeTuplet: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { long *actual_notes; long *normal_notes; diff --git a/gen/test/c/mx/mx_midi_instrument.h b/gen/test/c/mx/mx_midi_instrument.h index 0cd6b36df..4b5d60fc6 100644 --- a/gen/test/c/mx/mx_midi_instrument.h +++ b/gen/test/c/mx/mx_midi_instrument.h @@ -19,7 +19,9 @@ */ /* One child element of MxMIDIInstrument: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxMIDI16 *midi_channel; char *midi_name; diff --git a/gen/test/c/mx/mx_miscellaneous.h b/gen/test/c/mx/mx_miscellaneous.h index 0518d0a97..be159931a 100644 --- a/gen/test/c/mx/mx_miscellaneous.h +++ b/gen/test/c/mx/mx_miscellaneous.h @@ -15,7 +15,9 @@ */ /* One child element of MxMiscellaneous: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxMiscellaneousField *miscellaneous_field; } MxMiscellaneousChild; diff --git a/gen/test/c/mx/mx_name_display.h b/gen/test/c/mx/mx_name_display.h index 52175c3e8..87eb3e787 100644 --- a/gen/test/c/mx/mx_name_display.h +++ b/gen/test/c/mx/mx_name_display.h @@ -18,7 +18,9 @@ */ /* One child element of MxNameDisplay: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxFormattedText *display_text; MxAccidentalText *accidental_text; diff --git a/gen/test/c/mx/mx_notations.h b/gen/test/c/mx/mx_notations.h index 5abfdc1fe..46667d68d 100644 --- a/gen/test/c/mx/mx_notations.h +++ b/gen/test/c/mx/mx_notations.h @@ -32,7 +32,9 @@ */ /* One child element of MxNotations: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxFormattedText *footnote; MxLevel *level; diff --git a/gen/test/c/mx/mx_note.h b/gen/test/c/mx/mx_note.h index 68812a118..16b916a82 100644 --- a/gen/test/c/mx/mx_note.h +++ b/gen/test/c/mx/mx_note.h @@ -63,7 +63,9 @@ */ /* One child element of MxNote: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxGrace *grace; MxEmpty *chord; diff --git a/gen/test/c/mx/mx_notehead_text.h b/gen/test/c/mx/mx_notehead_text.h index d95d5c43c..549fd35db 100644 --- a/gen/test/c/mx/mx_notehead_text.h +++ b/gen/test/c/mx/mx_notehead_text.h @@ -17,7 +17,9 @@ */ /* One child element of MxNoteheadText: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxFormattedText *display_text; MxAccidentalText *accidental_text; diff --git a/gen/test/c/mx/mx_ornaments.h b/gen/test/c/mx/mx_ornaments.h index d8d908f91..0b4a03c2b 100644 --- a/gen/test/c/mx/mx_ornaments.h +++ b/gen/test/c/mx/mx_ornaments.h @@ -22,7 +22,9 @@ */ /* One child element of MxOrnaments: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxEmptyTrillSound *trill_mark; MxHorizontalTurn *turn; diff --git a/gen/test/c/mx/mx_page_layout.h b/gen/test/c/mx/mx_page_layout.h index 4f0fc9014..2af2c146c 100644 --- a/gen/test/c/mx/mx_page_layout.h +++ b/gen/test/c/mx/mx_page_layout.h @@ -17,7 +17,9 @@ */ /* One child element of MxPageLayout: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxTenths *page_height; MxTenths *page_width; diff --git a/gen/test/c/mx/mx_page_margins.h b/gen/test/c/mx/mx_page_margins.h index 236f8c086..4655acec9 100644 --- a/gen/test/c/mx/mx_page_margins.h +++ b/gen/test/c/mx/mx_page_margins.h @@ -16,7 +16,9 @@ */ /* One child element of MxPageMargins: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxTenths *left_margin; MxTenths *right_margin; diff --git a/gen/test/c/mx/mx_part_group.h b/gen/test/c/mx/mx_part_group.h index c0a962b90..dc5b6657f 100644 --- a/gen/test/c/mx/mx_part_group.h +++ b/gen/test/c/mx/mx_part_group.h @@ -29,7 +29,9 @@ */ /* One child element of MxPartGroup: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxGroupName *group_name; MxNameDisplay *group_name_display; diff --git a/gen/test/c/mx/mx_part_list.h b/gen/test/c/mx/mx_part_list.h index 73c00f0e5..499eeaa52 100644 --- a/gen/test/c/mx/mx_part_list.h +++ b/gen/test/c/mx/mx_part_list.h @@ -19,7 +19,9 @@ */ /* One child element of MxPartList: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxPartGroup *part_group; MxScorePart *score_part; diff --git a/gen/test/c/mx/mx_partwise_measure.h b/gen/test/c/mx/mx_partwise_measure.h index 3ccc3b77a..3c8b82f95 100644 --- a/gen/test/c/mx/mx_partwise_measure.h +++ b/gen/test/c/mx/mx_partwise_measure.h @@ -25,7 +25,9 @@ /* One child element of MxPartwiseMeasure: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxNote *note; MxBackup *backup; diff --git a/gen/test/c/mx/mx_partwise_part.h b/gen/test/c/mx/mx_partwise_part.h index ac6b3e985..c99512fd1 100644 --- a/gen/test/c/mx/mx_partwise_part.h +++ b/gen/test/c/mx/mx_partwise_part.h @@ -10,7 +10,9 @@ /* One child element of MxPartwisePart: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxPartwiseMeasure *measure; } MxPartwisePartChild; diff --git a/gen/test/c/mx/mx_pedal_tuning.h b/gen/test/c/mx/mx_pedal_tuning.h index ece3fa729..33980d07c 100644 --- a/gen/test/c/mx/mx_pedal_tuning.h +++ b/gen/test/c/mx/mx_pedal_tuning.h @@ -14,7 +14,9 @@ */ /* One child element of MxPedalTuning: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxStep *pedal_step; MxSemitones *pedal_alter; diff --git a/gen/test/c/mx/mx_percussion.h b/gen/test/c/mx/mx_percussion.h index 905cb3960..1df14a917 100644 --- a/gen/test/c/mx/mx_percussion.h +++ b/gen/test/c/mx/mx_percussion.h @@ -35,7 +35,9 @@ */ /* One child element of MxPercussion: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxGlass *glass; MxMetal *metal; diff --git a/gen/test/c/mx/mx_pitch.h b/gen/test/c/mx/mx_pitch.h index be5469a74..fac5e1e2f 100644 --- a/gen/test/c/mx/mx_pitch.h +++ b/gen/test/c/mx/mx_pitch.h @@ -16,7 +16,9 @@ */ /* One child element of MxPitch: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxStep *step; MxSemitones *alter; diff --git a/gen/test/c/mx/mx_play.h b/gen/test/c/mx/mx_play.h index 82ecf2662..058d3e22c 100644 --- a/gen/test/c/mx/mx_play.h +++ b/gen/test/c/mx/mx_play.h @@ -18,7 +18,9 @@ */ /* One child element of MxPlay: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { char *ipa; MxMute *mute; diff --git a/gen/test/c/mx/mx_print.h b/gen/test/c/mx/mx_print.h index 9eaf5667e..166ce7987 100644 --- a/gen/test/c/mx/mx_print.h +++ b/gen/test/c/mx/mx_print.h @@ -26,7 +26,9 @@ */ /* One child element of MxPrint: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxPageLayout *page_layout; MxSystemLayout *system_layout; diff --git a/gen/test/c/mx/mx_rest.h b/gen/test/c/mx/mx_rest.h index bb998892a..ca2d333da 100644 --- a/gen/test/c/mx/mx_rest.h +++ b/gen/test/c/mx/mx_rest.h @@ -17,7 +17,9 @@ */ /* One child element of MxRest: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxStep *display_step; MxOctave *display_octave; diff --git a/gen/test/c/mx/mx_root.h b/gen/test/c/mx/mx_root.h index b49c59b49..3a0872765 100644 --- a/gen/test/c/mx/mx_root.h +++ b/gen/test/c/mx/mx_root.h @@ -17,7 +17,9 @@ */ /* One child element of MxRoot: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxRootStep *root_step; MxRootAlter *root_alter; diff --git a/gen/test/c/mx/mx_runtime.h b/gen/test/c/mx/mx_runtime.h index 1d840410b..dd8fb58f1 100644 --- a/gen/test/c/mx/mx_runtime.h +++ b/gen/test/c/mx/mx_runtime.h @@ -5,6 +5,11 @@ #include +/* The MusicXML version of the schema this model was generated from. + Documents declaring a newer version may use types this model cannot + represent; harnesses gate on it. */ +#define MX_SUPPORTED_MUSICXML_VERSION "3.1" + /* Shared parse/format helpers for the generated MusicXML types. Lenient parses never fail: unparseable input becomes 0 (range clamping is the typed wrappers' job). Formatting returns malloc'd strings the caller diff --git a/gen/test/c/mx/mx_scaling.h b/gen/test/c/mx/mx_scaling.h index 185bba441..51ac57a3d 100644 --- a/gen/test/c/mx/mx_scaling.h +++ b/gen/test/c/mx/mx_scaling.h @@ -18,7 +18,9 @@ */ /* One child element of MxScaling: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxMillimeters *millimeters; MxTenths *tenths; diff --git a/gen/test/c/mx/mx_scordatura.h b/gen/test/c/mx/mx_scordatura.h index bd5b71bca..09631b139 100644 --- a/gen/test/c/mx/mx_scordatura.h +++ b/gen/test/c/mx/mx_scordatura.h @@ -14,7 +14,9 @@ */ /* One child element of MxScordatura: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxAccord *accord; } MxScordaturaChild; diff --git a/gen/test/c/mx/mx_score_instrument.h b/gen/test/c/mx/mx_score_instrument.h index d2b8345b8..16697162d 100644 --- a/gen/test/c/mx/mx_score_instrument.h +++ b/gen/test/c/mx/mx_score_instrument.h @@ -21,7 +21,9 @@ */ /* One child element of MxScoreInstrument: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { char *instrument_name; char *instrument_abbreviation; diff --git a/gen/test/c/mx/mx_score_part.h b/gen/test/c/mx/mx_score_part.h index 4ddd1f95b..1067cf598 100644 --- a/gen/test/c/mx/mx_score_part.h +++ b/gen/test/c/mx/mx_score_part.h @@ -21,7 +21,9 @@ */ /* One child element of MxScorePart: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxIdentification *identification; MxPartName *part_name; diff --git a/gen/test/c/mx/mx_score_partwise.h b/gen/test/c/mx/mx_score_partwise.h index 80827f819..a2999ca56 100644 --- a/gen/test/c/mx/mx_score_partwise.h +++ b/gen/test/c/mx/mx_score_partwise.h @@ -15,7 +15,9 @@ /* One child element of MxScorePartwise: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxWork *work; char *movement_number; diff --git a/gen/test/c/mx/mx_score_timewise.h b/gen/test/c/mx/mx_score_timewise.h index 2c9970da4..7b5f6636a 100644 --- a/gen/test/c/mx/mx_score_timewise.h +++ b/gen/test/c/mx/mx_score_timewise.h @@ -15,7 +15,9 @@ /* One child element of MxScoreTimewise: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxWork *work; char *movement_number; diff --git a/gen/test/c/mx/mx_slash.h b/gen/test/c/mx/mx_slash.h index e8b609775..39db3c3a3 100644 --- a/gen/test/c/mx/mx_slash.h +++ b/gen/test/c/mx/mx_slash.h @@ -20,7 +20,9 @@ */ /* One child element of MxSlash: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxNoteTypeValue *slash_type; MxEmpty *slash_dot; diff --git a/gen/test/c/mx/mx_sound.h b/gen/test/c/mx/mx_sound.h index 3d4cf3505..6bc76a0a7 100644 --- a/gen/test/c/mx/mx_sound.h +++ b/gen/test/c/mx/mx_sound.h @@ -57,7 +57,9 @@ */ /* One child element of MxSound: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxMIDIDevice *midi_device; MxMIDIInstrument *midi_instrument; diff --git a/gen/test/c/mx/mx_staff_details.h b/gen/test/c/mx/mx_staff_details.h index acd0cbdce..83e8d1b58 100644 --- a/gen/test/c/mx/mx_staff_details.h +++ b/gen/test/c/mx/mx_staff_details.h @@ -23,7 +23,9 @@ */ /* One child element of MxStaffDetails: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxStaffType *staff_type; long *staff_lines; diff --git a/gen/test/c/mx/mx_staff_layout.h b/gen/test/c/mx/mx_staff_layout.h index ece64f227..2ac6faaa0 100644 --- a/gen/test/c/mx/mx_staff_layout.h +++ b/gen/test/c/mx/mx_staff_layout.h @@ -18,7 +18,9 @@ */ /* One child element of MxStaffLayout: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxTenths *staff_distance; } MxStaffLayoutChild; diff --git a/gen/test/c/mx/mx_staff_tuning.h b/gen/test/c/mx/mx_staff_tuning.h index 3b02ce60e..acb7770c2 100644 --- a/gen/test/c/mx/mx_staff_tuning.h +++ b/gen/test/c/mx/mx_staff_tuning.h @@ -16,7 +16,9 @@ */ /* One child element of MxStaffTuning: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxStep *tuning_step; MxSemitones *tuning_alter; diff --git a/gen/test/c/mx/mx_stick.h b/gen/test/c/mx/mx_stick.h index 38ec5fdca..ac7099f63 100644 --- a/gen/test/c/mx/mx_stick.h +++ b/gen/test/c/mx/mx_stick.h @@ -18,7 +18,9 @@ */ /* One child element of MxStick: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxStickType *stick_type; MxStickMaterial *stick_material; diff --git a/gen/test/c/mx/mx_system_dividers.h b/gen/test/c/mx/mx_system_dividers.h index 0e149fe82..114b128f2 100644 --- a/gen/test/c/mx/mx_system_dividers.h +++ b/gen/test/c/mx/mx_system_dividers.h @@ -19,7 +19,9 @@ */ /* One child element of MxSystemDividers: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxEmptyPrintObjectStyleAlign *left_divider; MxEmptyPrintObjectStyleAlign *right_divider; diff --git a/gen/test/c/mx/mx_system_layout.h b/gen/test/c/mx/mx_system_layout.h index 58948c808..c8baf7da5 100644 --- a/gen/test/c/mx/mx_system_layout.h +++ b/gen/test/c/mx/mx_system_layout.h @@ -24,7 +24,9 @@ */ /* One child element of MxSystemLayout: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxSystemMargins *system_margins; MxTenths *system_distance; diff --git a/gen/test/c/mx/mx_system_margins.h b/gen/test/c/mx/mx_system_margins.h index 0fa1a20ec..b1af7bb23 100644 --- a/gen/test/c/mx/mx_system_margins.h +++ b/gen/test/c/mx/mx_system_margins.h @@ -14,7 +14,9 @@ */ /* One child element of MxSystemMargins: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxTenths *left_margin; MxTenths *right_margin; diff --git a/gen/test/c/mx/mx_technical.h b/gen/test/c/mx/mx_technical.h index eb3be8823..3853d9fbb 100644 --- a/gen/test/c/mx/mx_technical.h +++ b/gen/test/c/mx/mx_technical.h @@ -28,7 +28,9 @@ */ /* One child element of MxTechnical: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxEmptyPlacement *up_bow; MxEmptyPlacement *down_bow; diff --git a/gen/test/c/mx/mx_time.h b/gen/test/c/mx/mx_time.h index 76520d3fc..331001d5a 100644 --- a/gen/test/c/mx/mx_time.h +++ b/gen/test/c/mx/mx_time.h @@ -32,7 +32,9 @@ */ /* One child element of MxTime: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { char *beats; char *beat_type; diff --git a/gen/test/c/mx/mx_time_modification.h b/gen/test/c/mx/mx_time_modification.h index 712af4f6c..06a44ced7 100644 --- a/gen/test/c/mx/mx_time_modification.h +++ b/gen/test/c/mx/mx_time_modification.h @@ -18,7 +18,9 @@ */ /* One child element of MxTimeModification: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { long *actual_notes; long *normal_notes; diff --git a/gen/test/c/mx/mx_timewise_measure.h b/gen/test/c/mx/mx_timewise_measure.h index 807f73cd8..626bca4f1 100644 --- a/gen/test/c/mx/mx_timewise_measure.h +++ b/gen/test/c/mx/mx_timewise_measure.h @@ -13,7 +13,9 @@ /* One child element of MxTimewiseMeasure: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxTimewisePart *part; } MxTimewiseMeasureChild; diff --git a/gen/test/c/mx/mx_timewise_part.h b/gen/test/c/mx/mx_timewise_part.h index f41f85ab0..0c124b1ad 100644 --- a/gen/test/c/mx/mx_timewise_part.h +++ b/gen/test/c/mx/mx_timewise_part.h @@ -22,7 +22,9 @@ /* One child element of MxTimewisePart: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxNote *note; MxBackup *backup; diff --git a/gen/test/c/mx/mx_transpose.h b/gen/test/c/mx/mx_transpose.h index 7fcda6beb..5a622e9d1 100644 --- a/gen/test/c/mx/mx_transpose.h +++ b/gen/test/c/mx/mx_transpose.h @@ -18,7 +18,9 @@ */ /* One child element of MxTranspose: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { long *diatonic; MxSemitones *chromatic; diff --git a/gen/test/c/mx/mx_tuplet.h b/gen/test/c/mx/mx_tuplet.h index 5b5e3b2f3..133e17d4e 100644 --- a/gen/test/c/mx/mx_tuplet.h +++ b/gen/test/c/mx/mx_tuplet.h @@ -32,7 +32,9 @@ */ /* One child element of MxTuplet: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxTupletPortion *tuplet_actual; MxTupletPortion *tuplet_normal; diff --git a/gen/test/c/mx/mx_tuplet_portion.h b/gen/test/c/mx/mx_tuplet_portion.h index f78e71ba8..f20008809 100644 --- a/gen/test/c/mx/mx_tuplet_portion.h +++ b/gen/test/c/mx/mx_tuplet_portion.h @@ -18,7 +18,9 @@ */ /* One child element of MxTupletPortion: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxTupletNumber *tuplet_number; MxTupletType *tuplet_type; diff --git a/gen/test/c/mx/mx_unpitched.h b/gen/test/c/mx/mx_unpitched.h index ef2294185..eb3263898 100644 --- a/gen/test/c/mx/mx_unpitched.h +++ b/gen/test/c/mx/mx_unpitched.h @@ -15,7 +15,9 @@ */ /* One child element of MxUnpitched: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { MxStep *display_step; MxOctave *display_octave; diff --git a/gen/test/c/mx/mx_virtual_instrument.h b/gen/test/c/mx/mx_virtual_instrument.h index 1f3d0467a..2d3282c76 100644 --- a/gen/test/c/mx/mx_virtual_instrument.h +++ b/gen/test/c/mx/mx_virtual_instrument.h @@ -13,7 +13,9 @@ */ /* One child element of MxVirtualInstrument: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { char *virtual_library; char *virtual_name; diff --git a/gen/test/c/mx/mx_work.h b/gen/test/c/mx/mx_work.h index 8fbada532..25e1906ef 100644 --- a/gen/test/c/mx/mx_work.h +++ b/gen/test/c/mx/mx_work.h @@ -14,7 +14,9 @@ */ /* One child element of MxWork: exactly one field is non-NULL, and that pointer says which element this is. Document order is - the array order on the owning struct. */ + the array order on the owning struct. Zero or multiple fields + set is undefined: serialization writes the first non-NULL field + in schema order and nothing when all are NULL. */ typedef struct { char *work_number; char *work_title; diff --git a/gen/test/c/src/roundtrip.c b/gen/test/c/src/roundtrip.c index f9ff23902..8a66845ea 100644 --- a/gen/test/c/src/roundtrip.c +++ b/gen/test/c/src/roundtrip.c @@ -12,26 +12,24 @@ #include #include -/* The MusicXML version the generated model supports (the schema pinned in - this target's config.toml). Documents declaring a NEWER version may use - elements the model has no types for; MusicXML is backward compatible, so - older documents are fine. */ -#define MAX_SUPPORTED_MAJOR 3 -#define MAX_SUPPORTED_MINOR 1 - -/* Reports whether the root's version attribute declares a version newer - than the supported one. An absent attribute means MusicXML 1.0. */ +/* The MusicXML version the generated model supports comes from the model + itself (MX_SUPPORTED_MUSICXML_VERSION in mx_runtime.h, emitted from the + schema the config pins), so retargeting the schema cannot leave this gate + stale. Documents declaring a NEWER version may use elements the model has + no types for; MusicXML is backward compatible, so older documents are + fine. An absent version attribute means MusicXML 1.0. */ static int declared_version_exceeds(xmlDocPtr doc) { xmlNodePtr root = xmlDocGetRootElement(doc); if (!root) return 0; + int max_major = 0, max_minor = 0; + sscanf(MX_SUPPORTED_MUSICXML_VERSION, "%d.%d", &max_major, &max_minor); xmlChar *version = xmlGetProp(root, (const xmlChar *)"version"); int major = 1, minor = 0; if (version && version[0]) sscanf((const char *)version, "%d.%d", &major, &minor); xmlFree(version); - return major > MAX_SUPPORTED_MAJOR || - (major == MAX_SUPPORTED_MAJOR && minor > MAX_SUPPORTED_MINOR); + return major > max_major || (major == max_major && minor > max_minor); } RoundtripResult run_core_roundtrip(const char *abs_input_path) { @@ -46,10 +44,9 @@ RoundtripResult run_core_roundtrip(const char *abs_input_path) { if (declared_version_exceeds(input_doc)) { snprintf(r.message, sizeof(r.message), - "declares MusicXML > %d.%d; this target generates from the " - "%d.%d schema", - MAX_SUPPORTED_MAJOR, MAX_SUPPORTED_MINOR, - MAX_SUPPORTED_MAJOR, MAX_SUPPORTED_MINOR); + "declares MusicXML > %s; this target generates from the " + "%s schema", + MX_SUPPORTED_MUSICXML_VERSION, MX_SUPPORTED_MUSICXML_VERSION); r.skipped = 1; xmlFreeDoc(input_doc); return r; diff --git a/gen/test/go/corert/roundtrip.go b/gen/test/go/corert/roundtrip.go index 801312d53..f5e0b6575 100644 --- a/gen/test/go/corert/roundtrip.go +++ b/gen/test/go/corert/roundtrip.go @@ -15,11 +15,12 @@ import ( "github.com/webern/mx/gen/test/go/mx" ) -// The MusicXML version the generated model supports (the schema pinned in -// this target's config.toml). Documents declaring a NEWER version may use -// elements the model has no types for; MusicXML is backward compatible, so -// older documents are fine. -const maxSupportedVersion = "3.1" +// The MusicXML version the generated model supports comes from the model +// itself (the generator emits it from the schema the config pins), so +// retargeting the schema cannot leave the gate stale. Documents declaring a +// NEWER version may use elements the model has no types for; MusicXML is +// backward compatible, so older documents are fine. +const maxSupportedVersion = mx.SupportedMusicXMLVersion type Result struct { OK bool diff --git a/gen/test/go/mx/accord.go b/gen/test/go/mx/accord.go index 30c10ccf9..ee1bdfd15 100644 --- a/gen/test/go/mx/accord.go +++ b/gen/test/go/mx/accord.go @@ -17,7 +17,9 @@ type Accord struct { // AccordChild is one child element of Accord: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type AccordChild struct { TuningStep *Step TuningAlter *Semitones diff --git a/gen/test/go/mx/accordion_registration.go b/gen/test/go/mx/accordion_registration.go index 4f323cc99..a03d2e122 100644 --- a/gen/test/go/mx/accordion_registration.go +++ b/gen/test/go/mx/accordion_registration.go @@ -31,7 +31,9 @@ type AccordionRegistration struct { // AccordionRegistrationChild is one child element of AccordionRegistration: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type AccordionRegistrationChild struct { AccordionHigh *Empty AccordionMiddle *AccordionMiddle diff --git a/gen/test/go/mx/appearance.go b/gen/test/go/mx/appearance.go index 5d924f170..87297f520 100644 --- a/gen/test/go/mx/appearance.go +++ b/gen/test/go/mx/appearance.go @@ -18,7 +18,9 @@ type Appearance struct { // AppearanceChild is one child element of Appearance: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type AppearanceChild struct { LineWidth *LineWidth NoteSize *NoteSize diff --git a/gen/test/go/mx/arrow.go b/gen/test/go/mx/arrow.go index 653ed35de..272582dc1 100644 --- a/gen/test/go/mx/arrow.go +++ b/gen/test/go/mx/arrow.go @@ -30,7 +30,9 @@ type Arrow struct { // ArrowChild is one child element of Arrow: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type ArrowChild struct { ArrowDirection *ArrowDirection ArrowStyle *ArrowStyle diff --git a/gen/test/go/mx/articulations.go b/gen/test/go/mx/articulations.go index 4d7585e4d..2e6b6e691 100644 --- a/gen/test/go/mx/articulations.go +++ b/gen/test/go/mx/articulations.go @@ -16,7 +16,9 @@ type Articulations struct { // ArticulationsChild is one child element of Articulations: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type ArticulationsChild struct { Accent *EmptyPlacement StrongAccent *StrongAccent diff --git a/gen/test/go/mx/attributes.go b/gen/test/go/mx/attributes.go index e51ec9478..f8ad39b5e 100644 --- a/gen/test/go/mx/attributes.go +++ b/gen/test/go/mx/attributes.go @@ -17,7 +17,9 @@ type Attributes struct { // AttributesChild is one child element of Attributes: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type AttributesChild struct { Footnote *FormattedText Level *Level diff --git a/gen/test/go/mx/backup.go b/gen/test/go/mx/backup.go index afb607156..77744878b 100644 --- a/gen/test/go/mx/backup.go +++ b/gen/test/go/mx/backup.go @@ -19,7 +19,9 @@ type Backup struct { // BackupChild is one child element of Backup: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type BackupChild struct { Duration *PositiveDivisions Footnote *FormattedText diff --git a/gen/test/go/mx/barline.go b/gen/test/go/mx/barline.go index 054b8f955..ba14ad59d 100644 --- a/gen/test/go/mx/barline.go +++ b/gen/test/go/mx/barline.go @@ -34,7 +34,9 @@ type Barline struct { // BarlineChild is one child element of Barline: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type BarlineChild struct { BarStyle *BarStyleColor Footnote *FormattedText diff --git a/gen/test/go/mx/bass.go b/gen/test/go/mx/bass.go index 3e5cb0dee..b16f0bf3f 100644 --- a/gen/test/go/mx/bass.go +++ b/gen/test/go/mx/bass.go @@ -17,7 +17,9 @@ type Bass struct { // BassChild is one child element of Bass: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type BassChild struct { BassStep *BassStep BassAlter *BassAlter diff --git a/gen/test/go/mx/beat_repeat.go b/gen/test/go/mx/beat_repeat.go index 9709778bc..7c72712d1 100644 --- a/gen/test/go/mx/beat_repeat.go +++ b/gen/test/go/mx/beat_repeat.go @@ -24,7 +24,9 @@ type BeatRepeat struct { // BeatRepeatChild is one child element of BeatRepeat: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type BeatRepeatChild struct { SlashType *NoteTypeValue SlashDot *Empty diff --git a/gen/test/go/mx/beat_unit_tied.go b/gen/test/go/mx/beat_unit_tied.go index 0a0678de9..c48151226 100644 --- a/gen/test/go/mx/beat_unit_tied.go +++ b/gen/test/go/mx/beat_unit_tied.go @@ -18,7 +18,9 @@ type BeatUnitTied struct { // BeatUnitTiedChild is one child element of BeatUnitTied: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type BeatUnitTiedChild struct { BeatUnit *NoteTypeValue BeatUnitDot *Empty diff --git a/gen/test/go/mx/bend.go b/gen/test/go/mx/bend.go index f0e9cb6b5..5bd7555c4 100644 --- a/gen/test/go/mx/bend.go +++ b/gen/test/go/mx/bend.go @@ -33,7 +33,9 @@ type Bend struct { // BendChild is one child element of Bend: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type BendChild struct { BendAlter *Semitones PreBend *Empty diff --git a/gen/test/go/mx/clef.go b/gen/test/go/mx/clef.go index dfa387243..2bd728018 100644 --- a/gen/test/go/mx/clef.go +++ b/gen/test/go/mx/clef.go @@ -40,7 +40,9 @@ type Clef struct { // ClefChild is one child element of Clef: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type ClefChild struct { Sign *ClefSign Line *StaffLine diff --git a/gen/test/go/mx/credit.go b/gen/test/go/mx/credit.go index 6d27d3a73..d1508b133 100644 --- a/gen/test/go/mx/credit.go +++ b/gen/test/go/mx/credit.go @@ -31,7 +31,9 @@ type Credit struct { // CreditChild is one child element of Credit: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type CreditChild struct { CreditType *string Link *Link diff --git a/gen/test/go/mx/defaults.go b/gen/test/go/mx/defaults.go index 31e8d7298..cb61ab4f5 100644 --- a/gen/test/go/mx/defaults.go +++ b/gen/test/go/mx/defaults.go @@ -15,7 +15,9 @@ type Defaults struct { // DefaultsChild is one child element of Defaults: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type DefaultsChild struct { Scaling *Scaling PageLayout *PageLayout diff --git a/gen/test/go/mx/degree.go b/gen/test/go/mx/degree.go index c01b0e30b..ea3074588 100644 --- a/gen/test/go/mx/degree.go +++ b/gen/test/go/mx/degree.go @@ -21,7 +21,9 @@ type Degree struct { // DegreeChild is one child element of Degree: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type DegreeChild struct { DegreeValue *DegreeValue DegreeAlter *DegreeAlter diff --git a/gen/test/go/mx/direction.go b/gen/test/go/mx/direction.go index 3d3245db5..8d070628f 100644 --- a/gen/test/go/mx/direction.go +++ b/gen/test/go/mx/direction.go @@ -25,7 +25,9 @@ type Direction struct { // DirectionChild is one child element of Direction: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type DirectionChild struct { DirectionType *DirectionType Offset *Offset diff --git a/gen/test/go/mx/direction_type.go b/gen/test/go/mx/direction_type.go index 4d5ae18ac..d43e87071 100644 --- a/gen/test/go/mx/direction_type.go +++ b/gen/test/go/mx/direction_type.go @@ -18,7 +18,9 @@ type DirectionType struct { // DirectionTypeChild is one child element of DirectionType: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type DirectionTypeChild struct { Rehearsal *FormattedTextID Segno *Segno diff --git a/gen/test/go/mx/dynamics.go b/gen/test/go/mx/dynamics.go index 1f28fe118..5235249c7 100644 --- a/gen/test/go/mx/dynamics.go +++ b/gen/test/go/mx/dynamics.go @@ -43,7 +43,9 @@ type Dynamics struct { // DynamicsChild is one child element of Dynamics: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type DynamicsChild struct { P *Empty Pp *Empty diff --git a/gen/test/go/mx/encoding.go b/gen/test/go/mx/encoding.go index 00856a3e2..797cc52b8 100644 --- a/gen/test/go/mx/encoding.go +++ b/gen/test/go/mx/encoding.go @@ -18,7 +18,9 @@ type Encoding struct { // EncodingChild is one child element of Encoding: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type EncodingChild struct { EncodingDate *YyyyMmDd Encoder *TypedText diff --git a/gen/test/go/mx/figure.go b/gen/test/go/mx/figure.go index 33fd23a24..15526c806 100644 --- a/gen/test/go/mx/figure.go +++ b/gen/test/go/mx/figure.go @@ -15,7 +15,9 @@ type Figure struct { // FigureChild is one child element of Figure: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type FigureChild struct { Prefix *StyleText FigureNumber *StyleText diff --git a/gen/test/go/mx/figured_bass.go b/gen/test/go/mx/figured_bass.go index 19652f768..6b9c3321d 100644 --- a/gen/test/go/mx/figured_bass.go +++ b/gen/test/go/mx/figured_bass.go @@ -33,7 +33,9 @@ type FiguredBass struct { // FiguredBassChild is one child element of FiguredBass: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type FiguredBassChild struct { Figure *Figure Duration *PositiveDivisions diff --git a/gen/test/go/mx/forward.go b/gen/test/go/mx/forward.go index 616107431..e378f053c 100644 --- a/gen/test/go/mx/forward.go +++ b/gen/test/go/mx/forward.go @@ -18,7 +18,9 @@ type Forward struct { // ForwardChild is one child element of Forward: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type ForwardChild struct { Duration *PositiveDivisions Footnote *FormattedText diff --git a/gen/test/go/mx/frame.go b/gen/test/go/mx/frame.go index 039cddee8..cd89d4f3e 100644 --- a/gen/test/go/mx/frame.go +++ b/gen/test/go/mx/frame.go @@ -30,7 +30,9 @@ type Frame struct { // FrameChild is one child element of Frame: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type FrameChild struct { FrameStrings *int FrameFrets *int diff --git a/gen/test/go/mx/frame_note.go b/gen/test/go/mx/frame_note.go index eadd5af17..1072375db 100644 --- a/gen/test/go/mx/frame_note.go +++ b/gen/test/go/mx/frame_note.go @@ -16,7 +16,9 @@ type FrameNote struct { // FrameNoteChild is one child element of FrameNote: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type FrameNoteChild struct { String *String Fret *Fret diff --git a/gen/test/go/mx/grouping.go b/gen/test/go/mx/grouping.go index 3c8868048..e49c33cef 100644 --- a/gen/test/go/mx/grouping.go +++ b/gen/test/go/mx/grouping.go @@ -25,7 +25,9 @@ type Grouping struct { // GroupingChild is one child element of Grouping: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type GroupingChild struct { Feature *Feature } diff --git a/gen/test/go/mx/harmon_mute.go b/gen/test/go/mx/harmon_mute.go index d47a2e414..26ece6552 100644 --- a/gen/test/go/mx/harmon_mute.go +++ b/gen/test/go/mx/harmon_mute.go @@ -25,7 +25,9 @@ type HarmonMute struct { // HarmonMuteChild is one child element of HarmonMute: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type HarmonMuteChild struct { HarmonClosed *HarmonClosed } diff --git a/gen/test/go/mx/harmonic.go b/gen/test/go/mx/harmonic.go index fdcef056f..ae1bc4592 100644 --- a/gen/test/go/mx/harmonic.go +++ b/gen/test/go/mx/harmonic.go @@ -31,7 +31,9 @@ type Harmonic struct { // HarmonicChild is one child element of Harmonic: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type HarmonicChild struct { Natural *Empty Artificial *Empty diff --git a/gen/test/go/mx/harmony.go b/gen/test/go/mx/harmony.go index fcb1e636c..215a5cee3 100644 --- a/gen/test/go/mx/harmony.go +++ b/gen/test/go/mx/harmony.go @@ -37,7 +37,9 @@ type Harmony struct { // HarmonyChild is one child element of Harmony: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type HarmonyChild struct { Root *Root Function *StyleText diff --git a/gen/test/go/mx/harp_pedals.go b/gen/test/go/mx/harp_pedals.go index f634f4ec8..ca2135ba0 100644 --- a/gen/test/go/mx/harp_pedals.go +++ b/gen/test/go/mx/harp_pedals.go @@ -30,7 +30,9 @@ type HarpPedals struct { // HarpPedalsChild is one child element of HarpPedals: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type HarpPedalsChild struct { PedalTuning *PedalTuning } diff --git a/gen/test/go/mx/hole.go b/gen/test/go/mx/hole.go index 84a5db0d1..b5754e815 100644 --- a/gen/test/go/mx/hole.go +++ b/gen/test/go/mx/hole.go @@ -26,7 +26,9 @@ type Hole struct { // HoleChild is one child element of Hole: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type HoleChild struct { HoleType *string HoleClosed *HoleClosed diff --git a/gen/test/go/mx/identification.go b/gen/test/go/mx/identification.go index 1ed8f860c..21c2f7d71 100644 --- a/gen/test/go/mx/identification.go +++ b/gen/test/go/mx/identification.go @@ -17,7 +17,9 @@ type Identification struct { // IdentificationChild is one child element of Identification: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type IdentificationChild struct { Creator *TypedText Rights *TypedText diff --git a/gen/test/go/mx/interchangeable.go b/gen/test/go/mx/interchangeable.go index b19a28445..69bd8cdff 100644 --- a/gen/test/go/mx/interchangeable.go +++ b/gen/test/go/mx/interchangeable.go @@ -19,7 +19,9 @@ type Interchangeable struct { // InterchangeableChild is one child element of Interchangeable: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type InterchangeableChild struct { TimeRelation *TimeRelation Beats *string diff --git a/gen/test/go/mx/key.go b/gen/test/go/mx/key.go index 4d6cfb074..49e6bd233 100644 --- a/gen/test/go/mx/key.go +++ b/gen/test/go/mx/key.go @@ -30,7 +30,9 @@ type Key struct { // KeyChild is one child element of Key: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type KeyChild struct { Cancel *Cancel Fifths *Fifths diff --git a/gen/test/go/mx/lyric.go b/gen/test/go/mx/lyric.go index 90589aa90..ee53194f9 100644 --- a/gen/test/go/mx/lyric.go +++ b/gen/test/go/mx/lyric.go @@ -36,7 +36,9 @@ type Lyric struct { // LyricChild is one child element of Lyric: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type LyricChild struct { Syllabic *Syllabic Text *TextElementData diff --git a/gen/test/go/mx/measure_layout.go b/gen/test/go/mx/measure_layout.go index 73c230fe9..c913ec694 100644 --- a/gen/test/go/mx/measure_layout.go +++ b/gen/test/go/mx/measure_layout.go @@ -15,7 +15,9 @@ type MeasureLayout struct { // MeasureLayoutChild is one child element of MeasureLayout: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type MeasureLayoutChild struct { MeasureDistance *Tenths } diff --git a/gen/test/go/mx/measure_style.go b/gen/test/go/mx/measure_style.go index b8810395c..8e3879205 100644 --- a/gen/test/go/mx/measure_style.go +++ b/gen/test/go/mx/measure_style.go @@ -28,7 +28,9 @@ type MeasureStyle struct { // MeasureStyleChild is one child element of MeasureStyle: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type MeasureStyleChild struct { MultipleRest *MultipleRest MeasureRepeat *MeasureRepeat diff --git a/gen/test/go/mx/metronome.go b/gen/test/go/mx/metronome.go index 72e821f91..6b3b4f7e8 100644 --- a/gen/test/go/mx/metronome.go +++ b/gen/test/go/mx/metronome.go @@ -35,7 +35,9 @@ type Metronome struct { // MetronomeChild is one child element of Metronome: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type MetronomeChild struct { BeatUnit *NoteTypeValue BeatUnitDot *Empty diff --git a/gen/test/go/mx/metronome_note.go b/gen/test/go/mx/metronome_note.go index 577483934..2dd10a8d8 100644 --- a/gen/test/go/mx/metronome_note.go +++ b/gen/test/go/mx/metronome_note.go @@ -15,7 +15,9 @@ type MetronomeNote struct { // MetronomeNoteChild is one child element of MetronomeNote: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type MetronomeNoteChild struct { MetronomeType *NoteTypeValue MetronomeDot *Empty diff --git a/gen/test/go/mx/midi_instrument.go b/gen/test/go/mx/midi_instrument.go index a3cf1030e..89e1c41aa 100644 --- a/gen/test/go/mx/midi_instrument.go +++ b/gen/test/go/mx/midi_instrument.go @@ -18,7 +18,9 @@ type MIDIInstrument struct { // MIDIInstrumentChild is one child element of MIDIInstrument: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type MIDIInstrumentChild struct { MIDIChannel *MIDI16 MIDIName *string diff --git a/gen/test/go/mx/miscellaneous.go b/gen/test/go/mx/miscellaneous.go index 64547405e..79fd26aa8 100644 --- a/gen/test/go/mx/miscellaneous.go +++ b/gen/test/go/mx/miscellaneous.go @@ -17,7 +17,9 @@ type Miscellaneous struct { // MiscellaneousChild is one child element of Miscellaneous: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type MiscellaneousChild struct { MiscellaneousField *MiscellaneousField } diff --git a/gen/test/go/mx/name_display.go b/gen/test/go/mx/name_display.go index a7f2f014f..feaf2668d 100644 --- a/gen/test/go/mx/name_display.go +++ b/gen/test/go/mx/name_display.go @@ -19,7 +19,9 @@ type NameDisplay struct { // NameDisplayChild is one child element of NameDisplay: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type NameDisplayChild struct { DisplayText *FormattedText AccidentalText *AccidentalText diff --git a/gen/test/go/mx/notations.go b/gen/test/go/mx/notations.go index 19d850b0c..ed03639ff 100644 --- a/gen/test/go/mx/notations.go +++ b/gen/test/go/mx/notations.go @@ -20,7 +20,9 @@ type Notations struct { // NotationsChild is one child element of Notations: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type NotationsChild struct { Footnote *FormattedText Level *Level diff --git a/gen/test/go/mx/note.go b/gen/test/go/mx/note.go index fde796c8c..e4f1ee5ae 100644 --- a/gen/test/go/mx/note.go +++ b/gen/test/go/mx/note.go @@ -56,7 +56,9 @@ type Note struct { // NoteChild is one child element of Note: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type NoteChild struct { Grace *Grace Chord *Empty diff --git a/gen/test/go/mx/notehead_text.go b/gen/test/go/mx/notehead_text.go index 4d3abddab..c1cf20c98 100644 --- a/gen/test/go/mx/notehead_text.go +++ b/gen/test/go/mx/notehead_text.go @@ -18,7 +18,9 @@ type NoteheadText struct { // NoteheadTextChild is one child element of NoteheadText: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type NoteheadTextChild struct { DisplayText *FormattedText AccidentalText *AccidentalText diff --git a/gen/test/go/mx/ornaments.go b/gen/test/go/mx/ornaments.go index a29f211ca..dffb17594 100644 --- a/gen/test/go/mx/ornaments.go +++ b/gen/test/go/mx/ornaments.go @@ -18,7 +18,9 @@ type Ornaments struct { // OrnamentsChild is one child element of Ornaments: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type OrnamentsChild struct { TrillMark *EmptyTrillSound Turn *HorizontalTurn diff --git a/gen/test/go/mx/page_layout.go b/gen/test/go/mx/page_layout.go index b0c3ca7a2..ca12f84e1 100644 --- a/gen/test/go/mx/page_layout.go +++ b/gen/test/go/mx/page_layout.go @@ -18,7 +18,9 @@ type PageLayout struct { // PageLayoutChild is one child element of PageLayout: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type PageLayoutChild struct { PageHeight *Tenths PageWidth *Tenths diff --git a/gen/test/go/mx/page_margins.go b/gen/test/go/mx/page_margins.go index 11e261a83..602c715a1 100644 --- a/gen/test/go/mx/page_margins.go +++ b/gen/test/go/mx/page_margins.go @@ -18,7 +18,9 @@ type PageMargins struct { // PageMarginsChild is one child element of PageMargins: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type PageMarginsChild struct { LeftMargin *Tenths RightMargin *Tenths diff --git a/gen/test/go/mx/part_group.go b/gen/test/go/mx/part_group.go index 682725920..20e94559f 100644 --- a/gen/test/go/mx/part_group.go +++ b/gen/test/go/mx/part_group.go @@ -26,7 +26,9 @@ type PartGroup struct { // PartGroupChild is one child element of PartGroup: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type PartGroupChild struct { GroupName *GroupName GroupNameDisplay *NameDisplay diff --git a/gen/test/go/mx/part_list.go b/gen/test/go/mx/part_list.go index 083c78411..6179a3d78 100644 --- a/gen/test/go/mx/part_list.go +++ b/gen/test/go/mx/part_list.go @@ -20,7 +20,9 @@ type PartList struct { // PartListChild is one child element of PartList: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type PartListChild struct { PartGroup *PartGroup ScorePart *ScorePart diff --git a/gen/test/go/mx/partwise_measure.go b/gen/test/go/mx/partwise_measure.go index 46f0902b1..c4e7395f3 100644 --- a/gen/test/go/mx/partwise_measure.go +++ b/gen/test/go/mx/partwise_measure.go @@ -20,7 +20,9 @@ type PartwiseMeasure struct { // PartwiseMeasureChild is one child element of PartwiseMeasure: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type PartwiseMeasureChild struct { Note *Note Backup *Backup diff --git a/gen/test/go/mx/partwise_part.go b/gen/test/go/mx/partwise_part.go index 3859c0f6c..0bb67c6b2 100644 --- a/gen/test/go/mx/partwise_part.go +++ b/gen/test/go/mx/partwise_part.go @@ -15,7 +15,9 @@ type PartwisePart struct { // PartwisePartChild is one child element of PartwisePart: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type PartwisePartChild struct { Measure *PartwiseMeasure } diff --git a/gen/test/go/mx/pedal_tuning.go b/gen/test/go/mx/pedal_tuning.go index 6824e3ffd..e753041a8 100644 --- a/gen/test/go/mx/pedal_tuning.go +++ b/gen/test/go/mx/pedal_tuning.go @@ -15,7 +15,9 @@ type PedalTuning struct { // PedalTuningChild is one child element of PedalTuning: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type PedalTuningChild struct { PedalStep *Step PedalAlter *Semitones diff --git a/gen/test/go/mx/percussion.go b/gen/test/go/mx/percussion.go index f579cf917..20d66ec2f 100644 --- a/gen/test/go/mx/percussion.go +++ b/gen/test/go/mx/percussion.go @@ -31,7 +31,9 @@ type Percussion struct { // PercussionChild is one child element of Percussion: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type PercussionChild struct { Glass *Glass Metal *Metal diff --git a/gen/test/go/mx/pitch.go b/gen/test/go/mx/pitch.go index 6689a2b38..022089d3a 100644 --- a/gen/test/go/mx/pitch.go +++ b/gen/test/go/mx/pitch.go @@ -16,7 +16,9 @@ type Pitch struct { // PitchChild is one child element of Pitch: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type PitchChild struct { Step *Step Alter *Semitones diff --git a/gen/test/go/mx/play.go b/gen/test/go/mx/play.go index 102d88173..364a877a8 100644 --- a/gen/test/go/mx/play.go +++ b/gen/test/go/mx/play.go @@ -19,7 +19,9 @@ type Play struct { // PlayChild is one child element of Play: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type PlayChild struct { Ipa *string Mute *Mute diff --git a/gen/test/go/mx/print.go b/gen/test/go/mx/print.go index 44352f581..3ff22e1ad 100644 --- a/gen/test/go/mx/print.go +++ b/gen/test/go/mx/print.go @@ -27,7 +27,9 @@ type Print struct { // PrintChild is one child element of Print: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type PrintChild struct { PageLayout *PageLayout SystemLayout *SystemLayout diff --git a/gen/test/go/mx/rest.go b/gen/test/go/mx/rest.go index b679fb5fe..890bf9813 100644 --- a/gen/test/go/mx/rest.go +++ b/gen/test/go/mx/rest.go @@ -18,7 +18,9 @@ type Rest struct { // RestChild is one child element of Rest: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type RestChild struct { DisplayStep *Step DisplayOctave *Octave diff --git a/gen/test/go/mx/root.go b/gen/test/go/mx/root.go index 2a04679e2..6a9feaeed 100644 --- a/gen/test/go/mx/root.go +++ b/gen/test/go/mx/root.go @@ -18,7 +18,9 @@ type Root struct { // RootChild is one child element of Root: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type RootChild struct { RootStep *RootStep RootAlter *RootAlter diff --git a/gen/test/go/mx/runtime.go b/gen/test/go/mx/runtime.go index c48378e3c..4afb5d9ac 100644 --- a/gen/test/go/mx/runtime.go +++ b/gen/test/go/mx/runtime.go @@ -8,6 +8,11 @@ import ( "strings" ) +// SupportedMusicXMLVersion is the MusicXML version of the schema this +// package was generated from. Documents declaring a newer version may use +// types this model cannot represent; harnesses gate on it. +const SupportedMusicXMLVersion = "3.1" + // tryParseDecimal parses s strictly as a decimal number. func tryParseDecimal(s string) (float64, bool) { v, err := strconv.ParseFloat(strings.TrimSpace(s), 64) diff --git a/gen/test/go/mx/scaling.go b/gen/test/go/mx/scaling.go index 9bf6dc319..e2ad79abf 100644 --- a/gen/test/go/mx/scaling.go +++ b/gen/test/go/mx/scaling.go @@ -19,7 +19,9 @@ type Scaling struct { // ScalingChild is one child element of Scaling: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type ScalingChild struct { Millimeters *Millimeters Tenths *Tenths diff --git a/gen/test/go/mx/scordatura.go b/gen/test/go/mx/scordatura.go index eec22dd40..bc6be1da1 100644 --- a/gen/test/go/mx/scordatura.go +++ b/gen/test/go/mx/scordatura.go @@ -17,7 +17,9 @@ type Scordatura struct { // ScordaturaChild is one child element of Scordatura: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type ScordaturaChild struct { Accord *Accord } diff --git a/gen/test/go/mx/score_instrument.go b/gen/test/go/mx/score_instrument.go index 9a29fd739..f5763ec59 100644 --- a/gen/test/go/mx/score_instrument.go +++ b/gen/test/go/mx/score_instrument.go @@ -21,7 +21,9 @@ type ScoreInstrument struct { // ScoreInstrumentChild is one child element of ScoreInstrument: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type ScoreInstrumentChild struct { InstrumentName *string InstrumentAbbreviation *string diff --git a/gen/test/go/mx/score_part.go b/gen/test/go/mx/score_part.go index d4d1fe3b3..9432c051b 100644 --- a/gen/test/go/mx/score_part.go +++ b/gen/test/go/mx/score_part.go @@ -19,7 +19,9 @@ type ScorePart struct { // ScorePartChild is one child element of ScorePart: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type ScorePartChild struct { Identification *Identification PartName *PartName diff --git a/gen/test/go/mx/score_partwise.go b/gen/test/go/mx/score_partwise.go index d064cf442..c5e5d315e 100644 --- a/gen/test/go/mx/score_partwise.go +++ b/gen/test/go/mx/score_partwise.go @@ -15,7 +15,9 @@ type ScorePartwise struct { // ScorePartwiseChild is one child element of ScorePartwise: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type ScorePartwiseChild struct { Work *Work MovementNumber *string diff --git a/gen/test/go/mx/score_timewise.go b/gen/test/go/mx/score_timewise.go index 561a0ca22..18408a962 100644 --- a/gen/test/go/mx/score_timewise.go +++ b/gen/test/go/mx/score_timewise.go @@ -15,7 +15,9 @@ type ScoreTimewise struct { // ScoreTimewiseChild is one child element of ScoreTimewise: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type ScoreTimewiseChild struct { Work *Work MovementNumber *string diff --git a/gen/test/go/mx/slash.go b/gen/test/go/mx/slash.go index 02346e512..93d3c028b 100644 --- a/gen/test/go/mx/slash.go +++ b/gen/test/go/mx/slash.go @@ -22,7 +22,9 @@ type Slash struct { // SlashChild is one child element of Slash: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type SlashChild struct { SlashType *NoteTypeValue SlashDot *Empty diff --git a/gen/test/go/mx/sound.go b/gen/test/go/mx/sound.go index ba25edb46..2c8d33bf9 100644 --- a/gen/test/go/mx/sound.go +++ b/gen/test/go/mx/sound.go @@ -68,7 +68,9 @@ type Sound struct { // SoundChild is one child element of Sound: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type SoundChild struct { MIDIDevice *MIDIDevice MIDIInstrument *MIDIInstrument diff --git a/gen/test/go/mx/staff_details.go b/gen/test/go/mx/staff_details.go index ddc13373d..db4fb2cf5 100644 --- a/gen/test/go/mx/staff_details.go +++ b/gen/test/go/mx/staff_details.go @@ -24,7 +24,9 @@ type StaffDetails struct { // StaffDetailsChild is one child element of StaffDetails: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type StaffDetailsChild struct { StaffType *StaffType StaffLines *int diff --git a/gen/test/go/mx/staff_layout.go b/gen/test/go/mx/staff_layout.go index 3d1db8237..d9f842a7f 100644 --- a/gen/test/go/mx/staff_layout.go +++ b/gen/test/go/mx/staff_layout.go @@ -20,7 +20,9 @@ type StaffLayout struct { // StaffLayoutChild is one child element of StaffLayout: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type StaffLayoutChild struct { StaffDistance *Tenths } diff --git a/gen/test/go/mx/staff_tuning.go b/gen/test/go/mx/staff_tuning.go index 20b7ad002..e71c50962 100644 --- a/gen/test/go/mx/staff_tuning.go +++ b/gen/test/go/mx/staff_tuning.go @@ -16,7 +16,9 @@ type StaffTuning struct { // StaffTuningChild is one child element of StaffTuning: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type StaffTuningChild struct { TuningStep *Step TuningAlter *Semitones diff --git a/gen/test/go/mx/stick.go b/gen/test/go/mx/stick.go index 47a273b43..b209668fc 100644 --- a/gen/test/go/mx/stick.go +++ b/gen/test/go/mx/stick.go @@ -20,7 +20,9 @@ type Stick struct { // StickChild is one child element of Stick: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type StickChild struct { StickType *StickType StickMaterial *StickMaterial diff --git a/gen/test/go/mx/system_dividers.go b/gen/test/go/mx/system_dividers.go index 87ebe106d..b34f1c737 100644 --- a/gen/test/go/mx/system_dividers.go +++ b/gen/test/go/mx/system_dividers.go @@ -21,7 +21,9 @@ type SystemDividers struct { // SystemDividersChild is one child element of SystemDividers: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type SystemDividersChild struct { LeftDivider *EmptyPrintObjectStyleAlign RightDivider *EmptyPrintObjectStyleAlign diff --git a/gen/test/go/mx/system_layout.go b/gen/test/go/mx/system_layout.go index 7eb845ecc..9bfbdb68e 100644 --- a/gen/test/go/mx/system_layout.go +++ b/gen/test/go/mx/system_layout.go @@ -24,7 +24,9 @@ type SystemLayout struct { // SystemLayoutChild is one child element of SystemLayout: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type SystemLayoutChild struct { SystemMargins *SystemMargins SystemDistance *Tenths diff --git a/gen/test/go/mx/system_margins.go b/gen/test/go/mx/system_margins.go index 445f682f4..7c9ea06b6 100644 --- a/gen/test/go/mx/system_margins.go +++ b/gen/test/go/mx/system_margins.go @@ -16,7 +16,9 @@ type SystemMargins struct { // SystemMarginsChild is one child element of SystemMargins: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type SystemMarginsChild struct { LeftMargin *Tenths RightMargin *Tenths diff --git a/gen/test/go/mx/technical.go b/gen/test/go/mx/technical.go index e8df107a6..f33cc3f4f 100644 --- a/gen/test/go/mx/technical.go +++ b/gen/test/go/mx/technical.go @@ -16,7 +16,9 @@ type Technical struct { // TechnicalChild is one child element of Technical: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type TechnicalChild struct { UpBow *EmptyPlacement DownBow *EmptyPlacement diff --git a/gen/test/go/mx/time.go b/gen/test/go/mx/time.go index aa9b30053..a840a3651 100644 --- a/gen/test/go/mx/time.go +++ b/gen/test/go/mx/time.go @@ -38,7 +38,9 @@ type Time struct { // TimeChild is one child element of Time: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type TimeChild struct { Beats *string BeatType *string diff --git a/gen/test/go/mx/time_modification.go b/gen/test/go/mx/time_modification.go index eec53e9aa..74d8346bf 100644 --- a/gen/test/go/mx/time_modification.go +++ b/gen/test/go/mx/time_modification.go @@ -19,7 +19,9 @@ type TimeModification struct { // TimeModificationChild is one child element of TimeModification: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type TimeModificationChild struct { ActualNotes *int NormalNotes *int diff --git a/gen/test/go/mx/timewise_measure.go b/gen/test/go/mx/timewise_measure.go index 45722d3d9..84a3e6a95 100644 --- a/gen/test/go/mx/timewise_measure.go +++ b/gen/test/go/mx/timewise_measure.go @@ -20,7 +20,9 @@ type TimewiseMeasure struct { // TimewiseMeasureChild is one child element of TimewiseMeasure: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type TimewiseMeasureChild struct { Part *TimewisePart } diff --git a/gen/test/go/mx/timewise_part.go b/gen/test/go/mx/timewise_part.go index 438b71e33..4a1423288 100644 --- a/gen/test/go/mx/timewise_part.go +++ b/gen/test/go/mx/timewise_part.go @@ -15,7 +15,9 @@ type TimewisePart struct { // TimewisePartChild is one child element of TimewisePart: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type TimewisePartChild struct { Note *Note Backup *Backup diff --git a/gen/test/go/mx/transpose.go b/gen/test/go/mx/transpose.go index dff4ea5fb..3d6329f44 100644 --- a/gen/test/go/mx/transpose.go +++ b/gen/test/go/mx/transpose.go @@ -20,7 +20,9 @@ type Transpose struct { // TransposeChild is one child element of Transpose: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type TransposeChild struct { Diatonic *int Chromatic *Semitones diff --git a/gen/test/go/mx/tuplet.go b/gen/test/go/mx/tuplet.go index 10a0e535a..a404765b8 100644 --- a/gen/test/go/mx/tuplet.go +++ b/gen/test/go/mx/tuplet.go @@ -39,7 +39,9 @@ type Tuplet struct { // TupletChild is one child element of Tuplet: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type TupletChild struct { TupletActual *TupletPortion TupletNormal *TupletPortion diff --git a/gen/test/go/mx/tuplet_portion.go b/gen/test/go/mx/tuplet_portion.go index 0efcc314a..c56ca7792 100644 --- a/gen/test/go/mx/tuplet_portion.go +++ b/gen/test/go/mx/tuplet_portion.go @@ -18,7 +18,9 @@ type TupletPortion struct { // TupletPortionChild is one child element of TupletPortion: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type TupletPortionChild struct { TupletNumber *TupletNumber TupletType *TupletType diff --git a/gen/test/go/mx/unpitched.go b/gen/test/go/mx/unpitched.go index 002e2fcf5..c55fdb808 100644 --- a/gen/test/go/mx/unpitched.go +++ b/gen/test/go/mx/unpitched.go @@ -16,7 +16,9 @@ type Unpitched struct { // UnpitchedChild is one child element of Unpitched: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type UnpitchedChild struct { DisplayStep *Step DisplayOctave *Octave diff --git a/gen/test/go/mx/virtual_instrument.go b/gen/test/go/mx/virtual_instrument.go index df2643ada..1111b0693 100644 --- a/gen/test/go/mx/virtual_instrument.go +++ b/gen/test/go/mx/virtual_instrument.go @@ -16,7 +16,9 @@ type VirtualInstrument struct { // VirtualInstrumentChild is one child element of VirtualInstrument: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type VirtualInstrumentChild struct { VirtualLibrary *string VirtualName *string diff --git a/gen/test/go/mx/work.go b/gen/test/go/mx/work.go index 603043af6..1133303bf 100644 --- a/gen/test/go/mx/work.go +++ b/gen/test/go/mx/work.go @@ -16,7 +16,9 @@ type Work struct { // WorkChild is one child element of Work: exactly one field // is non-nil, and that pointer says which element this is. (No kind // discriminator: schema element names like harmony's would -// collide with a synthetic field.) +// collide with a synthetic field.) Constructing a child with zero or +// multiple fields set is undefined: serialization writes the first +// non-nil field in schema order and nothing when all are nil. type WorkChild struct { WorkNumber *string WorkTitle *string From 283372c87baf03989217229ceb2c0793477ffc82 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 19:39:47 +0000 Subject: [PATCH 25/45] gen: incorporate the final code review; harness verifies namespace fidelity The complex-type code review's accepted findings, and the real defect the first of them exposed: Harness soundness (the review's top three): - The C comparison now checks namespace declarations: libxml2 keeps xmlns/xmlns:foo in nsDef, never in the attribute list, so the model's namespace preservation was previously unverifiable. Turning the check on immediately caught a real round-trip defect: serialization built the tree DETACHED (the document was attached last), so when libxml2 resolved the reserved xml: prefix for xml:lang/xml:space with no document context it fabricated an xmlns:xml declaration on the carrying element -- 35 corpus files diverged from their inputs invisibly. The document template now serializes under a scratch parent attached to the document, where the implicit xml namespace resolves without inventing declarations. - The Go harness sorts and compares attributes by QUALIFIED name (FullKey): a defect dropping a prefix (xlink:href -> href) can no longer pass on the local name. The C side's qualified-name buffers abort on truncation rather than letting two truncated names compare equal. - Numeric-equivalence scope and the version-pinning rewrite were flagged; both are the documented corert design (AGENTS.md), shared with the C++ reference harness, and are deliberately unchanged. Template fixes: - String-plate children are stored unboxed: CValue carries an explicit is_pointer_value flag (raw string primitives AND char* typedefs like MxMode) instead of sniffing the spelled type, removing a needless allocation per string-valued child. - The Go backend fails loud on the one derivation shape its inherit template cannot render (element members spread across the base chain -- no MusicXML schema has one; the C flatten path is immune). - Serialize paths abort on libxml2 allocation failure, matching the runtime's OOM policy; the redundant Go string-primitive special cases collapsed into the shared parse expression (byte-identical output). Verified end to end after regeneration: 83 unit tests, go vet + Go corert green, zero-warning C build, both smoke binaries, C corert 776/0/52, and valgrind across the full C corert run: 0 leaks, 0 errors. --- gen/emit/c/api.py | 8 +++- gen/emit/c/complexes.py | 12 +++-- gen/emit/c/document.py | 18 ++++++- gen/emit/go/__init__.py | 23 ++++++++- gen/emit/go/complexes.py | 15 ++---- gen/test/c/mx/mx_accidental.c | 2 + gen/test/c/mx/mx_accidental_mark.c | 2 + gen/test/c/mx/mx_accidental_text.c | 2 + gen/test/c/mx/mx_accord.c | 2 + gen/test/c/mx/mx_accordion_registration.c | 2 + gen/test/c/mx/mx_appearance.c | 2 + gen/test/c/mx/mx_arpeggiate.c | 2 + gen/test/c/mx/mx_arrow.c | 2 + gen/test/c/mx/mx_articulations.c | 2 + gen/test/c/mx/mx_attributes.c | 2 + gen/test/c/mx/mx_backup.c | 2 + gen/test/c/mx/mx_bar_style_color.c | 2 + gen/test/c/mx/mx_barline.c | 2 + gen/test/c/mx/mx_barre.c | 2 + gen/test/c/mx/mx_bass.c | 2 + gen/test/c/mx/mx_bass_alter.c | 2 + gen/test/c/mx/mx_bass_step.c | 2 + gen/test/c/mx/mx_beam.c | 2 + gen/test/c/mx/mx_beat_repeat.c | 2 + gen/test/c/mx/mx_beat_unit_tied.c | 2 + gen/test/c/mx/mx_beater.c | 2 + gen/test/c/mx/mx_bend.c | 2 + gen/test/c/mx/mx_bookmark.c | 2 + gen/test/c/mx/mx_bracket.c | 2 + gen/test/c/mx/mx_breath_mark.c | 2 + gen/test/c/mx/mx_caesura.c | 2 + gen/test/c/mx/mx_cancel.c | 2 + gen/test/c/mx/mx_clef.c | 2 + gen/test/c/mx/mx_coda.c | 2 + gen/test/c/mx/mx_credit.c | 2 + gen/test/c/mx/mx_dashes.c | 2 + gen/test/c/mx/mx_defaults.c | 2 + gen/test/c/mx/mx_degree.c | 2 + gen/test/c/mx/mx_degree_alter.c | 2 + gen/test/c/mx/mx_degree_type.c | 2 + gen/test/c/mx/mx_degree_value.c | 2 + gen/test/c/mx/mx_direction.c | 2 + gen/test/c/mx/mx_direction_type.c | 2 + gen/test/c/mx/mx_directive.c | 2 + gen/test/c/mx/mx_distance.c | 2 + gen/test/c/mx/mx_document.c | 20 ++++++-- gen/test/c/mx/mx_dynamics.c | 2 + gen/test/c/mx/mx_elision.c | 2 + gen/test/c/mx/mx_empty.c | 2 + gen/test/c/mx/mx_empty_font.c | 2 + gen/test/c/mx/mx_empty_line.c | 2 + gen/test/c/mx/mx_empty_placement.c | 2 + gen/test/c/mx/mx_empty_placement_smufl.c | 2 + .../c/mx/mx_empty_print_object_style_align.c | 2 + gen/test/c/mx/mx_empty_print_style_align_id.c | 2 + gen/test/c/mx/mx_empty_trill_sound.c | 2 + gen/test/c/mx/mx_encoding.c | 14 ++---- gen/test/c/mx/mx_encoding.h | 2 +- gen/test/c/mx/mx_ending.c | 2 + gen/test/c/mx/mx_extend.c | 2 + gen/test/c/mx/mx_feature.c | 2 + gen/test/c/mx/mx_fermata.c | 2 + gen/test/c/mx/mx_figure.c | 2 + gen/test/c/mx/mx_figured_bass.c | 2 + gen/test/c/mx/mx_fingering.c | 2 + gen/test/c/mx/mx_first_fret.c | 2 + gen/test/c/mx/mx_formatted_symbol_id.c | 2 + gen/test/c/mx/mx_formatted_text.c | 2 + gen/test/c/mx/mx_formatted_text_id.c | 2 + gen/test/c/mx/mx_forward.c | 2 + gen/test/c/mx/mx_frame.c | 2 + gen/test/c/mx/mx_frame_note.c | 2 + gen/test/c/mx/mx_fret.c | 2 + gen/test/c/mx/mx_glass.c | 2 + gen/test/c/mx/mx_glissando.c | 2 + gen/test/c/mx/mx_glyph.c | 2 + gen/test/c/mx/mx_grace.c | 2 + gen/test/c/mx/mx_group_barline.c | 2 + gen/test/c/mx/mx_group_name.c | 2 + gen/test/c/mx/mx_group_symbol.c | 2 + gen/test/c/mx/mx_grouping.c | 2 + gen/test/c/mx/mx_hammer_on_pull_off.c | 2 + gen/test/c/mx/mx_handbell.c | 2 + gen/test/c/mx/mx_harmon_closed.c | 2 + gen/test/c/mx/mx_harmon_mute.c | 2 + gen/test/c/mx/mx_harmonic.c | 2 + gen/test/c/mx/mx_harmony.c | 2 + gen/test/c/mx/mx_harp_pedals.c | 2 + gen/test/c/mx/mx_heel_toe.c | 2 + gen/test/c/mx/mx_hole.c | 2 + gen/test/c/mx/mx_hole_closed.c | 2 + gen/test/c/mx/mx_horizontal_turn.c | 2 + gen/test/c/mx/mx_identification.c | 2 + gen/test/c/mx/mx_image.c | 2 + gen/test/c/mx/mx_instrument.c | 2 + gen/test/c/mx/mx_interchangeable.c | 2 + gen/test/c/mx/mx_inversion.c | 2 + gen/test/c/mx/mx_key.c | 14 ++---- gen/test/c/mx/mx_key.h | 2 +- gen/test/c/mx/mx_key_accidental.c | 2 + gen/test/c/mx/mx_key_octave.c | 2 + gen/test/c/mx/mx_kind.c | 2 + gen/test/c/mx/mx_level.c | 2 + gen/test/c/mx/mx_line_width.c | 2 + gen/test/c/mx/mx_link.c | 2 + gen/test/c/mx/mx_lyric.c | 2 + gen/test/c/mx/mx_lyric_font.c | 2 + gen/test/c/mx/mx_lyric_language.c | 2 + gen/test/c/mx/mx_measure_layout.c | 2 + gen/test/c/mx/mx_measure_numbering.c | 2 + gen/test/c/mx/mx_measure_repeat.c | 2 + gen/test/c/mx/mx_measure_style.c | 2 + gen/test/c/mx/mx_metronome.c | 2 + gen/test/c/mx/mx_metronome_beam.c | 2 + gen/test/c/mx/mx_metronome_note.c | 2 + gen/test/c/mx/mx_metronome_tied.c | 2 + gen/test/c/mx/mx_metronome_tuplet.c | 2 + gen/test/c/mx/mx_midi_device.c | 2 + gen/test/c/mx/mx_midi_instrument.c | 2 + gen/test/c/mx/mx_miscellaneous.c | 2 + gen/test/c/mx/mx_miscellaneous_field.c | 2 + gen/test/c/mx/mx_mordent.c | 2 + gen/test/c/mx/mx_multiple_rest.c | 2 + gen/test/c/mx/mx_name_display.c | 2 + gen/test/c/mx/mx_non_arpeggiate.c | 2 + gen/test/c/mx/mx_notations.c | 2 + gen/test/c/mx/mx_note.c | 2 + gen/test/c/mx/mx_note_size.c | 2 + gen/test/c/mx/mx_note_type.c | 2 + gen/test/c/mx/mx_notehead.c | 2 + gen/test/c/mx/mx_notehead_text.c | 2 + gen/test/c/mx/mx_octave_shift.c | 2 + gen/test/c/mx/mx_offset.c | 2 + gen/test/c/mx/mx_opus.c | 2 + gen/test/c/mx/mx_ornaments.c | 2 + gen/test/c/mx/mx_other_appearance.c | 2 + gen/test/c/mx/mx_other_direction.c | 2 + gen/test/c/mx/mx_other_notation.c | 2 + gen/test/c/mx/mx_other_placement_text.c | 2 + gen/test/c/mx/mx_other_play.c | 2 + gen/test/c/mx/mx_other_text.c | 2 + gen/test/c/mx/mx_page_layout.c | 2 + gen/test/c/mx/mx_page_margins.c | 2 + gen/test/c/mx/mx_part_group.c | 2 + gen/test/c/mx/mx_part_list.c | 2 + gen/test/c/mx/mx_part_name.c | 2 + gen/test/c/mx/mx_part_symbol.c | 2 + gen/test/c/mx/mx_partwise_measure.c | 2 + gen/test/c/mx/mx_partwise_part.c | 2 + gen/test/c/mx/mx_pedal.c | 2 + gen/test/c/mx/mx_pedal_tuning.c | 2 + gen/test/c/mx/mx_per_minute.c | 2 + gen/test/c/mx/mx_percussion.c | 2 + gen/test/c/mx/mx_pitch.c | 2 + gen/test/c/mx/mx_pitched.c | 2 + gen/test/c/mx/mx_placement_text.c | 2 + gen/test/c/mx/mx_play.c | 2 + gen/test/c/mx/mx_principal_voice.c | 2 + gen/test/c/mx/mx_print.c | 2 + gen/test/c/mx/mx_repeat.c | 2 + gen/test/c/mx/mx_rest.c | 2 + gen/test/c/mx/mx_root.c | 2 + gen/test/c/mx/mx_root_alter.c | 2 + gen/test/c/mx/mx_root_step.c | 2 + gen/test/c/mx/mx_scaling.c | 2 + gen/test/c/mx/mx_scordatura.c | 2 + gen/test/c/mx/mx_score_instrument.c | 2 + gen/test/c/mx/mx_score_part.c | 2 + gen/test/c/mx/mx_score_partwise.c | 2 + gen/test/c/mx/mx_score_timewise.c | 2 + gen/test/c/mx/mx_segno.c | 2 + gen/test/c/mx/mx_slash.c | 2 + gen/test/c/mx/mx_slide.c | 2 + gen/test/c/mx/mx_slur.c | 2 + gen/test/c/mx/mx_sound.c | 2 + gen/test/c/mx/mx_staff_details.c | 2 + gen/test/c/mx/mx_staff_divide.c | 2 + gen/test/c/mx/mx_staff_layout.c | 2 + gen/test/c/mx/mx_staff_tuning.c | 2 + gen/test/c/mx/mx_stem.c | 2 + gen/test/c/mx/mx_stick.c | 2 + gen/test/c/mx/mx_string.c | 2 + gen/test/c/mx/mx_string_mute.c | 2 + gen/test/c/mx/mx_strong_accent.c | 2 + gen/test/c/mx/mx_style_text.c | 2 + gen/test/c/mx/mx_supports.c | 2 + gen/test/c/mx/mx_system_dividers.c | 2 + gen/test/c/mx/mx_system_layout.c | 2 + gen/test/c/mx/mx_system_margins.c | 2 + gen/test/c/mx/mx_tap.c | 2 + gen/test/c/mx/mx_technical.c | 2 + gen/test/c/mx/mx_text_element_data.c | 2 + gen/test/c/mx/mx_tie.c | 2 + gen/test/c/mx/mx_tied.c | 2 + gen/test/c/mx/mx_time.c | 2 + gen/test/c/mx/mx_time_modification.c | 2 + gen/test/c/mx/mx_timewise_measure.c | 2 + gen/test/c/mx/mx_timewise_part.c | 2 + gen/test/c/mx/mx_transpose.c | 2 + gen/test/c/mx/mx_tremolo.c | 2 + gen/test/c/mx/mx_tuplet.c | 2 + gen/test/c/mx/mx_tuplet_dot.c | 2 + gen/test/c/mx/mx_tuplet_number.c | 2 + gen/test/c/mx/mx_tuplet_portion.c | 2 + gen/test/c/mx/mx_tuplet_type.c | 2 + gen/test/c/mx/mx_typed_text.c | 2 + gen/test/c/mx/mx_unpitched.c | 2 + gen/test/c/mx/mx_virtual_instrument.c | 2 + gen/test/c/mx/mx_wavy_line.c | 2 + gen/test/c/mx/mx_wedge.c | 2 + gen/test/c/mx/mx_work.c | 2 + gen/test/c/src/compare.c | 47 +++++++++++++++++-- gen/test/go/corert/normalize.go | 4 +- gen/test/go/corert/roundtrip.go | 10 ++-- 214 files changed, 539 insertions(+), 52 deletions(-) diff --git a/gen/emit/c/api.py b/gen/emit/c/api.py index 7ad7bc0d1..655088571 100644 --- a/gen/emit/c/api.py +++ b/gen/emit/c/api.py @@ -31,6 +31,8 @@ class CValue: to_string: str # the wire spelling of a value to_string_owned: bool # the to_string result must be freed free: str | None # statement template releasing a value's OWN memory + is_pointer_value: bool = False # the value IS a pointer (char* family): + # a child field stores it directly and discriminates on it, no boxing _PRIM_FAMILY = { @@ -55,7 +57,8 @@ def value_api(plates: Plates, ref: PlateRef) -> CValue: if family == "integer": return CValue("long", f"{p}parse_int({{0}})", f"{p}format_int({{0}})", True, None) - return CValue("char *", f"{p}strdup({{0}})", "{0}", False, "free({0});") + return CValue("char *", f"{p}strdup({{0}})", "{0}", False, "free({0});", + is_pointer_value=True) plate = plates.plate(ref.wire) parse = fn_name(plates, plate.name, "parse") + "({0})" @@ -66,7 +69,8 @@ def value_api(plates: Plates, ref: PlateRef) -> CValue: return CValue(ref.ident, parse, to_string, True, None) if plate.kind == "string": # The typedef is char*: the value is its own spelling and owns itself. - return CValue(ref.ident, parse, "{0}", False, "free({0});") + return CValue(ref.ident, parse, "{0}", False, "free({0});", + is_pointer_value=True) if plate.kind == "union": free = fn_name(plates, plate.name, "free") + "(&{0});" return CValue(ref.ident, parse, to_string, True, free) diff --git a/gen/emit/c/complexes.py b/gen/emit/c/complexes.py index d31e7a092..9d860e787 100644 --- a/gen/emit/c/complexes.py +++ b/gen/emit/c/complexes.py @@ -38,12 +38,14 @@ def _child_field(plates: Plates, member: Member) -> tuple[str, bool]: """(field declaration type, value-is-the-pointer). Children discriminate by non-NULL, so every field is a pointer: complex types and boxed values - point at their structs; string-family values ARE their char* pointer.""" - api = value_api(plates, member.type_ref) if member.type_ref.category != "complex" else None + point at their structs; char*-family values (raw string primitives AND + string-plate typedefs) ARE their pointer and are stored unboxed.""" if member.type_ref.category == "complex": return f"{member.type_ref.ident} *", False - if api.c_type.endswith("*"): - return api.c_type, True # char* family: the pointer is the value + api = value_api(plates, member.type_ref) + if api.is_pointer_value: + sep = "" if api.c_type.endswith("*") else " " + return f"{api.c_type}{sep}", True return f"{api.c_type} *", False @@ -227,6 +229,8 @@ def complex_files(plates: Plates, plate: ComplexPlate, includes: list[str], rt: f"xmlNodePtr {serialize}(const {ident} *m, xmlNodePtr parent, const char *tag) {{", " xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL)", " : xmlNewNode(NULL, BAD_CAST tag);", + " if (!el)", + " abort(); /* OOM policy: abort, matching the runtime's allocators */", ] if value is not None: api = value_api(plates, value.type_ref) diff --git a/gen/emit/c/document.py b/gen/emit/c/document.py index 48b81f22b..85794b15c 100644 --- a/gen/emit/c/document.py +++ b/gen/emit/c/document.py @@ -94,6 +94,17 @@ def document_files(plates: Plates, stem: str, rt: str): "", f"int {p}document_to_xdoc(const {doc_type} *d, xmlDocPtr *out) {{", " *out = NULL;", + ' xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");', + " if (!doc)", + " abort(); /* OOM policy: abort, matching the runtime's allocators */", + " /* Serialize under a scratch parent attached to the document so", + " reserved-prefix attributes (xml:lang, xml:space) resolve through", + " the document's implicit xml namespace. Building detached would", + " make libxml2 fabricate an xmlns:xml declaration on the element", + " that carries them, which the input never had. */", + ' xmlNodePtr scratch = xmlNewDocNode(doc, NULL, BAD_CAST "scratch", NULL);', + " if (!scratch)", + " abort();", " xmlNodePtr root = NULL;", ] first = True @@ -103,15 +114,18 @@ def document_files(plates: Plates, stem: str, rt: str): serialize = fn_name(plates, plate.name, "serialize") body += [ f" {kw} (d->{plate.name.snake})", - f" root = {serialize}(d->{plate.name.snake}, NULL, {c_string(ref.wire)});", + f" root = {serialize}(d->{plate.name.snake}, scratch, {c_string(ref.wire)});", ] body += [ " if (!root) {", f' {p}error_set("document has no root");', + " xmlFreeNode(scratch);", + " xmlFreeDoc(doc);", " return -1;", " }", - ' xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");', + " xmlUnlinkNode(root);", " xmlDocSetRootElement(doc, root);", + " xmlFreeNode(scratch);", " for (size_t i = 0; i < d->namespaces_count; i++)", " xmlNewNs(root, BAD_CAST d->namespaces[i].href,", " BAD_CAST d->namespaces[i].prefix);", diff --git a/gen/emit/go/__init__.py b/gen/emit/go/__init__.py index 03ef6bb67..b4678ce33 100644 --- a/gen/emit/go/__init__.py +++ b/gen/emit/go/__init__.py @@ -35,7 +35,9 @@ def _guard_identifiers(plates: Plates) -> None: """The plates' collision gate certifies projected identifiers; this backend also COMPOSES a few (the per-type Child struct, the Children field, its support types). A schema name landing on one of those must - fail loud here, not as a confusing compile error in generated code.""" + fail loud here, not as a confusing compile error in generated code. + It also rejects the one derivation shape the inherit template cannot + render (children spread across the chain).""" from gen.plates.model import element_members type_idents = {p.ident for p in list(plates.value_types) + list(plates.complex_types)} @@ -62,6 +64,25 @@ def _guard_identifiers(plates: Plates) -> None: f"'{plate.name.wire}' has a member projecting to 'Children', " f"which this backend reserves; rename it in config.toml" ) + for plate in plates.complex_types: + if plate.strategy != "inherit": + continue + # The inherit template dispatches children against ONE chain member's + # child struct. A derivation chain where two members carry element + # members (no MusicXML schema has one) would lose the others' children + # -- fail loud rather than emit a parser with a blind spot. + bearing = [] + cur = plate + while cur is not None: + if element_members(cur.members): + bearing.append(cur.name.wire) + cur = plates.plate(cur.base.wire) if cur.base is not None else None + if len(bearing) > 1: + raise ValueError( + f"'{plate.name.wire}': multiple chain members carry child " + f"elements ({', '.join(bearing)}); the inherit template " + f"cannot dispatch them all" + ) def render(plates: Plates) -> dict[str, str]: diff --git a/gen/emit/go/complexes.py b/gen/emit/go/complexes.py index 02b393686..be03b6701 100644 --- a/gen/emit/go/complexes.py +++ b/gen/emit/go/complexes.py @@ -130,10 +130,7 @@ def _attr_loop(plates: Plates, members: list[Member]) -> list[str]: ] for m in _attr_members(members): lines += [f"\t\tcase {go_string(m.name.wire)}:"] - if m.type_ref.category == "primitive" and _PRIM[m.type_ref.wire][0] == "string": - lines += ["\t\t\tv := a.Value"] - else: - lines += [f"\t\t\tv := {_parse_expr(m, 'a.Value')}"] + lines += [f"\t\t\tv := {_parse_expr(m, 'a.Value')}"] lines += [f"\t\t\tm.{m.ident} = &v"] lines += [ "\t\tdefault:", @@ -171,10 +168,7 @@ def _child_parse_loop(plates: Plates, owner: ComplexPlate) -> list[str]: f"\t\t\tm.Children = append(m.Children, {ident}Child{{{m.ident}: v}})", ] else: - if m.type_ref.category == "primitive" and _PRIM[m.type_ref.wire][0] == "string": - lines += ["\t\t\tv := c.Text()"] - else: - lines += [f"\t\t\tv := {_parse_expr(m, 'c.Text()')}"] + lines += [f"\t\t\tv := {_parse_expr(m, 'c.Text()')}"] lines += [ f"\t\t\tm.Children = append(m.Children, {ident}Child{{{m.ident}: &v}})", ] @@ -231,10 +225,7 @@ def _class_body(plates: Plates, plate: ComplexPlate) -> list[str]: lines += [f"\tm := &{ident}{{}}"] lines += _attr_loop(plates, plate.members) if value is not None: - if value.type_ref.category == "primitive" and _PRIM[value.type_ref.wire][0] == "string": - lines += [f"\tm.{value.ident} = el.Text()"] - else: - lines += [f"\tm.{value.ident} = {_parse_expr(value, 'el.Text()')}"] + lines += [f"\tm.{value.ident} = {_parse_expr(value, 'el.Text()')}"] if has_children: lines += _child_parse_loop(plates, plate) else: diff --git a/gen/test/c/mx/mx_accidental.c b/gen/test/c/mx/mx_accidental.c index 77e84d406..230fc21b1 100644 --- a/gen/test/c/mx/mx_accidental.c +++ b/gen/test/c/mx/mx_accidental.c @@ -97,6 +97,8 @@ MxAccidental *mx_accidental_parse(xmlNodePtr el) { xmlNodePtr mx_accidental_serialize(const MxAccidental *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_accidental_value_to_string(m->value))); if (m->has_cautionary) { xmlSetProp(el, BAD_CAST "cautionary", BAD_CAST mx_yes_no_to_string(m->cautionary)); diff --git a/gen/test/c/mx/mx_accidental_mark.c b/gen/test/c/mx/mx_accidental_mark.c index 1d15b877a..8f9362cb1 100644 --- a/gen/test/c/mx/mx_accidental_mark.c +++ b/gen/test/c/mx/mx_accidental_mark.c @@ -97,6 +97,8 @@ MxAccidentalMark *mx_accidental_mark_parse(xmlNodePtr el) { xmlNodePtr mx_accidental_mark_serialize(const MxAccidentalMark *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_accidental_value_to_string(m->value))); if (m->has_smufl) { xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); diff --git a/gen/test/c/mx/mx_accidental_text.c b/gen/test/c/mx/mx_accidental_text.c index 8a41a195e..4bdadc827 100644 --- a/gen/test/c/mx/mx_accidental_text.c +++ b/gen/test/c/mx/mx_accidental_text.c @@ -121,6 +121,8 @@ MxAccidentalText *mx_accidental_text_parse(xmlNodePtr el) { xmlNodePtr mx_accidental_text_serialize(const MxAccidentalText *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_accidental_value_to_string(m->value))); if (m->has_smufl) { xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); diff --git a/gen/test/c/mx/mx_accord.c b/gen/test/c/mx/mx_accord.c index cc14618bc..079eee218 100644 --- a/gen/test/c/mx/mx_accord.c +++ b/gen/test/c/mx/mx_accord.c @@ -84,6 +84,8 @@ MxAccord *mx_accord_parse(xmlNodePtr el) { xmlNodePtr mx_accord_serialize(const MxAccord *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_string) { char *s = mx_string_number_to_string(m->string); xmlSetProp(el, BAD_CAST "string", BAD_CAST s); diff --git a/gen/test/c/mx/mx_accordion_registration.c b/gen/test/c/mx/mx_accordion_registration.c index 4e4eb075b..40a254f2e 100644 --- a/gen/test/c/mx/mx_accordion_registration.c +++ b/gen/test/c/mx/mx_accordion_registration.c @@ -113,6 +113,8 @@ MxAccordionRegistration *mx_accordion_registration_parse(xmlNodePtr el) { xmlNodePtr mx_accordion_registration_serialize(const MxAccordionRegistration *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); diff --git a/gen/test/c/mx/mx_appearance.c b/gen/test/c/mx/mx_appearance.c index 5205cfa62..06f8f8c63 100644 --- a/gen/test/c/mx/mx_appearance.c +++ b/gen/test/c/mx/mx_appearance.c @@ -87,6 +87,8 @@ MxAppearance *mx_appearance_parse(xmlNodePtr el) { xmlNodePtr mx_appearance_serialize(const MxAppearance *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxAppearanceChild *ch = &m->children[i]; if (ch->line_width) { diff --git a/gen/test/c/mx/mx_arpeggiate.c b/gen/test/c/mx/mx_arpeggiate.c index 5f4c2f5c2..fa7aaf76f 100644 --- a/gen/test/c/mx/mx_arpeggiate.c +++ b/gen/test/c/mx/mx_arpeggiate.c @@ -73,6 +73,8 @@ MxArpeggiate *mx_arpeggiate_parse(xmlNodePtr el) { xmlNodePtr mx_arpeggiate_serialize(const MxArpeggiate *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_number) { char *s = mx_number_level_to_string(m->number); xmlSetProp(el, BAD_CAST "number", BAD_CAST s); diff --git a/gen/test/c/mx/mx_arrow.c b/gen/test/c/mx/mx_arrow.c index 7956bb6ec..976f869e5 100644 --- a/gen/test/c/mx/mx_arrow.c +++ b/gen/test/c/mx/mx_arrow.c @@ -120,6 +120,8 @@ MxArrow *mx_arrow_parse(xmlNodePtr el) { xmlNodePtr mx_arrow_serialize(const MxArrow *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); diff --git a/gen/test/c/mx/mx_articulations.c b/gen/test/c/mx/mx_articulations.c index dca35a98e..1256c98f1 100644 --- a/gen/test/c/mx/mx_articulations.c +++ b/gen/test/c/mx/mx_articulations.c @@ -162,6 +162,8 @@ MxArticulations *mx_articulations_parse(xmlNodePtr el) { xmlNodePtr mx_articulations_serialize(const MxArticulations *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_id) { xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); } diff --git a/gen/test/c/mx/mx_attributes.c b/gen/test/c/mx/mx_attributes.c index d2cc364f5..a4176af6e 100644 --- a/gen/test/c/mx/mx_attributes.c +++ b/gen/test/c/mx/mx_attributes.c @@ -141,6 +141,8 @@ MxAttributes *mx_attributes_parse(xmlNodePtr el) { xmlNodePtr mx_attributes_serialize(const MxAttributes *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxAttributesChild *ch = &m->children[i]; if (ch->footnote) { diff --git a/gen/test/c/mx/mx_backup.c b/gen/test/c/mx/mx_backup.c index 48bf0b7e6..6f40c2714 100644 --- a/gen/test/c/mx/mx_backup.c +++ b/gen/test/c/mx/mx_backup.c @@ -77,6 +77,8 @@ MxBackup *mx_backup_parse(xmlNodePtr el) { xmlNodePtr mx_backup_serialize(const MxBackup *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxBackupChild *ch = &m->children[i]; if (ch->duration) { diff --git a/gen/test/c/mx/mx_bar_style_color.c b/gen/test/c/mx/mx_bar_style_color.c index fa89f89a7..f7656e60e 100644 --- a/gen/test/c/mx/mx_bar_style_color.c +++ b/gen/test/c/mx/mx_bar_style_color.c @@ -55,6 +55,8 @@ MxBarStyleColor *mx_bar_style_color_parse(xmlNodePtr el) { xmlNodePtr mx_bar_style_color_serialize(const MxBarStyleColor *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_bar_style_to_string(m->value))); if (m->has_color) { xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); diff --git a/gen/test/c/mx/mx_barline.c b/gen/test/c/mx/mx_barline.c index 4726f819f..a277054fd 100644 --- a/gen/test/c/mx/mx_barline.c +++ b/gen/test/c/mx/mx_barline.c @@ -126,6 +126,8 @@ MxBarline *mx_barline_parse(xmlNodePtr el) { xmlNodePtr mx_barline_serialize(const MxBarline *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_location) { xmlSetProp(el, BAD_CAST "location", BAD_CAST mx_right_left_middle_to_string(m->location)); } diff --git a/gen/test/c/mx/mx_barre.c b/gen/test/c/mx/mx_barre.c index a3d4962d4..4aec0c873 100644 --- a/gen/test/c/mx/mx_barre.c +++ b/gen/test/c/mx/mx_barre.c @@ -52,6 +52,8 @@ MxBarre *mx_barre_parse(xmlNodePtr el) { xmlNodePtr mx_barre_serialize(const MxBarre *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_bass.c b/gen/test/c/mx/mx_bass.c index a63daf4ce..f7472bf98 100644 --- a/gen/test/c/mx/mx_bass.c +++ b/gen/test/c/mx/mx_bass.c @@ -69,6 +69,8 @@ MxBass *mx_bass_parse(xmlNodePtr el) { xmlNodePtr mx_bass_serialize(const MxBass *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxBassChild *ch = &m->children[i]; if (ch->bass_step) { diff --git a/gen/test/c/mx/mx_bass_alter.c b/gen/test/c/mx/mx_bass_alter.c index 9002414a1..ac82b75ec 100644 --- a/gen/test/c/mx/mx_bass_alter.c +++ b/gen/test/c/mx/mx_bass_alter.c @@ -85,6 +85,8 @@ MxBassAlter *mx_bass_alter_parse(xmlNodePtr el) { xmlNodePtr mx_bass_alter_serialize(const MxBassAlter *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ { char *s = mx_semitones_to_string(m->value); xmlAddChild(el, xmlNewText(BAD_CAST s)); diff --git a/gen/test/c/mx/mx_bass_step.c b/gen/test/c/mx/mx_bass_step.c index b9d4aae1e..4677be810 100644 --- a/gen/test/c/mx/mx_bass_step.c +++ b/gen/test/c/mx/mx_bass_step.c @@ -82,6 +82,8 @@ MxBassStep *mx_bass_step_parse(xmlNodePtr el) { xmlNodePtr mx_bass_step_serialize(const MxBassStep *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_step_to_string(m->value))); if (m->has_text) { xmlSetProp(el, BAD_CAST "text", BAD_CAST m->text); diff --git a/gen/test/c/mx/mx_beam.c b/gen/test/c/mx/mx_beam.c index be5df692b..4bfeae3d8 100644 --- a/gen/test/c/mx/mx_beam.c +++ b/gen/test/c/mx/mx_beam.c @@ -67,6 +67,8 @@ MxBeam *mx_beam_parse(xmlNodePtr el) { xmlNodePtr mx_beam_serialize(const MxBeam *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_beam_value_to_string(m->value))); if (m->has_number) { char *s = mx_beam_level_to_string(m->number); diff --git a/gen/test/c/mx/mx_beat_repeat.c b/gen/test/c/mx/mx_beat_repeat.c index 38f6a8910..734520a17 100644 --- a/gen/test/c/mx/mx_beat_repeat.c +++ b/gen/test/c/mx/mx_beat_repeat.c @@ -85,6 +85,8 @@ MxBeatRepeat *mx_beat_repeat_parse(xmlNodePtr el) { xmlNodePtr mx_beat_repeat_serialize(const MxBeatRepeat *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_beat_unit_tied.c b/gen/test/c/mx/mx_beat_unit_tied.c index 9454c8ce5..cc47b89be 100644 --- a/gen/test/c/mx/mx_beat_unit_tied.c +++ b/gen/test/c/mx/mx_beat_unit_tied.c @@ -71,6 +71,8 @@ MxBeatUnitTied *mx_beat_unit_tied_parse(xmlNodePtr el) { xmlNodePtr mx_beat_unit_tied_serialize(const MxBeatUnitTied *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxBeatUnitTiedChild *ch = &m->children[i]; if (ch->beat_unit) { diff --git a/gen/test/c/mx/mx_beater.c b/gen/test/c/mx/mx_beater.c index fcb4a9024..36dea0fd1 100644 --- a/gen/test/c/mx/mx_beater.c +++ b/gen/test/c/mx/mx_beater.c @@ -55,6 +55,8 @@ MxBeater *mx_beater_parse(xmlNodePtr el) { xmlNodePtr mx_beater_serialize(const MxBeater *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_beater_value_to_string(m->value))); if (m->has_tip) { xmlSetProp(el, BAD_CAST "tip", BAD_CAST mx_tip_direction_to_string(m->tip)); diff --git a/gen/test/c/mx/mx_bend.c b/gen/test/c/mx/mx_bend.c index 29a202705..fa5836708 100644 --- a/gen/test/c/mx/mx_bend.c +++ b/gen/test/c/mx/mx_bend.c @@ -122,6 +122,8 @@ MxBend *mx_bend_parse(xmlNodePtr el) { xmlNodePtr mx_bend_serialize(const MxBend *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); diff --git a/gen/test/c/mx/mx_bookmark.c b/gen/test/c/mx/mx_bookmark.c index cd095aab0..045d493c1 100644 --- a/gen/test/c/mx/mx_bookmark.c +++ b/gen/test/c/mx/mx_bookmark.c @@ -58,6 +58,8 @@ MxBookmark *mx_bookmark_parse(xmlNodePtr el) { xmlNodePtr mx_bookmark_serialize(const MxBookmark *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_id) { xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); } diff --git a/gen/test/c/mx/mx_bracket.c b/gen/test/c/mx/mx_bracket.c index 9cb5f7d5a..4059400a2 100644 --- a/gen/test/c/mx/mx_bracket.c +++ b/gen/test/c/mx/mx_bracket.c @@ -85,6 +85,8 @@ MxBracket *mx_bracket_parse(xmlNodePtr el) { xmlNodePtr mx_bracket_serialize(const MxBracket *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_continue_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_breath_mark.c b/gen/test/c/mx/mx_breath_mark.c index a44892bae..9554e9a15 100644 --- a/gen/test/c/mx/mx_breath_mark.c +++ b/gen/test/c/mx/mx_breath_mark.c @@ -82,6 +82,8 @@ MxBreathMark *mx_breath_mark_parse(xmlNodePtr el) { xmlNodePtr mx_breath_mark_serialize(const MxBreathMark *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_breath_mark_value_to_string(m->value))); if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); diff --git a/gen/test/c/mx/mx_caesura.c b/gen/test/c/mx/mx_caesura.c index 2f82ea4f2..0022a38c3 100644 --- a/gen/test/c/mx/mx_caesura.c +++ b/gen/test/c/mx/mx_caesura.c @@ -82,6 +82,8 @@ MxCaesura *mx_caesura_parse(xmlNodePtr el) { xmlNodePtr mx_caesura_serialize(const MxCaesura *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_caesura_value_to_string(m->value))); if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); diff --git a/gen/test/c/mx/mx_cancel.c b/gen/test/c/mx/mx_cancel.c index 3134dad58..287fe289a 100644 --- a/gen/test/c/mx/mx_cancel.c +++ b/gen/test/c/mx/mx_cancel.c @@ -55,6 +55,8 @@ MxCancel *mx_cancel_parse(xmlNodePtr el) { xmlNodePtr mx_cancel_serialize(const MxCancel *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ { char *s = mx_fifths_to_string(m->value); xmlAddChild(el, xmlNewText(BAD_CAST s)); diff --git a/gen/test/c/mx/mx_clef.c b/gen/test/c/mx/mx_clef.c index f002cc7a7..a92705cbc 100644 --- a/gen/test/c/mx/mx_clef.c +++ b/gen/test/c/mx/mx_clef.c @@ -126,6 +126,8 @@ MxClef *mx_clef_parse(xmlNodePtr el) { xmlNodePtr mx_clef_serialize(const MxClef *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_number) { char *s = mx_staff_number_to_string(m->number); xmlSetProp(el, BAD_CAST "number", BAD_CAST s); diff --git a/gen/test/c/mx/mx_coda.c b/gen/test/c/mx/mx_coda.c index aab88e110..a05cebcd3 100644 --- a/gen/test/c/mx/mx_coda.c +++ b/gen/test/c/mx/mx_coda.c @@ -85,6 +85,8 @@ MxCoda *mx_coda_parse(xmlNodePtr el) { xmlNodePtr mx_coda_serialize(const MxCoda *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_smufl) { xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); } diff --git a/gen/test/c/mx/mx_credit.c b/gen/test/c/mx/mx_credit.c index d5ea571e9..0cf4aa330 100644 --- a/gen/test/c/mx/mx_credit.c +++ b/gen/test/c/mx/mx_credit.c @@ -98,6 +98,8 @@ MxCredit *mx_credit_parse(xmlNodePtr el) { xmlNodePtr mx_credit_serialize(const MxCredit *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_page) { char *s = mx_format_int(m->page); xmlSetProp(el, BAD_CAST "page", BAD_CAST s); diff --git a/gen/test/c/mx/mx_dashes.c b/gen/test/c/mx/mx_dashes.c index ad6a834ef..eae8c59da 100644 --- a/gen/test/c/mx/mx_dashes.c +++ b/gen/test/c/mx/mx_dashes.c @@ -76,6 +76,8 @@ MxDashes *mx_dashes_parse(xmlNodePtr el) { xmlNodePtr mx_dashes_serialize(const MxDashes *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_continue_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_defaults.c b/gen/test/c/mx/mx_defaults.c index 188cdce00..346fff936 100644 --- a/gen/test/c/mx/mx_defaults.c +++ b/gen/test/c/mx/mx_defaults.c @@ -111,6 +111,8 @@ MxDefaults *mx_defaults_parse(xmlNodePtr el) { xmlNodePtr mx_defaults_serialize(const MxDefaults *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxDefaultsChild *ch = &m->children[i]; if (ch->scaling) { diff --git a/gen/test/c/mx/mx_degree.c b/gen/test/c/mx/mx_degree.c index 1c464791e..8137fbfd6 100644 --- a/gen/test/c/mx/mx_degree.c +++ b/gen/test/c/mx/mx_degree.c @@ -78,6 +78,8 @@ MxDegree *mx_degree_parse(xmlNodePtr el) { xmlNodePtr mx_degree_serialize(const MxDegree *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_print_object) { xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); } diff --git a/gen/test/c/mx/mx_degree_alter.c b/gen/test/c/mx/mx_degree_alter.c index 7e922885c..6ab97f44f 100644 --- a/gen/test/c/mx/mx_degree_alter.c +++ b/gen/test/c/mx/mx_degree_alter.c @@ -82,6 +82,8 @@ MxDegreeAlter *mx_degree_alter_parse(xmlNodePtr el) { xmlNodePtr mx_degree_alter_serialize(const MxDegreeAlter *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ { char *s = mx_semitones_to_string(m->value); xmlAddChild(el, xmlNewText(BAD_CAST s)); diff --git a/gen/test/c/mx/mx_degree_type.c b/gen/test/c/mx/mx_degree_type.c index 8829dc47f..74e730199 100644 --- a/gen/test/c/mx/mx_degree_type.c +++ b/gen/test/c/mx/mx_degree_type.c @@ -82,6 +82,8 @@ MxDegreeType *mx_degree_type_parse(xmlNodePtr el) { xmlNodePtr mx_degree_type_serialize(const MxDegreeType *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_degree_type_value_to_string(m->value))); if (m->has_text) { xmlSetProp(el, BAD_CAST "text", BAD_CAST m->text); diff --git a/gen/test/c/mx/mx_degree_value.c b/gen/test/c/mx/mx_degree_value.c index ae00b85dd..685d834e6 100644 --- a/gen/test/c/mx/mx_degree_value.c +++ b/gen/test/c/mx/mx_degree_value.c @@ -85,6 +85,8 @@ MxDegreeValue *mx_degree_value_parse(xmlNodePtr el) { xmlNodePtr mx_degree_value_serialize(const MxDegreeValue *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ { char *s = mx_format_int(m->value); xmlAddChild(el, xmlNewText(BAD_CAST s)); diff --git a/gen/test/c/mx/mx_direction.c b/gen/test/c/mx/mx_direction.c index c8ee751b7..631c1c521 100644 --- a/gen/test/c/mx/mx_direction.c +++ b/gen/test/c/mx/mx_direction.c @@ -109,6 +109,8 @@ MxDirection *mx_direction_parse(xmlNodePtr el) { xmlNodePtr mx_direction_serialize(const MxDirection *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_placement) { xmlSetProp(el, BAD_CAST "placement", BAD_CAST mx_above_below_to_string(m->placement)); } diff --git a/gen/test/c/mx/mx_direction_type.c b/gen/test/c/mx/mx_direction_type.c index eabaf6c20..30f41f855 100644 --- a/gen/test/c/mx/mx_direction_type.c +++ b/gen/test/c/mx/mx_direction_type.c @@ -204,6 +204,8 @@ MxDirectionType *mx_direction_type_parse(xmlNodePtr el) { xmlNodePtr mx_direction_type_serialize(const MxDirectionType *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_id) { xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); } diff --git a/gen/test/c/mx/mx_directive.c b/gen/test/c/mx/mx_directive.c index e0cec75c4..3d600bbf4 100644 --- a/gen/test/c/mx/mx_directive.c +++ b/gen/test/c/mx/mx_directive.c @@ -82,6 +82,8 @@ MxDirective *mx_directive_parse(xmlNodePtr el) { xmlNodePtr mx_directive_serialize(const MxDirective *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_xml_lang) { xmlSetProp(el, BAD_CAST "xml:lang", BAD_CAST m->xml_lang); diff --git a/gen/test/c/mx/mx_distance.c b/gen/test/c/mx/mx_distance.c index bb25815ba..570480a3a 100644 --- a/gen/test/c/mx/mx_distance.c +++ b/gen/test/c/mx/mx_distance.c @@ -55,6 +55,8 @@ MxDistance *mx_distance_parse(xmlNodePtr el) { xmlNodePtr mx_distance_serialize(const MxDistance *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ { char *s = mx_tenths_to_string(m->value); xmlAddChild(el, xmlNewText(BAD_CAST s)); diff --git a/gen/test/c/mx/mx_document.c b/gen/test/c/mx/mx_document.c index 7cb3005b9..055803589 100644 --- a/gen/test/c/mx/mx_document.c +++ b/gen/test/c/mx/mx_document.c @@ -51,17 +51,31 @@ MxDocument *mx_document_from_xdoc(xmlDocPtr doc) { int mx_document_to_xdoc(const MxDocument *d, xmlDocPtr *out) { *out = NULL; + xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); + if (!doc) + abort(); /* OOM policy: abort, matching the runtime's allocators */ + /* Serialize under a scratch parent attached to the document so + reserved-prefix attributes (xml:lang, xml:space) resolve through + the document's implicit xml namespace. Building detached would + make libxml2 fabricate an xmlns:xml declaration on the element + that carries them, which the input never had. */ + xmlNodePtr scratch = xmlNewDocNode(doc, NULL, BAD_CAST "scratch", NULL); + if (!scratch) + abort(); xmlNodePtr root = NULL; if (d->score_partwise) - root = mx_score_partwise_serialize(d->score_partwise, NULL, "score-partwise"); + root = mx_score_partwise_serialize(d->score_partwise, scratch, "score-partwise"); else if (d->score_timewise) - root = mx_score_timewise_serialize(d->score_timewise, NULL, "score-timewise"); + root = mx_score_timewise_serialize(d->score_timewise, scratch, "score-timewise"); if (!root) { mx_error_set("document has no root"); + xmlFreeNode(scratch); + xmlFreeDoc(doc); return -1; } - xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); + xmlUnlinkNode(root); xmlDocSetRootElement(doc, root); + xmlFreeNode(scratch); for (size_t i = 0; i < d->namespaces_count; i++) xmlNewNs(root, BAD_CAST d->namespaces[i].href, BAD_CAST d->namespaces[i].prefix); diff --git a/gen/test/c/mx/mx_dynamics.c b/gen/test/c/mx/mx_dynamics.c index 4e39eb067..fe5e43a6e 100644 --- a/gen/test/c/mx/mx_dynamics.c +++ b/gen/test/c/mx/mx_dynamics.c @@ -270,6 +270,8 @@ MxDynamics *mx_dynamics_parse(xmlNodePtr el) { xmlNodePtr mx_dynamics_serialize(const MxDynamics *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); diff --git a/gen/test/c/mx/mx_elision.c b/gen/test/c/mx/mx_elision.c index 189376604..61059241e 100644 --- a/gen/test/c/mx/mx_elision.c +++ b/gen/test/c/mx/mx_elision.c @@ -70,6 +70,8 @@ MxElision *mx_elision_parse(xmlNodePtr el) { xmlNodePtr mx_elision_serialize(const MxElision *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_smufl) { xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); diff --git a/gen/test/c/mx/mx_empty.c b/gen/test/c/mx/mx_empty.c index eb93c7bd2..ae7fbee0e 100644 --- a/gen/test/c/mx/mx_empty.c +++ b/gen/test/c/mx/mx_empty.c @@ -46,6 +46,8 @@ MxEmpty *mx_empty_parse(xmlNodePtr el) { xmlNodePtr mx_empty_serialize(const MxEmpty *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ (void)m; return el; } diff --git a/gen/test/c/mx/mx_empty_font.c b/gen/test/c/mx/mx_empty_font.c index 60a768a10..78617dce9 100644 --- a/gen/test/c/mx/mx_empty_font.c +++ b/gen/test/c/mx/mx_empty_font.c @@ -58,6 +58,8 @@ MxEmptyFont *mx_empty_font_parse(xmlNodePtr el) { xmlNodePtr mx_empty_font_serialize(const MxEmptyFont *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_font_family) { xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); } diff --git a/gen/test/c/mx/mx_empty_line.c b/gen/test/c/mx/mx_empty_line.c index d4cd22111..05ec361ee 100644 --- a/gen/test/c/mx/mx_empty_line.c +++ b/gen/test/c/mx/mx_empty_line.c @@ -91,6 +91,8 @@ MxEmptyLine *mx_empty_line_parse(xmlNodePtr el) { xmlNodePtr mx_empty_line_serialize(const MxEmptyLine *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_line_shape) { xmlSetProp(el, BAD_CAST "line-shape", BAD_CAST mx_line_shape_to_string(m->line_shape)); } diff --git a/gen/test/c/mx/mx_empty_placement.c b/gen/test/c/mx/mx_empty_placement.c index f1e48e881..ca5f6b12a 100644 --- a/gen/test/c/mx/mx_empty_placement.c +++ b/gen/test/c/mx/mx_empty_placement.c @@ -76,6 +76,8 @@ MxEmptyPlacement *mx_empty_placement_parse(xmlNodePtr el) { xmlNodePtr mx_empty_placement_serialize(const MxEmptyPlacement *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); diff --git a/gen/test/c/mx/mx_empty_placement_smufl.c b/gen/test/c/mx/mx_empty_placement_smufl.c index 591a06712..05ebc1779 100644 --- a/gen/test/c/mx/mx_empty_placement_smufl.c +++ b/gen/test/c/mx/mx_empty_placement_smufl.c @@ -79,6 +79,8 @@ MxEmptyPlacementSMUFL *mx_empty_placement_smufl_parse(xmlNodePtr el) { xmlNodePtr mx_empty_placement_smufl_serialize(const MxEmptyPlacementSMUFL *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); diff --git a/gen/test/c/mx/mx_empty_print_object_style_align.c b/gen/test/c/mx/mx_empty_print_object_style_align.c index cfb739b24..7b41f7834 100644 --- a/gen/test/c/mx/mx_empty_print_object_style_align.c +++ b/gen/test/c/mx/mx_empty_print_object_style_align.c @@ -82,6 +82,8 @@ MxEmptyPrintObjectStyleAlign *mx_empty_print_object_style_align_parse(xmlNodePtr xmlNodePtr mx_empty_print_object_style_align_serialize(const MxEmptyPrintObjectStyleAlign *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_print_object) { xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); } diff --git a/gen/test/c/mx/mx_empty_print_style_align_id.c b/gen/test/c/mx/mx_empty_print_style_align_id.c index 69af92405..904b1e47a 100644 --- a/gen/test/c/mx/mx_empty_print_style_align_id.c +++ b/gen/test/c/mx/mx_empty_print_style_align_id.c @@ -82,6 +82,8 @@ MxEmptyPrintStyleAlignID *mx_empty_print_style_align_id_parse(xmlNodePtr el) { xmlNodePtr mx_empty_print_style_align_id_serialize(const MxEmptyPrintStyleAlignID *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); diff --git a/gen/test/c/mx/mx_empty_trill_sound.c b/gen/test/c/mx/mx_empty_trill_sound.c index ecd3a0e3c..95dac78da 100644 --- a/gen/test/c/mx/mx_empty_trill_sound.c +++ b/gen/test/c/mx/mx_empty_trill_sound.c @@ -97,6 +97,8 @@ MxEmptyTrillSound *mx_empty_trill_sound_parse(xmlNodePtr el) { xmlNodePtr mx_empty_trill_sound_serialize(const MxEmptyTrillSound *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); diff --git a/gen/test/c/mx/mx_encoding.c b/gen/test/c/mx/mx_encoding.c index 0ab7e0c1b..b8e7bb0cc 100644 --- a/gen/test/c/mx/mx_encoding.c +++ b/gen/test/c/mx/mx_encoding.c @@ -46,10 +46,7 @@ MxEncoding *mx_encoding_parse(xmlNodePtr el) { if (strcmp(tag, "encoding-date") == 0) { xmlChar *text = xmlNodeGetContent(c); const char *s = text ? (const char *)text : ""; - ch->encoding_date = malloc(sizeof(*ch->encoding_date)); - if (!ch->encoding_date) - abort(); - *ch->encoding_date = mx_yyyy_mm_dd_parse(s); + ch->encoding_date = mx_yyyy_mm_dd_parse(s); xmlFree(text); } else if (strcmp(tag, "encoder") == 0) { ch->encoder = mx_typed_text_parse(c); @@ -87,10 +84,12 @@ MxEncoding *mx_encoding_parse(xmlNodePtr el) { xmlNodePtr mx_encoding_serialize(const MxEncoding *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxEncodingChild *ch = &m->children[i]; if (ch->encoding_date) { - xmlNewTextChild(el, NULL, BAD_CAST "encoding-date", BAD_CAST (*ch->encoding_date)); + xmlNewTextChild(el, NULL, BAD_CAST "encoding-date", BAD_CAST ch->encoding_date); } else if (ch->encoder) { mx_typed_text_serialize(ch->encoder, el, "encoder"); } else if (ch->software) { @@ -109,10 +108,7 @@ void mx_encoding_free(MxEncoding *m) { return; for (size_t i = 0; i < m->children_count; i++) { MxEncodingChild *ch = &m->children[i]; - if (ch->encoding_date) { - free((*ch->encoding_date)); - free(ch->encoding_date); - } + free(ch->encoding_date); if (ch->encoder) mx_typed_text_free(ch->encoder); free(ch->software); diff --git a/gen/test/c/mx/mx_encoding.h b/gen/test/c/mx/mx_encoding.h index 3af2c6dac..76d0bc8bd 100644 --- a/gen/test/c/mx/mx_encoding.h +++ b/gen/test/c/mx/mx_encoding.h @@ -22,7 +22,7 @@ set is undefined: serialization writes the first non-NULL field in schema order and nothing when all are NULL. */ typedef struct { - MxYyyyMmDd *encoding_date; + MxYyyyMmDd encoding_date; MxTypedText *encoder; char *software; char *encoding_description; diff --git a/gen/test/c/mx/mx_ending.c b/gen/test/c/mx/mx_ending.c index 4f2469431..e03eabb8c 100644 --- a/gen/test/c/mx/mx_ending.c +++ b/gen/test/c/mx/mx_ending.c @@ -97,6 +97,8 @@ MxEnding *mx_ending_parse(xmlNodePtr el) { xmlNodePtr mx_ending_serialize(const MxEnding *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_number) { xmlSetProp(el, BAD_CAST "number", BAD_CAST m->number); diff --git a/gen/test/c/mx/mx_extend.c b/gen/test/c/mx/mx_extend.c index 9a041faa6..04a4ede91 100644 --- a/gen/test/c/mx/mx_extend.c +++ b/gen/test/c/mx/mx_extend.c @@ -64,6 +64,8 @@ MxExtend *mx_extend_parse(xmlNodePtr el) { xmlNodePtr mx_extend_serialize(const MxExtend *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_continue_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_feature.c b/gen/test/c/mx/mx_feature.c index 9fd9791ad..9a069e407 100644 --- a/gen/test/c/mx/mx_feature.c +++ b/gen/test/c/mx/mx_feature.c @@ -55,6 +55,8 @@ MxFeature *mx_feature_parse(xmlNodePtr el) { xmlNodePtr mx_feature_serialize(const MxFeature *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST m->type); diff --git a/gen/test/c/mx/mx_fermata.c b/gen/test/c/mx/mx_fermata.c index d91e1d2b2..e72153801 100644 --- a/gen/test/c/mx/mx_fermata.c +++ b/gen/test/c/mx/mx_fermata.c @@ -85,6 +85,8 @@ MxFermata *mx_fermata_parse(xmlNodePtr el) { xmlNodePtr mx_fermata_serialize(const MxFermata *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_fermata_shape_to_string(m->value))); if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_upright_inverted_to_string(m->type)); diff --git a/gen/test/c/mx/mx_figure.c b/gen/test/c/mx/mx_figure.c index ff54452e4..77aee8a8b 100644 --- a/gen/test/c/mx/mx_figure.c +++ b/gen/test/c/mx/mx_figure.c @@ -93,6 +93,8 @@ MxFigure *mx_figure_parse(xmlNodePtr el) { xmlNodePtr mx_figure_serialize(const MxFigure *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxFigureChild *ch = &m->children[i]; if (ch->prefix) { diff --git a/gen/test/c/mx/mx_figured_bass.c b/gen/test/c/mx/mx_figured_bass.c index 56109c028..59229dcee 100644 --- a/gen/test/c/mx/mx_figured_bass.c +++ b/gen/test/c/mx/mx_figured_bass.c @@ -128,6 +128,8 @@ MxFiguredBass *mx_figured_bass_parse(xmlNodePtr el) { xmlNodePtr mx_figured_bass_serialize(const MxFiguredBass *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_parentheses) { xmlSetProp(el, BAD_CAST "parentheses", BAD_CAST mx_yes_no_to_string(m->parentheses)); } diff --git a/gen/test/c/mx/mx_fingering.c b/gen/test/c/mx/mx_fingering.c index be98146d2..c875f65a5 100644 --- a/gen/test/c/mx/mx_fingering.c +++ b/gen/test/c/mx/mx_fingering.c @@ -88,6 +88,8 @@ MxFingering *mx_fingering_parse(xmlNodePtr el) { xmlNodePtr mx_fingering_serialize(const MxFingering *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_substitution) { xmlSetProp(el, BAD_CAST "substitution", BAD_CAST mx_yes_no_to_string(m->substitution)); diff --git a/gen/test/c/mx/mx_first_fret.c b/gen/test/c/mx/mx_first_fret.c index 9c314ca46..dce55f8be 100644 --- a/gen/test/c/mx/mx_first_fret.c +++ b/gen/test/c/mx/mx_first_fret.c @@ -58,6 +58,8 @@ MxFirstFret *mx_first_fret_parse(xmlNodePtr el) { xmlNodePtr mx_first_fret_serialize(const MxFirstFret *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ { char *s = mx_format_int(m->value); xmlAddChild(el, xmlNewText(BAD_CAST s)); diff --git a/gen/test/c/mx/mx_formatted_symbol_id.c b/gen/test/c/mx/mx_formatted_symbol_id.c index e0f545378..3d4fbe067 100644 --- a/gen/test/c/mx/mx_formatted_symbol_id.c +++ b/gen/test/c/mx/mx_formatted_symbol_id.c @@ -115,6 +115,8 @@ MxFormattedSymbolID *mx_formatted_symbol_id_parse(xmlNodePtr el) { xmlNodePtr mx_formatted_symbol_id_serialize(const MxFormattedSymbolID *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_justify) { xmlSetProp(el, BAD_CAST "justify", BAD_CAST mx_left_center_right_to_string(m->justify)); diff --git a/gen/test/c/mx/mx_formatted_text.c b/gen/test/c/mx/mx_formatted_text.c index 3cf259fad..deff066b2 100644 --- a/gen/test/c/mx/mx_formatted_text.c +++ b/gen/test/c/mx/mx_formatted_text.c @@ -118,6 +118,8 @@ MxFormattedText *mx_formatted_text_parse(xmlNodePtr el) { xmlNodePtr mx_formatted_text_serialize(const MxFormattedText *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_xml_lang) { xmlSetProp(el, BAD_CAST "xml:lang", BAD_CAST m->xml_lang); diff --git a/gen/test/c/mx/mx_formatted_text_id.c b/gen/test/c/mx/mx_formatted_text_id.c index 81c7fa155..973a3701a 100644 --- a/gen/test/c/mx/mx_formatted_text_id.c +++ b/gen/test/c/mx/mx_formatted_text_id.c @@ -121,6 +121,8 @@ MxFormattedTextID *mx_formatted_text_id_parse(xmlNodePtr el) { xmlNodePtr mx_formatted_text_id_serialize(const MxFormattedTextID *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_xml_lang) { xmlSetProp(el, BAD_CAST "xml:lang", BAD_CAST m->xml_lang); diff --git a/gen/test/c/mx/mx_forward.c b/gen/test/c/mx/mx_forward.c index b8384d756..b17414f18 100644 --- a/gen/test/c/mx/mx_forward.c +++ b/gen/test/c/mx/mx_forward.c @@ -90,6 +90,8 @@ MxForward *mx_forward_parse(xmlNodePtr el) { xmlNodePtr mx_forward_serialize(const MxForward *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxForwardChild *ch = &m->children[i]; if (ch->duration) { diff --git a/gen/test/c/mx/mx_frame.c b/gen/test/c/mx/mx_frame.c index 4dccab886..4826edac7 100644 --- a/gen/test/c/mx/mx_frame.c +++ b/gen/test/c/mx/mx_frame.c @@ -118,6 +118,8 @@ MxFrame *mx_frame_parse(xmlNodePtr el) { xmlNodePtr mx_frame_serialize(const MxFrame *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_height) { char *s = mx_tenths_to_string(m->height); xmlSetProp(el, BAD_CAST "height", BAD_CAST s); diff --git a/gen/test/c/mx/mx_frame_note.c b/gen/test/c/mx/mx_frame_note.c index 5ae31d874..7fb80bb5d 100644 --- a/gen/test/c/mx/mx_frame_note.c +++ b/gen/test/c/mx/mx_frame_note.c @@ -81,6 +81,8 @@ MxFrameNote *mx_frame_note_parse(xmlNodePtr el) { xmlNodePtr mx_frame_note_serialize(const MxFrameNote *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxFrameNoteChild *ch = &m->children[i]; if (ch->string) { diff --git a/gen/test/c/mx/mx_fret.c b/gen/test/c/mx/mx_fret.c index 1bdde5201..f86d49c0e 100644 --- a/gen/test/c/mx/mx_fret.c +++ b/gen/test/c/mx/mx_fret.c @@ -67,6 +67,8 @@ MxFret *mx_fret_parse(xmlNodePtr el) { xmlNodePtr mx_fret_serialize(const MxFret *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ { char *s = mx_format_int(m->value); xmlAddChild(el, xmlNewText(BAD_CAST s)); diff --git a/gen/test/c/mx/mx_glass.c b/gen/test/c/mx/mx_glass.c index d7f8d9c72..b9cd0affa 100644 --- a/gen/test/c/mx/mx_glass.c +++ b/gen/test/c/mx/mx_glass.c @@ -55,6 +55,8 @@ MxGlass *mx_glass_parse(xmlNodePtr el) { xmlNodePtr mx_glass_serialize(const MxGlass *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_glass_value_to_string(m->value))); if (m->has_smufl) { xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); diff --git a/gen/test/c/mx/mx_glissando.c b/gen/test/c/mx/mx_glissando.c index 13a4cc200..d4247fcc9 100644 --- a/gen/test/c/mx/mx_glissando.c +++ b/gen/test/c/mx/mx_glissando.c @@ -97,6 +97,8 @@ MxGlissando *mx_glissando_parse(xmlNodePtr el) { xmlNodePtr mx_glissando_serialize(const MxGlissando *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); diff --git a/gen/test/c/mx/mx_glyph.c b/gen/test/c/mx/mx_glyph.c index 3aa7f7892..324515328 100644 --- a/gen/test/c/mx/mx_glyph.c +++ b/gen/test/c/mx/mx_glyph.c @@ -55,6 +55,8 @@ MxGlyph *mx_glyph_parse(xmlNodePtr el) { xmlNodePtr mx_glyph_serialize(const MxGlyph *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST m->type); diff --git a/gen/test/c/mx/mx_grace.c b/gen/test/c/mx/mx_grace.c index d4a287a18..31cf089a3 100644 --- a/gen/test/c/mx/mx_grace.c +++ b/gen/test/c/mx/mx_grace.c @@ -58,6 +58,8 @@ MxGrace *mx_grace_parse(xmlNodePtr el) { xmlNodePtr mx_grace_serialize(const MxGrace *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_steal_time_previous) { char *s = mx_percent_to_string(m->steal_time_previous); xmlSetProp(el, BAD_CAST "steal-time-previous", BAD_CAST s); diff --git a/gen/test/c/mx/mx_group_barline.c b/gen/test/c/mx/mx_group_barline.c index f2e37954f..7b81c9d3a 100644 --- a/gen/test/c/mx/mx_group_barline.c +++ b/gen/test/c/mx/mx_group_barline.c @@ -55,6 +55,8 @@ MxGroupBarline *mx_group_barline_parse(xmlNodePtr el) { xmlNodePtr mx_group_barline_serialize(const MxGroupBarline *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_group_barline_value_to_string(m->value))); if (m->has_color) { xmlSetProp(el, BAD_CAST "color", BAD_CAST m->color); diff --git a/gen/test/c/mx/mx_group_name.c b/gen/test/c/mx/mx_group_name.c index 9cd1634ac..6e5b469a3 100644 --- a/gen/test/c/mx/mx_group_name.c +++ b/gen/test/c/mx/mx_group_name.c @@ -82,6 +82,8 @@ MxGroupName *mx_group_name_parse(xmlNodePtr el) { xmlNodePtr mx_group_name_serialize(const MxGroupName *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); diff --git a/gen/test/c/mx/mx_group_symbol.c b/gen/test/c/mx/mx_group_symbol.c index b5c5a0cd0..f5d814c2d 100644 --- a/gen/test/c/mx/mx_group_symbol.c +++ b/gen/test/c/mx/mx_group_symbol.c @@ -67,6 +67,8 @@ MxGroupSymbol *mx_group_symbol_parse(xmlNodePtr el) { xmlNodePtr mx_group_symbol_serialize(const MxGroupSymbol *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_group_symbol_value_to_string(m->value))); if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); diff --git a/gen/test/c/mx/mx_grouping.c b/gen/test/c/mx/mx_grouping.c index f0e6ccdf2..8bbc2fe5c 100644 --- a/gen/test/c/mx/mx_grouping.c +++ b/gen/test/c/mx/mx_grouping.c @@ -75,6 +75,8 @@ MxGrouping *mx_grouping_parse(xmlNodePtr el) { xmlNodePtr mx_grouping_serialize(const MxGrouping *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_single_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_hammer_on_pull_off.c b/gen/test/c/mx/mx_hammer_on_pull_off.c index 2a0972f0a..4385177e6 100644 --- a/gen/test/c/mx/mx_hammer_on_pull_off.c +++ b/gen/test/c/mx/mx_hammer_on_pull_off.c @@ -88,6 +88,8 @@ MxHammerOnPullOff *mx_hammer_on_pull_off_parse(xmlNodePtr el) { xmlNodePtr mx_hammer_on_pull_off_serialize(const MxHammerOnPullOff *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); diff --git a/gen/test/c/mx/mx_handbell.c b/gen/test/c/mx/mx_handbell.c index 39540b048..b8cd07def 100644 --- a/gen/test/c/mx/mx_handbell.c +++ b/gen/test/c/mx/mx_handbell.c @@ -82,6 +82,8 @@ MxHandbell *mx_handbell_parse(xmlNodePtr el) { xmlNodePtr mx_handbell_serialize(const MxHandbell *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_handbell_value_to_string(m->value))); if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); diff --git a/gen/test/c/mx/mx_harmon_closed.c b/gen/test/c/mx/mx_harmon_closed.c index aea077b2d..515fa0905 100644 --- a/gen/test/c/mx/mx_harmon_closed.c +++ b/gen/test/c/mx/mx_harmon_closed.c @@ -55,6 +55,8 @@ MxHarmonClosed *mx_harmon_closed_parse(xmlNodePtr el) { xmlNodePtr mx_harmon_closed_serialize(const MxHarmonClosed *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_harmon_closed_value_to_string(m->value))); if (m->has_location) { xmlSetProp(el, BAD_CAST "location", BAD_CAST mx_harmon_closed_location_to_string(m->location)); diff --git a/gen/test/c/mx/mx_harmon_mute.c b/gen/test/c/mx/mx_harmon_mute.c index dbc551d75..23e67654f 100644 --- a/gen/test/c/mx/mx_harmon_mute.c +++ b/gen/test/c/mx/mx_harmon_mute.c @@ -93,6 +93,8 @@ MxHarmonMute *mx_harmon_mute_parse(xmlNodePtr el) { xmlNodePtr mx_harmon_mute_serialize(const MxHarmonMute *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); diff --git a/gen/test/c/mx/mx_harmonic.c b/gen/test/c/mx/mx_harmonic.c index 57d4380d4..d486dcc4a 100644 --- a/gen/test/c/mx/mx_harmonic.c +++ b/gen/test/c/mx/mx_harmonic.c @@ -120,6 +120,8 @@ MxHarmonic *mx_harmonic_parse(xmlNodePtr el) { xmlNodePtr mx_harmonic_serialize(const MxHarmonic *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_print_object) { xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); } diff --git a/gen/test/c/mx/mx_harmony.c b/gen/test/c/mx/mx_harmony.c index 5b1ac9385..6b62ae801 100644 --- a/gen/test/c/mx/mx_harmony.c +++ b/gen/test/c/mx/mx_harmony.c @@ -167,6 +167,8 @@ MxHarmony *mx_harmony_parse(xmlNodePtr el) { xmlNodePtr mx_harmony_serialize(const MxHarmony *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_harmony_type_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_harp_pedals.c b/gen/test/c/mx/mx_harp_pedals.c index 35badd8a7..1ce6ae7c1 100644 --- a/gen/test/c/mx/mx_harp_pedals.c +++ b/gen/test/c/mx/mx_harp_pedals.c @@ -99,6 +99,8 @@ MxHarpPedals *mx_harp_pedals_parse(xmlNodePtr el) { xmlNodePtr mx_harp_pedals_serialize(const MxHarpPedals *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); diff --git a/gen/test/c/mx/mx_heel_toe.c b/gen/test/c/mx/mx_heel_toe.c index cb1f835fe..5ff1ac899 100644 --- a/gen/test/c/mx/mx_heel_toe.c +++ b/gen/test/c/mx/mx_heel_toe.c @@ -79,6 +79,8 @@ MxHeelToe *mx_heel_toe_parse(xmlNodePtr el) { xmlNodePtr mx_heel_toe_serialize(const MxHeelToe *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); diff --git a/gen/test/c/mx/mx_hole.c b/gen/test/c/mx/mx_hole.c index 5234e6e5e..fbea87cf4 100644 --- a/gen/test/c/mx/mx_hole.c +++ b/gen/test/c/mx/mx_hole.c @@ -103,6 +103,8 @@ MxHole *mx_hole_parse(xmlNodePtr el) { xmlNodePtr mx_hole_serialize(const MxHole *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); diff --git a/gen/test/c/mx/mx_hole_closed.c b/gen/test/c/mx/mx_hole_closed.c index d468711c7..eeae36996 100644 --- a/gen/test/c/mx/mx_hole_closed.c +++ b/gen/test/c/mx/mx_hole_closed.c @@ -55,6 +55,8 @@ MxHoleClosed *mx_hole_closed_parse(xmlNodePtr el) { xmlNodePtr mx_hole_closed_serialize(const MxHoleClosed *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_hole_closed_value_to_string(m->value))); if (m->has_location) { xmlSetProp(el, BAD_CAST "location", BAD_CAST mx_hole_closed_location_to_string(m->location)); diff --git a/gen/test/c/mx/mx_horizontal_turn.c b/gen/test/c/mx/mx_horizontal_turn.c index 937c7c857..856ac5f0d 100644 --- a/gen/test/c/mx/mx_horizontal_turn.c +++ b/gen/test/c/mx/mx_horizontal_turn.c @@ -100,6 +100,8 @@ MxHorizontalTurn *mx_horizontal_turn_parse(xmlNodePtr el) { xmlNodePtr mx_horizontal_turn_serialize(const MxHorizontalTurn *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_slash) { xmlSetProp(el, BAD_CAST "slash", BAD_CAST mx_yes_no_to_string(m->slash)); } diff --git a/gen/test/c/mx/mx_identification.c b/gen/test/c/mx/mx_identification.c index 1e1d6a205..d062f3ea7 100644 --- a/gen/test/c/mx/mx_identification.c +++ b/gen/test/c/mx/mx_identification.c @@ -92,6 +92,8 @@ MxIdentification *mx_identification_parse(xmlNodePtr el) { xmlNodePtr mx_identification_serialize(const MxIdentification *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxIdentificationChild *ch = &m->children[i]; if (ch->creator) { diff --git a/gen/test/c/mx/mx_image.c b/gen/test/c/mx/mx_image.c index 01670db54..c1b4cd577 100644 --- a/gen/test/c/mx/mx_image.c +++ b/gen/test/c/mx/mx_image.c @@ -79,6 +79,8 @@ MxImage *mx_image_parse(xmlNodePtr el) { xmlNodePtr mx_image_serialize(const MxImage *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_source) { xmlSetProp(el, BAD_CAST "source", BAD_CAST m->source); } diff --git a/gen/test/c/mx/mx_instrument.c b/gen/test/c/mx/mx_instrument.c index 330af2e73..77832c9dd 100644 --- a/gen/test/c/mx/mx_instrument.c +++ b/gen/test/c/mx/mx_instrument.c @@ -49,6 +49,8 @@ MxInstrument *mx_instrument_parse(xmlNodePtr el) { xmlNodePtr mx_instrument_serialize(const MxInstrument *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_id) { xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); } diff --git a/gen/test/c/mx/mx_interchangeable.c b/gen/test/c/mx/mx_interchangeable.c index 8705003d5..a2572d623 100644 --- a/gen/test/c/mx/mx_interchangeable.c +++ b/gen/test/c/mx/mx_interchangeable.c @@ -81,6 +81,8 @@ MxInterchangeable *mx_interchangeable_parse(xmlNodePtr el) { xmlNodePtr mx_interchangeable_serialize(const MxInterchangeable *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_symbol) { xmlSetProp(el, BAD_CAST "symbol", BAD_CAST mx_time_symbol_to_string(m->symbol)); } diff --git a/gen/test/c/mx/mx_inversion.c b/gen/test/c/mx/mx_inversion.c index 5992c6f3d..904e763c6 100644 --- a/gen/test/c/mx/mx_inversion.c +++ b/gen/test/c/mx/mx_inversion.c @@ -79,6 +79,8 @@ MxInversion *mx_inversion_parse(xmlNodePtr el) { xmlNodePtr mx_inversion_serialize(const MxInversion *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ { char *s = mx_format_int(m->value); xmlAddChild(el, xmlNewText(BAD_CAST s)); diff --git a/gen/test/c/mx/mx_key.c b/gen/test/c/mx/mx_key.c index f660a27c9..5f8eb64b9 100644 --- a/gen/test/c/mx/mx_key.c +++ b/gen/test/c/mx/mx_key.c @@ -96,10 +96,7 @@ MxKey *mx_key_parse(xmlNodePtr el) { } else if (strcmp(tag, "mode") == 0) { xmlChar *text = xmlNodeGetContent(c); const char *s = text ? (const char *)text : ""; - ch->mode = malloc(sizeof(*ch->mode)); - if (!ch->mode) - abort(); - *ch->mode = mx_mode_parse(s); + ch->mode = mx_mode_parse(s); xmlFree(text); } else if (strcmp(tag, "key-step") == 0) { xmlChar *text = xmlNodeGetContent(c); @@ -143,6 +140,8 @@ MxKey *mx_key_parse(xmlNodePtr el) { xmlNodePtr mx_key_serialize(const MxKey *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_number) { char *s = mx_staff_number_to_string(m->number); xmlSetProp(el, BAD_CAST "number", BAD_CAST s); @@ -200,7 +199,7 @@ xmlNodePtr mx_key_serialize(const MxKey *m, xmlNodePtr parent, const char *tag) xmlNewTextChild(el, NULL, BAD_CAST "fifths", BAD_CAST s); free(s); } else if (ch->mode) { - xmlNewTextChild(el, NULL, BAD_CAST "mode", BAD_CAST (*ch->mode)); + xmlNewTextChild(el, NULL, BAD_CAST "mode", BAD_CAST ch->mode); } else if (ch->key_step) { xmlNewTextChild(el, NULL, BAD_CAST "key-step", BAD_CAST mx_step_to_string((*ch->key_step))); } else if (ch->key_alter) { @@ -232,10 +231,7 @@ void mx_key_free(MxKey *m) { if (ch->cancel) mx_cancel_free(ch->cancel); free(ch->fifths); - if (ch->mode) { - free((*ch->mode)); - free(ch->mode); - } + free(ch->mode); free(ch->key_step); free(ch->key_alter); if (ch->key_accidental) diff --git a/gen/test/c/mx/mx_key.h b/gen/test/c/mx/mx_key.h index d6d4ab6d9..fad010551 100644 --- a/gen/test/c/mx/mx_key.h +++ b/gen/test/c/mx/mx_key.h @@ -36,7 +36,7 @@ typedef struct { MxCancel *cancel; MxFifths *fifths; - MxMode *mode; + MxMode mode; MxStep *key_step; MxSemitones *key_alter; MxKeyAccidental *key_accidental; diff --git a/gen/test/c/mx/mx_key_accidental.c b/gen/test/c/mx/mx_key_accidental.c index 1741037a6..7dbdf034a 100644 --- a/gen/test/c/mx/mx_key_accidental.c +++ b/gen/test/c/mx/mx_key_accidental.c @@ -55,6 +55,8 @@ MxKeyAccidental *mx_key_accidental_parse(xmlNodePtr el) { xmlNodePtr mx_key_accidental_serialize(const MxKeyAccidental *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_accidental_value_to_string(m->value))); if (m->has_smufl) { xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); diff --git a/gen/test/c/mx/mx_key_octave.c b/gen/test/c/mx/mx_key_octave.c index 43582aede..4187bcd09 100644 --- a/gen/test/c/mx/mx_key_octave.c +++ b/gen/test/c/mx/mx_key_octave.c @@ -58,6 +58,8 @@ MxKeyOctave *mx_key_octave_parse(xmlNodePtr el) { xmlNodePtr mx_key_octave_serialize(const MxKeyOctave *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ { char *s = mx_octave_to_string(m->value); xmlAddChild(el, xmlNewText(BAD_CAST s)); diff --git a/gen/test/c/mx/mx_kind.c b/gen/test/c/mx/mx_kind.c index 3a9a3d209..9e951a317 100644 --- a/gen/test/c/mx/mx_kind.c +++ b/gen/test/c/mx/mx_kind.c @@ -100,6 +100,8 @@ MxKind *mx_kind_parse(xmlNodePtr el) { xmlNodePtr mx_kind_serialize(const MxKind *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_kind_value_to_string(m->value))); if (m->has_use_symbols) { xmlSetProp(el, BAD_CAST "use-symbols", BAD_CAST mx_yes_no_to_string(m->use_symbols)); diff --git a/gen/test/c/mx/mx_level.c b/gen/test/c/mx/mx_level.c index 85a09d6fc..88cae89a3 100644 --- a/gen/test/c/mx/mx_level.c +++ b/gen/test/c/mx/mx_level.c @@ -64,6 +64,8 @@ MxLevel *mx_level_parse(xmlNodePtr el) { xmlNodePtr mx_level_serialize(const MxLevel *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_reference) { xmlSetProp(el, BAD_CAST "reference", BAD_CAST mx_yes_no_to_string(m->reference)); diff --git a/gen/test/c/mx/mx_line_width.c b/gen/test/c/mx/mx_line_width.c index b22f9277f..69b929db4 100644 --- a/gen/test/c/mx/mx_line_width.c +++ b/gen/test/c/mx/mx_line_width.c @@ -55,6 +55,8 @@ MxLineWidth *mx_line_width_parse(xmlNodePtr el) { xmlNodePtr mx_line_width_serialize(const MxLineWidth *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ { char *s = mx_tenths_to_string(m->value); xmlAddChild(el, xmlNewText(BAD_CAST s)); diff --git a/gen/test/c/mx/mx_link.c b/gen/test/c/mx/mx_link.c index 25f66734d..17e217edb 100644 --- a/gen/test/c/mx/mx_link.c +++ b/gen/test/c/mx/mx_link.c @@ -85,6 +85,8 @@ MxLink *mx_link_parse(xmlNodePtr el) { xmlNodePtr mx_link_serialize(const MxLink *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_name) { xmlSetProp(el, BAD_CAST "name", BAD_CAST m->name); } diff --git a/gen/test/c/mx/mx_lyric.c b/gen/test/c/mx/mx_lyric.c index 44c0d2b5c..7905acd8c 100644 --- a/gen/test/c/mx/mx_lyric.c +++ b/gen/test/c/mx/mx_lyric.c @@ -155,6 +155,8 @@ MxLyric *mx_lyric_parse(xmlNodePtr el) { xmlNodePtr mx_lyric_serialize(const MxLyric *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_number) { xmlSetProp(el, BAD_CAST "number", BAD_CAST m->number); } diff --git a/gen/test/c/mx/mx_lyric_font.c b/gen/test/c/mx/mx_lyric_font.c index 453c1d514..5e0e3e9a0 100644 --- a/gen/test/c/mx/mx_lyric_font.c +++ b/gen/test/c/mx/mx_lyric_font.c @@ -64,6 +64,8 @@ MxLyricFont *mx_lyric_font_parse(xmlNodePtr el) { xmlNodePtr mx_lyric_font_serialize(const MxLyricFont *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_number) { xmlSetProp(el, BAD_CAST "number", BAD_CAST m->number); } diff --git a/gen/test/c/mx/mx_lyric_language.c b/gen/test/c/mx/mx_lyric_language.c index 87564daa9..d152f1650 100644 --- a/gen/test/c/mx/mx_lyric_language.c +++ b/gen/test/c/mx/mx_lyric_language.c @@ -55,6 +55,8 @@ MxLyricLanguage *mx_lyric_language_parse(xmlNodePtr el) { xmlNodePtr mx_lyric_language_serialize(const MxLyricLanguage *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_number) { xmlSetProp(el, BAD_CAST "number", BAD_CAST m->number); } diff --git a/gen/test/c/mx/mx_measure_layout.c b/gen/test/c/mx/mx_measure_layout.c index 0d0b1594b..e073acab6 100644 --- a/gen/test/c/mx/mx_measure_layout.c +++ b/gen/test/c/mx/mx_measure_layout.c @@ -65,6 +65,8 @@ MxMeasureLayout *mx_measure_layout_parse(xmlNodePtr el) { xmlNodePtr mx_measure_layout_serialize(const MxMeasureLayout *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxMeasureLayoutChild *ch = &m->children[i]; if (ch->measure_distance) { diff --git a/gen/test/c/mx/mx_measure_numbering.c b/gen/test/c/mx/mx_measure_numbering.c index a99b326c6..cde7b1cb0 100644 --- a/gen/test/c/mx/mx_measure_numbering.c +++ b/gen/test/c/mx/mx_measure_numbering.c @@ -85,6 +85,8 @@ MxMeasureNumbering *mx_measure_numbering_parse(xmlNodePtr el) { xmlNodePtr mx_measure_numbering_serialize(const MxMeasureNumbering *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_measure_numbering_value_to_string(m->value))); if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); diff --git a/gen/test/c/mx/mx_measure_repeat.c b/gen/test/c/mx/mx_measure_repeat.c index 3c1b4a88e..2c4b50b74 100644 --- a/gen/test/c/mx/mx_measure_repeat.c +++ b/gen/test/c/mx/mx_measure_repeat.c @@ -58,6 +58,8 @@ MxMeasureRepeat *mx_measure_repeat_parse(xmlNodePtr el) { xmlNodePtr mx_measure_repeat_serialize(const MxMeasureRepeat *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ { char *s = mx_positive_integer_or_empty_to_string(m->value); xmlAddChild(el, xmlNewText(BAD_CAST s)); diff --git a/gen/test/c/mx/mx_measure_style.c b/gen/test/c/mx/mx_measure_style.c index 5d38447f7..693972e18 100644 --- a/gen/test/c/mx/mx_measure_style.c +++ b/gen/test/c/mx/mx_measure_style.c @@ -102,6 +102,8 @@ MxMeasureStyle *mx_measure_style_parse(xmlNodePtr el) { xmlNodePtr mx_measure_style_serialize(const MxMeasureStyle *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_number) { char *s = mx_staff_number_to_string(m->number); xmlSetProp(el, BAD_CAST "number", BAD_CAST s); diff --git a/gen/test/c/mx/mx_metronome.c b/gen/test/c/mx/mx_metronome.c index 95233c7aa..bc47f937d 100644 --- a/gen/test/c/mx/mx_metronome.c +++ b/gen/test/c/mx/mx_metronome.c @@ -142,6 +142,8 @@ MxMetronome *mx_metronome_parse(xmlNodePtr el) { xmlNodePtr mx_metronome_serialize(const MxMetronome *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_parentheses) { xmlSetProp(el, BAD_CAST "parentheses", BAD_CAST mx_yes_no_to_string(m->parentheses)); } diff --git a/gen/test/c/mx/mx_metronome_beam.c b/gen/test/c/mx/mx_metronome_beam.c index 769db6253..bbac65bea 100644 --- a/gen/test/c/mx/mx_metronome_beam.c +++ b/gen/test/c/mx/mx_metronome_beam.c @@ -55,6 +55,8 @@ MxMetronomeBeam *mx_metronome_beam_parse(xmlNodePtr el) { xmlNodePtr mx_metronome_beam_serialize(const MxMetronomeBeam *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_beam_value_to_string(m->value))); if (m->has_number) { char *s = mx_beam_level_to_string(m->number); diff --git a/gen/test/c/mx/mx_metronome_note.c b/gen/test/c/mx/mx_metronome_note.c index 4990ab755..624d04099 100644 --- a/gen/test/c/mx/mx_metronome_note.c +++ b/gen/test/c/mx/mx_metronome_note.c @@ -89,6 +89,8 @@ MxMetronomeNote *mx_metronome_note_parse(xmlNodePtr el) { xmlNodePtr mx_metronome_note_serialize(const MxMetronomeNote *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxMetronomeNoteChild *ch = &m->children[i]; if (ch->metronome_type) { diff --git a/gen/test/c/mx/mx_metronome_tied.c b/gen/test/c/mx/mx_metronome_tied.c index 42c587241..997041c01 100644 --- a/gen/test/c/mx/mx_metronome_tied.c +++ b/gen/test/c/mx/mx_metronome_tied.c @@ -49,6 +49,8 @@ MxMetronomeTied *mx_metronome_tied_parse(xmlNodePtr el) { xmlNodePtr mx_metronome_tied_serialize(const MxMetronomeTied *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_metronome_tuplet.c b/gen/test/c/mx/mx_metronome_tuplet.c index 86499a228..d0af80988 100644 --- a/gen/test/c/mx/mx_metronome_tuplet.c +++ b/gen/test/c/mx/mx_metronome_tuplet.c @@ -96,6 +96,8 @@ MxMetronomeTuplet *mx_metronome_tuplet_parse(xmlNodePtr el) { xmlNodePtr mx_metronome_tuplet_serialize(const MxMetronomeTuplet *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_midi_device.c b/gen/test/c/mx/mx_midi_device.c index e9058ed9f..21ce5afd6 100644 --- a/gen/test/c/mx/mx_midi_device.c +++ b/gen/test/c/mx/mx_midi_device.c @@ -58,6 +58,8 @@ MxMIDIDevice *mx_midi_device_parse(xmlNodePtr el) { xmlNodePtr mx_midi_device_serialize(const MxMIDIDevice *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_port) { char *s = mx_midi_16_to_string(m->port); diff --git a/gen/test/c/mx/mx_midi_instrument.c b/gen/test/c/mx/mx_midi_instrument.c index 69b82e48c..0b7f60449 100644 --- a/gen/test/c/mx/mx_midi_instrument.c +++ b/gen/test/c/mx/mx_midi_instrument.c @@ -121,6 +121,8 @@ MxMIDIInstrument *mx_midi_instrument_parse(xmlNodePtr el) { xmlNodePtr mx_midi_instrument_serialize(const MxMIDIInstrument *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_id) { xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); } diff --git a/gen/test/c/mx/mx_miscellaneous.c b/gen/test/c/mx/mx_miscellaneous.c index ef37066de..1abaa6ed3 100644 --- a/gen/test/c/mx/mx_miscellaneous.c +++ b/gen/test/c/mx/mx_miscellaneous.c @@ -63,6 +63,8 @@ MxMiscellaneous *mx_miscellaneous_parse(xmlNodePtr el) { xmlNodePtr mx_miscellaneous_serialize(const MxMiscellaneous *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxMiscellaneousChild *ch = &m->children[i]; if (ch->miscellaneous_field) { diff --git a/gen/test/c/mx/mx_miscellaneous_field.c b/gen/test/c/mx/mx_miscellaneous_field.c index 05ef3e140..db9757552 100644 --- a/gen/test/c/mx/mx_miscellaneous_field.c +++ b/gen/test/c/mx/mx_miscellaneous_field.c @@ -55,6 +55,8 @@ MxMiscellaneousField *mx_miscellaneous_field_parse(xmlNodePtr el) { xmlNodePtr mx_miscellaneous_field_serialize(const MxMiscellaneousField *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_name) { xmlSetProp(el, BAD_CAST "name", BAD_CAST m->name); diff --git a/gen/test/c/mx/mx_mordent.c b/gen/test/c/mx/mx_mordent.c index eef7f16b7..97607d01e 100644 --- a/gen/test/c/mx/mx_mordent.c +++ b/gen/test/c/mx/mx_mordent.c @@ -106,6 +106,8 @@ MxMordent *mx_mordent_parse(xmlNodePtr el) { xmlNodePtr mx_mordent_serialize(const MxMordent *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); diff --git a/gen/test/c/mx/mx_multiple_rest.c b/gen/test/c/mx/mx_multiple_rest.c index 728cff5da..22b1ef978 100644 --- a/gen/test/c/mx/mx_multiple_rest.c +++ b/gen/test/c/mx/mx_multiple_rest.c @@ -55,6 +55,8 @@ MxMultipleRest *mx_multiple_rest_parse(xmlNodePtr el) { xmlNodePtr mx_multiple_rest_serialize(const MxMultipleRest *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ { char *s = mx_positive_integer_or_empty_to_string(m->value); xmlAddChild(el, xmlNewText(BAD_CAST s)); diff --git a/gen/test/c/mx/mx_name_display.c b/gen/test/c/mx/mx_name_display.c index e7d75ef11..39052fe8a 100644 --- a/gen/test/c/mx/mx_name_display.c +++ b/gen/test/c/mx/mx_name_display.c @@ -72,6 +72,8 @@ MxNameDisplay *mx_name_display_parse(xmlNodePtr el) { xmlNodePtr mx_name_display_serialize(const MxNameDisplay *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_print_object) { xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); } diff --git a/gen/test/c/mx/mx_non_arpeggiate.c b/gen/test/c/mx/mx_non_arpeggiate.c index 6a66bba99..1a486308c 100644 --- a/gen/test/c/mx/mx_non_arpeggiate.c +++ b/gen/test/c/mx/mx_non_arpeggiate.c @@ -73,6 +73,8 @@ MxNonArpeggiate *mx_non_arpeggiate_parse(xmlNodePtr el) { xmlNodePtr mx_non_arpeggiate_serialize(const MxNonArpeggiate *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_top_bottom_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_notations.c b/gen/test/c/mx/mx_notations.c index 0faadf2fe..e05ddaf55 100644 --- a/gen/test/c/mx/mx_notations.c +++ b/gen/test/c/mx/mx_notations.c @@ -159,6 +159,8 @@ MxNotations *mx_notations_parse(xmlNodePtr el) { xmlNodePtr mx_notations_serialize(const MxNotations *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_print_object) { xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); } diff --git a/gen/test/c/mx/mx_note.c b/gen/test/c/mx/mx_note.c index 0c9372a1b..446172574 100644 --- a/gen/test/c/mx/mx_note.c +++ b/gen/test/c/mx/mx_note.c @@ -267,6 +267,8 @@ MxNote *mx_note_parse(xmlNodePtr el) { xmlNodePtr mx_note_serialize(const MxNote *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_print_leger) { xmlSetProp(el, BAD_CAST "print-leger", BAD_CAST mx_yes_no_to_string(m->print_leger)); } diff --git a/gen/test/c/mx/mx_note_size.c b/gen/test/c/mx/mx_note_size.c index 4663cbb54..f7e4e66e8 100644 --- a/gen/test/c/mx/mx_note_size.c +++ b/gen/test/c/mx/mx_note_size.c @@ -55,6 +55,8 @@ MxNoteSize *mx_note_size_parse(xmlNodePtr el) { xmlNodePtr mx_note_size_serialize(const MxNoteSize *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ { char *s = mx_non_negative_decimal_to_string(m->value); xmlAddChild(el, xmlNewText(BAD_CAST s)); diff --git a/gen/test/c/mx/mx_note_type.c b/gen/test/c/mx/mx_note_type.c index e6385431a..e7a488e54 100644 --- a/gen/test/c/mx/mx_note_type.c +++ b/gen/test/c/mx/mx_note_type.c @@ -55,6 +55,8 @@ MxNoteType *mx_note_type_parse(xmlNodePtr el) { xmlNodePtr mx_note_type_serialize(const MxNoteType *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_note_type_value_to_string(m->value))); if (m->has_size) { xmlSetProp(el, BAD_CAST "size", BAD_CAST mx_symbol_size_to_string(m->size)); diff --git a/gen/test/c/mx/mx_notehead.c b/gen/test/c/mx/mx_notehead.c index 6784f018d..07337e2f1 100644 --- a/gen/test/c/mx/mx_notehead.c +++ b/gen/test/c/mx/mx_notehead.c @@ -76,6 +76,8 @@ MxNotehead *mx_notehead_parse(xmlNodePtr el) { xmlNodePtr mx_notehead_serialize(const MxNotehead *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_notehead_value_to_string(m->value))); if (m->has_filled) { xmlSetProp(el, BAD_CAST "filled", BAD_CAST mx_yes_no_to_string(m->filled)); diff --git a/gen/test/c/mx/mx_notehead_text.c b/gen/test/c/mx/mx_notehead_text.c index b6f9bcd0a..c8b7a78b0 100644 --- a/gen/test/c/mx/mx_notehead_text.c +++ b/gen/test/c/mx/mx_notehead_text.c @@ -69,6 +69,8 @@ MxNoteheadText *mx_notehead_text_parse(xmlNodePtr el) { xmlNodePtr mx_notehead_text_serialize(const MxNoteheadText *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxNoteheadTextChild *ch = &m->children[i]; if (ch->display_text) { diff --git a/gen/test/c/mx/mx_octave_shift.c b/gen/test/c/mx/mx_octave_shift.c index 79d4825d3..308c53446 100644 --- a/gen/test/c/mx/mx_octave_shift.c +++ b/gen/test/c/mx/mx_octave_shift.c @@ -91,6 +91,8 @@ MxOctaveShift *mx_octave_shift_parse(xmlNodePtr el) { xmlNodePtr mx_octave_shift_serialize(const MxOctaveShift *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_up_down_stop_continue_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_offset.c b/gen/test/c/mx/mx_offset.c index 85824cb6c..e95d0f6ed 100644 --- a/gen/test/c/mx/mx_offset.c +++ b/gen/test/c/mx/mx_offset.c @@ -55,6 +55,8 @@ MxOffset *mx_offset_parse(xmlNodePtr el) { xmlNodePtr mx_offset_serialize(const MxOffset *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ { char *s = mx_divisions_to_string(m->value); xmlAddChild(el, xmlNewText(BAD_CAST s)); diff --git a/gen/test/c/mx/mx_opus.c b/gen/test/c/mx/mx_opus.c index cc592d60d..058865b2c 100644 --- a/gen/test/c/mx/mx_opus.c +++ b/gen/test/c/mx/mx_opus.c @@ -64,6 +64,8 @@ MxOpus *mx_opus_parse(xmlNodePtr el) { xmlNodePtr mx_opus_serialize(const MxOpus *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_xlink_href) { xmlSetProp(el, BAD_CAST "xlink:href", BAD_CAST m->xlink_href); } diff --git a/gen/test/c/mx/mx_ornaments.c b/gen/test/c/mx/mx_ornaments.c index 253999dc4..14119ca17 100644 --- a/gen/test/c/mx/mx_ornaments.c +++ b/gen/test/c/mx/mx_ornaments.c @@ -156,6 +156,8 @@ MxOrnaments *mx_ornaments_parse(xmlNodePtr el) { xmlNodePtr mx_ornaments_serialize(const MxOrnaments *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_id) { xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); } diff --git a/gen/test/c/mx/mx_other_appearance.c b/gen/test/c/mx/mx_other_appearance.c index 282941576..c301e6a3a 100644 --- a/gen/test/c/mx/mx_other_appearance.c +++ b/gen/test/c/mx/mx_other_appearance.c @@ -55,6 +55,8 @@ MxOtherAppearance *mx_other_appearance_parse(xmlNodePtr el) { xmlNodePtr mx_other_appearance_serialize(const MxOtherAppearance *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST m->type); diff --git a/gen/test/c/mx/mx_other_direction.c b/gen/test/c/mx/mx_other_direction.c index 7bad2481c..56fa1d940 100644 --- a/gen/test/c/mx/mx_other_direction.c +++ b/gen/test/c/mx/mx_other_direction.c @@ -94,6 +94,8 @@ MxOtherDirection *mx_other_direction_parse(xmlNodePtr el) { xmlNodePtr mx_other_direction_serialize(const MxOtherDirection *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_print_object) { xmlSetProp(el, BAD_CAST "print-object", BAD_CAST mx_yes_no_to_string(m->print_object)); diff --git a/gen/test/c/mx/mx_other_notation.c b/gen/test/c/mx/mx_other_notation.c index a1f4d69b7..445069f57 100644 --- a/gen/test/c/mx/mx_other_notation.c +++ b/gen/test/c/mx/mx_other_notation.c @@ -97,6 +97,8 @@ MxOtherNotation *mx_other_notation_parse(xmlNodePtr el) { xmlNodePtr mx_other_notation_serialize(const MxOtherNotation *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_single_to_string(m->type)); diff --git a/gen/test/c/mx/mx_other_placement_text.c b/gen/test/c/mx/mx_other_placement_text.c index 8837332a1..da9769cd1 100644 --- a/gen/test/c/mx/mx_other_placement_text.c +++ b/gen/test/c/mx/mx_other_placement_text.c @@ -85,6 +85,8 @@ MxOtherPlacementText *mx_other_placement_text_parse(xmlNodePtr el) { xmlNodePtr mx_other_placement_text_serialize(const MxOtherPlacementText *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); diff --git a/gen/test/c/mx/mx_other_play.c b/gen/test/c/mx/mx_other_play.c index 2a0f4e1d1..977f4e742 100644 --- a/gen/test/c/mx/mx_other_play.c +++ b/gen/test/c/mx/mx_other_play.c @@ -55,6 +55,8 @@ MxOtherPlay *mx_other_play_parse(xmlNodePtr el) { xmlNodePtr mx_other_play_serialize(const MxOtherPlay *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST m->type); diff --git a/gen/test/c/mx/mx_other_text.c b/gen/test/c/mx/mx_other_text.c index ab6a8edb3..f7e0f215c 100644 --- a/gen/test/c/mx/mx_other_text.c +++ b/gen/test/c/mx/mx_other_text.c @@ -55,6 +55,8 @@ MxOtherText *mx_other_text_parse(xmlNodePtr el) { xmlNodePtr mx_other_text_serialize(const MxOtherText *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_smufl) { xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); diff --git a/gen/test/c/mx/mx_page_layout.c b/gen/test/c/mx/mx_page_layout.c index b85818c8a..46d2303fe 100644 --- a/gen/test/c/mx/mx_page_layout.c +++ b/gen/test/c/mx/mx_page_layout.c @@ -79,6 +79,8 @@ MxPageLayout *mx_page_layout_parse(xmlNodePtr el) { xmlNodePtr mx_page_layout_serialize(const MxPageLayout *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxPageLayoutChild *ch = &m->children[i]; if (ch->page_height) { diff --git a/gen/test/c/mx/mx_page_margins.c b/gen/test/c/mx/mx_page_margins.c index 338464a2a..15b3d532d 100644 --- a/gen/test/c/mx/mx_page_margins.c +++ b/gen/test/c/mx/mx_page_margins.c @@ -92,6 +92,8 @@ MxPageMargins *mx_page_margins_parse(xmlNodePtr el) { xmlNodePtr mx_page_margins_serialize(const MxPageMargins *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_margin_type_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_part_group.c b/gen/test/c/mx/mx_part_group.c index 1e84454b2..f00693980 100644 --- a/gen/test/c/mx/mx_part_group.c +++ b/gen/test/c/mx/mx_part_group.c @@ -117,6 +117,8 @@ MxPartGroup *mx_part_group_parse(xmlNodePtr el) { xmlNodePtr mx_part_group_serialize(const MxPartGroup *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_part_list.c b/gen/test/c/mx/mx_part_list.c index 2ef7dcf04..89661b677 100644 --- a/gen/test/c/mx/mx_part_list.c +++ b/gen/test/c/mx/mx_part_list.c @@ -69,6 +69,8 @@ MxPartList *mx_part_list_parse(xmlNodePtr el) { xmlNodePtr mx_part_list_serialize(const MxPartList *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxPartListChild *ch = &m->children[i]; if (ch->part_group) { diff --git a/gen/test/c/mx/mx_part_name.c b/gen/test/c/mx/mx_part_name.c index 4a9c83793..d8ce012fb 100644 --- a/gen/test/c/mx/mx_part_name.c +++ b/gen/test/c/mx/mx_part_name.c @@ -85,6 +85,8 @@ MxPartName *mx_part_name_parse(xmlNodePtr el) { xmlNodePtr mx_part_name_serialize(const MxPartName *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); diff --git a/gen/test/c/mx/mx_part_symbol.c b/gen/test/c/mx/mx_part_symbol.c index 6d6f9eb94..f47e84b89 100644 --- a/gen/test/c/mx/mx_part_symbol.c +++ b/gen/test/c/mx/mx_part_symbol.c @@ -73,6 +73,8 @@ MxPartSymbol *mx_part_symbol_parse(xmlNodePtr el) { xmlNodePtr mx_part_symbol_serialize(const MxPartSymbol *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_group_symbol_value_to_string(m->value))); if (m->has_top_staff) { char *s = mx_staff_number_to_string(m->top_staff); diff --git a/gen/test/c/mx/mx_partwise_measure.c b/gen/test/c/mx/mx_partwise_measure.c index 38b452a86..4d39a57cd 100644 --- a/gen/test/c/mx/mx_partwise_measure.c +++ b/gen/test/c/mx/mx_partwise_measure.c @@ -153,6 +153,8 @@ MxPartwiseMeasure *mx_partwise_measure_parse(xmlNodePtr el) { xmlNodePtr mx_partwise_measure_serialize(const MxPartwiseMeasure *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_number) { xmlSetProp(el, BAD_CAST "number", BAD_CAST m->number); } diff --git a/gen/test/c/mx/mx_partwise_part.c b/gen/test/c/mx/mx_partwise_part.c index 6be873e91..2133f0cf5 100644 --- a/gen/test/c/mx/mx_partwise_part.c +++ b/gen/test/c/mx/mx_partwise_part.c @@ -66,6 +66,8 @@ MxPartwisePart *mx_partwise_part_parse(xmlNodePtr el) { xmlNodePtr mx_partwise_part_serialize(const MxPartwisePart *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_id) { xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); } diff --git a/gen/test/c/mx/mx_pedal.c b/gen/test/c/mx/mx_pedal.c index da1e59cbf..2de295d75 100644 --- a/gen/test/c/mx/mx_pedal.c +++ b/gen/test/c/mx/mx_pedal.c @@ -97,6 +97,8 @@ MxPedal *mx_pedal_parse(xmlNodePtr el) { xmlNodePtr mx_pedal_serialize(const MxPedal *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_pedal_type_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_pedal_tuning.c b/gen/test/c/mx/mx_pedal_tuning.c index a6a7f78bd..180388f52 100644 --- a/gen/test/c/mx/mx_pedal_tuning.c +++ b/gen/test/c/mx/mx_pedal_tuning.c @@ -73,6 +73,8 @@ MxPedalTuning *mx_pedal_tuning_parse(xmlNodePtr el) { xmlNodePtr mx_pedal_tuning_serialize(const MxPedalTuning *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxPedalTuningChild *ch = &m->children[i]; if (ch->pedal_step) { diff --git a/gen/test/c/mx/mx_per_minute.c b/gen/test/c/mx/mx_per_minute.c index ba4dab0b1..a45750f41 100644 --- a/gen/test/c/mx/mx_per_minute.c +++ b/gen/test/c/mx/mx_per_minute.c @@ -64,6 +64,8 @@ MxPerMinute *mx_per_minute_parse(xmlNodePtr el) { xmlNodePtr mx_per_minute_serialize(const MxPerMinute *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_font_family) { xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); diff --git a/gen/test/c/mx/mx_percussion.c b/gen/test/c/mx/mx_percussion.c index 861230af4..ee3f5509d 100644 --- a/gen/test/c/mx/mx_percussion.c +++ b/gen/test/c/mx/mx_percussion.c @@ -172,6 +172,8 @@ MxPercussion *mx_percussion_parse(xmlNodePtr el) { xmlNodePtr mx_percussion_serialize(const MxPercussion *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); diff --git a/gen/test/c/mx/mx_pitch.c b/gen/test/c/mx/mx_pitch.c index 4a5dc88f0..b0e07ea19 100644 --- a/gen/test/c/mx/mx_pitch.c +++ b/gen/test/c/mx/mx_pitch.c @@ -81,6 +81,8 @@ MxPitch *mx_pitch_parse(xmlNodePtr el) { xmlNodePtr mx_pitch_serialize(const MxPitch *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxPitchChild *ch = &m->children[i]; if (ch->step) { diff --git a/gen/test/c/mx/mx_pitched.c b/gen/test/c/mx/mx_pitched.c index 6e1ad9140..de2fe8f49 100644 --- a/gen/test/c/mx/mx_pitched.c +++ b/gen/test/c/mx/mx_pitched.c @@ -55,6 +55,8 @@ MxPitched *mx_pitched_parse(xmlNodePtr el) { xmlNodePtr mx_pitched_serialize(const MxPitched *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_pitched_value_to_string(m->value))); if (m->has_smufl) { xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); diff --git a/gen/test/c/mx/mx_placement_text.c b/gen/test/c/mx/mx_placement_text.c index 18dbeff87..736597a7b 100644 --- a/gen/test/c/mx/mx_placement_text.c +++ b/gen/test/c/mx/mx_placement_text.c @@ -82,6 +82,8 @@ MxPlacementText *mx_placement_text_parse(xmlNodePtr el) { xmlNodePtr mx_placement_text_serialize(const MxPlacementText *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); diff --git a/gen/test/c/mx/mx_play.c b/gen/test/c/mx/mx_play.c index ffd62ea72..adf720a4b 100644 --- a/gen/test/c/mx/mx_play.c +++ b/gen/test/c/mx/mx_play.c @@ -87,6 +87,8 @@ MxPlay *mx_play_parse(xmlNodePtr el) { xmlNodePtr mx_play_serialize(const MxPlay *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_id) { xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); } diff --git a/gen/test/c/mx/mx_principal_voice.c b/gen/test/c/mx/mx_principal_voice.c index 77fb3e992..c02dbbc80 100644 --- a/gen/test/c/mx/mx_principal_voice.c +++ b/gen/test/c/mx/mx_principal_voice.c @@ -94,6 +94,8 @@ MxPrincipalVoice *mx_principal_voice_parse(xmlNodePtr el) { xmlNodePtr mx_principal_voice_serialize(const MxPrincipalVoice *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); diff --git a/gen/test/c/mx/mx_print.c b/gen/test/c/mx/mx_print.c index 2e969e100..4fb5ed5a0 100644 --- a/gen/test/c/mx/mx_print.c +++ b/gen/test/c/mx/mx_print.c @@ -117,6 +117,8 @@ MxPrint *mx_print_parse(xmlNodePtr el) { xmlNodePtr mx_print_serialize(const MxPrint *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_staff_spacing) { char *s = mx_tenths_to_string(m->staff_spacing); xmlSetProp(el, BAD_CAST "staff-spacing", BAD_CAST s); diff --git a/gen/test/c/mx/mx_repeat.c b/gen/test/c/mx/mx_repeat.c index 7cc276840..430d642d2 100644 --- a/gen/test/c/mx/mx_repeat.c +++ b/gen/test/c/mx/mx_repeat.c @@ -55,6 +55,8 @@ MxRepeat *mx_repeat_parse(xmlNodePtr el) { xmlNodePtr mx_repeat_serialize(const MxRepeat *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_direction) { xmlSetProp(el, BAD_CAST "direction", BAD_CAST mx_backward_forward_to_string(m->direction)); } diff --git a/gen/test/c/mx/mx_rest.c b/gen/test/c/mx/mx_rest.c index f21d2b1e2..b3857bb64 100644 --- a/gen/test/c/mx/mx_rest.c +++ b/gen/test/c/mx/mx_rest.c @@ -76,6 +76,8 @@ MxRest *mx_rest_parse(xmlNodePtr el) { xmlNodePtr mx_rest_serialize(const MxRest *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_measure) { xmlSetProp(el, BAD_CAST "measure", BAD_CAST mx_yes_no_to_string(m->measure)); } diff --git a/gen/test/c/mx/mx_root.c b/gen/test/c/mx/mx_root.c index 9c63f6118..754fb53e4 100644 --- a/gen/test/c/mx/mx_root.c +++ b/gen/test/c/mx/mx_root.c @@ -69,6 +69,8 @@ MxRoot *mx_root_parse(xmlNodePtr el) { xmlNodePtr mx_root_serialize(const MxRoot *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxRootChild *ch = &m->children[i]; if (ch->root_step) { diff --git a/gen/test/c/mx/mx_root_alter.c b/gen/test/c/mx/mx_root_alter.c index 2f2abc730..90702c37b 100644 --- a/gen/test/c/mx/mx_root_alter.c +++ b/gen/test/c/mx/mx_root_alter.c @@ -85,6 +85,8 @@ MxRootAlter *mx_root_alter_parse(xmlNodePtr el) { xmlNodePtr mx_root_alter_serialize(const MxRootAlter *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ { char *s = mx_semitones_to_string(m->value); xmlAddChild(el, xmlNewText(BAD_CAST s)); diff --git a/gen/test/c/mx/mx_root_step.c b/gen/test/c/mx/mx_root_step.c index 6a5d612f3..e5779d8d7 100644 --- a/gen/test/c/mx/mx_root_step.c +++ b/gen/test/c/mx/mx_root_step.c @@ -82,6 +82,8 @@ MxRootStep *mx_root_step_parse(xmlNodePtr el) { xmlNodePtr mx_root_step_serialize(const MxRootStep *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_step_to_string(m->value))); if (m->has_text) { xmlSetProp(el, BAD_CAST "text", BAD_CAST m->text); diff --git a/gen/test/c/mx/mx_scaling.c b/gen/test/c/mx/mx_scaling.c index 94dbc1756..4cbccb35d 100644 --- a/gen/test/c/mx/mx_scaling.c +++ b/gen/test/c/mx/mx_scaling.c @@ -73,6 +73,8 @@ MxScaling *mx_scaling_parse(xmlNodePtr el) { xmlNodePtr mx_scaling_serialize(const MxScaling *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxScalingChild *ch = &m->children[i]; if (ch->millimeters) { diff --git a/gen/test/c/mx/mx_scordatura.c b/gen/test/c/mx/mx_scordatura.c index 813b7f73c..99d0dc1c9 100644 --- a/gen/test/c/mx/mx_scordatura.c +++ b/gen/test/c/mx/mx_scordatura.c @@ -66,6 +66,8 @@ MxScordatura *mx_scordatura_parse(xmlNodePtr el) { xmlNodePtr mx_scordatura_serialize(const MxScordatura *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_id) { xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); } diff --git a/gen/test/c/mx/mx_score_instrument.c b/gen/test/c/mx/mx_score_instrument.c index 09c4dd6e7..bcd733694 100644 --- a/gen/test/c/mx/mx_score_instrument.c +++ b/gen/test/c/mx/mx_score_instrument.c @@ -98,6 +98,8 @@ MxScoreInstrument *mx_score_instrument_parse(xmlNodePtr el) { xmlNodePtr mx_score_instrument_serialize(const MxScoreInstrument *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_id) { xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); } diff --git a/gen/test/c/mx/mx_score_part.c b/gen/test/c/mx/mx_score_part.c index 6ac54ac7f..0e041f847 100644 --- a/gen/test/c/mx/mx_score_part.c +++ b/gen/test/c/mx/mx_score_part.c @@ -113,6 +113,8 @@ MxScorePart *mx_score_part_parse(xmlNodePtr el) { xmlNodePtr mx_score_part_serialize(const MxScorePart *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_id) { xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); } diff --git a/gen/test/c/mx/mx_score_partwise.c b/gen/test/c/mx/mx_score_partwise.c index 43fe4902b..06e3d5cb0 100644 --- a/gen/test/c/mx/mx_score_partwise.c +++ b/gen/test/c/mx/mx_score_partwise.c @@ -106,6 +106,8 @@ MxScorePartwise *mx_score_partwise_parse(xmlNodePtr el) { xmlNodePtr mx_score_partwise_serialize(const MxScorePartwise *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_version) { xmlSetProp(el, BAD_CAST "version", BAD_CAST m->version); } diff --git a/gen/test/c/mx/mx_score_timewise.c b/gen/test/c/mx/mx_score_timewise.c index d2a4a5471..779cb54a9 100644 --- a/gen/test/c/mx/mx_score_timewise.c +++ b/gen/test/c/mx/mx_score_timewise.c @@ -106,6 +106,8 @@ MxScoreTimewise *mx_score_timewise_parse(xmlNodePtr el) { xmlNodePtr mx_score_timewise_serialize(const MxScoreTimewise *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_version) { xmlSetProp(el, BAD_CAST "version", BAD_CAST m->version); } diff --git a/gen/test/c/mx/mx_segno.c b/gen/test/c/mx/mx_segno.c index 1a534ce84..95c5735be 100644 --- a/gen/test/c/mx/mx_segno.c +++ b/gen/test/c/mx/mx_segno.c @@ -85,6 +85,8 @@ MxSegno *mx_segno_parse(xmlNodePtr el) { xmlNodePtr mx_segno_serialize(const MxSegno *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_smufl) { xmlSetProp(el, BAD_CAST "smufl", BAD_CAST m->smufl); } diff --git a/gen/test/c/mx/mx_slash.c b/gen/test/c/mx/mx_slash.c index d86c70e31..e38365983 100644 --- a/gen/test/c/mx/mx_slash.c +++ b/gen/test/c/mx/mx_slash.c @@ -85,6 +85,8 @@ MxSlash *mx_slash_parse(xmlNodePtr el) { xmlNodePtr mx_slash_serialize(const MxSlash *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_slide.c b/gen/test/c/mx/mx_slide.c index 163a41a26..5d9f6e005 100644 --- a/gen/test/c/mx/mx_slide.c +++ b/gen/test/c/mx/mx_slide.c @@ -109,6 +109,8 @@ MxSlide *mx_slide_parse(xmlNodePtr el) { xmlNodePtr mx_slide_serialize(const MxSlide *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); diff --git a/gen/test/c/mx/mx_slur.c b/gen/test/c/mx/mx_slur.c index 8c7d59b81..c6792e0c6 100644 --- a/gen/test/c/mx/mx_slur.c +++ b/gen/test/c/mx/mx_slur.c @@ -103,6 +103,8 @@ MxSlur *mx_slur_parse(xmlNodePtr el) { xmlNodePtr mx_slur_serialize(const MxSlur *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_continue_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_sound.c b/gen/test/c/mx/mx_sound.c index 531bcdf70..78efca3e7 100644 --- a/gen/test/c/mx/mx_sound.c +++ b/gen/test/c/mx/mx_sound.c @@ -135,6 +135,8 @@ MxSound *mx_sound_parse(xmlNodePtr el) { xmlNodePtr mx_sound_serialize(const MxSound *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_tempo) { char *s = mx_non_negative_decimal_to_string(m->tempo); xmlSetProp(el, BAD_CAST "tempo", BAD_CAST s); diff --git a/gen/test/c/mx/mx_staff_details.c b/gen/test/c/mx/mx_staff_details.c index 2803f6626..b8d6301c8 100644 --- a/gen/test/c/mx/mx_staff_details.c +++ b/gen/test/c/mx/mx_staff_details.c @@ -107,6 +107,8 @@ MxStaffDetails *mx_staff_details_parse(xmlNodePtr el) { xmlNodePtr mx_staff_details_serialize(const MxStaffDetails *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_number) { char *s = mx_staff_number_to_string(m->number); xmlSetProp(el, BAD_CAST "number", BAD_CAST s); diff --git a/gen/test/c/mx/mx_staff_divide.c b/gen/test/c/mx/mx_staff_divide.c index e7758c4cf..1e0f07b71 100644 --- a/gen/test/c/mx/mx_staff_divide.c +++ b/gen/test/c/mx/mx_staff_divide.c @@ -85,6 +85,8 @@ MxStaffDivide *mx_staff_divide_parse(xmlNodePtr el) { xmlNodePtr mx_staff_divide_serialize(const MxStaffDivide *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_staff_divide_symbol_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_staff_layout.c b/gen/test/c/mx/mx_staff_layout.c index 281d28fb8..54a3c15b1 100644 --- a/gen/test/c/mx/mx_staff_layout.c +++ b/gen/test/c/mx/mx_staff_layout.c @@ -68,6 +68,8 @@ MxStaffLayout *mx_staff_layout_parse(xmlNodePtr el) { xmlNodePtr mx_staff_layout_serialize(const MxStaffLayout *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_number) { char *s = mx_staff_number_to_string(m->number); xmlSetProp(el, BAD_CAST "number", BAD_CAST s); diff --git a/gen/test/c/mx/mx_staff_tuning.c b/gen/test/c/mx/mx_staff_tuning.c index 18e9afdff..4dcd5daad 100644 --- a/gen/test/c/mx/mx_staff_tuning.c +++ b/gen/test/c/mx/mx_staff_tuning.c @@ -84,6 +84,8 @@ MxStaffTuning *mx_staff_tuning_parse(xmlNodePtr el) { xmlNodePtr mx_staff_tuning_serialize(const MxStaffTuning *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_line) { char *s = mx_staff_line_to_string(m->line); xmlSetProp(el, BAD_CAST "line", BAD_CAST s); diff --git a/gen/test/c/mx/mx_stem.c b/gen/test/c/mx/mx_stem.c index 98caf789b..cd6dff7ad 100644 --- a/gen/test/c/mx/mx_stem.c +++ b/gen/test/c/mx/mx_stem.c @@ -67,6 +67,8 @@ MxStem *mx_stem_parse(xmlNodePtr el) { xmlNodePtr mx_stem_serialize(const MxStem *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_stem_value_to_string(m->value))); if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); diff --git a/gen/test/c/mx/mx_stick.c b/gen/test/c/mx/mx_stick.c index 3912ccea6..f1329fb2d 100644 --- a/gen/test/c/mx/mx_stick.c +++ b/gen/test/c/mx/mx_stick.c @@ -82,6 +82,8 @@ MxStick *mx_stick_parse(xmlNodePtr el) { xmlNodePtr mx_stick_serialize(const MxStick *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_tip) { xmlSetProp(el, BAD_CAST "tip", BAD_CAST mx_tip_direction_to_string(m->tip)); } diff --git a/gen/test/c/mx/mx_string.c b/gen/test/c/mx/mx_string.c index 447ba6bd8..2933c6c15 100644 --- a/gen/test/c/mx/mx_string.c +++ b/gen/test/c/mx/mx_string.c @@ -82,6 +82,8 @@ MxString *mx_string_parse(xmlNodePtr el) { xmlNodePtr mx_string_serialize(const MxString *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ { char *s = mx_string_number_to_string(m->value); xmlAddChild(el, xmlNewText(BAD_CAST s)); diff --git a/gen/test/c/mx/mx_string_mute.c b/gen/test/c/mx/mx_string_mute.c index d88df87c5..cf011a3b1 100644 --- a/gen/test/c/mx/mx_string_mute.c +++ b/gen/test/c/mx/mx_string_mute.c @@ -85,6 +85,8 @@ MxStringMute *mx_string_mute_parse(xmlNodePtr el) { xmlNodePtr mx_string_mute_serialize(const MxStringMute *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_on_off_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_strong_accent.c b/gen/test/c/mx/mx_strong_accent.c index 2edd81cb6..bd98cb479 100644 --- a/gen/test/c/mx/mx_strong_accent.c +++ b/gen/test/c/mx/mx_strong_accent.c @@ -79,6 +79,8 @@ MxStrongAccent *mx_strong_accent_parse(xmlNodePtr el) { xmlNodePtr mx_strong_accent_serialize(const MxStrongAccent *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); xmlSetProp(el, BAD_CAST "default-x", BAD_CAST s); diff --git a/gen/test/c/mx/mx_style_text.c b/gen/test/c/mx/mx_style_text.c index 181fcf665..95248f79a 100644 --- a/gen/test/c/mx/mx_style_text.c +++ b/gen/test/c/mx/mx_style_text.c @@ -79,6 +79,8 @@ MxStyleText *mx_style_text_parse(xmlNodePtr el) { xmlNodePtr mx_style_text_serialize(const MxStyleText *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_default_x) { char *s = mx_tenths_to_string(m->default_x); diff --git a/gen/test/c/mx/mx_supports.c b/gen/test/c/mx/mx_supports.c index f68e19be0..63bb51fae 100644 --- a/gen/test/c/mx/mx_supports.c +++ b/gen/test/c/mx/mx_supports.c @@ -58,6 +58,8 @@ MxSupports *mx_supports_parse(xmlNodePtr el) { xmlNodePtr mx_supports_serialize(const MxSupports *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_yes_no_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_system_dividers.c b/gen/test/c/mx/mx_system_dividers.c index efbab7aa1..0ed6fa201 100644 --- a/gen/test/c/mx/mx_system_dividers.c +++ b/gen/test/c/mx/mx_system_dividers.c @@ -69,6 +69,8 @@ MxSystemDividers *mx_system_dividers_parse(xmlNodePtr el) { xmlNodePtr mx_system_dividers_serialize(const MxSystemDividers *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxSystemDividersChild *ch = &m->children[i]; if (ch->left_divider) { diff --git a/gen/test/c/mx/mx_system_layout.c b/gen/test/c/mx/mx_system_layout.c index 479e65286..001956b83 100644 --- a/gen/test/c/mx/mx_system_layout.c +++ b/gen/test/c/mx/mx_system_layout.c @@ -85,6 +85,8 @@ MxSystemLayout *mx_system_layout_parse(xmlNodePtr el) { xmlNodePtr mx_system_layout_serialize(const MxSystemLayout *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxSystemLayoutChild *ch = &m->children[i]; if (ch->system_margins) { diff --git a/gen/test/c/mx/mx_system_margins.c b/gen/test/c/mx/mx_system_margins.c index f1529ffb3..37e470465 100644 --- a/gen/test/c/mx/mx_system_margins.c +++ b/gen/test/c/mx/mx_system_margins.c @@ -73,6 +73,8 @@ MxSystemMargins *mx_system_margins_parse(xmlNodePtr el) { xmlNodePtr mx_system_margins_serialize(const MxSystemMargins *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxSystemMarginsChild *ch = &m->children[i]; if (ch->left_margin) { diff --git a/gen/test/c/mx/mx_tap.c b/gen/test/c/mx/mx_tap.c index ee30f21ae..9fcd22c24 100644 --- a/gen/test/c/mx/mx_tap.c +++ b/gen/test/c/mx/mx_tap.c @@ -85,6 +85,8 @@ MxTap *mx_tap_parse(xmlNodePtr el) { xmlNodePtr mx_tap_serialize(const MxTap *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_hand) { xmlSetProp(el, BAD_CAST "hand", BAD_CAST mx_tap_hand_to_string(m->hand)); diff --git a/gen/test/c/mx/mx_technical.c b/gen/test/c/mx/mx_technical.c index 3079b5fdd..87ff09273 100644 --- a/gen/test/c/mx/mx_technical.c +++ b/gen/test/c/mx/mx_technical.c @@ -246,6 +246,8 @@ MxTechnical *mx_technical_parse(xmlNodePtr el) { xmlNodePtr mx_technical_serialize(const MxTechnical *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_id) { xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); } diff --git a/gen/test/c/mx/mx_text_element_data.c b/gen/test/c/mx/mx_text_element_data.c index 94880753a..fb70aec73 100644 --- a/gen/test/c/mx/mx_text_element_data.c +++ b/gen/test/c/mx/mx_text_element_data.c @@ -88,6 +88,8 @@ MxTextElementData *mx_text_element_data_parse(xmlNodePtr el) { xmlNodePtr mx_text_element_data_serialize(const MxTextElementData *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_xml_lang) { xmlSetProp(el, BAD_CAST "xml:lang", BAD_CAST m->xml_lang); diff --git a/gen/test/c/mx/mx_tie.c b/gen/test/c/mx/mx_tie.c index e47d60e09..90ee9dcd6 100644 --- a/gen/test/c/mx/mx_tie.c +++ b/gen/test/c/mx/mx_tie.c @@ -52,6 +52,8 @@ MxTie *mx_tie_parse(xmlNodePtr el) { xmlNodePtr mx_tie_serialize(const MxTie *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_tied.c b/gen/test/c/mx/mx_tied.c index 05ac12dd2..2aceb20b4 100644 --- a/gen/test/c/mx/mx_tied.c +++ b/gen/test/c/mx/mx_tied.c @@ -103,6 +103,8 @@ MxTied *mx_tied_parse(xmlNodePtr el) { xmlNodePtr mx_tied_serialize(const MxTied *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_tied_type_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_time.c b/gen/test/c/mx/mx_time.c index 7418021de..fcb817363 100644 --- a/gen/test/c/mx/mx_time.c +++ b/gen/test/c/mx/mx_time.c @@ -126,6 +126,8 @@ MxTime *mx_time_parse(xmlNodePtr el) { xmlNodePtr mx_time_serialize(const MxTime *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_number) { char *s = mx_staff_number_to_string(m->number); xmlSetProp(el, BAD_CAST "number", BAD_CAST s); diff --git a/gen/test/c/mx/mx_time_modification.c b/gen/test/c/mx/mx_time_modification.c index dc7b82700..8e651d418 100644 --- a/gen/test/c/mx/mx_time_modification.c +++ b/gen/test/c/mx/mx_time_modification.c @@ -87,6 +87,8 @@ MxTimeModification *mx_time_modification_parse(xmlNodePtr el) { xmlNodePtr mx_time_modification_serialize(const MxTimeModification *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxTimeModificationChild *ch = &m->children[i]; if (ch->actual_notes) { diff --git a/gen/test/c/mx/mx_timewise_measure.c b/gen/test/c/mx/mx_timewise_measure.c index a2a419465..5806a22ab 100644 --- a/gen/test/c/mx/mx_timewise_measure.c +++ b/gen/test/c/mx/mx_timewise_measure.c @@ -81,6 +81,8 @@ MxTimewiseMeasure *mx_timewise_measure_parse(xmlNodePtr el) { xmlNodePtr mx_timewise_measure_serialize(const MxTimewiseMeasure *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_number) { xmlSetProp(el, BAD_CAST "number", BAD_CAST m->number); } diff --git a/gen/test/c/mx/mx_timewise_part.c b/gen/test/c/mx/mx_timewise_part.c index 30058072e..0af757182 100644 --- a/gen/test/c/mx/mx_timewise_part.c +++ b/gen/test/c/mx/mx_timewise_part.c @@ -138,6 +138,8 @@ MxTimewisePart *mx_timewise_part_parse(xmlNodePtr el) { xmlNodePtr mx_timewise_part_serialize(const MxTimewisePart *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_id) { xmlSetProp(el, BAD_CAST "id", BAD_CAST m->id); } diff --git a/gen/test/c/mx/mx_transpose.c b/gen/test/c/mx/mx_transpose.c index 5df17ded8..31c639417 100644 --- a/gen/test/c/mx/mx_transpose.c +++ b/gen/test/c/mx/mx_transpose.c @@ -93,6 +93,8 @@ MxTranspose *mx_transpose_parse(xmlNodePtr el) { xmlNodePtr mx_transpose_serialize(const MxTranspose *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_number) { char *s = mx_staff_number_to_string(m->number); xmlSetProp(el, BAD_CAST "number", BAD_CAST s); diff --git a/gen/test/c/mx/mx_tremolo.c b/gen/test/c/mx/mx_tremolo.c index 2d9222c16..8e0b20514 100644 --- a/gen/test/c/mx/mx_tremolo.c +++ b/gen/test/c/mx/mx_tremolo.c @@ -88,6 +88,8 @@ MxTremolo *mx_tremolo_parse(xmlNodePtr el) { xmlNodePtr mx_tremolo_serialize(const MxTremolo *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ { char *s = mx_tremolo_marks_to_string(m->value); xmlAddChild(el, xmlNewText(BAD_CAST s)); diff --git a/gen/test/c/mx/mx_tuplet.c b/gen/test/c/mx/mx_tuplet.c index 47af9d269..bbcd0b33f 100644 --- a/gen/test/c/mx/mx_tuplet.c +++ b/gen/test/c/mx/mx_tuplet.c @@ -105,6 +105,8 @@ MxTuplet *mx_tuplet_parse(xmlNodePtr el) { xmlNodePtr mx_tuplet_serialize(const MxTuplet *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_tuplet_dot.c b/gen/test/c/mx/mx_tuplet_dot.c index 1bb0a5d4f..27bc30325 100644 --- a/gen/test/c/mx/mx_tuplet_dot.c +++ b/gen/test/c/mx/mx_tuplet_dot.c @@ -61,6 +61,8 @@ MxTupletDot *mx_tuplet_dot_parse(xmlNodePtr el) { xmlNodePtr mx_tuplet_dot_serialize(const MxTupletDot *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_font_family) { xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); } diff --git a/gen/test/c/mx/mx_tuplet_number.c b/gen/test/c/mx/mx_tuplet_number.c index 851c8e000..ac271189b 100644 --- a/gen/test/c/mx/mx_tuplet_number.c +++ b/gen/test/c/mx/mx_tuplet_number.c @@ -67,6 +67,8 @@ MxTupletNumber *mx_tuplet_number_parse(xmlNodePtr el) { xmlNodePtr mx_tuplet_number_serialize(const MxTupletNumber *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ { char *s = mx_format_int(m->value); xmlAddChild(el, xmlNewText(BAD_CAST s)); diff --git a/gen/test/c/mx/mx_tuplet_portion.c b/gen/test/c/mx/mx_tuplet_portion.c index 33e54f445..d69f5d31a 100644 --- a/gen/test/c/mx/mx_tuplet_portion.c +++ b/gen/test/c/mx/mx_tuplet_portion.c @@ -75,6 +75,8 @@ MxTupletPortion *mx_tuplet_portion_parse(xmlNodePtr el) { xmlNodePtr mx_tuplet_portion_serialize(const MxTupletPortion *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxTupletPortionChild *ch = &m->children[i]; if (ch->tuplet_number) { diff --git a/gen/test/c/mx/mx_tuplet_type.c b/gen/test/c/mx/mx_tuplet_type.c index e14b400fc..9c7997a18 100644 --- a/gen/test/c/mx/mx_tuplet_type.c +++ b/gen/test/c/mx/mx_tuplet_type.c @@ -67,6 +67,8 @@ MxTupletType *mx_tuplet_type_parse(xmlNodePtr el) { xmlNodePtr mx_tuplet_type_serialize(const MxTupletType *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST mx_note_type_value_to_string(m->value))); if (m->has_font_family) { xmlSetProp(el, BAD_CAST "font-family", BAD_CAST m->font_family); diff --git a/gen/test/c/mx/mx_typed_text.c b/gen/test/c/mx/mx_typed_text.c index 78e4d4ebe..6d88d5408 100644 --- a/gen/test/c/mx/mx_typed_text.c +++ b/gen/test/c/mx/mx_typed_text.c @@ -55,6 +55,8 @@ MxTypedText *mx_typed_text_parse(xmlNodePtr el) { xmlNodePtr mx_typed_text_serialize(const MxTypedText *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ xmlAddChild(el, xmlNewText(BAD_CAST m->value)); if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST m->type); diff --git a/gen/test/c/mx/mx_unpitched.c b/gen/test/c/mx/mx_unpitched.c index 81e5b8661..bf2d53702 100644 --- a/gen/test/c/mx/mx_unpitched.c +++ b/gen/test/c/mx/mx_unpitched.c @@ -73,6 +73,8 @@ MxUnpitched *mx_unpitched_parse(xmlNodePtr el) { xmlNodePtr mx_unpitched_serialize(const MxUnpitched *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxUnpitchedChild *ch = &m->children[i]; if (ch->display_step) { diff --git a/gen/test/c/mx/mx_virtual_instrument.c b/gen/test/c/mx/mx_virtual_instrument.c index 1f3804be3..398fd388e 100644 --- a/gen/test/c/mx/mx_virtual_instrument.c +++ b/gen/test/c/mx/mx_virtual_instrument.c @@ -67,6 +67,8 @@ MxVirtualInstrument *mx_virtual_instrument_parse(xmlNodePtr el) { xmlNodePtr mx_virtual_instrument_serialize(const MxVirtualInstrument *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxVirtualInstrumentChild *ch = &m->children[i]; if (ch->virtual_library) { diff --git a/gen/test/c/mx/mx_wavy_line.c b/gen/test/c/mx/mx_wavy_line.c index d4fe29fc6..a3ce4d001 100644 --- a/gen/test/c/mx/mx_wavy_line.c +++ b/gen/test/c/mx/mx_wavy_line.c @@ -91,6 +91,8 @@ MxWavyLine *mx_wavy_line_parse(xmlNodePtr el) { xmlNodePtr mx_wavy_line_serialize(const MxWavyLine *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_start_stop_continue_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_wedge.c b/gen/test/c/mx/mx_wedge.c index 4cf442fed..571251b1d 100644 --- a/gen/test/c/mx/mx_wedge.c +++ b/gen/test/c/mx/mx_wedge.c @@ -85,6 +85,8 @@ MxWedge *mx_wedge_parse(xmlNodePtr el) { xmlNodePtr mx_wedge_serialize(const MxWedge *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ if (m->has_type) { xmlSetProp(el, BAD_CAST "type", BAD_CAST mx_wedge_type_to_string(m->type)); } diff --git a/gen/test/c/mx/mx_work.c b/gen/test/c/mx/mx_work.c index b6bee4150..a3fb4006f 100644 --- a/gen/test/c/mx/mx_work.c +++ b/gen/test/c/mx/mx_work.c @@ -73,6 +73,8 @@ MxWork *mx_work_parse(xmlNodePtr el) { xmlNodePtr mx_work_serialize(const MxWork *m, xmlNodePtr parent, const char *tag) { xmlNodePtr el = parent ? xmlNewChild(parent, NULL, BAD_CAST tag, NULL) : xmlNewNode(NULL, BAD_CAST tag); + if (!el) + abort(); /* OOM policy: abort, matching the runtime's allocators */ for (size_t i = 0; i < m->children_count; i++) { const MxWorkChild *ch = &m->children[i]; if (ch->work_number) { diff --git a/gen/test/c/src/compare.c b/gen/test/c/src/compare.c index 78ddbbc73..facf94721 100644 --- a/gen/test/c/src/compare.c +++ b/gen/test/c/src/compare.c @@ -49,11 +49,45 @@ static char *node_text(xmlNodePtr node) { (ns prefix "xlink", name "href") while a serialized one may carry the literal name "xlink:href"; both must compare equal. */ static void attr_qname(xmlAttrPtr a, char *buf, size_t cap) { + int n; if (a->ns && a->ns->prefix) - snprintf(buf, cap, "%s:%s", (const char *)a->ns->prefix, - (const char *)a->name); + n = snprintf(buf, cap, "%s:%s", (const char *)a->ns->prefix, + (const char *)a->name); else - snprintf(buf, cap, "%s", (const char *)a->name); + n = snprintf(buf, cap, "%s", (const char *)a->name); + if (n < 0 || (size_t)n >= cap) + abort(); /* a truncated name must never compare equal to another */ +} + +/* Namespace declarations live in nsDef, never in the attribute list, so + they need their own comparison or the model's namespace preservation + would be unverifiable. Counts each (prefix, href) pair of `a` found + exactly once in `b`. */ +static int ns_decls_match(xmlNodePtr a, xmlNodePtr b) { + int na = 0, nb = 0; + for (xmlNsPtr ns = a->nsDef; ns; ns = ns->next) + na++; + for (xmlNsPtr ns = b->nsDef; ns; ns = ns->next) + nb++; + if (na != nb) + return 0; + for (xmlNsPtr ns = a->nsDef; ns; ns = ns->next) { + int found = 0; + for (xmlNsPtr other = b->nsDef; other; other = other->next) { + int prefix_eq = + (!ns->prefix && !other->prefix) || + (ns->prefix && other->prefix && + strcmp((const char *)ns->prefix, (const char *)other->prefix) == 0); + if (prefix_eq && + strcmp((const char *)ns->href, (const char *)other->href) == 0) { + found = 1; + break; + } + } + if (!found) + return 0; + } + return 1; } static CompareResult do_compare(xmlNodePtr expected, xmlNodePtr actual, @@ -74,6 +108,13 @@ static CompareResult do_compare(xmlNodePtr expected, xmlNodePtr actual, return r; } + if (!ns_decls_match(expected, actual)) { + r.failed = 1; + snprintf(r.detail, sizeof(r.detail), + "namespace declaration mismatch at %s", path); + return r; + } + char *et = node_text(expected); char *at = node_text(actual); if (!is_equivalent(et, at)) { diff --git a/gen/test/go/corert/normalize.go b/gen/test/go/corert/normalize.go index dc5d00bda..a11942275 100644 --- a/gen/test/go/corert/normalize.go +++ b/gen/test/go/corert/normalize.go @@ -106,8 +106,10 @@ func sortAttributes(el *etree.Element) { return } if len(el.Attr) > 1 { + // Qualified names: xlink:href must sort (and compare) as itself, not + // as a second "href". sort.Slice(el.Attr, func(i, j int) bool { - return el.Attr[i].Key < el.Attr[j].Key + return el.Attr[i].FullKey() < el.Attr[j].FullKey() }) } for _, child := range el.ChildElements() { diff --git a/gen/test/go/corert/roundtrip.go b/gen/test/go/corert/roundtrip.go index f5e0b6575..1bfbcc89d 100644 --- a/gen/test/go/corert/roundtrip.go +++ b/gen/test/go/corert/roundtrip.go @@ -196,11 +196,13 @@ func compareElements(expected, actual *etree.Element, path []string) string { return fmt.Sprintf("attribute count mismatch at %s", nodePath(path)) } for i := range eAttrs { - if eAttrs[i].Key != aAttrs[i].Key || !IsEquivalent(eAttrs[i].Value, aAttrs[i].Value) { + // Compare qualified names: a defect that drops a prefix + // (xlink:href -> href) must fail, not slide by on the local name. + if eAttrs[i].FullKey() != aAttrs[i].FullKey() || !IsEquivalent(eAttrs[i].Value, aAttrs[i].Value) { return fmt.Sprintf("attribute mismatch at %s[@%s]: expected '%s=%s', actual '%s=%s'", - nodePath(path), eAttrs[i].Key, - eAttrs[i].Key, eAttrs[i].Value, - aAttrs[i].Key, aAttrs[i].Value) + nodePath(path), eAttrs[i].FullKey(), + eAttrs[i].FullKey(), eAttrs[i].Value, + aAttrs[i].FullKey(), aAttrs[i].Value) } } From 430aec29a141e6d1b24877022e6b3b984a26170c Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 11 Jun 2026 05:43:58 +0000 Subject: [PATCH 26/45] design: generator-agnosticism -- the cardinal rule and the pack/press redesign New cardinal rule (from the PR #168 review): the generator is language agnostic; adding a new language target must not require edits to the generator's Python files. The current implementation violates it -- roughly 2,500 lines of Python ARE Go and C (gen/emit/go/, gen/emit/c/, the language tables in gen/plates/languages.py, the BACKENDS registry, and prescribed config keys like [target] namespace that exist only because specific languages want them). The design doc specifies the redesign: - A target becomes a PACK: config.toml + a templates/ directory; the generator cannot tell which language it is emitting (no language registry, no language name anywhere in Python). - Config splits into the prescribed projection contract (every key definable without naming a language: conventions, renames, symbol-prefix, variant-scope, [types], [reserved]) and a freeform [vars] table passed verbatim to templates -- answering the review question about target.foo="bar" directly. languages.py's tables become required config. - The PRESS (gen/press/): a deliberately minimal, stdlib-only, mustache-class template engine -- variables, sections, inverted sections, recursive partials, loop metadata, one quoted-literal modifier, and constitutionally nothing else (no expressions, filters, or string functions). Its poverty is load-bearing: if a template cannot express something, the plates must carry it. Dispatch is by manifest (one template file per shape) and by mechanical discriminant expansion in the context builder, never by in-template logic. - A render MANIFEST in config declares template -> output-pattern rows, absorbing file layout generically: C's header/impl pairs are two rows, partitioning is implicit, [layout] dies, support files are templates, and the gofmt pass becomes a generic [render] format hook. - Small neutral plate additions so templates need no lookups: PlateRef gains the referenced type's name bundle and kind; plates gain deps for include/import composition; doc text arrives pre-wrapped. - Migration in six phases, each green, with a hard byte-parity gate (regenerate, git diff --exit-code over committed output) before each Python backend is deleted -- ending with the JSON Schema target added as a pure pack plus a CI assertion that the change touched no Python. - Rejected alternatives recorded: per-target Python plugins (satisfy the letter, not the spirit), Jinja2 (expressiveness lets backends reconstitute inside templates), AST emitters, and keeping languages.py 'as data'. AGENTS.md now states the cardinal rule up front and flags the current code as violating it, pointing here. --- AGENTS.md | 9 +- docs/ai/design/generator-agnosticism.md | 327 ++++++++++++++++++++++++ 2 files changed, 335 insertions(+), 1 deletion(-) create mode 100644 docs/ai/design/generator-agnosticism.md diff --git a/AGENTS.md b/AGENTS.md index 7daf456b5..8c60ffc3d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,6 +4,12 @@ A code generator that reads the MusicXML 4.0 XSD specification and emits typed d serialization/deserialization libraries in multiple languages. C++ is the primary target; Go and C are secondary targets that keep the generator architecture honest about extensibility. +**CARDINAL RULE: the generator is language agnostic.** Adding a new language target must not +require edits to the generator's Python files; all language knowledge lives in the target's own +directory as data and templates. The current code VIOLATES this rule (per-language backends under +`gen/emit/`, language tables in `gen/plates/languages.py`); the redesign that fixes it is +specified in `docs/ai/design/generator-agnosticism.md` and is the next body of work. + Ignore git history prior to `b01288`. Reading anything before that commit will only confuse you and degrade your performance. @@ -16,7 +22,8 @@ mx/ Dockerfile <- mx-sdk toolchain image (Ubuntu 24.04, GCC 14, Go, libxml2, Python 3) CMakeLists.txt <- C++ project: ezxml library + corert test harness data/ <- MusicXML test corpus (~1,347 files, see data/README.md) - docs/ai/design/ <- design docs (plates.md: the Plates, the template-facing layer) + docs/ai/design/ <- design docs (plates.md: the Plates; generator-agnosticism.md: + the cardinal rule and the pack/press redesign) src/private/ <- C++ source mx/ezxml/ <- vendored pugixml-backed XML layer mx/core/ <- generated C++ typed model diff --git a/docs/ai/design/generator-agnosticism.md b/docs/ai/design/generator-agnosticism.md new file mode 100644 index 000000000..33746e440 --- /dev/null +++ b/docs/ai/design/generator-agnosticism.md @@ -0,0 +1,327 @@ +# Generator agnosticism: removing language knowledge from the generator + +Status: design. This document specifies the redesign that removes all target-language knowledge +from the generator's Python code. It supersedes the emit-stage portions of +[`plates.md`](plates.md) (the Plates layer itself -- sections 1-8 there -- survives intact; what +changes is what a "template" is and where language facts live). + +## 1. The cardinal rule + +**The generator is language agnostic. Adding a new language target must not require edits to the +generator's Python files.** + +The rule has a letter and a spirit. The letter: `git diff --name-only` for the change that adds a +new target touches no `*.py` under `gen/` (outside the new target's own directory). The spirit: +the Python pipeline must be a closed machine -- schema in, files out -- that is *incapable* of +expressing a language-specific decision, so that language knowledge has nowhere to live except in +the target's own directory. Go and C were built first precisely to force this generality; the +current implementation failed the test, and the C++ target would have failed it a third time. + +A corollary worth stating: under this rule, the generator has no concept of "Go" or "C" at all. +There is no language registry, no language name in config, no per-language defaults. A target is a +directory of data and templates; the generator cannot tell which language it is emitting. + +## 2. What violates the rule today + +| Where | What it encodes | Lines | +|---|---|---| +| `gen/emit/go/` (6 modules) | Go grammar end to end: declarations, parse/serialize bodies, the runtime source as a Python string, import lists, string-literal quoting, the gofmt subprocess | ~970 | +| `gen/emit/c/` (7 modules) | C grammar end to end: header/impl frames, include guards, the calling-convention module (ownership rules per plate kind), the runtime source, memory management | ~1,360 | +| `gen/plates/languages.py` | Per-language type maps, reserved-word lists, doc-comment styles, variant scopes -- data tables keyed by language name | ~125 | +| `gen/emit/__init__.py` | `BACKENDS`: a language-name -> Python-module registry | -- | +| `gen/config.py` | `[target] language` (selects the above), and prescribed keys (`namespace`, `prefix`) that exist only because specific languages need them | -- | + +Roughly 2,500 lines of Python that are *about* Go and C. Everything else in the pipeline -- +the XSD parser, the IR and Resolver, the naming machinery (`gen/names.py`), the Plates projection, +the collision gate, the writer -- is already neutral, and the review rounds that pushed decisions +"into the plates" (final identifiers, clamp policy, union tags, effective cardinality) made the +*data* model genuinely language-free. The failure is confined to one question the plates design +left unanswered: **what is a template?** The design said "templates are dumb renderers" and the +implementation answered "a Python module per language." Every subsequent fix improved where +*decisions* live but left the *renderers* as per-language programs. + +## 3. The redesign in one paragraph + +A target becomes a **pack**: a directory containing `config.toml` and a `templates/` directory, +and nothing else the generator needs. Everything `languages.py` held becomes required config data. +Everything `gen/emit//` held becomes template files in a deliberately minimal, logic-less +template language, rendered by one generic engine (the **press**, completing the plates metaphor: +plates carry every decision; the press inks and prints them). A render **manifest** in the config +declares which template renders which plate shapes into which output paths, so file layout -- +including C's header/impl pairs -- is pack data, not Python. The generator's Python is then a +closed set: parse, lower, project, render, write. The proof obligation is concrete: port Go and C +to packs with byte-identical generated output, delete `gen/emit/go`, `gen/emit/c`, and +`languages.py`, then add a third target (the JSON Schema emitter that has been this project's +forcing function since the plates design) as a pure pack, and let CI assert that change touched no +Python. + +``` +gen/test/c/ <- a target pack + config.toml <- inputs, projection settings, vars, render manifest + templates/ + enum.h.tmpl enum.c.tmpl <- one template per shape (the original design principle, + composite.h.tmpl ... now literally one FILE per shape) + runtime.h.tmpl ... <- support files: a template with no tags is a static file + member-parse.tmpl ... <- partials shared by this pack's templates + src/ <- the hand-written corert harness (target code, not generator) + mx/ <- generated output (committed) +``` + +## 4. Config: the projection contract vs. freeform vars + +The PR review asked the right question about `[target] namespace`: is the config schema +prescribed, and is that not itself language-specific? The answer is a split, with a litmus test. + +**Prescribed keys are the projection contract**: every key the generator itself consumes must be +definable in projection terms, without reference to any language. These survive, because the +plates' work -- casing, renaming, sanitizing, collision-gating, strategy selection -- is real and +neutral: + +- `[naming]` conventions, acronyms, `[rename.*]`, `[reserved] words` -- already neutral. +- `[target] symbol-prefix` (today `prefix`): "prepended to every type identifier and composed + constant before sanitization." Neutral semantics; it must stay in the projection (not become a + template variable) because the collision gate certifies the *final* identifiers -- moving + composition into templates is exactly the regression the first review round fixed. +- `[target] variant-scope = "bare" | "composed"`: how constants are scoped, today seeded per + language in Python. Becomes explicit config. +- `[target] inheritance = true | false`: selects the derived strategy. Already neutral. +- `[types]`: the primitive -> spelling map, today defaulted per language. Becomes **required** for + any pack whose templates emit typed code (a pack that omits it gets primitive names passed + through, which is what a neutral target wants). +- `[reserved] words`: today extends per-language defaults. Becomes the **whole** list; packs own + their keyword lists. Two small additions let packs protect their template-synthesized names + generically: `members = [...]` (member identifiers the pack's templates reserve, e.g. Go's + `Children`) and `type-suffixes = [...]` (compositions like `Child` that the templates append to + type identifiers), both fed to the existing collision gate. +- `[docs] wrap`: the plates pre-wrap doc text into lines (`doc_lines`); comment *syntax* moves + into template text, so `[docs] style` and the `DocStyle` machinery are deleted. + +**Everything else is freeform.** A `[vars]` table of string key-values passes through to templates +verbatim (`{{target.vars.namespace}}`, `{{target.vars.package}}`, `{{target.vars.anything}}`). +`namespace` stops being generator schema; it becomes a variable that the Go pack's templates +happen to consume as a package name and the C++ pack's as a namespace. `target.foo = "bar"` is +exactly as legal as either. The litmus test for any future key: *if you cannot define it without +naming a language, it is a var, not a key.* + +**Deleted outright**: `[target] language` (nothing selects on it anymore), `[layout]` entirely +(partition, file-prefix, file-convention -- subsumed by the manifest, section 6), and +`languages.py` with all its tables. + +## 5. The press: a deliberately minimal template engine + +`gen/press/` renders template files against a context built from the plates. It is stdlib-only +(~300-400 lines plus tests) and mustache-class on purpose. The constraint is the feature: **if a +template cannot express something, the plates must carry it** -- the engine's poverty is what +keeps decisions in the projection, where they are dumpable, diffable, and collision-gated. + +The complete feature set, derived by walking every construct the current Python backends emit: + +- **Variables**: `{{ident}}`, dotted paths `{{name.snake}}`, `{{type_ref.ident}}`, + `{{target.vars.prefix}}`. Missing keys are a render error (fail loud), not empty output. +- **Sections**: `{{#members}}...{{/members}}` iterates lists (the cursor becomes the context) and + gates on truthiness for scalars/objects; `{{^x}}...{{/x}}` inverts. Loop metadata `{{@first}}`, + `{{@last}}`, `{{@index}}` is provided by the engine (it is presentation mechanics, not data) -- + this is what expresses `if`/`else if` chains and separator joins. +- **Partials**: `{{> member-parse}}`, resolved within the pack's `templates/` directory, recursion + permitted with a depth limit (a schema-shaped target walking `content` trees needs it). +- **One modifier**: `{{wire:q}}` renders a double-quoted, backslash-escaped string literal using + the JSON escape repertoire with non-ASCII as `\uXXXX` -- a subset valid verbatim in C, C++, Go, + Java, JavaScript, and Rust literals. This is the design's one acknowledged compromise: string + quoting is presentation that genuinely varies by language family, and encoding one universal + family in the engine beats either per-pack escape tables in config or hand-escaping in + templates. A future target outside that family is the trigger to revisit (section 10). +- **Whitespace discipline**: standalone section/partial lines collapse (the mustache standalone + rule), so templates can be indented readably without leaking blank lines. + +And, as a constitution, what the engine will **never** grow: expressions, comparisons, +arithmetic, filters, string manipulation, casing functions, variable assignment, or template +inheritance. In particular there is no equality test; dispatch happens two other ways: + +1. **By manifest**: each template entry declares which plate strategies it renders (section 6), + so per-shape dispatch never appears inside a template -- restoring "one template per shape" as + one *file* per shape. +2. **By discriminant expansion**: the context builder (neutral, mechanical) expands every closed + enumerated field into boolean companions -- `kind: "enum"` yields `is_enum`; `cardinality: + "vector"` yields `is_vector`; `category: "primitive"` yields `is_primitive` -- and exposes the + member list pre-split (`attributes`, `elements`, `value`) using the filters the plates already + define. Templates branch with plain sections: `{{#type_ref.is_complex}}...{{/type_ref.is_complex}}`. + +Two small, neutral additions to the plates feed this (the only model changes the redesign needs): + +- `PlateRef` gains the referenced type's `name` bundle and `kind` (plate kind, or the primitive's + family). Today the Python backends look these up via `plates.plate(wire)` to compose calls like + `mx_{{snake}}_parse(...)` and to choose ownership idioms; a logic-less template cannot perform + lookups, so the materialized tree denormalizes them (it is materialized precisely so templates + get random access without computation). +- Each plate gains `deps`: its dependency references with name bundles, replacing + `FileSpec.includes` so include/import lines become template text composed from data + (`{{#deps}}#include "mx_{{name.snake}}.h"{{/deps}}`). + +Worked example -- today's `gen/emit/c/complexes.py` attribute loop, as template text: + +``` + for (xmlAttrPtr a = el->properties; a; a = a->next) { + {{> attr-name}} +{{#attributes}} + {{#@first}}if{{/@first}}{{^@first}}}} else if{{/@first}} (strcmp(aname, {{name.wire:q}}) == 0) { + m->has_{{ident}} = true; + m->{{ident}} = {{> attr-parse-expr}}; +{{/attributes}} + {{#attributes}}} else {{{/attributes}}{{^attributes}}{{{/attributes}} + {{target.vars.fn_prefix}}error_set("unknown attribute \"%s\" on <%s>", aname, (const char *)el->name); + ... +``` + +Everything language-shaped (C's `strcmp`, `->`, `has_` prefixes, the error idiom) is pack content; +everything decided (idents, wire names, which members are attributes) is plate data. The press +contributes iteration and the `@first` chain mechanics, nothing more. + +## 6. The render manifest: file layout as pack data + +`config.toml` declares what gets rendered where. Two entry kinds: + +```toml +[render] +dir = "templates" + +# Per-type entries: rendered once per plate whose strategy matches. +[[render.type]] +strategies = ["enum-class"] +template = "enum.h.tmpl" +output = "mx_{snake}.h" # casing placeholders from the plate's Name + +[[render.type]] +strategies = ["enum-class"] +template = "enum.c.tmpl" +output = "mx_{snake}.c" + +[[render.type]] +strategies = ["composite-class", "value-class", "flag", "attrs-class", "flatten"] +template = "complex.h.tmpl" # or one entry per strategy; the pack chooses its granularity +output = "mx_{snake}.h" + +# Once entries: rendered once per target, against the whole Plates context. +[[render.once]] +template = "runtime.c.tmpl" +output = "mx_runtime.c" + +[[render.once]] +template = "sources.cmake.tmpl" # receives `outputs`: every path the manifest produced +output = "sources.cmake" +``` + +This mechanism absorbs, generically, several things that were Python: + +- **C's header/impl pairs**: two entries per strategy. The "one FileId, two files" wart in + `plates.md` dissolves -- file multiplicity is just manifest rows. +- **Partitioning**: per-type entries *are* `per-type` partition; a pack with only `once` entries + *is* `single` partition (the JSON Schema pack: one entry, one template, one output). `[layout]` + dies. +- **File naming**: `output` patterns with casing placeholders (`{snake}`, `{pascal}`, ...) replace + `file-prefix`/`file-convention` and plate file stems. The generator expands every pattern for + every matching plate and runs the existing case-insensitive uniqueness check over the full + expansion -- the file-collision gate survives, now over real paths, including collisions between + type outputs and `once` outputs (which retires the backend "reserved stem" guards). +- **Support files**: the runtime sources stop being Python string constants and become templates + (mostly static text; `{{plates.schema_version}}` and `{{target.vars.fn_prefix}}` are the only + tags the current runtimes need). The completeness check every manifest gets for free: every + plate must be matched by at least one entry, or none if the pack declares it renders only a + subset (a `strategies = []` is an error; an explicitly empty manifest is one too). +- **Formatting**: the gofmt pass becomes an optional, generic post-render hook -- + `[render] format = ["gofmt", "-w", "{dir}"]` -- run against the scratch render directory before + the writer's write-if-changed diff, preserving idempotence. The command is pack data; the + generator knows only "run this, fail loud if it fails or is absent." + +The writer (`gen/emit/writer.py`) is already neutral and survives unchanged: marker-gated pruning, +foreign-file safety, idempotence. + +## 7. What remains in Python, and why that is allowed + +The closed set, each definable without naming any language: `gen/xsd` (schema parsing), `gen/ir` +(lowering + Resolver), `gen/names.py` (tokenizer, casing registry, sanitizer -- string mechanics), +`gen/plates` (projection driven entirely by IR + config; `languages.py` deleted), `gen/press` +(template engine + context builder + manifest expansion), the writer, and the CLI. The litmus test +for every future line of generator Python: *could this be wrong for a language we have not heard +of?* If yes, it belongs in a pack. + +Explicitly **outside** the rule's scope: the corert harnesses (`gen/test/go/corert/`, +`gen/test/c/src/`), smoke tests, CMakeLists, go.mod. These are hand-written programs that *consume* +generated code, exactly like a downstream user; they are target code, not generator code. The rule +governs the machine, not the things the machine's output links against. + +## 8. Migration plan + +Each phase lands green (all suites pass) and pushed; phases 3-4 carry a hard parity gate: +regenerate and `git diff --exit-code` over the committed `mx/` output -- the port is proven by +byte-identical generation before the Python it replaces is deleted. + +1. **The press.** Engine + context builder + manifest expansion + format hook, with unit tests + (spec-level: sections, inversion, partial recursion, `:q`, standalone whitespace, loop + metadata, fail-loud on missing keys). No target changes. +2. **Config absorbs `languages.py`.** `[types]`/`[reserved]` become explicit in all three configs; + `variant-scope` explicit; `[vars]` introduced; `doc_lines` on plates; `PlateRef.name`/`kind` + and plate `deps` added. Generated output must not change (these are data motions). Delete + `languages.py`. +3. **Port the C pack.** Translate `gen/emit/c/*.py` into `gen/test/c/templates/` + manifest. + Byte-parity gate, corert green, valgrind clean. Delete `gen/emit/c/`. +4. **Port the Go pack.** Same, with the format hook carrying gofmt. Byte-parity gate, corert + green. Delete `gen/emit/go/`, the `BACKENDS` registry, and `[target] language`. +5. **Prove the rule.** Add the JSON Schema pack (`gen/schema/`: config.toml + one template) -- the + neutral target the plates design used as its forcing function, now actually built. Its + round-trip check: validate a corpus sample against the emitted schema. Add the CI assertion + that the pack's commit touches no `*.py`, and a structural test that `gen/` imports cleanly + with no module or table naming a language. +6. **Docs.** Update `plates.md` section 11 (supersession note), `gen/README.md` (pack anatomy, + press spec), `AGENTS.md` (the cardinal rule, stated as such). + +Then, and only then, the C++ target begins -- as a pack, written without touching Python, which is +the entire point. + +## 9. Alternatives considered and rejected + +- **Per-target Python plugins** (each pack ships a `backend.py` the generator loads dynamically). + Satisfies the letter of the rule -- no edits to the generator's files -- and would be the + cheapest migration (move the existing modules into the packs). Rejected on the spirit: the + language knowledge would still be Python programs, just relocated; the C++ backend would again + be two thousand lines of imperative emission; and nothing would force decisions into the plates, + because a plugin can compute anything. The review's instruction was that the bespoke backends + "should not exist," not that they should move. +- **Jinja2** (or any full template engine, vendored or as a dependency). Mature and expressive -- + and expressive is the problem: filters, macros, arbitrary expressions, and `set` would let the + Go backend be reconstituted *inside* template files, hiding naming logic where no gate can see + it. It also breaks the generator's stdlib-only property. The press's poverty is load-bearing. +- **AST-based emitters** (build a language-neutral syntax tree, print per language). A second + language-shaped abstraction to design, with the per-language printers landing right back in + Python. Wrong direction entirely. +- **Keeping `languages.py` as "just data."** It is data, but data keyed by language name inside + the generator is still the generator knowing languages; every new target edits it. Config is the + same data in the right place. + +## 10. Risks and open questions + +- **Template debuggability.** Generated-code bugs become template bugs; the press must report + `template:line` in every error, and a `python3 -m gen render --config C --type note` debugging + command (render one plate through its matching templates to stdout) should land with phase 1. +- **The `:q` escape compromise** (section 5). One escape family in the engine. Revisit trigger: a + target whose string literals are not C-family (YAML? Python with different conventions would + still parse `\uXXXX`, but e.g. a target needing single quotes would not). +- **Parity discipline.** The ports in phases 3-4 will be tedious precisely because parity is + byte-exact; resist "improving" generated output mid-port. Cleanups come after deletion, as + ordinary template edits. +- **Synthetic-name gating.** `[reserved] members` / `type-suffixes` (section 4) covers the known + cases (Go's `Children` field, `Child` struct suffix). The residual risk -- a pack composing an + identifier shape the gate cannot model -- is bounded by the compiler catching it in committed + output. +- **Cross-pack template sharing.** Go and C templates will rhyme (the same walk, two grammars). + No sharing mechanism in v1: a pack is self-contained, and duplication across packs is the + acceptable cost of packs being independently ownable. Revisit only if a third *code* pack makes + the rhyme painful -- and note the C++ pack is likely to diverge more than it rhymes (sum types, + references, exceptions). +- **The inherit-chain guard.** The Go backend's loud rejection of derivation chains with children + in multiple members was backend Python; as a pack, that knowledge has no generic home. Position: + drop it. The plates dump makes chain shapes visible, no MusicXML schema has the shape, and the + committed-output compile is the backstop. If it ever bites, the neutral fact ("N chain members + carry element members") can become plate data a template renders into a `#error`. +- **Engine creep.** The constitution in section 5 is the contract. The review question for any + proposed press feature: "does this let a template make a decision the plates should own?" If + yes, the answer is no. From 27ba5ee6b62d17bc91add19170473478f17ad3de Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 11 Jun 2026 05:57:21 +0000 Subject: [PATCH 27/45] design: record the template-engine decision honestly (Mustache the language) Answers the review question 'why hand-roll instead of using a library?' properly -- the original draft argued only against Jinja2 and never weighed the real alternative, an existing Mustache implementation. The load-bearing commitment is now stated as Mustache-the-LANGUAGE: the press implements the published spec's interpolation/sections/inverted/ partials core with exactly three documented deviations (missing keys are render errors with template:line -- the spec's silent empty output is disqualifying for a generator; no HTML escaping; no lambdas), and is tested against the official Mustache spec suite, buying the spec authors' edge-case coverage (especially the whitespace rules) without their code. The engine extensions the draft had invented (@first/@last loop metadata, the :q quote modifier) move out of the engine into the context builder as injected fields (is_first/is_last/index0, wire_q companions), so template syntax stays pure Mustache and the engine is swappable behind it. Section 9 now weighs chevron/pystache as the close call it is (spec-mandated silent missing keys, HTML escaping, weak diagnostics, unmaintained state, vs the repo's no-Python-deps precedent) and pre-commits the reversal trigger: if the press exceeds ~600 lines or cannot pass the spec suite in phase 1, vendor chevron and patch strictness/escaping/diagnostics -- zero template changes either way. --- docs/ai/design/generator-agnosticism.md | 113 ++++++++++++++++-------- 1 file changed, 76 insertions(+), 37 deletions(-) diff --git a/docs/ai/design/generator-agnosticism.md b/docs/ai/design/generator-agnosticism.md index 33746e440..e09ed3637 100644 --- a/docs/ai/design/generator-agnosticism.md +++ b/docs/ai/design/generator-agnosticism.md @@ -107,35 +107,46 @@ naming a language, it is a var, not a key.* (partition, file-prefix, file-convention -- subsumed by the manifest, section 6), and `languages.py` with all its tables. -## 5. The press: a deliberately minimal template engine +## 5. The press: a Mustache engine with three documented deviations -`gen/press/` renders template files against a context built from the plates. It is stdlib-only -(~300-400 lines plus tests) and mustache-class on purpose. The constraint is the feature: **if a -template cannot express something, the plates must carry it** -- the engine's poverty is what -keeps decisions in the projection, where they are dumpable, diffable, and collision-gated. +`gen/press/` renders template files against a context built from the plates. **The template +language is Mustache** -- the interpolation/sections/inverted-sections/partials core of the +published spec, nothing invented -- so the load-bearing commitment is to a frozen, logic-less +*language*, and the engine behind it is swappable (section 9 records why we implement it ourselves +and the trigger for reversing that). Mustache's poverty is the feature: **if a template cannot +express something, the plates must carry it** -- which keeps decisions in the projection, where +they are dumpable, diffable, and collision-gated. -The complete feature set, derived by walking every construct the current Python backends emit: +The implemented subset, derived by walking every construct the current Python backends emit: - **Variables**: `{{ident}}`, dotted paths `{{name.snake}}`, `{{type_ref.ident}}`, - `{{target.vars.prefix}}`. Missing keys are a render error (fail loud), not empty output. + `{{target.vars.prefix}}`. - **Sections**: `{{#members}}...{{/members}}` iterates lists (the cursor becomes the context) and - gates on truthiness for scalars/objects; `{{^x}}...{{/x}}` inverts. Loop metadata `{{@first}}`, - `{{@last}}`, `{{@index}}` is provided by the engine (it is presentation mechanics, not data) -- - this is what expresses `if`/`else if` chains and separator joins. -- **Partials**: `{{> member-parse}}`, resolved within the pack's `templates/` directory, recursion + gates on truthiness for scalars/objects; `{{^x}}...{{/x}}` inverts. +- **Partials**: `{{> member-parse}}`, resolved within the pack's `templates/` directory, with + spec-conformant call-site indentation (essential for readable generated code) and recursion permitted with a depth limit (a schema-shaped target walking `content` trees needs it). -- **One modifier**: `{{wire:q}}` renders a double-quoted, backslash-escaped string literal using - the JSON escape repertoire with non-ASCII as `\uXXXX` -- a subset valid verbatim in C, C++, Go, - Java, JavaScript, and Rust literals. This is the design's one acknowledged compromise: string - quoting is presentation that genuinely varies by language family, and encoding one universal - family in the engine beats either per-pack escape tables in config or hand-escaping in - templates. A future target outside that family is the trigger to revisit (section 10). -- **Whitespace discipline**: standalone section/partial lines collapse (the mustache standalone - rule), so templates can be indented readably without leaking blank lines. - -And, as a constitution, what the engine will **never** grow: expressions, comparisons, -arithmetic, filters, string manipulation, casing functions, variable assignment, or template -inheritance. In particular there is no equality test; dispatch happens two other ways: +- **Whitespace discipline**: the spec's standalone-line rules, so templates can be indented + readably without leaking blank lines. + +Three deliberate deviations from spec semantics, each because code generation is not HTML: + +1. **Missing keys are a render error**, with `template:line` in the message. The spec mandates + silent empty output -- the worst possible failure mode for a generator (a typo'd `{{indent}}` + emits nothing, and a pack with no compiler behind it, like JSON Schema, never finds out). This + project's ethos is fail-loud; the engine follows it. +2. **No HTML escaping**: `{{x}}` interpolates verbatim (the spec's `{{{x}}}` everywhere would be + noise; there is no HTML here to protect). +3. **No lambdas** (the spec's one escape hatch into logic). Closed. + +Conformance to everything else is *tested*: the press runs against the official Mustache spec test +suite (the published YAML cases for interpolation, sections, inverted sections, and partials), +asserting agreement everywhere except the three deviations above -- the spec authors' edge-case +coverage, especially the fiddly whitespace rules, without their code. + +Everything the engine does **not** do -- expressions, comparisons, arithmetic, filters, string +manipulation, casing, assignment -- stays not done; that is Mustache's constitution, not ours to +amend. In particular there is no equality test; dispatch happens three other ways: 1. **By manifest**: each template entry declares which plate strategies it renders (section 6), so per-shape dispatch never appears inside a template -- restoring "one template per shape" as @@ -145,6 +156,15 @@ inheritance. In particular there is no equality test; dispatch happens two other "vector"` yields `is_vector`; `category: "primitive"` yields `is_primitive` -- and exposes the member list pre-split (`attributes`, `elements`, `value`) using the filters the plates already define. Templates branch with plain sections: `{{#type_ref.is_complex}}...{{/type_ref.is_complex}}`. +3. **By injected context, not engine extensions**: loop metadata arrives as fields the context + builder adds to every list item (`is_first`, `is_last`, `index0`) -- this is what expresses + `if`/`else if` chains and separator joins -- and every wire-string leaf gets a quoted companion + (`wire` -> `wire_q`): a double-quoted, backslash-escaped literal using the JSON repertoire with + non-ASCII as `\uXXXX`, a subset valid verbatim in C, C++, Go, Java, JavaScript, and Rust. + Keeping both OUT of the engine keeps the template syntax pure Mustache (so the engine stays + swappable) and keeps the one acknowledged compromise -- that quoted-literal escaping encodes a + language *family* -- in the neutral context layer, where a future non-C-family target would + extend it (section 10). Two small, neutral additions to the plates feed this (the only model changes the redesign needs): @@ -163,7 +183,7 @@ Worked example -- today's `gen/emit/c/complexes.py` attribute loop, as template for (xmlAttrPtr a = el->properties; a; a = a->next) { {{> attr-name}} {{#attributes}} - {{#@first}}if{{/@first}}{{^@first}}}} else if{{/@first}} (strcmp(aname, {{name.wire:q}}) == 0) { + {{#is_first}}if{{/is_first}}{{^is_first}}}} else if{{/is_first}} (strcmp(aname, {{name.wire_q}}) == 0) { m->has_{{ident}} = true; m->{{ident}} = {{> attr-parse-expr}}; {{/attributes}} @@ -255,9 +275,11 @@ Each phase lands green (all suites pass) and pushed; phases 3-4 carry a hard par regenerate and `git diff --exit-code` over the committed `mx/` output -- the port is proven by byte-identical generation before the Python it replaces is deleted. -1. **The press.** Engine + context builder + manifest expansion + format hook, with unit tests - (spec-level: sections, inversion, partial recursion, `:q`, standalone whitespace, loop - metadata, fail-loud on missing keys). No target changes. +1. **The press.** Engine + context builder + manifest expansion + format hook. Tests: the + official Mustache spec suite for the implemented subset (minus the three documented + deviations), plus unit tests for the deviations themselves (fail-loud missing keys, identity + interpolation, no lambdas) and for the context builder's injections (discriminant expansion, + loop metadata, `_q` companions). No target changes. 2. **Config absorbs `languages.py`.** `[types]`/`[reserved]` become explicit in all three configs; `variant-scope` explicit; `[vars]` introduced; `doc_lines` on plates; `PlateRef.name`/`kind` and plate `deps` added. Generated output must not change (these are data motions). Delete @@ -286,10 +308,25 @@ the entire point. be two thousand lines of imperative emission; and nothing would force decisions into the plates, because a plugin can compute anything. The review's instruction was that the bespoke backends "should not exist," not that they should move. -- **Jinja2** (or any full template engine, vendored or as a dependency). Mature and expressive -- - and expressive is the problem: filters, macros, arbitrary expressions, and `set` would let the - Go backend be reconstituted *inside* template files, hiding naming logic where no gate can see - it. It also breaks the generator's stdlib-only property. The press's poverty is load-bearing. +- **Jinja2** (or any expressive template engine, vendored or as a dependency). Mature, excellent + diagnostics, configurable strictness -- and expressive is the problem: filters, macros, + arbitrary expressions, and `set` would let the Go backend be reconstituted *inside* template + files, hiding naming logic where no structural gate can see it; only review discipline would + stand between the packs and that, and this redesign exists because structure beats discipline. + It also adds a pip/vendored dependency tree to a deliberately dependency-free Python side. +- **An existing Mustache library** (chevron, pystache) -- the serious alternative, since it shares + the language's logic-less constitution and would spare us the parser. Weighed and declined, as a + close call, on four counts: (1) the spec mandates *silent empty output for missing keys*, which + is disqualifying for a generator and not configurable in chevron (pystache has a strict option + but is effectively unmaintained; both last released years ago); (2) spec HTML-escaping and weak + error locations mean we would patch a vendored copy in three places and own the result anyway -- + owning ~400 written-and-spec-tested lines beats owning ~500 vendored lines plus patches by a + thin margin; (3) the repo's Python side has a deliberate no-dependencies precedent (the + hand-written XSD parser, tomllib); (4) conformance risk -- the real argument FOR a library -- is + neutralized by running the official Mustache spec test suite against the press (section 5). + Because template syntax is pure Mustache, this decision is cheaply reversible: **if during phase + 1 the press exceeds ~600 lines or cannot pass the spec suite, the pre-committed fallback is to + vendor chevron and patch strictness/escaping/diagnostics** -- with zero template changes. - **AST-based emitters** (build a language-neutral syntax tree, print per language). A second language-shaped abstraction to design, with the per-language printers landing right back in Python. Wrong direction entirely. @@ -302,9 +339,10 @@ the entire point. - **Template debuggability.** Generated-code bugs become template bugs; the press must report `template:line` in every error, and a `python3 -m gen render --config C --type note` debugging command (render one plate through its matching templates to stdout) should land with phase 1. -- **The `:q` escape compromise** (section 5). One escape family in the engine. Revisit trigger: a - target whose string literals are not C-family (YAML? Python with different conventions would - still parse `\uXXXX`, but e.g. a target needing single quotes would not). +- **The quoted-literal compromise** (section 5). The `_q` companions encode one escape family + (C/C++/Go/Java/JS/Rust-compatible) in the neutral context layer. Revisit trigger: a target whose + string literals are outside that family (e.g. single-quote-only syntaxes); the extension point + is the context builder, not the engine. - **Parity discipline.** The ports in phases 3-4 will be tedious precisely because parity is byte-exact; resist "improving" generated output mid-port. Cleanups come after deletion, as ordinary template edits. @@ -322,6 +360,7 @@ the entire point. drop it. The plates dump makes chain shapes visible, no MusicXML schema has the shape, and the committed-output compile is the backstop. If it ever bites, the neutral fact ("N chain members carry element members") can become plate data a template renders into a `#error`. -- **Engine creep.** The constitution in section 5 is the contract. The review question for any - proposed press feature: "does this let a template make a decision the plates should own?" If - yes, the answer is no. +- **Engine creep.** The contract is "the template language is Mustache": the press neither adds + syntax nor restores the spec's lambdas, and the spec test suite pins it there. The review + question for any proposed press or context-builder feature: "does this let a template make a + decision the plates should own?" If yes, the answer is no. From 9cf36902b78263105183d6f4f6ae5c92daa80280 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 11 Jun 2026 06:03:10 +0000 Subject: [PATCH 28/45] design: drop the 'pack' coinage; the vocabulary is 'target' and 'templates' A target is a directory containing config.toml and templates/ -- the term 'target' already exists throughout the codebase and needs no companion word. The template collection is simply the target's templates. --- AGENTS.md | 2 +- docs/ai/design/generator-agnosticism.md | 74 ++++++++++++------------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 8c60ffc3d..a91d38ed3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -23,7 +23,7 @@ mx/ CMakeLists.txt <- C++ project: ezxml library + corert test harness data/ <- MusicXML test corpus (~1,347 files, see data/README.md) docs/ai/design/ <- design docs (plates.md: the Plates; generator-agnosticism.md: - the cardinal rule and the pack/press redesign) + the cardinal rule and the targets/press redesign) src/private/ <- C++ source mx/ezxml/ <- vendored pugixml-backed XML layer mx/core/ <- generated C++ typed model diff --git a/docs/ai/design/generator-agnosticism.md b/docs/ai/design/generator-agnosticism.md index e09ed3637..477ccb70a 100644 --- a/docs/ai/design/generator-agnosticism.md +++ b/docs/ai/design/generator-agnosticism.md @@ -42,27 +42,27 @@ implementation answered "a Python module per language." Every subsequent fix imp ## 3. The redesign in one paragraph -A target becomes a **pack**: a directory containing `config.toml` and a `templates/` directory, -and nothing else the generator needs. Everything `languages.py` held becomes required config data. +A target becomes self-describing: a directory containing `config.toml` and a `templates/` +directory, and nothing else the generator needs. Everything `languages.py` held becomes required config data. Everything `gen/emit//` held becomes template files in a deliberately minimal, logic-less template language, rendered by one generic engine (the **press**, completing the plates metaphor: plates carry every decision; the press inks and prints them). A render **manifest** in the config declares which template renders which plate shapes into which output paths, so file layout -- including C's header/impl pairs -- is pack data, not Python. The generator's Python is then a closed set: parse, lower, project, render, write. The proof obligation is concrete: port Go and C -to packs with byte-identical generated output, delete `gen/emit/go`, `gen/emit/c`, and +to targets with byte-identical generated output, delete `gen/emit/go`, `gen/emit/c`, and `languages.py`, then add a third target (the JSON Schema emitter that has been this project's -forcing function since the plates design) as a pure pack, and let CI assert that change touched no -Python. +forcing function since the plates design) as a pure target directory, and let CI assert that +change touched no Python. ``` -gen/test/c/ <- a target pack +gen/test/c/ <- a target: config + templates, nothing else the generator needs config.toml <- inputs, projection settings, vars, render manifest templates/ enum.h.tmpl enum.c.tmpl <- one template per shape (the original design principle, composite.h.tmpl ... now literally one FILE per shape) runtime.h.tmpl ... <- support files: a template with no tags is a static file - member-parse.tmpl ... <- partials shared by this pack's templates + member-parse.tmpl ... <- partials shared by this target's templates src/ <- the hand-written corert harness (target code, not generator) mx/ <- generated output (committed) ``` @@ -86,11 +86,11 @@ neutral: language in Python. Becomes explicit config. - `[target] inheritance = true | false`: selects the derived strategy. Already neutral. - `[types]`: the primitive -> spelling map, today defaulted per language. Becomes **required** for - any pack whose templates emit typed code (a pack that omits it gets primitive names passed + any pack whose templates emit typed code (a target that omits it gets primitive names passed through, which is what a neutral target wants). -- `[reserved] words`: today extends per-language defaults. Becomes the **whole** list; packs own - their keyword lists. Two small additions let packs protect their template-synthesized names - generically: `members = [...]` (member identifiers the pack's templates reserve, e.g. Go's +- `[reserved] words`: today extends per-language defaults. Becomes the **whole** list; targets own + their keyword lists. Two small additions let targets protect their template-synthesized names + generically: `members = [...]` (member identifiers the target's templates reserve, e.g. Go's `Children`) and `type-suffixes = [...]` (compositions like `Child` that the templates append to type identifiers), both fed to the existing collision gate. - `[docs] wrap`: the plates pre-wrap doc text into lines (`doc_lines`); comment *syntax* moves @@ -98,8 +98,8 @@ neutral: **Everything else is freeform.** A `[vars]` table of string key-values passes through to templates verbatim (`{{target.vars.namespace}}`, `{{target.vars.package}}`, `{{target.vars.anything}}`). -`namespace` stops being generator schema; it becomes a variable that the Go pack's templates -happen to consume as a package name and the C++ pack's as a namespace. `target.foo = "bar"` is +`namespace` stops being generator schema; it becomes a variable that the Go target's templates +happen to consume as a targetage name and the C++ target's as a namespace. `target.foo = "bar"` is exactly as legal as either. The litmus test for any future key: *if you cannot define it without naming a language, it is a var, not a key.* @@ -123,7 +123,7 @@ The implemented subset, derived by walking every construct the current Python ba `{{target.vars.prefix}}`. - **Sections**: `{{#members}}...{{/members}}` iterates lists (the cursor becomes the context) and gates on truthiness for scalars/objects; `{{^x}}...{{/x}}` inverts. -- **Partials**: `{{> member-parse}}`, resolved within the pack's `templates/` directory, with +- **Partials**: `{{> member-parse}}`, resolved within the target's `templates/` directory, with spec-conformant call-site indentation (essential for readable generated code) and recursion permitted with a depth limit (a schema-shaped target walking `content` trees needs it). - **Whitespace discipline**: the spec's standalone-line rules, so templates can be indented @@ -133,7 +133,7 @@ Three deliberate deviations from spec semantics, each because code generation is 1. **Missing keys are a render error**, with `template:line` in the message. The spec mandates silent empty output -- the worst possible failure mode for a generator (a typo'd `{{indent}}` - emits nothing, and a pack with no compiler behind it, like JSON Schema, never finds out). This + emits nothing, and a target with no compiler behind it, like JSON Schema, never finds out). This project's ethos is fail-loud; the engine follows it. 2. **No HTML escaping**: `{{x}}` interpolates verbatim (the spec's `{{{x}}}` everywhere would be noise; there is no HTML here to protect). @@ -217,7 +217,7 @@ output = "mx_{snake}.c" [[render.type]] strategies = ["composite-class", "value-class", "flag", "attrs-class", "flatten"] -template = "complex.h.tmpl" # or one entry per strategy; the pack chooses its granularity +template = "complex.h.tmpl" # or one entry per strategy; the target chooses its granularity output = "mx_{snake}.h" # Once entries: rendered once per target, against the whole Plates context. @@ -234,8 +234,8 @@ This mechanism absorbs, generically, several things that were Python: - **C's header/impl pairs**: two entries per strategy. The "one FileId, two files" wart in `plates.md` dissolves -- file multiplicity is just manifest rows. -- **Partitioning**: per-type entries *are* `per-type` partition; a pack with only `once` entries - *is* `single` partition (the JSON Schema pack: one entry, one template, one output). `[layout]` +- **Partitioning**: per-type entries *are* `per-type` partition; a target with only `once` entries + *is* `single` partition (the JSON Schema target: one entry, one template, one output). `[layout]` dies. - **File naming**: `output` patterns with casing placeholders (`{snake}`, `{pascal}`, ...) replace `file-prefix`/`file-convention` and plate file stems. The generator expands every pattern for @@ -245,11 +245,11 @@ This mechanism absorbs, generically, several things that were Python: - **Support files**: the runtime sources stop being Python string constants and become templates (mostly static text; `{{plates.schema_version}}` and `{{target.vars.fn_prefix}}` are the only tags the current runtimes need). The completeness check every manifest gets for free: every - plate must be matched by at least one entry, or none if the pack declares it renders only a + plate must be matched by at least one entry, or none if the target declares it renders only a subset (a `strategies = []` is an error; an explicitly empty manifest is one too). - **Formatting**: the gofmt pass becomes an optional, generic post-render hook -- `[render] format = ["gofmt", "-w", "{dir}"]` -- run against the scratch render directory before - the writer's write-if-changed diff, preserving idempotence. The command is pack data; the + the writer's write-if-changed diff, preserving idempotence. The command is target data; the generator knows only "run this, fail loud if it fails or is absent." The writer (`gen/emit/writer.py`) is already neutral and survives unchanged: marker-gated pruning, @@ -262,7 +262,7 @@ The closed set, each definable without naming any language: `gen/xsd` (schema pa `gen/plates` (projection driven entirely by IR + config; `languages.py` deleted), `gen/press` (template engine + context builder + manifest expansion), the writer, and the CLI. The litmus test for every future line of generator Python: *could this be wrong for a language we have not heard -of?* If yes, it belongs in a pack. +of?* If yes, it belongs in a target's directory. Explicitly **outside** the rule's scope: the corert harnesses (`gen/test/go/corert/`, `gen/test/c/src/`), smoke tests, CMakeLists, go.mod. These are hand-written programs that *consume* @@ -284,26 +284,26 @@ byte-identical generation before the Python it replaces is deleted. `variant-scope` explicit; `[vars]` introduced; `doc_lines` on plates; `PlateRef.name`/`kind` and plate `deps` added. Generated output must not change (these are data motions). Delete `languages.py`. -3. **Port the C pack.** Translate `gen/emit/c/*.py` into `gen/test/c/templates/` + manifest. +3. **Port the C target.** Translate `gen/emit/c/*.py` into `gen/test/c/templates/` + manifest. Byte-parity gate, corert green, valgrind clean. Delete `gen/emit/c/`. -4. **Port the Go pack.** Same, with the format hook carrying gofmt. Byte-parity gate, corert +4. **Port the Go target.** Same, with the format hook carrying gofmt. Byte-parity gate, corert green. Delete `gen/emit/go/`, the `BACKENDS` registry, and `[target] language`. -5. **Prove the rule.** Add the JSON Schema pack (`gen/schema/`: config.toml + one template) -- the +5. **Prove the rule.** Add the JSON Schema target (`gen/schema/`: config.toml + one template) -- the neutral target the plates design used as its forcing function, now actually built. Its round-trip check: validate a corpus sample against the emitted schema. Add the CI assertion - that the pack's commit touches no `*.py`, and a structural test that `gen/` imports cleanly + that the target's commit touches no `*.py`, and a structural test that `gen/` imports cleanly with no module or table naming a language. -6. **Docs.** Update `plates.md` section 11 (supersession note), `gen/README.md` (pack anatomy, +6. **Docs.** Update `plates.md` section 11 (supersession note), `gen/README.md` (target anatomy, press spec), `AGENTS.md` (the cardinal rule, stated as such). -Then, and only then, the C++ target begins -- as a pack, written without touching Python, which is +Then, and only then, the C++ target begins -- as a target directory, written without touching Python, which is the entire point. ## 9. Alternatives considered and rejected -- **Per-target Python plugins** (each pack ships a `backend.py` the generator loads dynamically). +- **Per-target Python plugins** (each target ships a `backend.py` the generator loads dynamically). Satisfies the letter of the rule -- no edits to the generator's files -- and would be the - cheapest migration (move the existing modules into the packs). Rejected on the spirit: the + cheapest migration (move the existing modules into the targets). Rejected on the spirit: the language knowledge would still be Python programs, just relocated; the C++ backend would again be two thousand lines of imperative emission; and nothing would force decisions into the plates, because a plugin can compute anything. The review's instruction was that the bespoke backends @@ -312,7 +312,7 @@ the entire point. diagnostics, configurable strictness -- and expressive is the problem: filters, macros, arbitrary expressions, and `set` would let the Go backend be reconstituted *inside* template files, hiding naming logic where no structural gate can see it; only review discipline would - stand between the packs and that, and this redesign exists because structure beats discipline. + stand between the targets and that, and this redesign exists because structure beats discipline. It also adds a pip/vendored dependency tree to a deliberately dependency-free Python side. - **An existing Mustache library** (chevron, pystache) -- the serious alternative, since it shares the language's logic-less constitution and would spare us the parser. Weighed and declined, as a @@ -347,16 +347,16 @@ the entire point. byte-exact; resist "improving" generated output mid-port. Cleanups come after deletion, as ordinary template edits. - **Synthetic-name gating.** `[reserved] members` / `type-suffixes` (section 4) covers the known - cases (Go's `Children` field, `Child` struct suffix). The residual risk -- a pack composing an - identifier shape the gate cannot model -- is bounded by the compiler catching it in committed + cases (Go's `Children` field, `Child` struct suffix). The residual risk -- a target's templates + composing an identifier shape the gate cannot model -- is bounded by the compiler catching it in committed output. -- **Cross-pack template sharing.** Go and C templates will rhyme (the same walk, two grammars). - No sharing mechanism in v1: a pack is self-contained, and duplication across packs is the - acceptable cost of packs being independently ownable. Revisit only if a third *code* pack makes - the rhyme painful -- and note the C++ pack is likely to diverge more than it rhymes (sum types, +- **Cross-target template sharing.** Go and C templates will rhyme (the same walk, two grammars). + No sharing mechanism in v1: a target is self-contained, and duplication across targets is the + acceptable cost of targets being independently ownable. Revisit only if a third *code* target + makes the rhyme painful -- and note the C++ target is likely to diverge more than it rhymes (sum types, references, exceptions). - **The inherit-chain guard.** The Go backend's loud rejection of derivation chains with children - in multiple members was backend Python; as a pack, that knowledge has no generic home. Position: + in multiple members was backend Python; once the backends are templates, that knowledge has no generic home. Position: drop it. The plates dump makes chain shapes visible, no MusicXML schema has the shape, and the committed-output compile is the backstop. If it ever bites, the neutral fact ("N chain members carry element members") can become plate data a template renders into a `#error`. From 1787960a86fd7dd797926b22b3e5ec6372b33917 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 11 Jun 2026 06:07:40 +0000 Subject: [PATCH 29/45] gen/press: the Mustache engine, pinned to the official spec suite Phase 1 of the generator-agnosticism redesign (commit series: engine -> data motions -> context/manifest -> C port -> Go port -> proof target). gen/press/engine.py implements the Mustache core -- interpolation (incl. dotted names and implicit iterators), sections, inverted sections, partials with call-site indentation, comments, set-delimiters, and the spec's standalone-line whitespace rules -- with the three documented deviations for code generation: missing keys are render errors carrying template:line (present-but-None renders empty and is falsey; only absence errors), no HTML escaping ({{{x}}}/{{&x}} are accepted synonyms), and no lambdas (a callable in the context is an error). Conformance is tested, not asserted: the five core modules of the official Mustache spec test suite (mustache/spec, MIT) are vendored under gen/tests/mustache_spec/ and all 122 cases pass with zero skips -- the engine's strict/escape constructor knobs let the suite run under the spec's own semantics while the production pipeline uses the defaults. Deviation and robustness tests (error locations, recursion depth limit, callable partial loaders, context-stack fallthrough) cover the rest. Also sweeps the last 'pack' stragglers out of the design doc. --- docs/ai/design/generator-agnosticism.md | 8 +- gen/press/__init__.py | 5 + gen/press/engine.py | 395 +++++++++++++++++++ gen/tests/mustache_spec/README.md | 9 + gen/tests/mustache_spec/comments.json | 106 ++++++ gen/tests/mustache_spec/interpolation.json | 422 ++++++++++++++++++++ gen/tests/mustache_spec/inverted.json | 227 +++++++++++ gen/tests/mustache_spec/partials.json | 153 ++++++++ gen/tests/mustache_spec/sections.json | 423 +++++++++++++++++++++ gen/tests/test_press.py | 130 +++++++ 10 files changed, 1874 insertions(+), 4 deletions(-) create mode 100644 gen/press/__init__.py create mode 100644 gen/press/engine.py create mode 100644 gen/tests/mustache_spec/README.md create mode 100644 gen/tests/mustache_spec/comments.json create mode 100644 gen/tests/mustache_spec/interpolation.json create mode 100644 gen/tests/mustache_spec/inverted.json create mode 100644 gen/tests/mustache_spec/partials.json create mode 100644 gen/tests/mustache_spec/sections.json create mode 100644 gen/tests/test_press.py diff --git a/docs/ai/design/generator-agnosticism.md b/docs/ai/design/generator-agnosticism.md index 477ccb70a..e9b5368a5 100644 --- a/docs/ai/design/generator-agnosticism.md +++ b/docs/ai/design/generator-agnosticism.md @@ -48,7 +48,7 @@ Everything `gen/emit//` held becomes template files in a deliberately mini template language, rendered by one generic engine (the **press**, completing the plates metaphor: plates carry every decision; the press inks and prints them). A render **manifest** in the config declares which template renders which plate shapes into which output paths, so file layout -- -including C's header/impl pairs -- is pack data, not Python. The generator's Python is then a +including C's header/impl pairs -- is target data, not Python. The generator's Python is then a closed set: parse, lower, project, render, write. The proof obligation is concrete: port Go and C to targets with byte-identical generated output, delete `gen/emit/go`, `gen/emit/c`, and `languages.py`, then add a third target (the JSON Schema emitter that has been this project's @@ -86,7 +86,7 @@ neutral: language in Python. Becomes explicit config. - `[target] inheritance = true | false`: selects the derived strategy. Already neutral. - `[types]`: the primitive -> spelling map, today defaulted per language. Becomes **required** for - any pack whose templates emit typed code (a target that omits it gets primitive names passed + any target whose templates emit typed code (a target that omits it gets primitive names passed through, which is what a neutral target wants). - `[reserved] words`: today extends per-language defaults. Becomes the **whole** list; targets own their keyword lists. Two small additions let targets protect their template-synthesized names @@ -192,11 +192,11 @@ Worked example -- today's `gen/emit/c/complexes.py` attribute loop, as template ... ``` -Everything language-shaped (C's `strcmp`, `->`, `has_` prefixes, the error idiom) is pack content; +Everything language-shaped (C's `strcmp`, `->`, `has_` prefixes, the error idiom) is target content; everything decided (idents, wire names, which members are attributes) is plate data. The press contributes iteration and the `@first` chain mechanics, nothing more. -## 6. The render manifest: file layout as pack data +## 6. The render manifest: file layout as target data `config.toml` declares what gets rendered where. Two entry kinds: diff --git a/gen/press/__init__.py b/gen/press/__init__.py new file mode 100644 index 000000000..c762fe0fa --- /dev/null +++ b/gen/press/__init__.py @@ -0,0 +1,5 @@ +"""The press: renders the targets' templates. See gen.press.engine.""" + +from gen.press.engine import Press, PressError + +__all__ = ["Press", "PressError"] diff --git a/gen/press/engine.py b/gen/press/engine.py new file mode 100644 index 000000000..369493900 --- /dev/null +++ b/gen/press/engine.py @@ -0,0 +1,395 @@ +"""The press: a Mustache template engine. + +The press renders the targets' templates. The template language is Mustache +-- the published spec's interpolation, sections, inverted sections, partials, +comments, and set-delimiter core, with spec whitespace semantics (standalone +lines, partial call-site indentation) -- and three deliberate deviations, +because code generation is not HTML (design: generator-agnosticism.md): + + 1. Missing keys are render errors (template:line in the message). The spec + mandates silent empty output, which is the worst failure mode a code + generator can have. A key that is PRESENT with a None/empty value + renders empty and is falsey in sections; only absence is an error. + 2. No HTML escaping: `{{x}}` interpolates verbatim ({{{x}}} and {{&x}} are + accepted synonyms). + 3. No lambdas (the spec's escape hatch into logic). A callable in the + context is an error. + +Conformance to everything else is tested against the vendored official spec +suite (gen/tests/mustache_spec/); the constructor's `strict` and `escape` +parameters exist so that suite can exercise the spec's own semantics -- the +production pipeline never passes them. + +What the engine will never grow: expressions, comparisons, arithmetic, +filters, string manipulation, casing, assignment, or new syntax. Dispatch +data (booleans, loop metadata, quoted literals) is the context builder's +job; if a template cannot express something, the plates must carry it. +""" + +from __future__ import annotations + +from collections.abc import Callable, Mapping +from dataclasses import dataclass, field + + +class PressError(Exception): + """A template problem, always reported as `template:line: message`.""" + + def __init__(self, name: str, line: int, message: str): + self.template = name + self.line = line + super().__init__(f"{name}:{line}: {message}") + + +# --------------------------------------------------------------------------- # +# Parse tree +# --------------------------------------------------------------------------- # + + +@dataclass +class _Text: + text: str + + +@dataclass +class _Var: + path: tuple[str, ...] + raw: bool # {{{x}}} / {{&x}}: spec semantics; identical here by default + line: int + + +@dataclass +class _Section: + path: tuple[str, ...] + inverted: bool + line: int + children: list = field(default_factory=list) + + +@dataclass +class _Partial: + name: str + indent: str + line: int + + +# --------------------------------------------------------------------------- # +# Tokenizer (with set-delimiter support and the spec's standalone-line rules) +# --------------------------------------------------------------------------- # + +_STANDALONE_KINDS = {"open", "inv", "close", "comment", "delim", "partial"} + + +def _tokenize(template: str, name: str) -> list: + """Produce ('text', str) and ('tag', kind, key, line, indent) tokens. + kind: var | raw | open | inv | close | partial | comment | delim.""" + tokens: list = [] + odelim, cdelim = "{{", "}}" + pos = 0 + line = 1 + while pos < len(template): + start = template.find(odelim, pos) + if start < 0: + tokens.append(("text", template[pos:])) + break + if start > pos: + text = template[pos:start] + tokens.append(("text", text)) + line += text.count("\n") + + # Triple mustache is only meaningful with the default delimiters. + if odelim == "{{" and template.startswith("{{{", start): + end = template.find("}}}", start + 3) + if end < 0: + raise PressError(name, line, "unclosed '{{{' tag") + key = template[start + 3 : end].strip() + tokens.append(("tag", "raw", key, line, "")) + pos = end + 3 + continue + + end = template.find(cdelim, start + len(odelim)) + if end < 0: + raise PressError(name, line, f"unclosed '{odelim}' tag") + content = template[start + len(odelim) : end] + pos = end + len(cdelim) + line += content.count("\n") + + sigil = content[:1] + if sigil == "#": + tokens.append(("tag", "open", content[1:].strip(), line, "")) + elif sigil == "^": + tokens.append(("tag", "inv", content[1:].strip(), line, "")) + elif sigil == "/": + tokens.append(("tag", "close", content[1:].strip(), line, "")) + elif sigil == ">": + tokens.append(("tag", "partial", content[1:].strip(), line, "")) + elif sigil == "!": + tokens.append(("tag", "comment", "", line, "")) + elif sigil == "&": + tokens.append(("tag", "raw", content[1:].strip(), line, "")) + elif sigil == "=": + inner = content[1:].rstrip() + if not inner.endswith("="): + raise PressError(name, line, "malformed set-delimiter tag") + parts = inner[:-1].split() + if len(parts) != 2: + raise PressError(name, line, "malformed set-delimiter tag") + tokens.append(("tag", "delim", "", line, "")) + odelim, cdelim = parts + else: + tokens.append(("tag", "var", content.strip(), line, "")) + return _strip_standalone(tokens) + + +def _strip_standalone(tokens: list) -> list: + """The spec's standalone-line rule: a line whose text is all whitespace + and which carries exactly one section/inverted/close/comment/partial/ + set-delimiter tag contributes no output for the line itself. A standalone + partial keeps the line's leading whitespace as the indentation applied to + its rendered content.""" + # Split text tokens so each line of the template is its own token run. + split: list = [] + for tok in tokens: + if tok[0] != "text": + split.append(tok) + continue + text = tok[1] + while True: + nl = text.find("\n") + if nl < 0: + if text: + split.append(("text", text)) + break + split.append(("text", text[: nl + 1])) + text = text[nl + 1 :] + + out: list = [] + line: list = [] + + def flush(line_tokens: list) -> None: + tags = [t for t in line_tokens if t[0] == "tag"] + texts = [t[1] for t in line_tokens if t[0] == "text"] + standalone = ( + len(tags) == 1 + and tags[0][1] in _STANDALONE_KINDS + and all(not t.strip() for t in texts) + ) + if not standalone: + out.extend(line_tokens) + return + tag = tags[0] + if tag[1] == "partial": + indent = "" + for t in line_tokens: + if t[0] == "tag": + break + indent += t[1] + tag = ("tag", "partial", tag[2], tag[3], indent) + out.append(tag) + + for tok in split: + line.append(tok) + if tok[0] == "text" and tok[1].endswith("\n"): + flush(line) + line = [] + if line: + flush(line) + return out + + +# --------------------------------------------------------------------------- # +# Parser +# --------------------------------------------------------------------------- # + + +def _path(key: str) -> tuple[str, ...]: + return (".",) if key == "." else tuple(key.split(".")) + + +def _parse(tokens: list, name: str) -> list: + root: list = [] + stack: list[tuple[_Section, str]] = [] + current = root + for tok in tokens: + if tok[0] == "text": + if tok[1]: + current.append(_Text(tok[1])) + continue + _, kind, key, line, indent = tok + if kind in ("var", "raw"): + current.append(_Var(_path(key), kind == "raw", line)) + elif kind in ("open", "inv"): + section = _Section(_path(key), kind == "inv", line) + current.append(section) + stack.append((section, key)) + current = section.children + elif kind == "close": + if not stack or stack[-1][1] != key: + raise PressError(name, line, f"unexpected section close '{key}'") + stack.pop() + current = stack[-1][0].children if stack else root + elif kind == "partial": + current.append(_Partial(key, indent, line)) + # comments and delim changes contribute nothing + if stack: + section, key = stack[-1] + raise PressError(name, section.line, f"unclosed section '{key}'") + return root + + +# --------------------------------------------------------------------------- # +# Renderer +# --------------------------------------------------------------------------- # + +_MISS = object() + + +class Press: + """Renders Mustache templates. `partials` maps a partial name to its + template text (a dict, or a callable for file-backed loading). The + `strict` and `escape` knobs exist for the spec conformance suite; the + production pipeline uses the defaults (strict, verbatim).""" + + def __init__( + self, + partials: Mapping[str, str] | Callable[[str], str] | None = None, + strict: bool = True, + escape: Callable[[str], str] | None = None, + max_partial_depth: int = 64, + ): + self._partials = partials or {} + self._strict = strict + self._escape = escape + self._max_depth = max_partial_depth + self._cache: dict[tuple[str, str, str], list] = {} + + def render(self, template: str, context, name: str = "