├── docs ├── content.md ├── whitepapers │ └── MERGE.md ├── highlightjs │ ├── es │ │ ├── package.json │ │ └── languages │ │ │ ├── shell.min.js │ │ │ ├── bnf.min.js │ │ │ ├── json.min.js │ │ │ ├── dockerfile.min.js │ │ │ ├── xfer.min.js │ │ │ ├── go.min.js │ │ │ ├── yaml.min.js │ │ │ ├── xml.min.js │ │ │ └── markdown.min.js │ ├── styles │ │ ├── pojoaque.jpg │ │ ├── brown-papersq.png │ │ ├── vs.min.css │ │ ├── monokai.min.css │ │ ├── atom-one-dark.min.css │ │ ├── atom-one-light.min.css │ │ ├── vs2015.min.css │ │ ├── default.min.css │ │ ├── tokyo-night-dark.min.css │ │ ├── tokyo-night-light.min.css │ │ ├── github.min.css │ │ ├── base16 │ │ │ ├── github.min.css │ │ │ ├── monokai.min.css │ │ │ ├── default-dark.min.css │ │ │ ├── default-light.min.css │ │ │ ├── solarized-dark.min.css │ │ │ └── solarized-light.min.css │ │ └── github-dark.min.css │ ├── languages │ │ ├── shell.min.js │ │ ├── bnf.min.js │ │ ├── json.min.js │ │ ├── dockerfile.min.js │ │ ├── xfer.min.js │ │ ├── go.min.js │ │ ├── yaml.min.js │ │ ├── xml.min.js │ │ └── markdown.min.js │ ├── LICENSE │ └── README.md ├── favicon.ico ├── XferLang.png ├── XferLang-lg.png ├── XferLang-md.png ├── XferLang-sm.png ├── favicon-16x16.png ├── favicon-32x32.png ├── apple-touch-icon.png ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── site.webmanifest ├── source │ └── documentation.md ├── script.js └── template.html ├── version.txt ├── XferLangVSCode ├── images │ └── icon.png ├── .vscodeignore ├── XferLang-128.png ├── xfer-0.0.1.vsix ├── xferlang-0.15.3.vsix ├── xferlang-0.15.4.vsix ├── package-lock.json ├── .github │ └── copilot-instructions.md ├── .vscode │ ├── tasks.json │ └── launch.json ├── CHANGELOG.md ├── publish-extension.ps1 ├── build-and-install.ps1 ├── package.json ├── vsc-extension-quickstart.md └── README.md ├── examples ├── if-pi-demo │ ├── mixed-types.xfer │ ├── complex-document.xfer │ ├── error-conditions.xfer │ ├── numeric-comparisons.xfer │ ├── test.xfer │ ├── equality-tests.xfer │ ├── IfPiDemo.csproj │ ├── basic-conditions.xfer │ └── test-removal.xfer ├── dynamic-db-resolver-demo │ ├── demo.db │ ├── dynamic-db-resolver-demo.csproj │ └── sample.xfer ├── XferService │ ├── Xfer.Service │ │ ├── Xfer.Service.http │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── WeatherForecast.cs │ │ ├── Dockerfile │ │ ├── Xfer.Service.csproj │ │ ├── Properties │ │ │ └── launchSettings.json │ │ └── Controllers │ │ │ └── WeatherForecastController.cs │ ├── Xfer.Data │ │ ├── Xfer.Data.csproj │ │ └── SampleData.cs │ ├── Xfer.Client │ │ └── Xfer.Client.csproj │ └── .dockerignore ├── processing-instruction-demo │ ├── Properties │ │ └── launchSettings.json │ ├── processing-instruction-demo.csproj │ ├── CharDefProcessorSetup.cs │ └── sample.xfer ├── AsyncApiDemo │ └── AsyncApiDemo.csproj ├── let-demo │ ├── let-demo.csproj │ └── let-demo.xfer ├── external-log-pi-demo │ ├── external-log-pi-demo.csproj │ └── sample.xfer ├── nullable-scripting-demo │ └── nullable-scripting-demo.csproj └── xfer2yaml │ └── README.md ├── ParksComputing.Xfer.Lang.Tests ├── BasicTests.cs ├── ParserTests.cs ├── WarningSystemTests.cs ├── CharacterDefinitionTests.cs ├── ParserComprehensiveTests.cs ├── XferContractResolverTests.cs ├── XferDateTimePrecisionTests.cs ├── CharacterDefinitionTests_Fixed.cs ├── WarningSystemComprehensiveTests.cs ├── GlobalUsings.cs ├── DebugToXferTests.cs ├── ScriptLetProcessingInstructionTests.cs ├── ParksComputing.Xfer.Lang.Tests.csproj ├── IfProcessingInstructionDereferenceTests.cs ├── ParserFragmentTests.cs ├── LetProcessingInstructionTests.cs ├── DereferenceElementTests.cs ├── IfProcessingInstructionSerializationTests.cs ├── BasicTests.cs.bak └── CollectionProcessingInstructionTests.cs ├── ParksComputing.Xfer.Lang ├── Scripting │ ├── Program.cs │ ├── Logical │ │ ├── NotOperator.cs │ │ ├── XorOperator.cs │ │ ├── OrOperator.cs │ │ └── AndOperator.cs │ └── Comparison │ │ └── NotEqualOperator.cs ├── logo │ ├── XferLang.pdn │ ├── XferLang.png │ ├── XferLang-lg.png │ ├── XferLang-md.png │ └── XferLang-sm.png ├── Attributes │ ├── XferEvaluatedAttribute.cs │ ├── XferNumericFormat.cs │ ├── XferPropertyAttribute.cs │ ├── XferNumericFormatAttribute.cs │ ├── XferDecimalPrecisionAttribute.cs │ └── XferCaptureIdAttribute.cs ├── Configuration │ ├── NullValueHandling.cs │ ├── XferProcessingInstruction.cs │ └── ElementStylePreference.cs ├── Extensions │ ├── StringExtensions.cs │ └── ObjectExtensions.cs ├── Elements │ ├── NumericBase.cs │ ├── ElementStyle.cs │ ├── NullElement.cs │ ├── IntegerElementDelimiter.cs │ ├── InterpolatedElement.cs │ └── StringElement.cs ├── DynamicSource │ └── IDynamicSourceResolver.cs ├── Schema │ ├── SchemaObject.cs │ ├── Constraint.cs │ ├── XferSchema.cs │ ├── SchemaField.cs │ ├── ConstraintEvaluator.cs │ ├── SchemaDefinition.cs │ └── ExpressionConstraint.cs ├── Helpers │ └── ElementCloner.cs ├── Formatting.cs ├── LICENSE.txt ├── DateTimeHandling.cs ├── ProcessingInstructions │ ├── PropertiesProcessingInstruction.cs │ ├── IdProcessingInstruction.cs │ └── TagProcessingInstruction.cs ├── ContractResolvers │ ├── IContractResolver.cs │ └── DefaultContractResolver.cs ├── Types │ └── XferKeyedValue.cs ├── Services │ └── IXferParser.cs ├── XferDocumentMetadata.cs ├── Converters │ ├── KeyedValueConverter.cs │ └── IXferConverter.cs ├── ParseError.cs └── ParksComputing.Xfer.Lang.csproj ├── demo.db ├── simple.xfer ├── logo ├── XferLang.pdn ├── XferLang.png ├── XferLang-128.png ├── XferLang-lg.png ├── XferLang-md.png └── XferLang-sm.png ├── xferlang-rs ├── Cargo.toml ├── Cargo.lock ├── src │ ├── element.rs │ └── lib.rs ├── examples │ └── read_file.rs └── README.md ├── actiontest.ps1 ├── tools └── json2xfer │ ├── input.json │ ├── Constants.cs │ ├── README.md │ ├── json2xfer.csproj │ ├── Utility.cs │ └── Program.cs ├── nuget.config ├── xferc ├── Commands │ ├── RootCommand.cs │ └── SerializeCommand.cs ├── Constants.cs ├── SampleData.cs ├── XfercReplContext.cs ├── Services │ ├── PersistenceService.cs │ └── Utility.cs ├── xferc.csproj └── Program.cs ├── .vscode ├── settings.json └── launch.json ├── XferDocBuilder └── XferDocBuilder.csproj ├── schemas ├── person.xfer ├── blog_post.xfer ├── blog_post_inline.xfer └── address.xfer ├── experiments.xfer ├── LICENSE.txt ├── sample.xfer ├── .editorconfig └── agent ├── SESSION_PROMPTS.md ├── PROJECT_CONTEXT.md └── RECENT_DECISIONS.md /docs/content.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /version.txt: -------------------------------------------------------------------------------- 1 | 0.10 2 | -------------------------------------------------------------------------------- /docs/whitepapers/MERGE.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /XferLangVSCode/images/icon.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/if-pi-demo/mixed-types.xfer: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/if-pi-demo/complex-document.xfer: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/if-pi-demo/error-conditions.xfer: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang.Tests/BasicTests.cs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang.Tests/ParserTests.cs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Scripting/Program.cs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/if-pi-demo/numeric-comparisons.xfer: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang.Tests/WarningSystemTests.cs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/highlightjs/es/package.json: -------------------------------------------------------------------------------- 1 | { "type": "module" } -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang.Tests/CharacterDefinitionTests.cs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang.Tests/ParserComprehensiveTests.cs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang.Tests/XferContractResolverTests.cs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang.Tests/XferDateTimePrecisionTests.cs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang.Tests/CharacterDefinitionTests_Fixed.cs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang.Tests/WarningSystemComprehensiveTests.cs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/demo.db -------------------------------------------------------------------------------- /simple.xfer: -------------------------------------------------------------------------------- 1 | {name "Alice" age #30 isMember ~true scores[*85 *90 *78.5]} 2 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /examples/if-pi-demo/test.xfer: -------------------------------------------------------------------------------- 1 | ( testElement { value "hello" } ) 2 | -------------------------------------------------------------------------------- /docs/XferLang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/docs/XferLang.png -------------------------------------------------------------------------------- /logo/XferLang.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/logo/XferLang.pdn -------------------------------------------------------------------------------- /logo/XferLang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/logo/XferLang.png -------------------------------------------------------------------------------- /docs/XferLang-lg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/docs/XferLang-lg.png -------------------------------------------------------------------------------- /docs/XferLang-md.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/docs/XferLang-md.png -------------------------------------------------------------------------------- /docs/XferLang-sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/docs/XferLang-sm.png -------------------------------------------------------------------------------- /logo/XferLang-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/logo/XferLang-128.png -------------------------------------------------------------------------------- /logo/XferLang-lg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/logo/XferLang-lg.png -------------------------------------------------------------------------------- /logo/XferLang-md.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/logo/XferLang-md.png -------------------------------------------------------------------------------- /logo/XferLang-sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/logo/XferLang-sm.png -------------------------------------------------------------------------------- /docs/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/docs/favicon-16x16.png -------------------------------------------------------------------------------- /docs/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/docs/favicon-32x32.png -------------------------------------------------------------------------------- /docs/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/docs/apple-touch-icon.png -------------------------------------------------------------------------------- /XferLangVSCode/.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | .gitignore 4 | vsc-extension-quickstart.md 5 | -------------------------------------------------------------------------------- /XferLangVSCode/XferLang-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/XferLangVSCode/XferLang-128.png -------------------------------------------------------------------------------- /XferLangVSCode/xfer-0.0.1.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/XferLangVSCode/xfer-0.0.1.vsix -------------------------------------------------------------------------------- /docs/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/docs/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/docs/android-chrome-512x512.png -------------------------------------------------------------------------------- /XferLangVSCode/xferlang-0.15.3.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/XferLangVSCode/xferlang-0.15.3.vsix -------------------------------------------------------------------------------- /XferLangVSCode/xferlang-0.15.4.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/XferLangVSCode/xferlang-0.15.4.vsix -------------------------------------------------------------------------------- /docs/highlightjs/styles/pojoaque.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/docs/highlightjs/styles/pojoaque.jpg -------------------------------------------------------------------------------- /xferlang-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xferlang-rs" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /docs/highlightjs/styles/brown-papersq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/docs/highlightjs/styles/brown-papersq.png -------------------------------------------------------------------------------- /examples/dynamic-db-resolver-demo/demo.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/examples/dynamic-db-resolver-demo/demo.db -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/logo/XferLang.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/ParksComputing.Xfer.Lang/logo/XferLang.pdn -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/logo/XferLang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/ParksComputing.Xfer.Lang/logo/XferLang.png -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/logo/XferLang-lg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/ParksComputing.Xfer.Lang/logo/XferLang-lg.png -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/logo/XferLang-md.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/ParksComputing.Xfer.Lang/logo/XferLang-md.png -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/logo/XferLang-sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulmooreparks/Xfer/HEAD/ParksComputing.Xfer.Lang/logo/XferLang-sm.png -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang.Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | // Global usings for test project to simplify element references 2 | global using ParksComputing.Xfer.Lang.Elements; 3 | -------------------------------------------------------------------------------- /actiontest.ps1: -------------------------------------------------------------------------------- 1 | act push -P ubuntu-latest=catthehacker/ubuntu:act-latest -P windows-latest=catthehacker/ubuntu:act-latest -P macos-latest=catthehacker/ubuntu:act-latest -s ACTIONS_RUNTIME_TOKEN=testtoken -------------------------------------------------------------------------------- /examples/XferService/Xfer.Service/Xfer.Service.http: -------------------------------------------------------------------------------- 1 | @Xfer.Service_HostAddress = http://localhost:5171 2 | 3 | GET {{Xfer.Service_HostAddress}}/weatherforecast/ 4 | Accept: application/json 5 | 6 | ### 7 | -------------------------------------------------------------------------------- /xferlang-rs/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "xferlang-rs" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /examples/XferService/Xfer.Service/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/XferService/Xfer.Service/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /tools/json2xfer/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "person": { 3 | "name": "Alice", 4 | "age": 30, 5 | "isMember": true, 6 | "tags": ["admin", "user"], 7 | "address": { 8 | "city": "Wonderland", 9 | "zip": "12345" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /docs/site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /examples/XferService/Xfer.Data/Xfer.Data.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/processing-instruction-demo/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "processing-instruction-demo": { 4 | "commandName": "Project", 5 | "workingDirectory": "C:\\Users\\paul\\source\\repos\\Xfer\\examples\\processing-instruction-demo\\" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /XferLangVSCode/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xferlang", 3 | "version": "0.15.4", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "xferlang", 9 | "version": "0.15.4", 10 | "license": "MIT", 11 | "engines": { 12 | "vscode": "^1.102.0" 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docs/highlightjs/es/languages/shell.min.js: -------------------------------------------------------------------------------- 1 | /*! `shell` grammar compiled for Highlight.js 11.7.0 */ 2 | var hljsGrammar=(()=>{"use strict";return s=>({name:"Shell Session", 3 | aliases:["console","shellsession"],contains:[{className:"meta.prompt", 4 | begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/, 5 | subLanguage:"bash"}}]})})();export default hljsGrammar; -------------------------------------------------------------------------------- /docs/highlightjs/languages/shell.min.js: -------------------------------------------------------------------------------- 1 | /*! `shell` grammar compiled for Highlight.js 11.7.0 */ 2 | (()=>{var s=(()=>{"use strict";return s=>({name:"Shell Session", 3 | aliases:["console","shellsession"],contains:[{className:"meta.prompt", 4 | begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/, 5 | subLanguage:"bash"}}]})})();hljs.registerLanguage("shell",s)})(); -------------------------------------------------------------------------------- /xferlang-rs/src/element.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, PartialEq)] 2 | pub enum Element { 3 | Null, 4 | Boolean(bool), 5 | Integer(i64), 6 | Double(f64), 7 | String(String), 8 | Array(Vec), 9 | Object(Vec<(String, Element)>), 10 | } 11 | 12 | #[derive(Debug, Clone, PartialEq)] 13 | pub struct Document { 14 | pub root: Element, 15 | } 16 | -------------------------------------------------------------------------------- /XferLangVSCode/.github/copilot-instructions.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | This is a VS Code extension project. Please use the get_vscode_api with a query as input to fetch the latest VS Code API references. 4 | -------------------------------------------------------------------------------- /docs/highlightjs/es/languages/bnf.min.js: -------------------------------------------------------------------------------- 1 | /*! `bnf` grammar compiled for Highlight.js 11.7.0 */ 2 | var hljsGrammar=(()=>{"use strict";return a=>({name:"Backus\u2013Naur Form", 3 | contains:[{className:"attribute",begin://},{begin:/::=/,end:/$/, 4 | contains:[{begin:// 5 | },a.C_LINE_COMMENT_MODE,a.C_BLOCK_COMMENT_MODE,a.APOS_STRING_MODE,a.QUOTE_STRING_MODE] 6 | }]})})();export default hljsGrammar; -------------------------------------------------------------------------------- /docs/highlightjs/languages/bnf.min.js: -------------------------------------------------------------------------------- 1 | /*! `bnf` grammar compiled for Highlight.js 11.7.0 */ 2 | (()=>{var e=(()=>{"use strict";return e=>({name:"Backus\u2013Naur Form", 3 | contains:[{className:"attribute",begin://},{begin:/::=/,end:/$/, 4 | contains:[{begin:// 5 | },e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] 6 | }]})})();hljs.registerLanguage("bnf",e)})(); -------------------------------------------------------------------------------- /examples/if-pi-demo/equality-tests.xfer: -------------------------------------------------------------------------------- 1 | ( 2 | 3 | "Linux") !> 4 | linuxConfig { 5 | packageManager "apt" 6 | shell "/bin/bash" 7 | } 8 | 9 | 10 | "Windows") !> 11 | windowsConfig { 12 | packageManager "chocolatey" 13 | shell "cmd.exe" 14 | } 15 | ) 16 | -------------------------------------------------------------------------------- /XferLangVSCode/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "type": "shell", 7 | "command": "npm run compile", 8 | "group": "build", 9 | "problemMatcher": ["$tsc"] 10 | }, 11 | { 12 | "label": "test", 13 | "type": "shell", 14 | "command": "npm test", 15 | "group": "test", 16 | "problemMatcher": [] 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /examples/AsyncApiDemo/AsyncApiDemo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /xferc/Commands/RootCommand.cs: -------------------------------------------------------------------------------- 1 | using Cliffer; 2 | 3 | using System.CommandLine; 4 | using System.CommandLine.Invocation; 5 | 6 | namespace ParksComputing.Xferc; 7 | 8 | [RootCommand("Xfer CLI Application")] 9 | internal class RootCommand { 10 | public async Task Execute(Command command, IServiceProvider serviceProvider, InvocationContext context) { 11 | return await command.Repl(serviceProvider, context, new XfercReplContext()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/XferService/Xfer.Service/WeatherForecast.cs: -------------------------------------------------------------------------------- 1 | using ParksComputing.Xfer.Lang.Attributes; 2 | 3 | namespace Xfer.Service; 4 | 5 | public class WeatherForecast { 6 | public DateOnly Date { get; set; } 7 | 8 | [XferDecimalPrecision(1)] 9 | public decimal TemperatureC { get; set; } 10 | 11 | [XferDecimalPrecision(1)] 12 | public decimal TemperatureF => 32 + (TemperatureC / 0.5556m); 13 | 14 | public string? Summary { get; set; } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /xferc/Constants.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ParksComputing.Xferc; 8 | internal static class Constants { 9 | public const string XfercDirectoryName = ".xferc"; 10 | public const string ConfigFileName = "appsettings.json"; 11 | public const string MutexName = "Global\\XfercMutex"; // Global mutex name for cross-process synchronization 12 | } 13 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "github.copilot.enable": { 3 | "*": true, 4 | "plaintext": true, 5 | "markdown": true, 6 | "scminput": false 7 | }, 8 | "files.associations": { 9 | "*.xfer": "xferlang" 10 | }, 11 | "editor.suggest.snippetsPreventQuickSuggestions": false, 12 | "editor.inlineSuggest.enabled": true, 13 | "github.copilot.chat.welcomeMessage": "always", 14 | "workbench.startupEditor": "readme" 15 | } 16 | -------------------------------------------------------------------------------- /examples/let-demo/let-demo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | net8.0 5 | LetDemo 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /XferDocBuilder/XferDocBuilder.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/processing-instruction-demo/processing-instruction-demo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | net8.0 5 | ProcessingInstructionDemo 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /tools/json2xfer/Constants.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Json2Xfer; 8 | 9 | internal static class Constants { 10 | public const string XfercDirectoryName = ".json2xfer"; 11 | public const string ConfigFileName = "appsettings.json"; 12 | public const string MutexName = "Global\\Json2XferMutex"; // Global mutex name for cross-process synchronization 13 | } 14 | -------------------------------------------------------------------------------- /xferlang-rs/examples/read_file.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | 4 | fn main() { 5 | let args: Vec = env::args().collect(); 6 | if args.len() != 2 { 7 | eprintln!("Usage: {} ", args[0]); 8 | std::process::exit(1); 9 | } 10 | let data = fs::read_to_string(&args[1]).expect("failed to read file"); 11 | match xferlang_rs::parse(&data) { 12 | Ok(doc) => println!("{:#?}", doc), 13 | Err(e) => eprintln!("parse error: {}", e), 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /docs/highlightjs/languages/json.min.js: -------------------------------------------------------------------------------- 1 | /*! `json` grammar compiled for Highlight.js 11.7.0 */ 2 | (()=>{var e=(()=>{"use strict";return e=>{const a=["true","false","null"],n={ 3 | scope:"literal",beginKeywords:a.join(" ")};return{name:"JSON",keywords:{ 4 | literal:a},contains:[{className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/, 5 | relevance:1.01},{match:/[{}[\],:]/,className:"punctuation",relevance:0 6 | },e.QUOTE_STRING_MODE,n,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE], 7 | illegal:"\\S"}}})();hljs.registerLanguage("json",e)})(); -------------------------------------------------------------------------------- /examples/external-log-pi-demo/external-log-pi-demo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | external_log_pi_demo 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/highlightjs/es/languages/json.min.js: -------------------------------------------------------------------------------- 1 | /*! `json` grammar compiled for Highlight.js 11.7.0 */ 2 | var hljsGrammar=(()=>{"use strict";return e=>{ 3 | const a=["true","false","null"],r={scope:"literal",beginKeywords:a.join(" ")} 4 | ;return{name:"JSON",keywords:{literal:a},contains:[{className:"attr", 5 | begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01},{match:/[{}[\],:]/, 6 | className:"punctuation",relevance:0 7 | },e.QUOTE_STRING_MODE,r,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE], 8 | illegal:"\\S"}}})();export default hljsGrammar; -------------------------------------------------------------------------------- /examples/XferService/Xfer.Client/Xfer.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/dynamic-db-resolver-demo/dynamic-db-resolver-demo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/nullable-scripting-demo/nullable-scripting-demo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | NullableScriptingDemo 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/XferService/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/bin 16 | **/charts 17 | **/docker-compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | LICENSE 25 | README.md 26 | !**/.gitignore 27 | !.git/HEAD 28 | !.git/config 29 | !.git/packed-refs 30 | !.git/refs/heads/** -------------------------------------------------------------------------------- /XferLangVSCode/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that launches the extension inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "args": [ 13 | "--extensionDevelopmentPath=${workspaceFolder}" 14 | ] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /docs/highlightjs/es/languages/dockerfile.min.js: -------------------------------------------------------------------------------- 1 | /*! `dockerfile` grammar compiled for Highlight.js 11.7.0 */ 2 | var hljsGrammar=(()=>{"use strict";return e=>({name:"Dockerfile", 3 | aliases:["docker"],case_insensitive:!0, 4 | keywords:["from","maintainer","expose","env","arg","user","onbuild","stopsignal"], 5 | contains:[e.HASH_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.NUMBER_MODE,{ 6 | beginKeywords:"run cmd entrypoint volume add copy workdir label healthcheck shell", 7 | starts:{end:/[^\\]$/,subLanguage:"bash"}}],illegal:" 6 | /// Indicates that a property should be serialized as an evaluated element in XferLang. 7 | /// Evaluated elements use angle bracket delimiters and their content is processed 8 | /// for dynamic content, variable substitution, or expressions during serialization. 9 | /// 10 | [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] 11 | public class XferEvaluatedAttribute : Attribute { 12 | } 13 | -------------------------------------------------------------------------------- /docs/highlightjs/languages/dockerfile.min.js: -------------------------------------------------------------------------------- 1 | /*! `dockerfile` grammar compiled for Highlight.js 11.7.0 */ 2 | (()=>{var e=(()=>{"use strict";return e=>({name:"Dockerfile",aliases:["docker"], 3 | case_insensitive:!0, 4 | keywords:["from","maintainer","expose","env","arg","user","onbuild","stopsignal"], 5 | contains:[e.HASH_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.NUMBER_MODE,{ 6 | beginKeywords:"run cmd entrypoint volume add copy workdir label healthcheck shell", 7 | starts:{end:/[^\\]$/,subLanguage:"bash"}}],illegal:" 4 | 5 | 6 | { 7 | name "Alice Johnson" 8 | email "alice@example.com" 9 | 10 | displayName "Alice" 11 | } 12 | -------------------------------------------------------------------------------- /xferc/SampleData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ParksComputing.Xferc; 8 | public class SampleData { 9 | public DateTime DateTime { get; set; } = DateTime.Now; 10 | public DateTimeOffset DateTimeOffset { get; set; } 11 | public TimeOnly TimeOnly { get; set; } = new TimeOnly(11, 39, 44); 12 | public DateOnly DateOnly { get; set; } = new DateOnly(2024, 12, 5); 13 | public TimeSpan TimeSpan { get; set; } = new TimeSpan(48, 39, 44); 14 | } 15 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Configuration/NullValueHandling.cs: -------------------------------------------------------------------------------- 1 | namespace ParksComputing.Xfer.Lang.Configuration { 2 | /// 3 | /// Specifies how null values should be handled during XferLang serialization. 4 | /// 5 | public enum NullValueHandling { 6 | /// 7 | /// Include null values in the serialized output. 8 | /// 9 | Include, 10 | 11 | /// 12 | /// Ignore (exclude) null values from the serialized output. 13 | /// 14 | Ignore 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /xferc/XfercReplContext.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace ParksComputing.Xferc; 4 | 5 | internal class XfercReplContext : Cliffer.DefaultReplContext { 6 | public string Title => "Xfer CLI Application"; 7 | public override string[] GetPopCommands() => []; 8 | 9 | public override string GetTitleMessage() { 10 | Assembly assembly = Assembly.GetExecutingAssembly(); 11 | Version? version = assembly.GetName().Version; 12 | string versionString = version?.ToString() ?? "Unknown"; 13 | return $"{Title} v{versionString}"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace ParksComputing.Xfer.Lang.Extensions; 2 | 3 | internal static class StringExtensions { 4 | public static bool IsKeyword(this string compare, IDictionary keywords, out string? keyword) { 5 | return keywords.TryGetValue(compare, out keyword); 6 | } 7 | 8 | public static bool IsKeywordString(this string input) { 9 | for (int i = 0; i < input.Length; i++) { 10 | if (!input[i].IsKeywordChar()) { 11 | return false; 12 | } 13 | } 14 | return true; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /xferlang-rs/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod element; 2 | mod parser; 3 | 4 | pub use element::{Document, Element}; 5 | 6 | pub fn parse(input: &str) -> Result { 7 | parser::parse(input) 8 | } 9 | 10 | #[cfg(test)] 11 | mod tests { 12 | use super::*; 13 | 14 | #[test] 15 | fn parse_simple_example() { 16 | let input = "{name\"Alice\"age 30 isMember~true scores[*85 *90 *78.5]}"; 17 | let doc = parse(input).expect("parse failed"); 18 | match doc.root { 19 | Element::Object(ref pairs) => assert_eq!(pairs.len(), 4), 20 | _ => panic!("not object"), 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/processing-instruction-demo/CharDefProcessorSetup.cs: -------------------------------------------------------------------------------- 1 | using ParksComputing.Xfer.Lang.CharDef; 2 | using ParksComputing.Xfer.Lang.Services; 3 | using ParksComputing.Xfer.Lang.Elements; 4 | 5 | namespace ProcessingInstructionDemo { 6 | public static class CharDefProcessorSetup { 7 | public static CharDefProcessor RegisterWith(Parser parser) { 8 | var processor = new CharDefProcessor(); 9 | parser.RegisterPIProcessor(CharDefProcessor.CharDefKey, processor.PIHandler); 10 | parser.RegisterElementProcessor(processor.ElementHandler); 11 | return processor; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /docs/highlightjs/styles/vs.min.css: -------------------------------------------------------------------------------- 1 | pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#fff;color:#000}.hljs-comment,.hljs-quote,.hljs-variable{color:green}.hljs-built_in,.hljs-keyword,.hljs-name,.hljs-selector-tag,.hljs-tag{color:#00f}.hljs-addition,.hljs-attribute,.hljs-literal,.hljs-section,.hljs-string,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-type{color:#a31515}.hljs-deletion,.hljs-meta,.hljs-selector-attr,.hljs-selector-pseudo{color:#2b91af}.hljs-doctag{color:grey}.hljs-attr{color:red}.hljs-bullet,.hljs-link,.hljs-symbol{color:#00b0e8}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700} -------------------------------------------------------------------------------- /examples/if-pi-demo/IfPiDemo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | IfPiDemo 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | PreserveNewest 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tools/json2xfer/README.md: -------------------------------------------------------------------------------- 1 | # json2xfer 2 | 3 | A command-line tool to convert JSON to XferLang format. 4 | 5 | ## Goals 6 | - Accept JSON input (file or stdin) 7 | - Output XferLang to stdout or file 8 | - Support options for formatting, error handling, and schema mapping 9 | 10 | ## Design Discussion 11 | - Specify input/output conventions 12 | - Discuss mapping rules (types, collections, nulls, etc.) 13 | - Plan for extensibility (flags, schema, etc.) 14 | 15 | --- 16 | 17 | **To propose:** 18 | - CLI usage and options 19 | - Example conversions 20 | - Error handling strategy 21 | - Integration with XferLang library 22 | 23 | --- 24 | 25 | Add your design notes below: 26 | -------------------------------------------------------------------------------- /tools/json2xfer/json2xfer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/dynamic-db-resolver-demo/sample.xfer: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | 14 | ( 15 | credentials { 16 | password '<|dbpassword|>' 17 | greeting '<|greeting|>' 18 | username '<|username|>' 19 | apikey '<|apikey|>' 20 | } 21 | ) 22 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | "version": "0.2.0", 5 | "configurations": [ 6 | { 7 | "name": "Run processing-instruction-demo", 8 | "type": "coreclr", 9 | "request": "launch", 10 | "preLaunchTask": "build", 11 | "program": "${workspaceFolder}/examples/processing-instruction-demo/bin/Release/net8.0/processing-instruction-demo.exe", 12 | "args": [], 13 | "cwd": "${workspaceFolder}/examples/processing-instruction-demo", 14 | "stopAtEntry": false, 15 | "console": "internalConsole" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /schemas/person.xfer: -------------------------------------------------------------------------------- 1 | 27 | 28 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Attributes/XferNumericFormat.cs: -------------------------------------------------------------------------------- 1 | namespace ParksComputing.Xfer.Lang.Attributes; 2 | 3 | /// 4 | /// Specifies the format for numeric serialization in XferLang. 5 | /// 6 | public enum XferNumericFormat 7 | { 8 | /// 9 | /// Use default format based on settings and type (implicit for int, compact for others). 10 | /// 11 | Default, 12 | 13 | /// 14 | /// Serialize as decimal number: 42 15 | /// 16 | Decimal, 17 | 18 | /// 19 | /// Serialize as hexadecimal with prefix: #$2A 20 | /// 21 | Hexadecimal, 22 | 23 | /// 24 | /// Serialize as binary with prefix: #%00101010 25 | /// 26 | Binary 27 | } 28 | -------------------------------------------------------------------------------- /xferlang-rs/README.md: -------------------------------------------------------------------------------- 1 | # XferLang Rust Prototype 2 | 3 | This crate provides a minimal Rust implementation of the XferLang data 4 | format. Only a few element types are supported (strings, integers, 5 | booleans and lists) but the structure should be easy to extend. The 6 | library exposes a small C API so that it can be called from other 7 | languages. 8 | 9 | ## Building 10 | 11 | ``` 12 | cargo build 13 | ``` 14 | 15 | ## Testing 16 | 17 | ``` 18 | cargo test 19 | ``` 20 | 21 | The `xfer_parse` function accepts a C string containing XferLang 22 | content and returns a pointer to an `XferDocument`. The document can 23 | be converted to JSON using `xfer_document_to_json` and released with 24 | `xfer_document_free`. Any returned strings must be released with 25 | `xfer_string_free`. 26 | -------------------------------------------------------------------------------- /xferc/Services/PersistenceService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ParksComputing.Xferc.Services; 8 | internal class PersistenceService { 9 | private readonly string _xfercDirectory; 10 | private readonly Mutex _mutex; 11 | 12 | public PersistenceService() { 13 | var homeDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); 14 | _xfercDirectory = Path.Combine(homeDirectory, Constants.XfercDirectoryName); 15 | 16 | if (!Directory.Exists(_xfercDirectory)) { 17 | Directory.CreateDirectory(_xfercDirectory); 18 | } 19 | 20 | _mutex = new Mutex(false, Constants.MutexName); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /docs/highlightjs/styles/monokai.min.css: -------------------------------------------------------------------------------- 1 | pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#272822;color:#ddd}.hljs-keyword,.hljs-literal,.hljs-name,.hljs-selector-tag,.hljs-strong,.hljs-tag{color:#f92672}.hljs-code{color:#66d9ef}.hljs-attribute,.hljs-link,.hljs-regexp,.hljs-symbol{color:#bf79db}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-emphasis,.hljs-section,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-string,.hljs-subst,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-type,.hljs-variable{color:#a6e22e}.hljs-class .hljs-title,.hljs-title.class_{color:#fff}.hljs-comment,.hljs-deletion,.hljs-meta,.hljs-quote{color:#75715e}.hljs-doctag,.hljs-keyword,.hljs-literal,.hljs-section,.hljs-selector-id,.hljs-selector-tag,.hljs-title,.hljs-type{font-weight:700} -------------------------------------------------------------------------------- /schemas/blog_post.xfer: -------------------------------------------------------------------------------- 1 | 12 | 13 | definitions [ 14 | blog_post type object { 15 | title string { 16 | required ~true 17 | } 18 | content string { 19 | required ~true 20 | } 21 | publishedDate dateTime { 22 | required ~true 23 | } 24 | author user_profile { 25 | required ~true 26 | } 27 | } 28 | ] 29 | 30 | body ( 31 | type blog_post 32 | ) 33 | -------------------------------------------------------------------------------- /experiments.xfer: -------------------------------------------------------------------------------- 1 | { 2 | name "Alice" 3 | age #30 /> 4 | isMember ~true /> 5 | 6 | 7 | scores [ 8 | *85 9 | *90 10 | *78.5 11 | ] 12 | 13 | profile { 14 | email "alice@example.com" 15 | joinedDate @2023-01-15T12:00:00 /> 16 | } 17 | } 18 | 19 | 20 | {name"Alice"age#30 isMember~true scores[*85 *90 *78.5]profile{email<"alice@example.com">joinedDate@2023-01-15T12:00:00}} 21 | -------------------------------------------------------------------------------- /XferLangVSCode/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to the "XferLang" extension will be documented in this file. 4 | 5 | ## [Unreleased] 6 | 7 | ## [0.15.4] - 2025-08-13 8 | 9 | ### Fixed 10 | 11 | - Interpolated strings now only treat `<...>` as embedded Xfer elements when the `<` is followed by a valid explicit-element starter (quotes, numeric specifiers, dynamic `|`, deref `_`, date `@`, collection openers, or `=`). This prevents false positives from `<` and `'` inside embedded code blocks like `<' JS code '>`. 12 | 13 | ## [0.15.3] - 2025-08-12 14 | 15 | - Improved syntax highlighting 16 | 17 | ## [0.15.2] - 2025-08-11 18 | 19 | - Improved syntax highlighting 20 | 21 | ## [0.15.1] - 2025-08-11 22 | 23 | - Improved syntax highlighting 24 | 25 | ## [0.15.0] - 2025-08-11 26 | 27 | - Initial public release 28 | -------------------------------------------------------------------------------- /XferLangVSCode/publish-extension.ps1: -------------------------------------------------------------------------------- 1 | # This script automates publishing the XferLang VS Code extension to the Marketplace. 2 | # Usage: pwsh ./publish-extension.ps1 3 | 4 | # Ensure vsce is installed 5 | Write-Output "Checking for vsce..." 6 | if (-not (Get-Command vsce -ErrorAction SilentlyContinue)) { 7 | Write-Output "vsce not found. Installing..." 8 | npm install -g @vscode/vsce 9 | } 10 | 11 | # Prompt for publisher login if needed 12 | Write-Output "Checking for VSCE publisher login..." 13 | $token = vsce ls-publishers 2>&1 | Select-String 'No publishers found' 14 | if ($token) { 15 | Write-Output "No publisher found. Run 'vsce create-publisher ' and follow the prompts." 16 | exit 1 17 | } 18 | 19 | # Publish the extension 20 | Write-Output "Publishing the extension..." 21 | vsce publish 22 | 23 | Write-Output "Done." 24 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Elements/NumericBase.cs: -------------------------------------------------------------------------------- 1 | namespace ParksComputing.Xfer.Lang.Elements; 2 | 3 | /// 4 | /// Represents the numeric base used when interpreting or serializing a . 5 | /// Prefix conventions (when serialized through helper types): 6 | /// 7 | /// : no prefix 8 | /// : '$' prefix 9 | /// : '%' prefix 10 | /// 11 | /// 12 | public enum NumericBase { 13 | /// Base 10 (no prefix) 14 | Decimal = 10, 15 | /// Base 16 ('$' prefix) 16 | Hexadecimal = 16, 17 | /// Base 2 ('%' prefix) 18 | Binary = 2 19 | } 20 | -------------------------------------------------------------------------------- /tools/json2xfer/Utility.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace Json2Xfer; 4 | 5 | internal class Utility { 6 | private static IServiceProvider? _serviceProvider; 7 | 8 | internal static void SetServiceProvider(IServiceProvider provider) { 9 | _serviceProvider = provider; 10 | } 11 | 12 | internal static IServiceProvider GetServiceProvider() { 13 | if (_serviceProvider is null) { 14 | throw new InvalidOperationException("Service provider is not set."); 15 | } 16 | 17 | return _serviceProvider; 18 | } 19 | 20 | internal static T? GetService() { 21 | if (_serviceProvider is null) { 22 | throw new InvalidOperationException("Service provider is not set."); 23 | } 24 | 25 | return _serviceProvider.GetService(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /docs/highlightjs/styles/atom-one-dark.min.css: -------------------------------------------------------------------------------- 1 | pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#c678dd}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#98c379}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#d19a66}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#61aeee}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#e6c07b}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline} -------------------------------------------------------------------------------- /docs/highlightjs/styles/atom-one-light.min.css: -------------------------------------------------------------------------------- 1 | pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#383a42;background:#fafafa}.hljs-comment,.hljs-quote{color:#a0a1a7;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#a626a4}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e45649}.hljs-literal{color:#0184bb}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#50a14f}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#986801}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#4078f2}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#c18401}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline} -------------------------------------------------------------------------------- /xferc/xferc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | ParksComputing.Xferc 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Elements/ElementStyle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ParksComputing.Xfer.Lang.Elements; 8 | 9 | /// 10 | /// Specifies the different styles in which XferLang elements can be written and parsed. 11 | /// 12 | public enum ElementStyle 13 | { 14 | /// 15 | /// Explicit style with full element delimiters: <elementType content elementType> 16 | /// 17 | Explicit = 0, 18 | 19 | /// 20 | /// Compact style with simplified delimiters and minimal whitespace. 21 | /// 22 | Compact = 1, 23 | 24 | /// 25 | /// Implicit style where element type is inferred from content format. 26 | /// 27 | Implicit = 2, 28 | } 29 | -------------------------------------------------------------------------------- /xferc/Services/Utility.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace ParksComputing.Xferc.Services; 4 | internal class Utility { 5 | private static IServiceProvider? _serviceProvider; 6 | 7 | internal static void SetServiceProvider(IServiceProvider provider) { 8 | _serviceProvider = provider; 9 | } 10 | 11 | internal static IServiceProvider GetServiceProvider() { 12 | if (_serviceProvider is null) { 13 | throw new InvalidOperationException("Service provider is not set."); 14 | } 15 | 16 | return _serviceProvider; 17 | } 18 | 19 | internal static T? GetService() { 20 | if (_serviceProvider is null) { 21 | throw new InvalidOperationException("Service provider is not set."); 22 | } 23 | 24 | return _serviceProvider.GetService(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/DynamicSource/IDynamicSourceResolver.cs: -------------------------------------------------------------------------------- 1 | using ParksComputing.Xfer.Lang.Elements; 2 | 3 | namespace ParksComputing.Xfer.Lang.DynamicSource 4 | { 5 | /// 6 | /// Interface for resolving dynamic values in XferLang documents. 7 | /// Implementations can resolve dynamic keys using processing instructions or other mechanisms. 8 | /// 9 | public interface IDynamicSourceResolver 10 | { 11 | /// 12 | /// Resolves a dynamic value for the given key using PI directives in the document. 13 | /// 14 | /// The dynamic key to resolve. 15 | /// The parsed XferDocument. 16 | /// The resolved value, or null if not found. 17 | string? Resolve(string key, XferDocument document); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Schema/SchemaObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ParksComputing.Xfer.Lang.Schema; 8 | 9 | /// 10 | /// Represents a structured object definition within a schema. 11 | /// Contains a collection of named fields that define the object's structure and validation rules. 12 | /// 13 | public class SchemaObject { 14 | /// 15 | /// Gets or sets the name of the schema object. 16 | /// 17 | public string Name { get; set; } = string.Empty; 18 | 19 | /// 20 | /// Gets or sets the collection of fields that make up this schema object. 21 | /// Maps field names to their corresponding SchemaField definitions. 22 | /// 23 | public Dictionary Fields { get; set; } = []; 24 | } 25 | -------------------------------------------------------------------------------- /docs/source/documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: XferLang Documentation 3 | description: Documentation for the XferLang data-interchange format 4 | --- 5 | 6 | # XferLang Documentation 7 | 8 | ## Introduction 9 | 10 | XferLang is a data-interchange format designed to support data serialization, data transmission, and offline use cases such as configuration management. 11 | 12 | ```xfer 13 | { 14 | name "Alice" 15 | age 30 16 | isActive ~true 17 | } 18 | ``` 19 | 20 | ## Design Philosophy 21 | 22 | XferLang is built around four core principles: 23 | 24 | ### 1. Clarity and Readability 25 | The syntax is designed to be human-readable without requiring separators like commas. 26 | 27 | ### 2. Explicit Typing 28 | All values are explicitly typed using prefixes. 29 | 30 | ## Basic Syntax 31 | 32 | XferLang documents consist of elements separated by whitespace. 33 | 34 | ```xfer 35 | 36 | name "John Doe" 37 | age 30 38 | ``` 39 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang.Tests/DebugToXferTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using ParksComputing.Xfer.Lang.Elements; 3 | using System; 4 | 5 | namespace ParksComputing.Xfer.Lang.Tests 6 | { 7 | [TestClass] 8 | public class DebugToXferTests 9 | { 10 | [TestMethod] 11 | public void DebugIdentifierFormats() 12 | { 13 | var invalidIdent = new IdentifierElement("invalid identifier"); 14 | var toXferResult = invalidIdent.ToXfer(); 15 | 16 | Console.WriteLine($"Style: {invalidIdent.Delimiter.Style}"); 17 | Console.WriteLine($"ToXfer: '{toXferResult}'"); 18 | Console.WriteLine($"Starts with colon: {toXferResult.StartsWith(":")}"); 19 | Console.WriteLine($"Ends with colon: {toXferResult.EndsWith(":")}"); 20 | 21 | // This test will always pass to see the output 22 | Assert.IsTrue(true); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/XferService/Xfer.Service/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base 4 | USER app 5 | WORKDIR /app 6 | EXPOSE 8080 7 | EXPOSE 8081 8 | 9 | FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build 10 | ARG BUILD_CONFIGURATION=Release 11 | WORKDIR /src 12 | COPY ["Xfer.Service/Xfer.Service.csproj", "Xfer.Service/"] 13 | RUN dotnet restore "./Xfer.Service/Xfer.Service.csproj" 14 | COPY . . 15 | WORKDIR "/src/Xfer.Service" 16 | RUN dotnet build "./Xfer.Service.csproj" -c $BUILD_CONFIGURATION -o /app/build 17 | 18 | FROM build AS publish 19 | ARG BUILD_CONFIGURATION=Release 20 | RUN dotnet publish "./Xfer.Service.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false 21 | 22 | FROM base AS final 23 | WORKDIR /app 24 | COPY --from=publish /app/publish . 25 | ENTRYPOINT ["dotnet", "Xfer.Service.dll"] -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Extensions/ObjectExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ParksComputing.Xfer.Lang.Extensions; 8 | 9 | /// 10 | /// Provides extension methods for converting .NET objects to XferLang format. 11 | /// Simplifies serialization by adding convenient ToXfer() method to all objects. 12 | /// 13 | public static class ObjectExtensions { 14 | /// 15 | /// Converts any .NET object to its XferLang string representation. 16 | /// Uses XferConvert.Serialize internally with default settings. 17 | /// 18 | /// The object to serialize to XferLang format. 19 | /// The XferLang string representation of the object. 20 | public static string ToXfer(this object obj) { 21 | string xfer = XferConvert.Serialize(obj); 22 | return xfer; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Schema/Constraint.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ParksComputing.Xfer.Lang.Schema; 8 | 9 | /// 10 | /// Represents a validation constraint that can be applied to schema fields or objects. 11 | /// Constraints define validation rules such as required fields, value ranges, patterns, or custom expressions. 12 | /// 13 | public class Constraint { 14 | /// 15 | /// Gets or sets the name of the constraint (e.g., "required", "minLength", "pattern"). 16 | /// 17 | public string Name { get; set; } = string.Empty; // e.g., "required" 18 | 19 | /// 20 | /// Gets or sets the constraint value, which can be a boolean, string, number, or evaluable expression 21 | /// depending on the constraint type. 22 | /// 23 | public object? Value { get; set; } // Boolean or evaluable expression 24 | } 25 | -------------------------------------------------------------------------------- /examples/if-pi-demo/basic-conditions.xfer: -------------------------------------------------------------------------------- 1 | ( 2 | 3 | 4 | 5 | !> 6 | debugConfig { 7 | enabled ~true 8 | verboseLogging ~true 9 | logLevel "debug" 10 | note "DEBUG_MODE exists and is truthy" 11 | } 12 | 13 | 14 | ) !> 15 | emptyStringExistsConfig { 16 | exists ~true 17 | value <|EMPTY_STRING|> 18 | note "Variable exists even though it's empty" 19 | } 20 | 21 | 22 | ) !> 23 | falseBooleanExistsConfig { 24 | exists ~true 25 | value <|FALSE_BOOLEAN|> 26 | note "Variable exists even though it's false" 27 | } 28 | ) 29 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang.Tests/ScriptLetProcessingInstructionTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using ParksComputing.Xfer.Lang.Services; 3 | 4 | namespace ParksComputing.Xfer.Lang.Tests; 5 | 6 | // Focused tests specifically for the original script-based let processing instruction scenario 7 | [TestClass] 8 | public class ScriptLetProcessingInstructionTests { 9 | [TestMethod] 10 | public void ScriptLetPI_ResolvesDereference_InFollowingInterpolated() { 11 | var parser = new Parser(); 12 | var doc = parser.Parse(" 'Hello, <_greetee_>.'"); 13 | var output = doc.Root.ToXfer(); 14 | Assert.IsTrue(output.Contains("Hello, World."), $"Expected interpolation to resolve greetee binding. Output: {output}"); 15 | // Script PI containing only let should be suppressed from serialization (mirrors standalone let behavior) 16 | Assert.IsFalse(output.Contains(" 6 | /// Specifies the XferLang property name for a .NET property during serialization and deserialization. 7 | /// When applied to a property, it overrides the default property name mapping behavior. 8 | /// 9 | [AttributeUsage(AttributeTargets.Property)] 10 | public class XferPropertyAttribute : Attribute { 11 | /// 12 | /// Gets or sets the custom name to use for this property in XferLang serialization. 13 | /// If null or not specified, the property's actual name will be used. 14 | /// 15 | public string? Name { get; set; } 16 | 17 | /// 18 | /// Initializes a new instance of the class. 19 | /// 20 | /// The custom name to use for this property, or null to use the property's actual name. 21 | public XferPropertyAttribute(string? name = null) { 22 | Name = name; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /XferLangVSCode/build-and-install.ps1: -------------------------------------------------------------------------------- 1 | // This script automates packaging and installing the XferLang VS Code extension. 2 | // Usage: pwsh ./build-and-install.ps1 3 | 4 | # Ensure vsce is installed 5 | Write-Output "Checking for vsce..." 6 | if (-not (Get-Command vsce -ErrorAction SilentlyContinue)) { 7 | Write-Output "vsce not found. Installing..." 8 | npm install -g @vscode/vsce 9 | } 10 | 11 | # Install dependencies 12 | Write-Output "Installing npm dependencies..." 13 | npm install 14 | 15 | # Package the extension 16 | Write-Output "Packaging the extension..." 17 | $vsix = (vsce package | Select-String -Pattern '\.vsix' | ForEach-Object { $_.Line.Trim() }) 18 | if (-not $vsix) { 19 | $vsix = Get-ChildItem *.vsix | Sort-Object LastWriteTime -Descending | Select-Object -First 1 | Select-Object -ExpandProperty Name 20 | } 21 | 22 | if (-not $vsix) { 23 | Write-Output "Failed to package extension." 24 | exit 1 25 | } 26 | 27 | # Install the extension 28 | Write-Output "Installing the extension: $vsix ..." 29 | code --install-extension $vsix 30 | 31 | Write-Output "Done." 32 | -------------------------------------------------------------------------------- /docs/highlightjs/styles/vs2015.min.css: -------------------------------------------------------------------------------- 1 | pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#1e1e1e;color:#dcdcdc}.hljs-keyword,.hljs-literal,.hljs-name,.hljs-symbol{color:#569cd6}.hljs-link{color:#569cd6;text-decoration:underline}.hljs-built_in,.hljs-type{color:#4ec9b0}.hljs-class,.hljs-number{color:#b8d7a3}.hljs-meta .hljs-string,.hljs-string{color:#d69d85}.hljs-regexp,.hljs-template-tag{color:#9a5334}.hljs-formula,.hljs-function,.hljs-params,.hljs-subst,.hljs-title{color:#dcdcdc}.hljs-comment,.hljs-quote{color:#57a64a;font-style:italic}.hljs-doctag{color:#608b4e}.hljs-meta,.hljs-meta .hljs-keyword,.hljs-tag{color:#9b9b9b}.hljs-template-variable,.hljs-variable{color:#bd63c5}.hljs-attr,.hljs-attribute{color:#9cdcfe}.hljs-section{color:gold}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-bullet,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-selector-tag{color:#d7ba7d}.hljs-addition{background-color:#144212;display:inline-block;width:100%}.hljs-deletion{background-color:#600;display:inline-block;width:100%} -------------------------------------------------------------------------------- /examples/XferService/Xfer.Service/Xfer.Service.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 905cb2cf-22cf-498a-83bd-a28b8153a6d6 8 | Linux 9 | 10 | true 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Helpers/ElementCloner.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using ParksComputing.Xfer.Lang.Elements; 3 | using ParksComputing.Xfer.Lang.Services; 4 | 5 | namespace ParksComputing.Xfer.Lang.Helpers { 6 | /// 7 | /// Utility to deep-clone an element via serialize/parse round-trip. Trades performance for simplicity. 8 | /// Optimizable later with explicit clone graph. 9 | /// 10 | internal static class ElementCloner { 11 | public static Element Clone(Element element) { 12 | if (element == null) { 13 | return new EmptyElement(); 14 | } 15 | // Wrap in tuple to ensure a single root collection for parsing 16 | var tmp = $"({element.ToXfer()})"; 17 | var parser = new Parser(); 18 | var doc = parser.Parse(tmp); 19 | var tuple = doc.Root as TupleElement; 20 | if (tuple != null && tuple.Children.Count > 0) { 21 | return tuple.Children[0]; 22 | } 23 | return new EmptyElement(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Formatting.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ParksComputing.Xfer.Lang; 8 | 9 | /// 10 | /// Specifies formatting options for XferLang document serialization. 11 | /// These options control how the output text is structured and formatted. 12 | /// 13 | [Flags] 14 | public enum Formatting { 15 | /// 16 | /// No special formatting applied. Output is compact with minimal whitespace. 17 | /// 18 | None = 0x00, 19 | 20 | /// 21 | /// Apply indentation to nested elements for improved readability. 22 | /// 23 | Indented = 0x01, 24 | 25 | /// 26 | /// Add spacing between elements for better visual separation. 27 | /// 28 | Spaced = 0x10, 29 | 30 | /// 31 | /// Combines indented and spaced formatting for maximum readability. 32 | /// Equivalent to | . 33 | /// 34 | Pretty = Indented | Spaced 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Paul M. Parks 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Configuration/XferProcessingInstruction.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ParksComputing.Xfer.Lang.Configuration 4 | { 5 | /// 6 | /// Represents a processing instruction in an XferLang document. 7 | /// 8 | public class XferProcessingInstruction 9 | { 10 | /// 11 | /// The type or name of the processing instruction (e.g., "processor", "id", "culture"). 12 | /// 13 | public required string Type { get; set; } 14 | 15 | /// 16 | /// Parameters for the processing instruction, as parsed from the document. 17 | /// 18 | public Dictionary Parameters { get; set; } = []; 19 | 20 | /// 21 | /// Reference to the element this PI applies to (if any). 22 | /// 23 | public Elements.Element? AppliesTo { get; set; } 24 | 25 | /// 26 | /// Optional: position of the PI in the document. 27 | /// 28 | public int DocumentIndex { get; set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Schema/XferSchema.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ParksComputing.Xfer.Lang.Schema; 8 | 9 | /// 10 | /// Represents an XferLang schema that defines the structure, validation rules, 11 | /// and constraints for XferLang documents. Contains schema definitions, 12 | /// type specifications, and validation metadata. 13 | /// 14 | public class XferSchema { 15 | /// 16 | /// Gets or sets the name of the schema. 17 | /// 18 | public string Name { get; set; } = string.Empty; 19 | 20 | /// 21 | /// Gets or sets an optional description of the schema's purpose and usage. 22 | /// 23 | public string? Description { get; set; } 24 | 25 | /// 26 | /// Gets or sets the collection of schema definitions contained in this schema. 27 | /// Maps definition names to their corresponding SchemaDefinition objects. 28 | /// 29 | public Dictionary Definitions { get; set; } = []; 30 | } 31 | -------------------------------------------------------------------------------- /examples/if-pi-demo/test-removal.xfer: -------------------------------------------------------------------------------- 1 | ( 2 | 3 | !> 4 | shouldBeIncluded { 5 | reason "DEBUG_MODE is true" 6 | value "this should appear" 7 | } 8 | 9 | 10 | !> 11 | shouldBeRemoved { 12 | reason "NONEXISTENT_VAR is undefined" 13 | value "this should NOT appear" 14 | } 15 | 16 | 17 | !> 18 | shouldAlsoBeRemoved { 19 | reason "FALSE_BOOLEAN exists but is false" 20 | value "this should NOT appear either" 21 | } 22 | 23 | 24 | !> 25 | shouldBeRemovedToo { 26 | reason "EMPTY_STRING exists but is falsy" 27 | value "this should NOT appear" 28 | } 29 | 30 | 31 | alwaysIncluded { 32 | reason "no condition" 33 | value "this should always appear" 34 | } 35 | ) 36 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Paul M. Parks 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /docs/highlightjs/styles/default.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Theme: Default 3 | Description: Original highlight.js style 4 | Author: (c) Ivan Sagalaev 5 | Maintainer: @highlightjs/core-team 6 | Website: https://highlightjs.org/ 7 | License: see project LICENSE 8 | Touched: 2021 9 | */pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#f3f3f3;color:#444}.hljs-comment{color:#697070}.hljs-punctuation,.hljs-tag{color:#444a}.hljs-tag .hljs-attr,.hljs-tag .hljs-name{color:#444}.hljs-attribute,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-name,.hljs-selector-tag{font-weight:700}.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-template-tag,.hljs-type{color:#800}.hljs-section,.hljs-title{color:#800;font-weight:700}.hljs-link,.hljs-operator,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#ab5656}.hljs-literal{color:#695}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-code{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta .hljs-string{color:#38a}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700} -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/DateTimeHandling.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ParksComputing.Xfer.Lang; 8 | 9 | /// 10 | /// Specifies how DateTime values should be handled during serialization and deserialization. 11 | /// Controls time zone information and formatting in the resulting XferLang output. 12 | /// 13 | public enum DateTimeHandling { 14 | /// 15 | /// Serialize without time zone information using ISO format. 16 | /// 17 | Unspecified, // Serialize without time zone (ISO format) 18 | 19 | /// 20 | /// Serialize with local time zone information. 21 | /// 22 | Local, // Serialize with local time zone 23 | 24 | /// 25 | /// Serialize with UTC time zone information. 26 | /// 27 | Utc, // Serialize with UTC time zone 28 | 29 | /// 30 | /// Serialize preserving the original DateTimeOffset information for round-trip fidelity. 31 | /// 32 | RoundTrip // Serialize preserving the original offset 33 | } 34 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang.Tests/ParksComputing.Xfer.Lang.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | latest 6 | enable 7 | enable 8 | false 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | all 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Configuration/ElementStylePreference.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ParksComputing.Xfer.Lang.Configuration 4 | { 5 | /// 6 | /// Defines preferences for element serialization styles. 7 | /// 8 | public enum ElementStylePreference 9 | { 10 | /// 11 | /// Use explicit style for maximum safety and compatibility (default). 12 | /// Strings use angle brackets: <"value"> 13 | /// 14 | Explicit, 15 | 16 | /// 17 | /// Use compact style when safe, explicit when necessary. 18 | /// Strings use quotes: "value" (when safe) 19 | /// 20 | CompactWhenSafe, 21 | 22 | /// 23 | /// Use most compact form possible, including implicit syntax. 24 | /// Integers without # prefix, strings without quotes when possible. 25 | /// 26 | MinimalWhenSafe, 27 | 28 | /// 29 | /// Always use compact style, even if potentially unsafe. 30 | /// Use with caution - may produce unparseable output. 31 | /// 32 | ForceCompact 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /docs/highlightjs/languages/xfer.min.js: -------------------------------------------------------------------------------- 1 | /*! `xfer` grammar compiled for Highlight.js 11.7.0 */ 2 | (()=>{var e=(()=>{"use strict";return e=>{const t=/[a-zA-Z_][a-zA-Z0-9_]*/,n=/[+-]?\d+(\.\d+)?([eE][+-]?\d+)?/,a=/0[xX][0-9a-fA-F]+/,r=/0[bB][01]+/;return{name:"XferLang",aliases:["xfer"],case_insensitive:!1,contains:[{className:"string",begin:/<"/,end:/">/,relevance:10},{className:"string",begin:/"/,end:/"/,relevance:10},{className:"string",begin:/'/,end:/'/,relevance:10},{className:"comment",begin:/<\//,end:/\/>/,relevance:5},{className:"meta",begin://,relevance:5},{className:"number",begin:/@\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{1,3})?(Z|[+-]\d{2}:\d{2})?)?@/,relevance:5},{className:"number", begin:/@P(?:\d+Y)?(?:\d+M)?(?:\d+D)?(?:T(?:\d+H)?(?:\d+M)?(?:\d+(?:\.\d+)?S)?)?@/,relevance:5},{className:"literal",begin:/\b(true|false)\b/,relevance:4},{className:"built_in",begin:/\b(newline|tab|space|cr|lf|crlf)\b/,relevance:4},{className:"number",begin:a,relevance:3},{className:"number",begin:r,relevance:3},{className:"number",begin:n,relevance:2},{className:"operator",begin:/[#*~?@\\$]/,relevance:1},{className:"punctuation",begin:/[\[\]{}()<>]/,relevance:1},{className:"attr",begin:t,relevance:0}]}}})();hljs.registerLanguage("xfer",e)})(); 3 | -------------------------------------------------------------------------------- /docs/highlightjs/es/languages/xfer.min.js: -------------------------------------------------------------------------------- 1 | /*! `xfer` grammar compiled for Highlight.js 11.7.0 */ 2 | (()=>{var e=(()=>{"use strict";return e=>{const t=/[a-zA-Z_][a-zA-Z0-9_]*/,n=/[+-]?\d+(\.\d+)?([eE][+-]?\d+)?/,a=/0[xX][0-9a-fA-F]+/,r=/0[bB][01]+/;return{name:"XferLang",aliases:["xfer"],case_insensitive:!1,contains:[{className:"string",begin:/<"/,end:/">/,relevance:10},{className:"string",begin:/"/,end:/"/,relevance:10},{className:"string",begin:/'/,end:/'/,relevance:10},{className:"comment",begin:/<\//,end:/\/>/,relevance:5},{className:"meta",begin://,relevance:5},{className:"number",begin:/@\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{1,3})?(Z|[+-]\d{2}:\d{2})?)?@/,relevance:5},{className:"number", begin:/@P(?:\d+Y)?(?:\d+M)?(?:\d+D)?(?:T(?:\d+H)?(?:\d+M)?(?:\d+(?:\.\d+)?S)?)?@/,relevance:5},{className:"literal",begin:/\b(true|false)\b/,relevance:4},{className:"built_in",begin:/\b(newline|tab|space|cr|lf|crlf)\b/,relevance:4},{className:"number",begin:a,relevance:3},{className:"number",begin:r,relevance:3},{className:"number",begin:n,relevance:2},{className:"operator",begin:/[#*~?@\\$]/,relevance:1},{className:"punctuation",begin:/[\[\]{}()<>]/,relevance:1},{className:"attr",begin:t,relevance:0}]}}})();hljs.registerLanguage("xfer",e)})(); 3 | -------------------------------------------------------------------------------- /docs/highlightjs/es/languages/go.min.js: -------------------------------------------------------------------------------- 1 | /*! `go` grammar compiled for Highlight.js 11.7.0 */ 2 | var hljsGrammar=(()=>{"use strict";return e=>{const n={ 3 | keyword:["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"], 4 | type:["bool","byte","complex64","complex128","error","float32","float64","int8","int16","int32","int64","string","uint8","uint16","uint32","uint64","int","uint","uintptr","rune"], 5 | literal:["true","false","iota","nil"], 6 | built_in:["append","cap","close","complex","copy","imag","len","make","new","panic","print","println","real","recover","delete"] 7 | };return{name:"Go",aliases:["golang"],keywords:n,illegal:"{var e=(()=>{"use strict";return e=>{const n={ 3 | keyword:["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"], 4 | type:["bool","byte","complex64","complex128","error","float32","float64","int8","int16","int32","int64","string","uint8","uint16","uint32","uint64","int","uint","uintptr","rune"], 5 | literal:["true","false","iota","nil"], 6 | built_in:["append","cap","close","complex","copy","imag","len","make","new","panic","print","println","real","recover","delete"] 7 | };return{name:"Go",aliases:["golang"],keywords:n,illegal:" { debug { level \"verbose\" } }"); 13 | var root = doc.Root.ToXfer(); 14 | // Expect empty root collection (object removed) or absence of 'debug' 15 | Assert.IsFalse(root.Contains("debug"), $"Debug object should be suppressed. Output: {root}"); 16 | } 17 | 18 | [TestMethod] 19 | public void IfPI_DereferenceBinding_True_IncludesElement() { 20 | var parser = new Parser(); 21 | var doc = parser.Parse(" { debug { level \"verbose\" } }"); 22 | var root = doc.Root.ToXfer(); 23 | Assert.IsTrue(root.Contains("debug"), $"Debug object should be present. Output: {root}"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /docs/highlightjs/styles/tokyo-night-dark.min.css: -------------------------------------------------------------------------------- 1 | pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*! 2 | Theme: Tokyo-night-Dark 3 | origin: https://github.com/enkia/tokyo-night-vscode-theme 4 | Description: Original highlight.js style 5 | Author: (c) Henri Vandersleyen 6 | License: see project LICENSE 7 | Touched: 2022 8 | */.hljs-comment,.hljs-meta{color:#565f89}.hljs-deletion,.hljs-doctag,.hljs-regexp,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-tag,.hljs-template-tag,.hljs-variable.language_{color:#f7768e}.hljs-link,.hljs-literal,.hljs-number,.hljs-params,.hljs-template-variable,.hljs-type,.hljs-variable{color:#ff9e64}.hljs-attribute,.hljs-built_in{color:#e0af68}.hljs-keyword,.hljs-property,.hljs-subst,.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#7dcfff}.hljs-selector-tag{color:#73daca}.hljs-addition,.hljs-bullet,.hljs-quote,.hljs-string,.hljs-symbol{color:#9ece6a}.hljs-code,.hljs-formula,.hljs-section{color:#7aa2f7}.hljs-attr,.hljs-char.escape_,.hljs-keyword,.hljs-name,.hljs-operator{color:#bb9af7}.hljs-punctuation{color:#c0caf5}.hljs{background:#1a1b26;color:#9aa5ce}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700} -------------------------------------------------------------------------------- /docs/highlightjs/styles/tokyo-night-light.min.css: -------------------------------------------------------------------------------- 1 | pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*! 2 | Theme: Tokyo-night-light 3 | origin: https://github.com/enkia/tokyo-night-vscode-theme 4 | Description: Original highlight.js style 5 | Author: (c) Henri Vandersleyen 6 | License: see project LICENSE 7 | Touched: 2022 8 | */.hljs-comment,.hljs-meta{color:#9699a3}.hljs-deletion,.hljs-doctag,.hljs-regexp,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-tag,.hljs-template-tag,.hljs-variable.language_{color:#8c4351}.hljs-link,.hljs-literal,.hljs-number,.hljs-params,.hljs-template-variable,.hljs-type,.hljs-variable{color:#965027}.hljs-attribute,.hljs-built_in{color:#8f5e15}.hljs-keyword,.hljs-property,.hljs-subst,.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#0f4b6e}.hljs-selector-tag{color:#33635c}.hljs-addition,.hljs-bullet,.hljs-quote,.hljs-string,.hljs-symbol{color:#485e30}.hljs-code,.hljs-formula,.hljs-section{color:#34548a}.hljs-attr,.hljs-char.escape_,.hljs-keyword,.hljs-name,.hljs-operator{color:#5a4a78}.hljs-punctuation{color:#343b58}.hljs{background:#d5d6db;color:#565a6e}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700} -------------------------------------------------------------------------------- /examples/xfer2yaml/README.md: -------------------------------------------------------------------------------- 1 | # Xfer ↔ YAML (GitHub Actions example) 2 | 3 | This example shows how to represent a GitHub Actions workflow YAML in XferLang, and sketches a path for tooling to convert between the two. 4 | 5 | ## Why manage YAML with XferLang? 6 | 7 | - Explicit typing and unambiguous syntax 8 | - Structural interpolation and bindings (avoid copy-paste in large workflows) 9 | - Conditional inclusion (e.g., feature flags) with `` 10 | - Dynamic values via `|KEY|` (environment/file/const resolvers) 11 | - Semantic formatting and linting (future: `xferc fmt`, `xferc lint`) 12 | 13 | ## Proposed tool surface 14 | 15 | - CLI: `xferc yaml2xfer -o ` and `xferc xfer2yaml -o ` 16 | - API: `YamlConvert.Serialize(Element)` and `YamlConvert.Deserialize(string)` (built on YamlDotNet) 17 | - Options: 18 | - Preserve ordering for readability (stable key ordering by known schemas) 19 | - Style preferences (compact/explicit) 20 | - Policy flags to disable dynamic/file resolution during materialization 21 | 22 | ## Files 23 | 24 | - `dotnet.yml` – source GitHub Actions workflow 25 | - `dotnet.xfer` – equivalent XferLang representation 26 | 27 | Round-trip is intended to be lossless for data; comments/anchors would require extra mapping conventions. 28 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Attributes/XferNumericFormatAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ParksComputing.Xfer.Lang.Attributes; 4 | 5 | /// 6 | /// Specifies how a numeric property should be formatted when serialized to XferLang. 7 | /// 8 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] 9 | public class XferNumericFormatAttribute : Attribute 10 | { 11 | /// 12 | /// The format to use for numeric serialization. 13 | /// 14 | public XferNumericFormat Format { get; } 15 | 16 | /// 17 | /// For binary format, specifies the minimum number of bits to display. 18 | /// Default is to use the minimum required bits. 19 | /// 20 | public int MinBits { get; set; } = 0; 21 | 22 | /// 23 | /// For hex format, specifies the minimum number of hex digits to display. 24 | /// Default is to use the minimum required digits. 25 | /// 26 | public int MinDigits { get; set; } = 0; 27 | 28 | /// 29 | /// Initializes a new instance of the XferNumericFormatAttribute. 30 | /// 31 | /// The numeric format to use. 32 | public XferNumericFormatAttribute(XferNumericFormat format) 33 | { 34 | Format = format; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /docs/highlightjs/styles/github.min.css: -------------------------------------------------------------------------------- 1 | pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*! 2 | Theme: GitHub 3 | Description: Light theme as seen on github.com 4 | Author: github.com 5 | Maintainer: @Hirse 6 | Updated: 2021-05-15 7 | 8 | Outdated base version: https://github.com/primer/github-syntax-light 9 | Current colors taken from GitHub's CSS 10 | */.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#005cc5}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-code,.hljs-comment,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0} -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang.Tests/ParserFragmentTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using ParksComputing.Xfer.Lang.Elements; 4 | using ParksComputing.Xfer.Lang.Services; 5 | 6 | namespace ParksComputing.Xfer.Lang.Tests; 7 | 8 | [TestClass] 9 | public class ParserFragmentTests 10 | { 11 | [TestMethod] 12 | public void ParseFragment_String_ReturnsStringElement() 13 | { 14 | var parser = new Parser(); 15 | var elem = parser.ParseFragment("<\"hello\">"); 16 | Assert.IsInstanceOfType(elem, typeof(StringElement)); 17 | Assert.AreEqual("hello", ((StringElement)elem).Value); 18 | } 19 | 20 | [TestMethod] 21 | public void ParseFragmentMany_TwoStrings_ReturnsTupleWithTwoChildren() 22 | { 23 | var parser = new Parser(); 24 | var tuple = parser.ParseFragmentMany("<\"a\"> <\"b\">"); 25 | Assert.AreEqual(2, tuple.Children.Count); 26 | Assert.AreEqual("a", ((StringElement)tuple.Children[0]).Value); 27 | Assert.AreEqual("b", ((StringElement)tuple.Children[1]).Value); 28 | } 29 | 30 | [TestMethod] 31 | public void ParseFragment_TrailingContent_Throws() 32 | { 33 | var parser = new Parser(); 34 | Assert.ThrowsException(() => parser.ParseFragment("<\"a\"> junk")); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /docs/highlightjs/styles/base16/github.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Theme: Github 3 | Author: Defman21 4 | License: ~ MIT (or more permissive) [via base16-schemes-source] 5 | Maintainer: @highlightjs/core-team 6 | Version: 2021.09.0 7 | */pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#333;background:#fff}.hljs ::selection,.hljs::selection{background-color:#c8c8fa;color:#333}.hljs-comment{color:#969896}.hljs-tag{color:#e8e8e8}.hljs-operator,.hljs-punctuation,.hljs-subst{color:#333}.hljs-operator{opacity:.7}.hljs-bullet,.hljs-deletion,.hljs-name,.hljs-selector-tag,.hljs-template-variable,.hljs-variable{color:#ed6a43}.hljs-attr,.hljs-link,.hljs-literal,.hljs-number,.hljs-symbol,.hljs-variable.constant_{color:#0086b3}.hljs-class .hljs-title,.hljs-title,.hljs-title.class_{color:#795da3}.hljs-strong{font-weight:700;color:#795da3}.hljs-addition,.hljs-built_in,.hljs-code,.hljs-doctag,.hljs-keyword.hljs-atrule,.hljs-quote,.hljs-regexp,.hljs-string,.hljs-title.class_.inherited__{color:#183691}.hljs-attribute,.hljs-function .hljs-title,.hljs-section,.hljs-title.function_,.ruby .hljs-property{color:#795da3}.diff .hljs-meta,.hljs-keyword,.hljs-template-tag,.hljs-type{color:#a71d5d}.hljs-emphasis{color:#a71d5d;font-style:italic}.hljs-meta,.hljs-meta .hljs-keyword,.hljs-meta .hljs-string{color:#333}.hljs-meta .hljs-keyword,.hljs-meta-keyword{font-weight:700} -------------------------------------------------------------------------------- /docs/highlightjs/styles/github-dark.min.css: -------------------------------------------------------------------------------- 1 | pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*! 2 | Theme: GitHub Dark 3 | Description: Dark theme as seen on github.com 4 | Author: github.com 5 | Maintainer: @Hirse 6 | Updated: 2021-05-15 7 | 8 | Outdated base version: https://github.com/primer/github-syntax-dark 9 | Current colors taken from GitHub's CSS 10 | */.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#79c0ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-code,.hljs-comment,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c} -------------------------------------------------------------------------------- /sample.xfer: -------------------------------------------------------------------------------- 1 | 2 | ( 3 | { 4 | name "Alice" 5 | age #30 6 | isMember ~true 7 | scores [*85 *90 *78.5] 8 | profile { 9 | email "alice@example.com" 10 | joinedDate @2023-05-05T12:00:00@ 11 | } 12 | settings { 13 | theme "dark" 14 | notifications { 15 | email ~true 16 | sms ~false 17 | } 18 | preferences [ 19 | { 20 | key "timezone" 21 | value "UTC+0" 22 | } 23 | { 24 | key "language" 25 | value "en" 26 | } 27 | ] 28 | } 29 | items [ 30 | { 31 | id #1 32 | name "Item 1" 33 | price *19.99 34 | } 35 | { 36 | id #2 37 | name "Item 2" 38 | price *5.49 39 | } 40 | { 41 | id #3 42 | name "Item 3" 43 | price *10.0 44 | } 45 | ] 46 | tags ["tag1" "tag2" "tag3"] 47 | metadata{ 48 | created @2024-11-10T08:00:00@ 49 | modified <@2024-11-11T12:30:00@> 50 | } 51 | } 52 | ) 53 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/ProcessingInstructions/PropertiesProcessingInstruction.cs: -------------------------------------------------------------------------------- 1 | using ParksComputing.Xfer.Lang.Elements; 2 | 3 | namespace ParksComputing.Xfer.Lang.ProcessingInstructions; 4 | 5 | /// 6 | /// Processing instruction for defining custom properties in XferLang documents. 7 | /// Allows specification of additional property-value pairs that can be used for 8 | /// application-specific configuration, validation rules, or other custom behaviors. 9 | /// 10 | public class PropertiesProcessingInstruction : ProcessingInstruction { 11 | /// 12 | /// The keyword used to identify properties processing instructions. 13 | /// 14 | public const string Keyword = "properties"; 15 | 16 | /// 17 | /// Gets a dictionary of custom properties defined in this processing instruction. 18 | /// Maps property names to their corresponding string values. 19 | /// 20 | public Dictionary CustomProperties { get; } = new(); 21 | 22 | /// 23 | /// Initializes a new instance of the PropertiesProcessingInstruction class with the specified properties object. 24 | /// 25 | /// The object element containing custom property definitions. 26 | public PropertiesProcessingInstruction(ObjectElement value) : base(value, Keyword) { } 27 | } 28 | -------------------------------------------------------------------------------- /XferLangVSCode/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xferlang", 3 | "displayName": "XferLang", 4 | "description": "Syntax highlighting and language basics for XferLang (.xfer) – delimiter lengthening, processing instructions, dynamic & interpolation.", 5 | "version": "0.15.4", 6 | "publisher": "paulmooreparks", 7 | "license": "MIT", 8 | "homepage": "https://xferlang.org/", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/paulmooreparks/Xfer.git" 12 | }, 13 | "bugs": { 14 | "url": "https://github.com/paulmooreparks/Xfer/issues" 15 | }, 16 | "engines": { 17 | "vscode": "^1.102.0" 18 | }, 19 | "categories": [ 20 | "Programming Languages" 21 | ], 22 | "keywords": [ 23 | "xferlang", 24 | "configuration", 25 | "data-format", 26 | "processing-instruction", 27 | "dynamic", 28 | "interpolation" 29 | ], 30 | "icon": "XferLang-128.png", 31 | "galleryBanner": { 32 | "color": "#0b1b2b", 33 | "theme": "dark" 34 | }, 35 | "contributes": { 36 | "languages": [{ 37 | "id": "xferlang", 38 | "aliases": ["XferLang", "xferlang"], 39 | "extensions": ["xfer"], 40 | "configuration": "./language-configuration.json" 41 | }], 42 | "grammars": [{ 43 | "language": "xferlang", 44 | "scopeName": "source.xferlang", 45 | "path": "./syntaxes/xferlang.tmLanguage.json" 46 | }] 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # root = true tells editors this is the root of the project 2 | root = true 3 | 4 | # All files 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [.yml] 14 | indent_size = 2 15 | indent_style = space 16 | 17 | # C# files 18 | [*.cs] 19 | # Indentation and spacing 20 | csharp_indent_braces = false 21 | csharp_indent_case_contents = true 22 | csharp_indent_labels = one_less_than_current 23 | csharp_new_line_before_open_brace = none 24 | csharp_new_line_before_else = true 25 | csharp_new_line_before_catch = true 26 | csharp_new_line_before_finally = true 27 | csharp_new_line_before_members_in_object_initializers = true 28 | csharp_new_line_between_query_expression_clauses = true 29 | csharp_prefer_braces = true:error 30 | 31 | # Space preferences 32 | csharp_space_after_cast = true 33 | csharp_space_after_keywords_in_control_flow_statements = true 34 | csharp_space_between_parentheses = false 35 | csharp_space_before_colon_in_inheritance_clause = true 36 | csharp_space_after_colon_in_inheritance_clause = true 37 | csharp_space_around_binary_operators = before_and_after 38 | 39 | # Using directive preferences 40 | csharp_using_directive_placement = outside_namespace 41 | namespace_placement = outside_namespace 42 | namespace_braces = same_line 43 | namespace_new_line_before_open_brace = false 44 | -------------------------------------------------------------------------------- /docs/highlightjs/styles/base16/monokai.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Theme: Monokai 3 | Author: Wimer Hazenberg (http://www.monokai.nl) 4 | License: ~ MIT (or more permissive) [via base16-schemes-source] 5 | Maintainer: @highlightjs/core-team 6 | Version: 2021.09.0 7 | */pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#f8f8f2;background:#272822}.hljs ::selection,.hljs::selection{background-color:#49483e;color:#f8f8f2}.hljs-comment{color:#75715e}.hljs-tag{color:#a59f85}.hljs-operator,.hljs-punctuation,.hljs-subst{color:#f8f8f2}.hljs-operator{opacity:.7}.hljs-bullet,.hljs-deletion,.hljs-name,.hljs-selector-tag,.hljs-template-variable,.hljs-variable{color:#f92672}.hljs-attr,.hljs-link,.hljs-literal,.hljs-number,.hljs-symbol,.hljs-variable.constant_{color:#fd971f}.hljs-class .hljs-title,.hljs-title,.hljs-title.class_{color:#f4bf75}.hljs-strong{font-weight:700;color:#f4bf75}.hljs-addition,.hljs-code,.hljs-string,.hljs-title.class_.inherited__{color:#a6e22e}.hljs-built_in,.hljs-doctag,.hljs-keyword.hljs-atrule,.hljs-quote,.hljs-regexp{color:#a1efe4}.hljs-attribute,.hljs-function .hljs-title,.hljs-section,.hljs-title.function_,.ruby .hljs-property{color:#66d9ef}.diff .hljs-meta,.hljs-keyword,.hljs-template-tag,.hljs-type{color:#ae81ff}.hljs-emphasis{color:#ae81ff;font-style:italic}.hljs-meta,.hljs-meta .hljs-keyword,.hljs-meta .hljs-string{color:#c63}.hljs-meta .hljs-keyword,.hljs-meta-keyword{font-weight:700} -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Schema/SchemaField.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | using ParksComputing.Xfer.Lang.Elements; 8 | 9 | namespace ParksComputing.Xfer.Lang.Schema; 10 | 11 | /// 12 | /// Represents a field definition within a schema object or structure. 13 | /// Defines the field's name, type, requirement status, and optional custom validation logic. 14 | /// 15 | public class SchemaField { 16 | /// 17 | /// Gets or sets the name of the schema field. 18 | /// 19 | public string Name { get; set; } = string.Empty; 20 | 21 | /// 22 | /// Gets or sets the data type of the field (e.g., "string", "integer", "boolean"). 23 | /// 24 | public string Type { get; set; } = string.Empty; // Data type: string, integer, etc. 25 | 26 | /// 27 | /// Gets or sets a value indicating whether this field is required in the schema. 28 | /// 29 | public bool IsRequired { get; set; } = false; 30 | 31 | /// 32 | /// Gets or sets an optional custom validation function for complex field validation scenarios. 33 | /// The function receives a ListElement and returns true if validation passes. 34 | /// 35 | public Func? CustomValidation { get; set; } = null; 36 | } 37 | -------------------------------------------------------------------------------- /docs/highlightjs/styles/base16/default-dark.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Theme: Default Dark 3 | Author: Chris Kempson (http://chriskempson.com) 4 | License: ~ MIT (or more permissive) [via base16-schemes-source] 5 | Maintainer: @highlightjs/core-team 6 | Version: 2021.09.0 7 | */pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#d8d8d8;background:#181818}.hljs ::selection,.hljs::selection{background-color:#383838;color:#d8d8d8}.hljs-comment{color:#585858}.hljs-tag{color:#b8b8b8}.hljs-operator,.hljs-punctuation,.hljs-subst{color:#d8d8d8}.hljs-operator{opacity:.7}.hljs-bullet,.hljs-deletion,.hljs-name,.hljs-selector-tag,.hljs-template-variable,.hljs-variable{color:#ab4642}.hljs-attr,.hljs-link,.hljs-literal,.hljs-number,.hljs-symbol,.hljs-variable.constant_{color:#dc9656}.hljs-class .hljs-title,.hljs-title,.hljs-title.class_{color:#f7ca88}.hljs-strong{font-weight:700;color:#f7ca88}.hljs-addition,.hljs-code,.hljs-string,.hljs-title.class_.inherited__{color:#a1b56c}.hljs-built_in,.hljs-doctag,.hljs-keyword.hljs-atrule,.hljs-quote,.hljs-regexp{color:#86c1b9}.hljs-attribute,.hljs-function .hljs-title,.hljs-section,.hljs-title.function_,.ruby .hljs-property{color:#7cafc2}.diff .hljs-meta,.hljs-keyword,.hljs-template-tag,.hljs-type{color:#ba8baf}.hljs-emphasis{color:#ba8baf;font-style:italic}.hljs-meta,.hljs-meta .hljs-keyword,.hljs-meta .hljs-string{color:#a16946}.hljs-meta .hljs-keyword,.hljs-meta-keyword{font-weight:700} -------------------------------------------------------------------------------- /docs/highlightjs/styles/base16/default-light.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Theme: Default Light 3 | Author: Chris Kempson (http://chriskempson.com) 4 | License: ~ MIT (or more permissive) [via base16-schemes-source] 5 | Maintainer: @highlightjs/core-team 6 | Version: 2021.09.0 7 | */pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#383838;background:#f8f8f8}.hljs ::selection,.hljs::selection{background-color:#d8d8d8;color:#383838}.hljs-comment{color:#b8b8b8}.hljs-tag{color:#585858}.hljs-operator,.hljs-punctuation,.hljs-subst{color:#383838}.hljs-operator{opacity:.7}.hljs-bullet,.hljs-deletion,.hljs-name,.hljs-selector-tag,.hljs-template-variable,.hljs-variable{color:#ab4642}.hljs-attr,.hljs-link,.hljs-literal,.hljs-number,.hljs-symbol,.hljs-variable.constant_{color:#dc9656}.hljs-class .hljs-title,.hljs-title,.hljs-title.class_{color:#f7ca88}.hljs-strong{font-weight:700;color:#f7ca88}.hljs-addition,.hljs-code,.hljs-string,.hljs-title.class_.inherited__{color:#a1b56c}.hljs-built_in,.hljs-doctag,.hljs-keyword.hljs-atrule,.hljs-quote,.hljs-regexp{color:#86c1b9}.hljs-attribute,.hljs-function .hljs-title,.hljs-section,.hljs-title.function_,.ruby .hljs-property{color:#7cafc2}.diff .hljs-meta,.hljs-keyword,.hljs-template-tag,.hljs-type{color:#ba8baf}.hljs-emphasis{color:#ba8baf;font-style:italic}.hljs-meta,.hljs-meta .hljs-keyword,.hljs-meta .hljs-string{color:#a16946}.hljs-meta .hljs-keyword,.hljs-meta-keyword{font-weight:700} -------------------------------------------------------------------------------- /docs/highlightjs/styles/base16/solarized-dark.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Theme: Solarized Dark 3 | Author: Ethan Schoonover (modified by aramisgithub) 4 | License: ~ MIT (or more permissive) [via base16-schemes-source] 5 | Maintainer: @highlightjs/core-team 6 | Version: 2021.09.0 7 | */pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#93a1a1;background:#002b36}.hljs ::selection,.hljs::selection{background-color:#586e75;color:#93a1a1}.hljs-comment{color:#657b83}.hljs-tag{color:#839496}.hljs-operator,.hljs-punctuation,.hljs-subst{color:#93a1a1}.hljs-operator{opacity:.7}.hljs-bullet,.hljs-deletion,.hljs-name,.hljs-selector-tag,.hljs-template-variable,.hljs-variable{color:#dc322f}.hljs-attr,.hljs-link,.hljs-literal,.hljs-number,.hljs-symbol,.hljs-variable.constant_{color:#cb4b16}.hljs-class .hljs-title,.hljs-title,.hljs-title.class_{color:#b58900}.hljs-strong{font-weight:700;color:#b58900}.hljs-addition,.hljs-code,.hljs-string,.hljs-title.class_.inherited__{color:#859900}.hljs-built_in,.hljs-doctag,.hljs-keyword.hljs-atrule,.hljs-quote,.hljs-regexp{color:#2aa198}.hljs-attribute,.hljs-function .hljs-title,.hljs-section,.hljs-title.function_,.ruby .hljs-property{color:#268bd2}.diff .hljs-meta,.hljs-keyword,.hljs-template-tag,.hljs-type{color:#6c71c4}.hljs-emphasis{color:#6c71c4;font-style:italic}.hljs-meta,.hljs-meta .hljs-keyword,.hljs-meta .hljs-string{color:#d33682}.hljs-meta .hljs-keyword,.hljs-meta-keyword{font-weight:700} -------------------------------------------------------------------------------- /docs/highlightjs/styles/base16/solarized-light.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Theme: Solarized Light 3 | Author: Ethan Schoonover (modified by aramisgithub) 4 | License: ~ MIT (or more permissive) [via base16-schemes-source] 5 | Maintainer: @highlightjs/core-team 6 | Version: 2021.09.0 7 | */pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#586e75;background:#fdf6e3}.hljs ::selection,.hljs::selection{background-color:#93a1a1;color:#586e75}.hljs-comment{color:#839496}.hljs-tag{color:#657b83}.hljs-operator,.hljs-punctuation,.hljs-subst{color:#586e75}.hljs-operator{opacity:.7}.hljs-bullet,.hljs-deletion,.hljs-name,.hljs-selector-tag,.hljs-template-variable,.hljs-variable{color:#dc322f}.hljs-attr,.hljs-link,.hljs-literal,.hljs-number,.hljs-symbol,.hljs-variable.constant_{color:#cb4b16}.hljs-class .hljs-title,.hljs-title,.hljs-title.class_{color:#b58900}.hljs-strong{font-weight:700;color:#b58900}.hljs-addition,.hljs-code,.hljs-string,.hljs-title.class_.inherited__{color:#859900}.hljs-built_in,.hljs-doctag,.hljs-keyword.hljs-atrule,.hljs-quote,.hljs-regexp{color:#2aa198}.hljs-attribute,.hljs-function .hljs-title,.hljs-section,.hljs-title.function_,.ruby .hljs-property{color:#268bd2}.diff .hljs-meta,.hljs-keyword,.hljs-template-tag,.hljs-type{color:#6c71c4}.hljs-emphasis{color:#6c71c4;font-style:italic}.hljs-meta,.hljs-meta .hljs-keyword,.hljs-meta .hljs-string{color:#d33682}.hljs-meta .hljs-keyword,.hljs-meta-keyword{font-weight:700} -------------------------------------------------------------------------------- /schemas/blog_post_inline.xfer: -------------------------------------------------------------------------------- 1 | 31 | 32 | { 33 | title "Introduction to Xfer" 34 | content <"" 35 | 36 | 37 | Introduction to Xfer 38 | 39 | 40 |

This is the blog content

41 | 42 | ""> 43 | publishedDate @2020-12-06T13:53:00@ 44 | author { 45 | username "paulmooreparks" 46 | email "paul@parkscomputing.com" 47 | fullname "Paul Moore Parks" 48 | age 54 49 | location "Singapore" 50 | interests ["guitar" "running" "weightlifting"] 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/ContractResolvers/IContractResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Reflection; 7 | 8 | namespace ParksComputing.Xfer.Lang.ContractResolvers { 9 | /// 10 | /// Defines contract resolution for XferLang serialization. Contract resolvers determine 11 | /// which properties to serialize and how property names should be transformed during 12 | /// the serialization process. 13 | /// 14 | public interface IContractResolver { 15 | /// 16 | /// Resolves which properties of a type should be included in serialization. 17 | /// Returns a list of PropertyInfo objects representing the properties to serialize. 18 | /// 19 | /// The type to resolve properties for. 20 | /// A list of properties to include in serialization. 21 | List ResolveProperties(Type type); 22 | 23 | /// 24 | /// Resolves the name to use for a property during serialization. 25 | /// Allows transformation of property names (e.g., camelCase conversion). 26 | /// 27 | /// The original property name. 28 | /// The transformed property name to use in the serialized output. 29 | string ResolvePropertyName(string propertyName); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/ContractResolvers/DefaultContractResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace ParksComputing.Xfer.Lang.ContractResolvers { 7 | /// 8 | /// Default implementation of contract resolution for XferLang serialization. 9 | /// Resolves public instance properties and preserves original property names 10 | /// without modification. Provides the standard behavior for most serialization scenarios. 11 | /// 12 | public class DefaultContractResolver : IContractResolver { 13 | /// 14 | /// Resolves all public instance properties of the specified type for serialization. 15 | /// 16 | /// The type to resolve properties for. 17 | /// A list of all public instance properties. 18 | public List ResolveProperties(Type type) { 19 | return type.GetProperties(BindingFlags.Public | BindingFlags.Instance).ToList(); 20 | } 21 | 22 | /// 23 | /// Returns the property name unchanged. Override to implement custom name transformation. 24 | /// 25 | /// The original property name. 26 | /// The unmodified property name. 27 | public virtual string ResolvePropertyName(string propertyName) { 28 | return propertyName; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Types/XferKeyedValue.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using ParksComputing.Xfer.Lang.Elements; 3 | 4 | namespace ParksComputing.Xfer.Lang { 5 | /// 6 | /// Represents a chained keyword/value path with a terminal payload element. 7 | /// For example, the Xfer value javascript <"scriptBody()"> maps to Keys=["javascript"], Payload=StringElement("scriptBody()"). 8 | /// When no chained keywords are present (e.g., just <"scriptBody()">) then Keys is empty. 9 | /// 10 | public sealed class XferKeyedValue { 11 | /// The sequence of nested keyword keys leading to the payload. 12 | public IReadOnlyList Keys { get; } 13 | 14 | /// The terminal payload element (e.g., string, text, tuple, etc.). 15 | public Element Payload { get; } 16 | 17 | /// 18 | /// Convenience accessor: returns the payload as a string if it is a string-like element; otherwise null. 19 | /// 20 | public string? PayloadAsString { 21 | get { 22 | return Payload switch { 23 | StringElement s => s.Value, 24 | TextElement t => t.Value, 25 | _ => null 26 | }; 27 | } 28 | } 29 | 30 | public XferKeyedValue(IReadOnlyList keys, Element payload) { 31 | Keys = keys; 32 | Payload = payload; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Services/IXferParser.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace ParksComputing.Xfer.Lang.Services; 4 | 5 | /// 6 | /// Defines the contract for XferLang parsers that convert XferLang text into document objects. 7 | /// 8 | public interface IXferParser { 9 | /// 10 | /// Gets the text encoding used by this parser. 11 | /// 12 | Encoding Encoding { get; } 13 | 14 | /// 15 | /// Parses XferLang content from a byte array into an XferDocument. 16 | /// 17 | /// The byte array containing XferLang content to parse. 18 | /// An XferDocument representing the parsed content. 19 | /// Thrown when input is null or empty. 20 | /// Thrown when the input contains invalid XferLang syntax. 21 | XferDocument Parse(byte[] input); 22 | 23 | /// 24 | /// Parses XferLang content from a string into an XferDocument. 25 | /// 26 | /// The string containing XferLang content to parse. 27 | /// An XferDocument representing the parsed content. 28 | /// Thrown when input is null or empty. 29 | /// Thrown when the input contains invalid XferLang syntax. 30 | XferDocument Parse(string input); 31 | } 32 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Attributes/XferDecimalPrecisionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ParksComputing.Xfer.Lang.Attributes; 4 | 5 | /// 6 | /// Specifies the maximum number of decimal places to display when serializing decimal and double values to XferLang. 7 | /// This attribute only affects the string representation and does not modify the underlying value. 8 | /// 9 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] 10 | public class XferDecimalPrecisionAttribute : Attribute 11 | { 12 | /// 13 | /// The maximum number of decimal places to display. 14 | /// 15 | public int DecimalPlaces { get; } 16 | 17 | /// 18 | /// Whether to remove trailing zeros after the decimal point. 19 | /// Default is true. 20 | /// 21 | public bool RemoveTrailingZeros { get; set; } = true; 22 | 23 | /// 24 | /// Initializes a new instance of the XferDecimalPrecisionAttribute. 25 | /// 26 | /// The maximum number of decimal places to display. Must be 0 or greater. 27 | /// Thrown when decimalPlaces is negative. 28 | public XferDecimalPrecisionAttribute(int decimalPlaces) 29 | { 30 | if (decimalPlaces < 0) { 31 | throw new ArgumentOutOfRangeException(nameof(decimalPlaces), "Decimal places must be 0 or greater."); 32 | } 33 | 34 | DecimalPlaces = decimalPlaces; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/XferDocumentMetadata.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using ParksComputing.Xfer.Lang.Services; 3 | 4 | namespace ParksComputing.Xfer.Lang; 5 | 6 | /// 7 | /// Represents metadata information for an XferLang document. 8 | /// Contains standard metadata fields and supports extensible custom metadata. 9 | /// 10 | public class XferMetadata 11 | { 12 | /// 13 | /// The standard key name for the XferLang version metadata field. 14 | /// 15 | public const string XferKey = "xfer"; // Default version, can be overridden 16 | 17 | /// 18 | /// The standard key name for the document version metadata field. 19 | /// 20 | public const string VersionKey = "version"; // Default version, can be overridden 21 | 22 | /// 23 | /// Gets or sets the XferLang parser version used to create this document. 24 | /// Defaults to the current parser version. 25 | /// 26 | public string? Xfer { get; set; } = Parser.Version; 27 | 28 | /// 29 | /// Gets or sets the document version specified by the author. 30 | /// 31 | public string? Version { get; set; } 32 | // Add more known properties as needed 33 | 34 | /// 35 | /// Gets a dictionary containing user-defined or unknown metadata keys and their values. 36 | /// This allows for extensible metadata beyond the standard fields. 37 | /// 38 | public Dictionary Extensions { get; } = []; 39 | } 40 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Schema/ConstraintEvaluator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | using ParksComputing.Xfer.Lang.Elements; 8 | 9 | 10 | namespace ParksComputing.Xfer.Lang.Schema; 11 | 12 | /// 13 | /// Provides static methods for evaluating schema constraints against document values. 14 | /// Handles boolean literals, expression constraints, and custom validation logic. 15 | /// 16 | public class ConstraintEvaluator { 17 | /// 18 | /// Evaluates a constraint value against a document context. 19 | /// Supports boolean literals, expression constraints, and complex validation scenarios. 20 | /// 21 | /// The constraint value to evaluate (boolean, expression, etc.). 22 | /// The document context for expression evaluation. 23 | /// True if the constraint is satisfied; otherwise, false. 24 | public static bool Evaluate(object? value, ObjectElement document) { 25 | // Handle boolean literals directly 26 | if (value is bool boolValue) { 27 | return boolValue; 28 | } 29 | 30 | // Evaluate expressions like "any", "all", or custom conditions 31 | if (value is ExpressionConstraint expression) { 32 | return expression.Evaluate(document); 33 | } 34 | 35 | throw new InvalidOperationException($"Unsupported constraint value: {value}"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Scripting/Logical/NotOperator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ParksComputing.Xfer.Lang.Elements; 3 | 4 | namespace ParksComputing.Xfer.Lang.Scripting.Logical; 5 | 6 | /// 7 | /// Logical negation operator that inverts the truthiness of a single argument. 8 | /// 9 | public class NotOperator : ScriptingOperator { 10 | /// 11 | public override string OperatorName => "not"; 12 | /// 13 | public override string Description => "Logical negation"; 14 | /// 15 | public override int MinArguments => 1; 16 | /// Exactly one argument. 17 | public override int MaxArguments => 1; 18 | /// Returns true when the argument is falsey; otherwise false. 19 | public override object? Evaluate(ScriptingContext context, params Element[] arguments) { 20 | ValidateArguments(arguments); 21 | var val = ResolveValue(arguments[0], context); 22 | return !ToBoolean(val); 23 | } 24 | /// Applies standard truthiness conversion rules. 25 | private bool ToBoolean(object? v) => v switch { 26 | null => false, 27 | bool b => b, 28 | string s => !string.IsNullOrEmpty(s) && !s.Equals("false", StringComparison.OrdinalIgnoreCase), 29 | int i => i != 0, 30 | long l => l != 0, 31 | double d => d != 0 && !double.IsNaN(d), 32 | decimal m => m != 0, 33 | float f => f != 0 && !float.IsNaN(f), 34 | _ => true 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Scripting/Comparison/NotEqualOperator.cs: -------------------------------------------------------------------------------- 1 | using ParksComputing.Xfer.Lang.Elements; 2 | 3 | namespace ParksComputing.Xfer.Lang.Scripting.Comparison; 4 | 5 | /// 6 | /// Logical negation of EqualsOperator (eq). Returns true when operands are not equal. 7 | /// 8 | public class NotEqualOperator : ScriptingOperator { 9 | /// 10 | public override string OperatorName => "ne"; 11 | 12 | /// 13 | public override string Description => "Returns true if left != right"; 14 | 15 | /// 16 | public override int MinArguments => 2; 17 | 18 | /// 19 | public override int MaxArguments => 2; 20 | 21 | /// 22 | /// Evaluates the operator returning true when the two resolved argument values are not equal. 23 | /// Delegates equality logic to and negates the result. 24 | /// 25 | /// Active scripting context used to resolve element values. 26 | /// Exactly two elements to compare. 27 | /// true if arguments are not equal; otherwise false. 28 | public override object? Evaluate(ScriptingContext context, params Element[] arguments) { 29 | ValidateArguments(arguments); 30 | // Reuse eq operator logic via EqualsOperator implementation 31 | var eq = new EqualsOperator(); 32 | var equal = (bool)eq.Evaluate(context, arguments)!; 33 | return !equal; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /schemas/address.xfer: -------------------------------------------------------------------------------- 1 | 43 | 44 | { 45 | postOfficeBox "123" 46 | extendedAddress "Block 470" 47 | streetAddress "Jurong West Street 41" 48 | locality "Jurong West" 49 | region "Singapore" 50 | postalCode "640470" 51 | countryName "Republic of Singapore" 52 | } 53 | -------------------------------------------------------------------------------- /docs/highlightjs/LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2006, Ivan Sagalaev. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/ProcessingInstructions/IdProcessingInstruction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | using ParksComputing.Xfer.Lang.Elements; 8 | 9 | namespace ParksComputing.Xfer.Lang.ProcessingInstructions; 10 | 11 | /// 12 | /// Processing instruction for assigning unique identifiers to elements in XferLang. 13 | /// The ID processing instruction associates a string identifier with an element, 14 | /// enabling element referencing and uniqueness validation within the document. 15 | /// 16 | public class IdProcessingInstruction : ProcessingInstruction { 17 | /// 18 | /// The keyword used to identify ID processing instructions. 19 | /// 20 | public const string Keyword = "id"; 21 | 22 | /// 23 | /// Initializes a new instance of the IdProcessingInstruction class with the specified ID value. 24 | /// 25 | /// The text element containing the ID value to assign to the target element. 26 | public IdProcessingInstruction(TextElement value) : base(value, Keyword) { } 27 | 28 | /// 29 | /// Handles element processing by assigning the ID value to the target element. 30 | /// Sets the element's Id property to the string representation of the processing instruction's value. 31 | /// 32 | /// The element to assign the ID to. 33 | public override void ElementHandler(Element element) { 34 | element.Id = Value.ToString(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Scripting/Logical/XorOperator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ParksComputing.Xfer.Lang.Elements; 3 | 4 | namespace ParksComputing.Xfer.Lang.Scripting.Logical; 5 | 6 | /// 7 | /// Logical exclusive OR operator returning true only when exactly one of the two arguments is truthy. 8 | /// 9 | public class XorOperator : ScriptingOperator { 10 | /// 11 | public override string OperatorName => "xor"; 12 | /// 13 | public override string Description => "Logical exclusive OR"; 14 | /// 15 | public override int MinArguments => 2; 16 | /// Exactly two arguments. 17 | public override int MaxArguments => 2; 18 | /// Returns true when exactly one argument is truthy. 19 | public override object? Evaluate(ScriptingContext context, params Element[] arguments) { 20 | ValidateArguments(arguments); 21 | var a = ToBoolean(ResolveValue(arguments[0], context)); 22 | var b = ToBoolean(ResolveValue(arguments[1], context)); 23 | return a ^ b; 24 | } 25 | /// Applies standard truthiness conversion rules. 26 | private bool ToBoolean(object? v) => v switch { 27 | null => false, 28 | bool b => b, 29 | string s => !string.IsNullOrEmpty(s) && !s.Equals("false", StringComparison.OrdinalIgnoreCase), 30 | int i => i != 0, 31 | long l => l != 0, 32 | double d => d != 0 && !double.IsNaN(d), 33 | decimal m => m != 0, 34 | float f => f != 0 && !float.IsNaN(f), 35 | _ => true 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Converters/KeyedValueConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ParksComputing.Xfer.Lang.Configuration; 4 | using ParksComputing.Xfer.Lang.Converters; 5 | using ParksComputing.Xfer.Lang.Elements; 6 | 7 | namespace ParksComputing.Xfer.Lang.Converters { 8 | /// 9 | /// Converter for XferKeyedValue. Reads nested KeyValuePairElement chains into a flattened key list + terminal payload element. 10 | /// Writes an XferKeyedValue by wrapping the payload element in nested key/value pairs according to Keys. 11 | /// 12 | public sealed class KeyedValueConverter : XferConverter { 13 | public override bool CanConvert(Type objectType) => typeof(XferKeyedValue).IsAssignableFrom(objectType); 14 | 15 | public override Element WriteXfer(XferKeyedValue value, XferSerializerSettings settings) { 16 | Element current = value.Payload; 17 | // Wrap keys from last to first 18 | for (int i = value.Keys.Count - 1; i >= 0; i--) { 19 | current = new KeyValuePairElement(new KeywordElement(value.Keys[i]), current); 20 | } 21 | return current; 22 | } 23 | 24 | public override XferKeyedValue? ReadXfer(Element element, XferSerializerSettings settings) { 25 | var keys = new List(); 26 | Element current = element; 27 | while (current is KeyValuePairElement kvp) { 28 | keys.Add(kvp.Key); 29 | current = kvp.Value; 30 | } 31 | return new XferKeyedValue(keys, current); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang.Tests/LetProcessingInstructionTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using ParksComputing.Xfer.Lang.Services; 3 | 4 | namespace ParksComputing.Xfer.Lang.Tests; 5 | 6 | [TestClass] 7 | public class LetProcessingInstructionTests { 8 | [TestMethod] 9 | public void StandaloneLetPI_ResolvesDereference_InFollowingInterpolated() { 10 | var parser = new Parser(); 11 | // Standalone let syntax uses tuple form: 12 | var doc = parser.Parse(" 'Hello, <_greetee_>.'"); 13 | // Expect root to be a tuple containing the interpolated element only (let PI suppressed) 14 | var output = doc.Root.ToXfer(); 15 | Assert.IsTrue(output.Contains("Hello, World."), $"Expected interpolation to resolve greetee binding. Output: {output}"); 16 | Assert.IsFalse(output.Contains(" (_x _x) )"); 23 | var output = doc.Root.ToXfer(); 24 | // Expect two 42 values cloned 25 | Assert.IsTrue(output.Contains("(42 42)"), $"Dereferences not resolved: {output}"); 26 | } 27 | 28 | [TestMethod] 29 | public void StandaloneLetPI_InsideTupleThenInterpolated() { 30 | var parser = new Parser(); 31 | var doc = parser.Parse("( 'Hi, <_name_>.' )"); 32 | var output = doc.Root.ToXfer(); 33 | Assert.IsTrue(output.Contains("Hi, Ada."), output); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /docs/script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function () { 2 | const sidebar = document.querySelector('.sidebar'); 3 | const mobileNavToggle = document.querySelector('.mobile-nav-toggle'); 4 | const navLinks = document.querySelectorAll('.sidebar a'); 5 | const sections = document.querySelectorAll('.content section'); 6 | 7 | // Mobile navigation toggle 8 | mobileNavToggle.addEventListener('click', function() { 9 | sidebar.classList.toggle('active'); 10 | mobileNavToggle.classList.toggle('active'); 11 | }); 12 | 13 | // Close sidebar when a link is clicked on mobile 14 | navLinks.forEach(link => { 15 | link.addEventListener('click', function() { 16 | if (window.innerWidth <= 768) { 17 | sidebar.classList.remove('active'); 18 | mobileNavToggle.classList.remove('active'); 19 | } 20 | }); 21 | }); 22 | 23 | // Active link highlighting on scroll 24 | function onScroll() { 25 | let currentSection = ''; 26 | sections.forEach(section => { 27 | const sectionTop = section.offsetTop; 28 | if (pageYOffset >= sectionTop - 60) { 29 | currentSection = section.getAttribute('id'); 30 | } 31 | }); 32 | 33 | navLinks.forEach(link => { 34 | link.classList.remove('active'); 35 | if (link.getAttribute('href').substring(1) === currentSection) { 36 | link.classList.add('active'); 37 | } 38 | }); 39 | } 40 | 41 | window.addEventListener('scroll', onScroll); 42 | onScroll(); // Initial check 43 | }); 44 | -------------------------------------------------------------------------------- /examples/XferService/Xfer.Service/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "http": { 4 | "commandName": "Project", 5 | "launchBrowser": true, 6 | "launchUrl": "swagger", 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | }, 10 | "dotnetRunMessages": true, 11 | "applicationUrl": "http://localhost:5171" 12 | }, 13 | "https": { 14 | "commandName": "Project", 15 | "launchBrowser": true, 16 | "launchUrl": "index.html", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | }, 20 | "dotnetRunMessages": true, 21 | "applicationUrl": "https://localhost:7021;http://localhost:5171" 22 | }, 23 | "IIS Express": { 24 | "commandName": "IISExpress", 25 | "launchBrowser": true, 26 | "launchUrl": "swagger", 27 | "environmentVariables": { 28 | "ASPNETCORE_ENVIRONMENT": "Development" 29 | } 30 | }, 31 | "Container (Dockerfile)": { 32 | "commandName": "Docker", 33 | "launchBrowser": true, 34 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", 35 | "environmentVariables": { 36 | "ASPNETCORE_HTTPS_PORTS": "8081", 37 | "ASPNETCORE_HTTP_PORTS": "8080" 38 | }, 39 | "publishAllPorts": true, 40 | "useSSL": true 41 | } 42 | }, 43 | "$schema": "http://json.schemastore.org/launchsettings.json", 44 | "iisSettings": { 45 | "windowsAuthentication": false, 46 | "anonymousAuthentication": true, 47 | "iisExpress": { 48 | "applicationUrl": "http://localhost:2937", 49 | "sslPort": 44312 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /tools/json2xfer/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Cliffer; 3 | 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | 8 | namespace Json2Xfer; 9 | 10 | internal class Program { 11 | private static readonly string _configFilePath; 12 | private static readonly string _xfercDirectory; 13 | 14 | static Program() { 15 | var homeDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); 16 | _xfercDirectory = Path.Combine(homeDirectory, Constants.XfercDirectoryName); 17 | 18 | if (!Directory.Exists(_xfercDirectory)) { 19 | Directory.CreateDirectory(_xfercDirectory); 20 | } 21 | 22 | _configFilePath = Path.Combine(_xfercDirectory, Constants.ConfigFileName); 23 | } 24 | 25 | static async Task Main(string[] args) { 26 | Console.OutputEncoding = Encoding.UTF8; 27 | 28 | var cli = new ClifferBuilder() 29 | .ConfigureAppConfiguration((configurationBuilder) => { 30 | configurationBuilder.AddJsonFile(_configFilePath, true); 31 | }) 32 | .Build(); 33 | 34 | Utility.SetServiceProvider(cli.ServiceProvider); 35 | 36 | ClifferEventHandler.OnExit += () => { 37 | }; 38 | 39 | 40 | return await cli.RunAsync(args); 41 | } 42 | } 43 | 44 | internal abstract class Element { 45 | public abstract T TypedValue { get; set; } 46 | public abstract string Value { get; } 47 | } 48 | 49 | internal class IntegerElement : Element { 50 | public override int TypedValue { get; set; } 51 | public override string Value => TypedValue.ToString(); 52 | } 53 | 54 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Schema/SchemaDefinition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ParksComputing.Xfer.Lang.Schema; 8 | 9 | /// 10 | /// Defines the structure and validation rules for a schema type in XferLang. 11 | /// Can represent objects, arrays, or individual elements with associated constraints and field definitions. 12 | /// 13 | public class SchemaDefinition { 14 | /// 15 | /// Gets or sets the name of the schema definition (e.g., "address", "person"). 16 | /// 17 | public string Name { get; set; } = string.Empty; // e.g., "address" 18 | 19 | /// 20 | /// Gets or sets the type category of the schema ("object", "array", "element", etc.). 21 | /// 22 | public string Type { get; set; } = string.Empty; // e.g., "object", "element" 23 | 24 | /// 25 | /// Gets or sets the field definitions for object-type schemas. 26 | /// Maps field names to their corresponding schema field specifications. 27 | /// 28 | public Dictionary? Fields { get; set; } // For objects 29 | 30 | /// 31 | /// Gets or sets the element type for array or collection schemas. 32 | /// Specifies what type of elements the array contains. 33 | /// 34 | public string? ElementType { get; set; } // For array or element 35 | 36 | /// 37 | /// Gets or sets the validation constraints that apply to this schema definition. 38 | /// 39 | public List? Constraints { get; set; } // Validation constraints 40 | } 41 | -------------------------------------------------------------------------------- /docs/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {{TITLE}} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | ☰ 21 |
22 | 32 |
33 | 34 | {{CONTENT}} 35 | 36 |
37 |

Documentation generated on {{GENERATED_DATE}}

38 |
39 |
40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Schema/ExpressionConstraint.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | using ParksComputing.Xfer.Lang.Elements; 8 | 9 | namespace ParksComputing.Xfer.Lang.Schema; 10 | 11 | /// 12 | /// Represents a constraint that uses logical expressions to validate fields in a document. 13 | /// Supports operators like "any" and "all" to check field presence or other conditions. 14 | /// 15 | public class ExpressionConstraint { 16 | /// 17 | /// Gets or sets the logical operator for the expression ("any", "all", etc.). 18 | /// 19 | public string Operator { get; set; } = string.Empty; // "any", "all" 20 | 21 | /// 22 | /// Gets or sets the list of field names that the expression operates on. 23 | /// 24 | public List Fields { get; set; } = []; 25 | 26 | /// 27 | /// Evaluates the expression constraint against the specified document. 28 | /// 29 | /// The object element to evaluate against. 30 | /// True if the expression constraint is satisfied; otherwise, false. 31 | /// Thrown when an unsupported operator is used. 32 | public bool Evaluate(ObjectElement document) { 33 | return Operator switch { 34 | "any" => Fields.Any(field => document.Dictionary.ContainsKey(field)), 35 | "all" => Fields.All(field => document.Dictionary.ContainsKey(field)), 36 | _ => throw new InvalidOperationException($"Unsupported operator: {Operator}") 37 | }; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Scripting/Logical/OrOperator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ParksComputing.Xfer.Lang.Elements; 3 | 4 | namespace ParksComputing.Xfer.Lang.Scripting.Logical; 5 | 6 | /// 7 | /// Logical disjunction operator that evaluates arguments left-to-right and short-circuits on the first truthy value. 8 | /// 9 | public class OrOperator : ScriptingOperator { 10 | /// 11 | public override string OperatorName => "or"; 12 | /// 13 | public override string Description => "Logical OR with short-circuit"; 14 | /// 15 | public override int MinArguments => 2; 16 | /// Unlimited additional arguments may be supplied. 17 | public override int MaxArguments => int.MaxValue; 18 | /// Returns true for the first truthy argument; otherwise false. 19 | public override object? Evaluate(ScriptingContext context, params Element[] arguments) { 20 | ValidateArguments(arguments); 21 | foreach (var arg in arguments) { 22 | var val = ResolveValue(arg, context); 23 | if (ToBoolean(val)) { 24 | return true; 25 | } 26 | } 27 | return false; 28 | } 29 | /// Applies standard truthiness conversion rules. 30 | private bool ToBoolean(object? v) => v switch { 31 | null => false, 32 | bool b => b, 33 | string s => !string.IsNullOrEmpty(s) && !s.Equals("false", StringComparison.OrdinalIgnoreCase), 34 | int i => i != 0, 35 | long l => l != 0, 36 | double d => d != 0 && !double.IsNaN(d), 37 | decimal m => m != 0, 38 | float f => f != 0 && !float.IsNaN(f), 39 | _ => true 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Scripting/Logical/AndOperator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ParksComputing.Xfer.Lang.Elements; 3 | 4 | namespace ParksComputing.Xfer.Lang.Scripting.Logical; 5 | 6 | /// 7 | /// Logical conjunction operator that evaluates arguments left-to-right and short-circuits on the first falsey value. 8 | /// 9 | public class AndOperator : ScriptingOperator { 10 | /// 11 | public override string OperatorName => "and"; 12 | /// 13 | public override string Description => "Logical AND with short-circuit"; 14 | /// 15 | public override int MinArguments => 2; 16 | /// Unlimited additional arguments may be supplied. 17 | public override int MaxArguments => int.MaxValue; 18 | /// Returns false for the first falsey argument; otherwise true. 19 | public override object? Evaluate(ScriptingContext context, params Element[] arguments) { 20 | ValidateArguments(arguments); 21 | foreach (var arg in arguments) { 22 | var val = ResolveValue(arg, context); 23 | if (!ToBoolean(val)) { 24 | return false; 25 | } 26 | } 27 | return true; 28 | } 29 | /// Applies standard truthiness conversion rules. 30 | private bool ToBoolean(object? v) => v switch { 31 | null => false, 32 | bool b => b, 33 | string s => !string.IsNullOrEmpty(s) && !s.Equals("false", StringComparison.OrdinalIgnoreCase), 34 | int i => i != 0, 35 | long l => l != 0, 36 | double d => d != 0 && !double.IsNaN(d), 37 | decimal m => m != 0, 38 | float f => f != 0 && !float.IsNaN(f), 39 | _ => true 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /XferLangVSCode/vsc-extension-quickstart.md: -------------------------------------------------------------------------------- 1 | # Welcome to your VS Code Extension 2 | 3 | ## What's in the folder 4 | 5 | * This folder contains all of the files necessary for your extension. 6 | * `package.json` - this is the manifest file in which you declare your language support and define the location of the grammar file that has been copied into your extension. 7 | * `syntaxes/xferlang.tmLanguage.json` - this is the Text mate grammar file that is used for tokenization. 8 | * `language-configuration.json` - this is the language configuration, defining the tokens that are used for comments and brackets. 9 | 10 | ## Get up and running straight away 11 | 12 | * Make sure the language configuration settings in `language-configuration.json` are accurate. 13 | * Press `F5` to open a new window with your extension loaded. 14 | * Create a new file with a file name suffix matching your language. 15 | * Verify that syntax highlighting works and that the language configuration settings are working. 16 | 17 | ## Make changes 18 | 19 | * You can relaunch the extension from the debug toolbar after making changes to the files listed above. 20 | * You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes. 21 | 22 | ## Add more language features 23 | 24 | * To add features such as IntelliSense, hovers and validators check out the VS Code extenders documentation at https://code.visualstudio.com/api/language-extensions/overview 25 | 26 | ## Install your extension 27 | 28 | * To start using your extension with Visual Studio Code copy it into the `/.vscode/extensions` folder and restart Code. 29 | * To share your extension with the world, read on https://code.visualstudio.com/api/working-with-extensions/publishing-extension about publishing an extension. 30 | -------------------------------------------------------------------------------- /agent/SESSION_PROMPTS.md: -------------------------------------------------------------------------------- 1 | # XferLang Development Session Prompts 2 | 3 | ## Context Loading Prompt 4 | ``` 5 | You are working on XferLang, a data serialization language project. Before responding to any request: 6 | 7 | 1. Read PROJECT_CONTEXT.md for current project state and design decisions 8 | 2. Read STANDING_INSTRUCTIONS.md for development guidelines and protocols 9 | 3. Check RECENT_DECISIONS.md for latest architectural choices 10 | 4. Always verify current file contents before making any edits 11 | 12 | The user may reference previous conversations - use these files to understand the current context. 13 | ``` 14 | 15 | ## Architecture Discussion Prompt 16 | ``` 17 | When discussing XferLang architecture or design decisions: 18 | 19 | 1. Consider consistency with existing element-based design 20 | 2. Evaluate impact on parsing, serialization, and the type system 21 | 3. Think about .NET integration and reflection capabilities 22 | 4. Maintain the "one language" philosophy - avoid mixing external DSLs 23 | 5. Reference current character assignments and element types 24 | ``` 25 | 26 | ## Implementation Prompt 27 | ``` 28 | When implementing XferLang features: 29 | 30 | 1. Follow established patterns in existing Processing Instructions 31 | 2. Consider both parsing and serialization paths 32 | 3. Handle edge cases and error conditions 33 | 4. Maintain backwards compatibility with existing syntax 34 | 5. Add appropriate unit tests for new functionality 35 | ``` 36 | 37 | ## Testing Prompt 38 | ``` 39 | After any significant code changes: 40 | 41 | 1. Run the full test suite to ensure no regressions 42 | 2. Create focused debug applications for complex scenarios 43 | 3. Verify all 170 tests continue to pass 44 | 4. Test edge cases and error conditions 45 | 5. Clean up temporary files after debugging 46 | ``` 47 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang.Tests/DereferenceElementTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using ParksComputing.Xfer.Lang.Services; 3 | using ParksComputing.Xfer.Lang.Elements; 4 | 5 | namespace ParksComputing.Xfer.Lang.Tests; 6 | 7 | [TestClass] 8 | public class DereferenceElementTests { 9 | [TestMethod] 10 | public void Deref_Parses_As_DereferenceElement_When_Unbound() { 11 | var parser = new Parser(); 12 | var doc = parser.Parse("(_foo)"); 13 | Assert.AreEqual(1, doc.Root.Count, "Tuple should contain one element (the dereference)"); 14 | var first = doc.Root.GetElementAt(0); 15 | Assert.IsInstanceOfType(first, typeof(ReferenceElement)); 16 | var d = (ReferenceElement)first!; 17 | Assert.AreEqual("foo", d.Value); 18 | } 19 | 20 | [TestMethod] 21 | public void Identifier_With_Underscore_And_Colons_Not_Deref() { 22 | var parser = new Parser(); 23 | var doc = parser.Parse("(:_foo:)"); 24 | Assert.AreEqual(1, doc.Root.Count, "Tuple should contain one element (the identifier)"); 25 | var first = doc.Root.GetElementAt(0); 26 | Assert.IsInstanceOfType(first, typeof(IdentifierElement)); 27 | var id = (IdentifierElement)first!; 28 | Assert.AreEqual("_foo", id.Value); 29 | } 30 | 31 | [TestMethod] 32 | public void Deref_Resolves_To_Clone_When_Bound() { 33 | var parser = new Parser(); 34 | var doc = parser.Parse(" (_value)"); 35 | // Root is the tuple after the script PI 36 | Assert.AreEqual(1, doc.Root.Count, "Dereferenced object clone expected as sole tuple element"); 37 | var obj = doc.Root.GetElementAt(0) as ObjectElement; 38 | Assert.IsNotNull(obj); 39 | // Object should have key 'a' 40 | Assert.IsNotNull(obj!.GetValue("a")); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /xferc/Commands/SerializeCommand.cs: -------------------------------------------------------------------------------- 1 | using Cliffer; 2 | 3 | using ParksComputing.Xfer.Lang; 4 | using ParksComputing.Xfer.Lang.Attributes; 5 | using ParksComputing.Xfer.Lang.Services; 6 | using ParksComputing.Xfer.Lang.Extensions; 7 | using ParksComputing.Xfer.Lang.Elements; 8 | 9 | namespace ParksComputing.Xferc.Commands; 10 | 11 | [Command("serialize", "Serialize an object to an Xfer document and display the result.")] 12 | // [Argument(typeof(string), "file", "The path to the class file for which to serialize to Xfer")] 13 | internal class SerializeCommand { 14 | public int Execute(string file) { 15 | var data = new SampleData { 16 | }; 17 | 18 | string xferContent = XferConvert.Serialize(data, Formatting.Indented | Formatting.Spaced); 19 | Console.WriteLine(xferContent); 20 | 21 | var deserializedData = XferConvert.Deserialize(xferContent); 22 | if (deserializedData != null) 23 | { 24 | Console.WriteLine(deserializedData.DateTimeOffset); 25 | } 26 | 27 | var parser = new Parser(); 28 | var document = parser.Parse(xferContent); 29 | if (document != null) 30 | { 31 | Console.WriteLine(document.ToXfer(Formatting.Pretty)); 32 | 33 | var x = document.Root.Values.FirstOrDefault(); 34 | 35 | if (x is ObjectElement o) 36 | { 37 | if (o.TryGetElement("Dto", out DateTimeElement? element)) 38 | { 39 | Console.WriteLine(element?.ToXfer(Formatting.Pretty)); 40 | } 41 | } 42 | } 43 | 44 | return Result.Success; 45 | } 46 | } 47 | 48 | public class Person { 49 | [XferProperty("Full name:")] 50 | public string? Name { get; set; } = string.Empty; 51 | 52 | public int Age { get; set; } 53 | } 54 | -------------------------------------------------------------------------------- /examples/XferService/Xfer.Data/SampleData.cs: -------------------------------------------------------------------------------- 1 | using System.Xml; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace Xfer.Data; 5 | 6 | public enum TestEnum { 7 | None, 8 | Indented, 9 | Spaced, 10 | Pretty 11 | } 12 | 13 | public class SampleData { 14 | [Required] 15 | [StringLength(100, MinimumLength = 1)] 16 | public string Name { get; set; } = string.Empty; 17 | 18 | [Range(0, 150)] 19 | public int Age { get; set; } 20 | 21 | public TimeOnly TimeOnly { get; set; } 22 | public TimeSpan TimeSpan { get; set; } 23 | public DateTime DateTime { get; set; } 24 | public TestEnum TestEnum { get; set; } = TestEnum.Pretty; 25 | 26 | // Additional properties to showcase XferLang capabilities 27 | public decimal? Salary { get; set; } 28 | public bool IsActive { get; set; } = true; 29 | public List Tags { get; set; } = new(); 30 | public Dictionary Metadata { get; set; } = new(); 31 | 32 | public override string ToString() { 33 | return $"SampleData {{ Name: {Name}, Age: {Age}, TestEnum: {TestEnum}, IsActive: {IsActive} }}"; 34 | } 35 | 36 | public override bool Equals(object? obj) { 37 | if (obj is not SampleData other) { 38 | return false; 39 | } 40 | 41 | return Name == other.Name && 42 | Age == other.Age && 43 | TimeOnly == other.TimeOnly && 44 | TimeSpan == other.TimeSpan && 45 | DateTime == other.DateTime && 46 | TestEnum == other.TestEnum && 47 | Salary == other.Salary && 48 | IsActive == other.IsActive; 49 | } 50 | 51 | public override int GetHashCode() { 52 | return HashCode.Combine(Name, Age, TimeOnly, TimeSpan, DateTime, TestEnum, Salary, IsActive); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Elements/NullElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ParksComputing.Xfer.Lang.Elements; 8 | /// 9 | /// Represents a null literal element in XferLang. Public so scripting tests can construct it. 10 | /// 11 | public class NullElement : TypedElement 12 | { 13 | /// Element name used for null literal. 14 | public static readonly string ElementName = "null"; 15 | /// Opening specifier ('?'). 16 | public const char OpeningSpecifier = '?'; 17 | /// Closing specifier (same as opening). 18 | public const char ClosingSpecifier = OpeningSpecifier; 19 | /// Delimiter configuration for null literal. 20 | public static readonly ElementDelimiter ElementDelimiter = new ElementDelimiter(OpeningSpecifier, ClosingSpecifier); 21 | 22 | /// Create a new null element. 23 | /// Delimiter style (implicit, compact, explicit). 24 | public NullElement(ElementStyle style = ElementStyle.Compact) 25 | : base(null, ElementName, new ElementDelimiter(OpeningSpecifier, ClosingSpecifier, 1, style)) 26 | { 27 | } 28 | 29 | /// 30 | public override string ToXfer() 31 | { 32 | return ToXfer(Formatting.None); 33 | } 34 | 35 | /// 36 | public override string ToXfer(Formatting formatting, char indentChar = ' ', int indentation = 2, int depth = 0) 37 | { 38 | var sb = new StringBuilder(); 39 | sb.Append($"{Delimiter.CompactOpening}"); 40 | return sb.ToString(); 41 | } 42 | 43 | /// 44 | public override string ToString() 45 | { 46 | return "null"; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /docs/highlightjs/README.md: -------------------------------------------------------------------------------- 1 | # Highlight.js CDN Assets 2 | 3 | [![install size](https://packagephobia.now.sh/badge?p=highlight.js)](https://packagephobia.now.sh/result?p=highlight.js) 4 | 5 | **This package contains only the CDN build assets of highlight.js.** 6 | 7 | This may be what you want if you'd like to install the pre-built distributable highlight.js client-side assets via NPM. If you're wanting to use highlight.js mainly on the server-side you likely want the [highlight.js][1] package instead. 8 | 9 | To access these files via CDN:
10 | https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@latest/build/ 11 | 12 | **If you just want a single .js file with the common languages built-in: 13 | ** 14 | 15 | --- 16 | 17 | ## Highlight.js 18 | 19 | Highlight.js is a syntax highlighter written in JavaScript. It works in 20 | the browser as well as on the server. It works with pretty much any 21 | markup, doesn’t depend on any framework, and has automatic language 22 | detection. 23 | 24 | If you'd like to read the full README:
25 | 26 | 27 | ## License 28 | 29 | Highlight.js is released under the BSD License. See [LICENSE][7] file 30 | for details. 31 | 32 | ## Links 33 | 34 | The official site for the library is at . 35 | 36 | The Github project may be found at: 37 | 38 | Further in-depth documentation for the API and other topics is at 39 | . 40 | 41 | A list of the Core Team and contributors can be found in the [CONTRIBUTORS.md][8] file. 42 | 43 | [1]: https://www.npmjs.com/package/highlight.js 44 | [7]: https://github.com/highlightjs/highlight.js/blob/main/LICENSE 45 | [8]: https://github.com/highlightjs/highlight.js/blob/main/CONTRIBUTORS.md 46 | -------------------------------------------------------------------------------- /docs/highlightjs/es/languages/yaml.min.js: -------------------------------------------------------------------------------- 1 | /*! `yaml` grammar compiled for Highlight.js 11.7.0 */ 2 | var hljsGrammar=(()=>{"use strict";return e=>{ 3 | const n="true false yes no null",a="[\\w#;/?:@&=+$,.~*'()[\\]]+",s={ 4 | className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/ 5 | },{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable", 6 | variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(s,{ 7 | variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),l={ 8 | end:",",endsWithParent:!0,excludeEnd:!0,keywords:n,relevance:0},t={begin:/\{/, 9 | end:/\}/,contains:[l],illegal:"\\n",relevance:0},r={begin:"\\[",end:"\\]", 10 | contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr",variants:[{ 11 | begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{ 12 | begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$", 13 | relevance:10},{className:"string", 14 | begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{ 15 | begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0, 16 | relevance:0},{className:"type",begin:"!\\w+!"+a},{className:"type", 17 | begin:"!<"+a+">"},{className:"type",begin:"!"+a},{className:"type",begin:"!!"+a 18 | },{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta", 19 | begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)", 20 | relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{ 21 | className:"number", 22 | begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b" 23 | },{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},t,r,s],c=[...b] 24 | ;return c.pop(),c.push(i),l.contains=c,{name:"YAML",case_insensitive:!0, 25 | aliases:["yml"],contains:b}}})();export default hljsGrammar; -------------------------------------------------------------------------------- /docs/highlightjs/languages/yaml.min.js: -------------------------------------------------------------------------------- 1 | /*! `yaml` grammar compiled for Highlight.js 11.7.0 */ 2 | (()=>{var e=(()=>{"use strict";return e=>{ 3 | const n="true false yes no null",a="[\\w#;/?:@&=+$,.~*'()[\\]]+",s={ 4 | className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/ 5 | },{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable", 6 | variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(s,{ 7 | variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),l={ 8 | end:",",endsWithParent:!0,excludeEnd:!0,keywords:n,relevance:0},t={begin:/\{/, 9 | end:/\}/,contains:[l],illegal:"\\n",relevance:0},g={begin:"\\[",end:"\\]", 10 | contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr",variants:[{ 11 | begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{ 12 | begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$", 13 | relevance:10},{className:"string", 14 | begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{ 15 | begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0, 16 | relevance:0},{className:"type",begin:"!\\w+!"+a},{className:"type", 17 | begin:"!<"+a+">"},{className:"type",begin:"!"+a},{className:"type",begin:"!!"+a 18 | },{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta", 19 | begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)", 20 | relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{ 21 | className:"number", 22 | begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b" 23 | },{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},t,g,s],r=[...b] 24 | ;return r.pop(),r.push(i),l.contains=r,{name:"YAML",case_insensitive:!0, 25 | aliases:["yml"],contains:b}}})();hljs.registerLanguage("yaml",e)})(); -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang.Tests/IfProcessingInstructionSerializationTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using ParksComputing.Xfer.Lang.Services; 3 | 4 | namespace ParksComputing.Xfer.Lang.Tests; 5 | 6 | [TestClass] 7 | public class IfProcessingInstructionSerializationTests { 8 | [TestMethod] 9 | public void IfPI_SuccessfulCondition_StrippedFromSerialization() { 10 | var parser = new Parser(); 11 | var doc = parser.Parse("[ \"A\"]"); 12 | var output = doc.ToXfer(); 13 | Assert.IsTrue(output.Contains("\"A\"")); 14 | Assert.IsFalse(output.Contains(" \"A\"]"); 21 | var output = doc.ToXfer(); 22 | // New policy: all if PIs are stripped from serialization regardless of outcome; failed condition suppresses element. 23 | Assert.IsFalse(output.Contains("\"A\""), $"Element should be suppressed when condition false. Output: {output}"); 24 | Assert.IsFalse(output.Contains(" \"A\"]"); 31 | var output = doc.ToXfer(); 32 | // New policy: unknown operator treated as no-op (element included) but PI still stripped. 33 | Assert.IsTrue(output.Contains("\"A\""), $"Element should remain when unknown operator treated as no-op. Output: {output}"); 34 | Assert.IsFalse(output.Contains(" Main(string[] args) { 27 | var cli = new ClifferBuilder() 28 | .ConfigureAppConfiguration((configurationBuilder) => { 29 | configurationBuilder.AddJsonFile(_configFilePath, true); 30 | }) 31 | .ConfigureServices(services => { 32 | services.AddSingleton(); 33 | }) 34 | .Build(); 35 | 36 | Utility.SetServiceProvider(cli.ServiceProvider); 37 | 38 | ClifferEventHandler.OnExit += () => { 39 | var persistenceService = Utility.GetService()!; 40 | }; 41 | 42 | Console.OutputEncoding = Encoding.UTF8; 43 | 44 | return await cli.RunAsync(args); 45 | } 46 | } 47 | 48 | internal abstract class Element { 49 | public abstract T TypedValue { get; set; } 50 | public abstract string Value { get; } 51 | } 52 | 53 | internal class IntegerElement : Element { 54 | public override int TypedValue { get; set; } 55 | public override string Value => TypedValue.ToString(); 56 | } 57 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/ParseError.cs: -------------------------------------------------------------------------------- 1 | namespace ParksComputing.Xfer.Lang; 2 | 3 | /// 4 | /// Represents an error encountered during XferLang document parsing. 5 | /// Parse errors indicate syntax or structural problems that prevent successful parsing. 6 | /// 7 | public class ParseError { 8 | /// 9 | /// Gets or sets the error message describing the issue. 10 | /// 11 | public string Message { get; set; } 12 | 13 | /// 14 | /// Gets or sets the row number where the error occurred (1-based). 15 | /// 16 | public int Row { get; set; } 17 | 18 | /// 19 | /// Gets or sets the column number where the error occurred (1-based). 20 | /// 21 | public int Column { get; set; } 22 | 23 | /// 24 | /// Gets or sets additional context information related to the error, if available. 25 | /// 26 | public string? Context { get; set; } 27 | 28 | /// 29 | /// Initializes a new instance of the class. 30 | /// 31 | /// The error message. 32 | /// The row number where the error occurred (1-based). 33 | /// The column number where the error occurred (1-based). 34 | /// Optional additional context information. 35 | public ParseError(string message, int row, int column, string? context = null) { 36 | Message = message; 37 | Row = row; 38 | Column = column; 39 | Context = context; 40 | } 41 | 42 | /// 43 | /// Returns a string representation of the error including location information. 44 | /// 45 | /// A formatted string describing the error and its location. 46 | public override string ToString() => $"Error at row {Row}, column {Column}: {Message}"; 47 | } 48 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Converters/IXferConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ParksComputing.Xfer.Lang.Elements; 3 | using ParksComputing.Xfer.Lang.Configuration; 4 | 5 | namespace ParksComputing.Xfer.Lang.Converters 6 | { 7 | /// 8 | /// Defines custom conversion logic for XferLang serialization and deserialization. 9 | /// Custom converters enable specialized handling of specific types during the 10 | /// conversion process between .NET objects and XferLang elements. 11 | /// 12 | public interface IXferConverter 13 | { 14 | /// 15 | /// Determines whether this converter can handle the specified object type. 16 | /// 17 | /// The type to check for conversion support. 18 | /// True if this converter can handle the type; otherwise, false. 19 | bool CanConvert(Type objectType); 20 | 21 | /// 22 | /// Converts a .NET object to a XferLang element during serialization. 23 | /// 24 | /// The object to convert. 25 | /// Serializer settings to control conversion behavior. 26 | /// The XferLang element representation of the object. 27 | Element WriteXfer(object value, XferSerializerSettings settings); 28 | 29 | /// 30 | /// Converts a XferLang element back to a .NET object during deserialization. 31 | /// 32 | /// The XferLang element to convert. 33 | /// The target .NET type to convert to. 34 | /// Serializer settings to control conversion behavior. 35 | /// The deserialized .NET object, or null if conversion fails. 36 | object? ReadXfer(Element element, Type objectType, XferSerializerSettings settings); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /XferLangVSCode/README.md: -------------------------------------------------------------------------------- 1 | 2 | # XferLang VS Code Extension 3 | 4 | This extension provides syntax highlighting and basic editor help for [XferLang](https://xferlang.org/) files in Visual Studio Code, aligned with the current core language implementation. 5 | 6 | ## Features 7 | * Processing instructions (document, dynamicSource, chardef, if, script, let, defined, id, tag) 8 | * Explicit + compact forms for: strings, numbers (int/long/decimal/double), booleans, null, objects, arrays, tuples 9 | * Signed numeric literals (+ / -) in implicit, compact, and explicit forms 10 | * Dynamic elements (|key| / <| key |>) and references (leading underscores) highlighting 11 | * Interpolated elements inside quoted strings 12 | * Hexadecimal and binary numeric forms ($, %, #$, #%, &$, &%) 13 | * Auto-closing & surrounding pairs for all specifiers and collection delimiters 14 | * On-enter smart indent/outdent for {}, [], (), and their explicit <...> counterparts plus processing instructions 15 | * Basic syntax resilience: nested comments & delimiter length variations 16 | 17 | ## Usage 18 | Open any `.xfer` file to activate. Most constructs highlight as you type; increasing a delimiter run (adding more of the same specifier character) is treated uniformly. 19 | 20 | ## Installation 21 | Install from the VS Code Marketplace (search: XferLang) or build locally: 22 | 1. Clone the repository 23 | 2. From `XferLangVSCode` run the build script (coming soon) or `vsce package` 24 | 3. Install the generated `.vsix` in VS Code 25 | 26 | ## Contributing 27 | Pull requests and issues are welcome! See the repo for details. 28 | 29 | ## License 30 | MIT License 31 | 32 | ## Changelog 33 | ### 0.15.3 34 | Initial public version to coincide with XferLang pre-release v0.15: 35 | * Updated grammar to current language spec (dynamic, defined, tag, signed numbers, references) 36 | * Added indentation rules for collections and PIs 37 | * Improved numeric patterns (explicit vs implicit, hex/binary prefixes) 38 | * Added keyword PI highlighting 39 | 40 | **Enjoy!** 41 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Elements/IntegerElementDelimiter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ParksComputing.Xfer.Lang.Elements; 4 | 5 | /// 6 | /// Delimiter variant whose compact closing sequence is empty (used by dereference & numeric forms). 7 | /// 8 | public class EmptyClosingElementDelimiter : ElementDelimiter { 9 | /// 10 | /// The opening delimiter character for integer elements. 11 | /// 12 | public override string CompactOpening { get => base.CompactOpening; protected set => base.CompactOpening = value; } 13 | 14 | /// 15 | /// The closing delimiter character for integer elements. 16 | /// 17 | public override string CompactClosing { get => string.Empty; protected set => base.CompactClosing = value; } 18 | 19 | /// Create a delimiter with empty closing sequence and specifier count 1. 20 | /// Opening delimiter character. 21 | /// Closing delimiter character (same as opening). 22 | /// Delimiter style. 23 | public EmptyClosingElementDelimiter(char openingSpecifier, char closingSpecifier, ElementStyle style = ElementStyle.Compact) : base(openingSpecifier, closingSpecifier, 1, style) { 24 | // No additional initialization needed 25 | } 26 | 27 | /// Create a delimiter with an explicit specifier count. 28 | /// Opening delimiter character. 29 | /// Closing delimiter character (same as opening). 30 | /// Number of specifier characters. 31 | /// Delimiter style. 32 | public EmptyClosingElementDelimiter(char openingSpecifier, char closingSpecifier, int specifierCount, ElementStyle style = ElementStyle.Compact) : base(openingSpecifier, closingSpecifier, specifierCount, style) { 33 | // No additional initialization needed 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Attributes/XferCaptureIdAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ParksComputing.Xfer.Lang.Attributes; 4 | 5 | /// 6 | /// Apply this to the ID TARGET property to capture the ID from another property during deserialization. 7 | /// The attribute references the SOURCE property that carries the Xfer key/value pair. 8 | /// 9 | /// 10 | /// Usage: 11 | /// name "bar" } => NameId == "foo" 18 | /// ]]> 19 | /// Notes: 20 | /// 21 | /// If no ID is present, the target string remains null. 22 | /// Property name matching is case-insensitive. The attribute argument may be the CLR property name (respects any [XferProperty] rename) or the exact document key. 23 | /// 24 | /// 25 | [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] 26 | public sealed class XferCaptureIdAttribute : Attribute { 27 | /// 28 | /// The SOURCE identifier: either the CLR property name on the same type, or the exact document key. 29 | /// The decorated target property's type should be string (nullable allowed). 30 | /// 31 | public string TargetPropertyName { get; } 32 | 33 | /// 34 | /// Initializes a new instance of the class. 35 | /// 36 | /// The SOURCE identifier to capture from: either a CLR property name on the same type (case-insensitive, honoring any [XferProperty] rename) or the exact document key. 37 | public XferCaptureIdAttribute(string sourceName) { 38 | TargetPropertyName = sourceName ?? throw new ArgumentNullException(nameof(sourceName)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Elements/InterpolatedElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ParksComputing.Xfer.Lang.Elements; 8 | 9 | /// 10 | /// Represents an interpolated string element in XferLang. 11 | /// Uses single quote (') delimiters and supports string interpolation with embedded expressions. 12 | /// 13 | public class InterpolatedElement : TextElement 14 | { 15 | /// 16 | /// The element name used in XferLang serialization for interpolated strings. 17 | /// 18 | public static readonly string ElementName = "interpolated"; 19 | 20 | /// 21 | /// The opening delimiter character (single quote) for interpolated string elements. 22 | /// 23 | public const char OpeningSpecifier = '\''; 24 | 25 | /// 26 | /// The closing delimiter character (single quote) for interpolated string elements. 27 | /// 28 | public const char ClosingSpecifier = OpeningSpecifier; 29 | 30 | /// 31 | /// The delimiter configuration for interpolated string elements using single quote characters. 32 | /// 33 | public static readonly ElementDelimiter ElementDelimiter = new ElementDelimiter(OpeningSpecifier, ClosingSpecifier); 34 | 35 | /// 36 | /// Initializes a new instance of the InterpolatedElement class with the specified text and formatting options. 37 | /// 38 | /// The interpolated string text with embedded expressions 39 | /// The number of delimiter characters to use (default: 1) 40 | /// The element style for delimiter handling (default: Compact) 41 | public InterpolatedElement(string text, int specifierCount = 1, ElementStyle style = ElementStyle.Compact) 42 | : base(text, ElementName, new(OpeningSpecifier, ClosingSpecifier, specifierCount, style)) 43 | { 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/ProcessingInstructions/TagProcessingInstruction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | using ParksComputing.Xfer.Lang.Elements; 8 | 9 | namespace ParksComputing.Xfer.Lang.ProcessingInstructions; 10 | 11 | /// 12 | /// Processing instruction for assigning a single tag to elements in XferLang. 13 | /// The tag processing instruction associates a string tag with an element, 14 | /// enabling element categorization and group selection within the document. 15 | /// Multiple elements can share the same tag, and elements may have multiple tags. 16 | /// 17 | public class TagProcessingInstruction : ProcessingInstruction { 18 | /// 19 | /// The keyword used to identify tag processing instructions. 20 | /// 21 | public const string Keyword = "tag"; 22 | 23 | /// 24 | /// Initializes a new instance of the TagProcessingInstruction class with the specified tag value. 25 | /// 26 | /// The text element containing the tag value to assign to the target element. 27 | public TagProcessingInstruction(TextElement value) : base(value, Keyword) { } 28 | 29 | /// 30 | /// Handles element processing by setting the tag value on the target element. 31 | /// Throws an exception if the element already has a tag assigned. 32 | /// 33 | /// The element to assign the tag to. 34 | /// Thrown when the element already has a tag assigned. 35 | public override void ElementHandler(Element element) { 36 | var tagName = Value.ToString(); 37 | if (string.IsNullOrEmpty(tagName)) { 38 | return; // Ignore empty tags 39 | } 40 | 41 | // Add tag if not empty and not already present 42 | if (!element.Tags.Contains(tagName)) { 43 | element.Tags.Add(tagName); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /examples/XferService/Xfer.Service/Controllers/WeatherForecastController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace Xfer.Service.Controllers; 4 | 5 | [ApiController] 6 | [Route("[controller]")] 7 | public class WeatherForecastController : ControllerBase { 8 | private readonly ILogger _logger; 9 | 10 | public WeatherForecastController(ILogger logger) { 11 | _logger = logger; 12 | } 13 | 14 | [HttpGet(Name = "WeatherForecast")] 15 | public ActionResult> Get() { 16 | return Enumerable.Range(1, 5).Select(index => { 17 | var temperatureC = (Random.Shared.Next(-200, 555)) / 10m; 18 | var summary = GetSummaryForTemperature(temperatureC); 19 | 20 | return new WeatherForecast { 21 | Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), 22 | TemperatureC = temperatureC, 23 | Summary = summary 24 | }; 25 | }) 26 | .ToArray(); 27 | } 28 | 29 | /// 30 | /// Determines the appropriate weather summary based on temperature in Celsius. 31 | /// 32 | /// Temperature in Celsius 33 | /// Weather summary that matches the temperature range 34 | private static string GetSummaryForTemperature(decimal temperatureC) { 35 | return temperatureC switch { 36 | < -10m => "Freezing", // Below -10°C 37 | < 0m => "Bracing", // -10°C to 0°C 38 | < 5m => "Chilly", // 0°C to 5°C 39 | < 15m => "Cool", // 5°C to 15°C 40 | < 20m => "Mild", // 15°C to 20°C 41 | < 25m => "Warm", // 20°C to 25°C 42 | < 30m => "Balmy", // 25°C to 30°C 43 | < 35m => "Hot", // 30°C to 35°C 44 | < 40m => "Sweltering", // 35°C to 40°C 45 | _ => "Scorching" // 40°C and above 46 | }; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /agent/PROJECT_CONTEXT.md: -------------------------------------------------------------------------------- 1 | # XferLang Project Context 2 | 3 | ## Project Overview 4 | XferLang is a data serialization and configuration language with advanced processing instruction capabilities, designed to be more expressive and powerful than JSON/XML while maintaining readability. 5 | 6 | ## Key Concepts 7 | - **Elements**: Core data types (strings, numbers, objects, arrays, etc.) 8 | - **Processing Instructions (PIs)**: Special directives that modify or enhance elements 9 | - **Delimiters**: Characters that define element boundaries and types 10 | - **XferPath**: Query/navigation system for accessing elements 11 | 12 | ## Current Character Assignments 13 | - **Query Elements**: `` ` `` (backtick) - for queries/operations 14 | - **Reference Elements**: `;` (semicolon) - for referencing other elements 15 | - **Processing Instructions**: `` - for directives and commands 16 | - **Comments**: `` - for documentation 17 | - **Dollar ($)**: Reserved for hex numbers 18 | - **Percent (%)**: Reserved for binary numbers 19 | 20 | ## Active Development Areas 21 | 1. **Tag Processing Instructions**: `` - assigns tags to elements 22 | 2. **Script Processing Instructions**: `` - embedded scripting language 23 | 3. **Reference System**: Using backticks for object references and method calls 24 | 4. **Method Call Elements**: New element type for object.method() style operations 25 | 26 | ## Design Philosophy 27 | - One unified language (avoid mixing with JavaScript/other languages) 28 | - Minimalistic scripting/pathing extensions 29 | - Direct mapping to .NET methods and properties 30 | - Functional programming style with method chaining 31 | - Element-based approach consistent with XferLang core 32 | 33 | ## Project Structure 34 | - `ParksComputing.Xfer.Lang/` - Core library 35 | - `ParksComputing.Xfer.Lang.Tests/` - Unit tests 36 | - `examples/` - Various demonstration projects 37 | - `docs/` - Documentation and specifications 38 | - `docs/whitepapers/` - Design documents, ideas, and architectural whitepapers 39 | - `agent/` - AI assistant context and instruction files 40 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/ParksComputing.Xfer.Lang.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | false 8 | True 9 | XferLang Data-Transfer Language 10 | Paul M. Parks 11 | XferLang is a data-interchange format designed to support data serialization, data transmission, and offline use cases such as configuration management. XferLang is designed with explicit typing, human readability, and safe data embedding at its core. 12 | Copyright © 2025 Paul M. Parks 13 | https://xferlang.org/ 14 | XferLang-sm.png 15 | README.md 16 | https://github.com/paulmooreparks/Xfer 17 | xfer;language;data transfer;data;json 18 | False 19 | LICENSE.txt 20 | True 21 | 0.15.0-prerelease 22 | True 23 | snupkg 24 | 25 | 26 | 27 | 28 | True 29 | \ 30 | 31 | 32 | True 33 | \ 34 | 35 | 36 | 37 | 38 | 39 | True 40 | \ 41 | 42 | 43 | True 44 | \ 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang/Elements/StringElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ParksComputing.Xfer.Lang.Elements; 8 | 9 | /// 10 | /// Represents a string element in XferLang using double quote (") delimiters. 11 | /// String elements store text values and handle proper escaping of special characters 12 | /// within the XferLang format. 13 | /// 14 | public class StringElement : TextElement 15 | { 16 | /// 17 | /// The element type name for string elements. 18 | /// 19 | public static readonly string ElementName = "string"; 20 | 21 | /// 22 | /// The opening delimiter character for string elements. 23 | /// 24 | public const char OpeningSpecifier = '"'; 25 | 26 | /// 27 | /// The closing delimiter character for string elements. 28 | /// 29 | public const char ClosingSpecifier = OpeningSpecifier; 30 | 31 | /// 32 | /// The default element delimiter configuration for string elements. 33 | /// 34 | public static readonly ElementDelimiter ElementDelimiter = new ElementDelimiter(OpeningSpecifier, ClosingSpecifier); 35 | 36 | /// 37 | /// Initializes a new StringElement with an empty string value. 38 | /// 39 | public StringElement() : this(string.Empty) { } 40 | 41 | /// 42 | /// Initializes a new StringElement with the specified text value and formatting options. 43 | /// 44 | /// The string value to store. 45 | /// The number of delimiter characters to use (default: 1). 46 | /// The element style for formatting (default: Compact). 47 | public StringElement(string text, int specifierCount = 1, ElementStyle style = ElementStyle.Compact) : 48 | base(text, ElementName, new(OpeningSpecifier, ClosingSpecifier, specifierCount, style)) 49 | { 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /docs/highlightjs/es/languages/xml.min.js: -------------------------------------------------------------------------------- 1 | /*! `xml` grammar compiled for Highlight.js 11.7.0 */ 2 | var hljsGrammar=(()=>{"use strict";return e=>{ 3 | const a=e.regex,n=a.concat(/[\p{L}_]/u,a.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),s={ 4 | className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},t={begin:/\s/, 5 | contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}] 6 | },i=e.inherit(t,{begin:/\(/,end:/\)/}),c=e.inherit(e.APOS_STRING_MODE,{ 7 | className:"string"}),l=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),r={ 8 | endsWithParent:!0,illegal:/`]+/}]}]}]};return{ 12 | name:"HTML, XML", 13 | aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"], 14 | case_insensitive:!0,unicodeRegex:!0,contains:[{className:"meta",begin://,relevance:10,contains:[t,l,c,i,{begin:/\[/,end:/\]/,contains:[{ 16 | className:"meta",begin://,contains:[t,i,l,c]}]}] 17 | },e.COMMENT(//,{relevance:10}),{begin://, 18 | relevance:10},s,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/, 19 | relevance:10,contains:[l]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag", 20 | begin:/)/,end:/>/,keywords:{name:"style"},contains:[r],starts:{ 21 | end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag", 22 | begin:/)/,end:/>/,keywords:{name:"script"},contains:[r],starts:{ 23 | end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{ 24 | className:"tag",begin:/<>|<\/>/},{className:"tag", 25 | begin:a.concat(//,/>/,/\s/)))), 26 | end:/\/?>/,contains:[{className:"name",begin:n,relevance:0,starts:r}]},{ 27 | className:"tag",begin:a.concat(/<\//,a.lookahead(a.concat(n,/>/))),contains:[{ 28 | className:"name",begin:n,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}} 29 | })();export default hljsGrammar; -------------------------------------------------------------------------------- /docs/highlightjs/languages/xml.min.js: -------------------------------------------------------------------------------- 1 | /*! `xml` grammar compiled for Highlight.js 11.7.0 */ 2 | (()=>{var e=(()=>{"use strict";return e=>{ 3 | const a=e.regex,n=a.concat(/[\p{L}_]/u,a.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),s={ 4 | className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},t={begin:/\s/, 5 | contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}] 6 | },i=e.inherit(t,{begin:/\(/,end:/\)/}),c=e.inherit(e.APOS_STRING_MODE,{ 7 | className:"string"}),l=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),r={ 8 | endsWithParent:!0,illegal:/`]+/}]}]}]};return{ 12 | name:"HTML, XML", 13 | aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"], 14 | case_insensitive:!0,unicodeRegex:!0,contains:[{className:"meta",begin://,relevance:10,contains:[t,l,c,i,{begin:/\[/,end:/\]/,contains:[{ 16 | className:"meta",begin://,contains:[t,i,l,c]}]}] 17 | },e.COMMENT(//,{relevance:10}),{begin://, 18 | relevance:10},s,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/, 19 | relevance:10,contains:[l]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag", 20 | begin:/)/,end:/>/,keywords:{name:"style"},contains:[r],starts:{ 21 | end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag", 22 | begin:/)/,end:/>/,keywords:{name:"script"},contains:[r],starts:{ 23 | end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{ 24 | className:"tag",begin:/<>|<\/>/},{className:"tag", 25 | begin:a.concat(//,/>/,/\s/)))), 26 | end:/\/?>/,contains:[{className:"name",begin:n,relevance:0,starts:r}]},{ 27 | className:"tag",begin:a.concat(/<\//,a.lookahead(a.concat(n,/>/))),contains:[{ 28 | className:"name",begin:n,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}} 29 | })();hljs.registerLanguage("xml",e)})(); -------------------------------------------------------------------------------- /examples/processing-instruction-demo/sample.xfer: -------------------------------------------------------------------------------- 1 | ( 2 | ' 5 | let answer 42 6 | )!> 7 | { message _greeting number _answer } 8 | 9 | 13 | { 14 | summary 'Hello, <_greetee_>. Your score is <_score_>.' 15 | } 16 | 17 | <'It's <_greetee_>'> 18 | 19 | 23 | { 24 | serviceUrl 'https://<_host_>:<_port_>/' 25 | } 26 | 27 | 28 | 29 | { debug { level "verbose" } } 30 | 31 | !> 32 | { debug { level "verbose" } } 33 | 34 | 36 | ( 37 | year (getElementById (_document "date")) 38 | count (root (_document)) 39 | 40 | MultiParam (_sampleClass year (getElementById (_document "date")) count (root (_document))) 41 | 42 | 45 | ) 46 | //> 47 | 48 | !charDef { 49 | fooz \zeta 50 | theend \omega 51 | pin \pushpin 52 | answer \$42 53 | }! 54 | [~true ~false ~true ~false] 55 | (~false ~true ~false ~true) 56 | {one\1 two#$2 three&%11} 57 | (x y "z") 58 | 59 | 60 | alphabet { 61 | a '<\alpha\>' 62 | b '<\beta\>' 63 | g '<\gamma\>' 64 | d '<\delta\>' 65 | e '<\epsilon\>' 66 | z '<\zeta\>' 67 | } 68 | 69 | "] !> 70 | profile{name"Bob"role"admin"verified~true} 71 | 72 | #8] !> 73 | mixedData ( "Sample" @2023-12-25@ *99.5 ) 74 | 75 | age $18 76 | foo "bar" 77 | [1 2 3] 78 | <\answer\> 79 | 'I <\heart\><\vsemoji\> XferLang!' 80 | 'Put a <\pin\> in it!' 81 | foo"bar" 82 | 83 | [ 1 2 3] 84 | 85 | ) 86 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang.Tests/BasicTests.cs.bak: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using ParksComputing.Xfer.Lang; 3 | using ParksComputing.Xfer.Lang.Elements; 4 | using ParksComputing.Xfer.Lang.Services; 5 | using System.Text; 6 | 7 | namespace ParksComputing.Xfer.Lang.Tests; 8 | 9 | [TestClass] 10 | public class BasicParserTests 11 | { 12 | [TestMethod] 13 | public void Parse_EmptyObject_ShouldSucceed() 14 | { 15 | // Arrange 16 | var parser = new Parser(); 17 | var input = "{}"; 18 | var bytes = Encoding.UTF8.GetBytes(input); 19 | 20 | // Act 21 | var document = parser.Parse(bytes); 22 | 23 | // Assert 24 | Assert.IsNotNull(document); 25 | Assert.IsNotNull(document.Root); 26 | Assert.IsInstanceOfType(document.Root); 27 | Assert.IsFalse(document.HasError); 28 | } 29 | 30 | [TestMethod] 31 | public void Parse_SimpleKeyValue_ShouldSucceed() 32 | { 33 | // Arrange 34 | var parser = new Parser(); 35 | var input = """{ name "Alice" }"""; 36 | var bytes = Encoding.UTF8.GetBytes(input); 37 | 38 | // Act 39 | var document = parser.Parse(bytes); 40 | 41 | // Assert 42 | Assert.IsNotNull(document); 43 | Assert.IsInstanceOfType(document.Root); 44 | var obj = (ObjectElement)document.Root; 45 | Assert.IsTrue(obj.Dictionary.ContainsKey("name")); 46 | } 47 | 48 | [TestMethod] 49 | public void WarningSystem_UnknownCharacter_ShouldGenerateWarning() 50 | { 51 | // Arrange 52 | var parser = new Parser(); 53 | var input = """{ test <\unknown_char\> }"""; 54 | var bytes = Encoding.UTF8.GetBytes(input); 55 | 56 | // Act 57 | var document = parser.Parse(bytes); 58 | 59 | // Assert 60 | Assert.IsNotNull(document); 61 | Assert.IsFalse(document.HasError); 62 | Assert.IsTrue(document.HasWarnings); 63 | Assert.AreEqual(1, document.Warnings.Count); 64 | Assert.AreEqual(WarningType.CharacterResolutionFailure, document.Warnings[0].Type); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /agent/RECENT_DECISIONS.md: -------------------------------------------------------------------------------- 1 | # Recent Architectural Decisions 2 | 3 | ## Character Assignments (Latest Update) 4 | - **Query Elements**: Changed from `$` to `` ` `` (backtick) - `$` reserved for hex numbers 5 | - **Reference Elements**: Changed from `%` to `;` (semicolon) - `%` reserved for binary numbers 6 | - **Processing Instructions**: Remain `` 7 | - **Tag Processing Instructions**: `` implemented and working 8 | 9 | ## Parser Fixes Applied 10 | - **Multiple PI Application Bug**: Fixed issue where multiple processing instructions targeting the same element only applied the last one 11 | - **Root Cause**: Shared `_pendingPIs` instance variable caused call stack interference 12 | - **Solution**: Each parsing method now uses local pending PI lists instead of shared state 13 | - **Status**: All 170 tests passing, fix verified with complex scenarios 14 | 15 | ## Script Processing Instructions (In Progress) 16 | - **Goal**: Create scripting language within `` PI 17 | - **Approach**: Functional style mapping to .NET methods - `let x get_property (:obj:)` 18 | - **Syntax**: Using reference elements `(:name:)` for variable references 19 | - **Method Calls**: Considering new element type for object.method() operations 20 | 21 | ## Reference System Evolution 22 | - **Current**: Backtick (`) for reference elements 23 | - **Usage**: Inside script PIs for variable references: `(:variableName:)` 24 | - **Method Chaining**: Exploring functional approach: `get_baz (get_bar (:foo:))` 25 | - **Goal**: Direct mapping to .NET reflection without external scripting engines 26 | 27 | ## Design Philosophy Reinforced 28 | - **One Language**: Avoid mixing JavaScript or other external DSLs 29 | - **Minimalistic Extensions**: Keep scripting/pathing features simple and XferLang-native 30 | - **Element-Based**: All new features should fit within existing element architecture 31 | - **Functional Style**: Method calls as functions with object as first parameter 32 | 33 | ## Next Priorities 34 | 1. Design method call element type for object.method() operations 35 | 2. Implement script PI parsing and evaluation 36 | 3. Establish variable scoping and reference resolution 37 | 4. Create comprehensive examples and documentation 38 | -------------------------------------------------------------------------------- /docs/highlightjs/es/languages/markdown.min.js: -------------------------------------------------------------------------------- 1 | /*! `markdown` grammar compiled for Highlight.js 11.7.0 */ 2 | var hljsGrammar=(()=>{"use strict";return e=>{const n={begin:/<\/?[A-Za-z_]/, 3 | end:">",subLanguage:"xml",relevance:0},a={variants:[{begin:/\[.+?\]\[.*?\]/, 4 | relevance:0},{ 5 | begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, 6 | relevance:2},{ 7 | begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/), 8 | relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{ 9 | begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/ 10 | },{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0, 11 | returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)", 12 | excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[", 13 | end:"\\]",excludeBegin:!0,excludeEnd:!0}]},i={className:"strong",contains:[], 14 | variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}] 15 | },s={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{ 16 | begin:/_(?![_\s])/,end:/_/,relevance:0}]},c=e.inherit(i,{contains:[] 17 | }),t=e.inherit(s,{contains:[]});i.contains.push(t),s.contains.push(c) 18 | ;let l=[n,a];return[i,s,c,t].forEach((e=>{e.contains=e.contains.concat(l) 19 | })),l=l.concat(i,s),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{ 20 | className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:l},{ 21 | begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n", 22 | contains:l}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", 23 | end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:l, 24 | end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{ 25 | begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{ 26 | begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))", 27 | contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{ 28 | begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{ 29 | className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{ 30 | className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}})() 31 | ;export default hljsGrammar; -------------------------------------------------------------------------------- /docs/highlightjs/languages/markdown.min.js: -------------------------------------------------------------------------------- 1 | /*! `markdown` grammar compiled for Highlight.js 11.7.0 */ 2 | (()=>{var e=(()=>{"use strict";return e=>{const n={begin:/<\/?[A-Za-z_]/, 3 | end:">",subLanguage:"xml",relevance:0},a={variants:[{begin:/\[.+?\]\[.*?\]/, 4 | relevance:0},{ 5 | begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, 6 | relevance:2},{ 7 | begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/), 8 | relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{ 9 | begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/ 10 | },{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0, 11 | returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)", 12 | excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[", 13 | end:"\\]",excludeBegin:!0,excludeEnd:!0}]},i={className:"strong",contains:[], 14 | variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}] 15 | },s={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{ 16 | begin:/_(?![_\s])/,end:/_/,relevance:0}]},c=e.inherit(i,{contains:[] 17 | }),t=e.inherit(s,{contains:[]});i.contains.push(t),s.contains.push(c) 18 | ;let g=[n,a];return[i,s,c,t].forEach((e=>{e.contains=e.contains.concat(g) 19 | })),g=g.concat(i,s),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{ 20 | className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:g},{ 21 | begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n", 22 | contains:g}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", 23 | end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:g, 24 | end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{ 25 | begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{ 26 | begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))", 27 | contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{ 28 | begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{ 29 | className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{ 30 | className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}})() 31 | ;hljs.registerLanguage("markdown",e)})(); -------------------------------------------------------------------------------- /examples/let-demo/let-demo.xfer: -------------------------------------------------------------------------------- 1 | 2 | ... ) then use _name to dereference. /> 3 | 4 | 5 | 6 | 7 | ( 8 | "Basic duplication: " _greeting 9 | Result: "Basic duplication: " "Hello, world!" /> 10 | 11 | 'Hello, <_greetee_>.' 12 | 13 | 14 | 15 | { user _user } 16 | Two deep-cloned copies of the bound object. /> 17 | 18 | 19 | 20 | ( _nums _nums _nums ) 21 | Three independent clones of the array. /> 22 | 23 | 24 | 25 | ( "Wrapping" ("before" _payload "after" _payload) ) 26 | 27 | 28 | 29 | ( { primary _color secondary _color complementary ("not" _color) } ) 30 | 31 | 32 | 33 | ( _a _b _c ) 34 | 35 | 36 | 37 | ( ("first" _complex) ("second" _complex) ) 38 | 39 | 40 | 41 | ( _undefinedRef _x _undefinedRef ) 42 | 43 | 44 | 45 | ( _tail ) 46 | 47 | 48 | 49 | ( [ _inner _inner ] ) 50 | 51 | 52 | 53 | ( _obj _obj _obj ) 54 | ) 55 | -------------------------------------------------------------------------------- /ParksComputing.Xfer.Lang.Tests/CollectionProcessingInstructionTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using ParksComputing.Xfer.Lang.Services; 3 | 4 | namespace ParksComputing.Xfer.Lang.Tests; 5 | 6 | [TestClass] 7 | public class CollectionProcessingInstructionTests { 8 | 9 | [TestMethod] 10 | public void Array_AllowsProcessingInstructions_ConditionalSuppression() { 11 | // First PI condition false (defined on missing var), second true 12 | var input = "[1 2 !> 99 !> 3]"; 13 | var parser = new Parser(); 14 | var doc = parser.Parse(input); 15 | var output = doc.ToXfer(); 16 | 17 | // 99 suppressed, 3 included 18 | Assert.IsFalse(output.Contains(" 99") || output.EndsWith("99]"), $"Expected 99 to be suppressed. Output: {output}"); 19 | Assert.IsTrue(output.Contains("3"), $"Expected 3 to be present. Output: {output}"); 20 | } 21 | 22 | [TestMethod] 23 | public void Object_AllowsProcessingInstructions_ConditionalSuppression() { 24 | // First key 'a' suppressed, second key 'b' included 25 | var input = "{ a 1 b 2 }"; 26 | var parser = new Parser(); 27 | var doc = parser.Parse(input); 28 | var output = doc.ToXfer(); 29 | 30 | Assert.IsFalse(output.Contains("a 1"), $"Expected key 'a' to be suppressed. Output: {output}"); 31 | Assert.IsTrue(output.Contains("b 2"), $"Expected key 'b' to be present. Output: {output}"); 32 | } 33 | 34 | [TestMethod] 35 | public void Tuple_AllowsProcessingInstructions_ConditionalSuppression() { 36 | // Middle element suppressed, last included 37 | var input = "(1 !> 2 !> 3)"; 38 | var parser = new Parser(); 39 | var doc = parser.Parse(input); 40 | var output = doc.ToXfer(); 41 | 42 | Assert.IsTrue(output.Contains("1"), $"Tuple should contain 1. Output: {output}"); 43 | Assert.IsFalse(output.Contains(" 2 ") || output.Contains("(1 2 3") , $"Tuple should not contain 2. Output: {output}"); 44 | Assert.IsTrue(output.Contains("3"), $"Tuple should contain 3. Output: {output}"); 45 | } 46 | } 47 | --------------------------------------------------------------------------------