Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/LexiconGenerators.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1330"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "LexiconGenerators"
BuildableName = "LexiconGenerators"
BlueprintName = "LexiconGenerators"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "KotlinStandAloneTests"
BuildableName = "KotlinStandAloneTests"
BlueprintName = "KotlinStandAloneTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "SwiftLexiconTests"
BuildableName = "SwiftLexiconTests"
BlueprintName = "SwiftLexiconTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "SwiftStandAloneTests"
BuildableName = "SwiftStandAloneTests"
BlueprintName = "SwiftStandAloneTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CSharpStandAloneTests"
BuildableName = "CSharpStandAloneTests"
BlueprintName = "CSharpStandAloneTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "LexiconGenerators"
BuildableName = "LexiconGenerators"
BlueprintName = "LexiconGenerators"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
18 changes: 16 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ let package = Package(
"SwiftLexicon",
"SwiftStandAlone",
"KotlinStandAlone",
"TypeScriptStandAlone"
"TypeScriptStandAlone"
]
),
.target(
Expand Down Expand Up @@ -92,7 +92,7 @@ let package = Package(
],
resources: [.copy("Resources")]
),
.target(
.target(
name: "TypeScriptStandAlone",
dependencies: [
"Lexicon",
Expand All @@ -106,6 +106,20 @@ let package = Package(
],
resources: [.copy("Resources")]
),
.target(
name: "CSharpStandAlone",
dependencies: [
"Lexicon",
]
),
.testTarget(
name: "CSharpStandAloneTests",
dependencies: [
"Hope",
"CSharpStandAlone"
],
resources: [.copy("Resources")]
),
.executableTarget(
name: "lexicon-generate",
dependencies: [
Expand Down
123 changes: 123 additions & 0 deletions Sources/CSharpStandAlone/Generator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import Lexicon
import UniformTypeIdentifiers

public enum Generator: CodeGenerator {

// TODO: prefixes?

public static let utType = UTType(filenameExtension: "cs", conformingTo: .sourceCode)!

public static func generate(_ json: Lexicon.Graph.JSON) throws -> Data {
return Data(json.cSharp().utf8)
}
}

private extension Lexicon.Graph.JSON {

func cSharp() -> String {
return """
global using static \(name.capitalized)Lexicon;

public static class \(name.capitalized)Lexicon
{
public static I\(name.capitalized) \(name) = new L\(name.capitalized)(nameof(\(name)));
}

public abstract class LexiconType
{
protected string Identifier { get; }

public LexiconType(string identifier)
{
Identifier = identifier;
}

public override string ToString()
{
return Identifier;
}
}

// MARK: generated types

\(classes.flatMap{ $0.cSharp(prefix: ("L", "I")) }.joined(separator: "\n"))

"""
}
}

private extension Lexicon.Graph.Node.Class.JSON {

// TODO: make this more readable

func cSharp(prefix: (class: String, protocol: String)) -> [String] {

guard mixin == nil else {
return []
}

var lines: [String] = []
let T = id.split(separator: ".").map{$0.capitalized}.joined().idToClassSuffix
let (L, I) = prefix

let supertype = supertype?
.split(separator: ".")
.map({$0.capitalized})
.joined()
.replacingOccurrences(of: "_", with: "__")
.replacingOccurrences(of: ".", with: "")
.replacingOccurrences(of: "__&__", with: ", I")

if let protonym = protonym?.split(separator: ".").map({$0.capitalized}).joined().idToClassSuffix {
lines += "public sealed class \(L)\(T) : LexiconType, \(I)\(T), \(I)\(protonym)"

} else {
lines += "public sealed class \(L)\(T) : LexiconType, \(I)\(T)\(supertype.map{ ", \(I)\($0)" } ?? "")"
}

lines += "{"

for child in children ?? [] {
let id = "\(T).\(child.capitalized)"
lines += "\tpublic \(I)\(id.idToClassSuffix) \(child) => new \(L)\(id.idToClassSuffix)($\"{Identifier}.{nameof(\(child))}\");"
}

// TODO: Generate vars for interface'd properties

for (synonym, protonym) in (synonyms?.sortedByLocalizedStandard(by: \.key) ?? []) {
let id = "\(T).\(synonym.capitalized)"
lines += "\tpublic \(I)\(id.idToClassSuffix) \(synonym) => new \(L)\(id.idToClassSuffix)($\"{Identifier}.{nameof(\(protonym))}\");"
}

lines += "\n\tpublic \(L)\(T)(string identifier) : base(identifier) { }"

lines += "}"

lines += "public interface \(I)\(T)\(supertype.map{ " : \(I)\($0)" } ?? "")"

lines += "{"

for child in children ?? [] {
let id = "\(T).\(child.capitalized)"
lines += "\t\(I)\(id.idToClassSuffix) \(child) { get }"
}

for (synonym, protonym) in (synonyms?.sortedByLocalizedStandard(by: \.key) ?? []) {
let id = "\(T).\(synonym.capitalized)"
lines += "\t\(I)\(id.idToClassSuffix) \(synonym) { get }"
}

lines += "}"

return lines
}
}

private extension String {

var idToClassSuffix: String {
replacingOccurrences(of: "_", with: "__")
.replacingOccurrences(of: ".", with: "")
.replacingOccurrences(of: "_&_", with: "")
}
}
3 changes: 3 additions & 0 deletions Sources/LexiconGenerators/List.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Collections
import SwiftLexicon
import SwiftStandAlone
import KotlinStandAlone
import CSharpStandAlone
import TypeScriptStandAlone

public extension Lexicon.Graph.JSON {
Expand All @@ -21,6 +22,8 @@ public extension Lexicon.Graph.JSON {

"TypeScript Stand-Alone": TypeScriptStandAlone.Generator.self,

"C# Stand-Alone": CSharpStandAlone.Generator.self,

"JSON Classes & Mixins": JSONClasses.self,
]
}
20 changes: 20 additions & 0 deletions Tests/CSharpStandAloneTests/ CSharpStandAlone™.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@_exported import Hope
@_exported import Combine
@_exported import Lexicon
@_exported import CSharpStandAlone

final class CSharpLexicon™: Hopes {

func test_generator() async throws {

var json = try await "test".taskpaper().lexicon().json()
json.date = Date(timeIntervalSinceReferenceDate: 0)

let code = try Generator.generate(json).string()

try hope(code) == "test.cs".file().string()
}

func test_code() throws {
}
}
17 changes: 17 additions & 0 deletions Tests/CSharpStandAloneTests/Resources/test.taskpaper
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
test:
one:
+ test.type.odd
more:
time:
+ test
two:
+ test.type.even
timing:
type:
even:
bad:
= no.good
no:
good:
odd:
good:
26 changes: 26 additions & 0 deletions Tests/CSharpStandAloneTests/util.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Foundation

extension String {

func taskpaper() throws -> String {
try "\(self).taskpaper".file().string()
}

func file() throws -> Data {
guard let url = Bundle.module.url(forResource: "Resources/\(self)", withExtension: nil) else {
throw "Could not find '\(self)'"
}
return try Data(contentsOf: url)
}

func lexicon() async throws -> Lexicon {
try await Lexicon.from(TaskPaper(self).decode())
}
}

extension Data {

func string(encoding: String.Encoding = .utf8) throws -> String {
try String(data: self, encoding: encoding).try()
}
}