-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from tomsugarev/TypeScript-generator
TypeScript Standalone
- Loading branch information
Showing
7 changed files
with
321 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
// | ||
// github.com/screensailor 2022 | ||
// | ||
|
||
import Lexicon | ||
import UniformTypeIdentifiers | ||
|
||
public extension UTType { | ||
static var typescript = UTType(filenameExtension: "ts", conformingTo: .sourceCode)! | ||
} | ||
|
||
public enum Generator: CodeGenerator { | ||
|
||
// TODO: prefixes? | ||
|
||
public static let utType = UTType.typescript | ||
public static let command = "ts" | ||
|
||
public static func generate(_ json: Lexicon.Graph.JSON) throws -> Data { | ||
return Data(json.ts().utf8) | ||
} | ||
} | ||
|
||
private extension Lexicon.Graph.JSON { | ||
|
||
func ts() -> String { | ||
return """ | ||
interface I { } | ||
// L | ||
class L implements I { | ||
protected id: string; | ||
constructor(id: string) { | ||
this.id = id; | ||
} | ||
get ['__']() { | ||
return this.id; | ||
} | ||
} | ||
// MARK: generated types | ||
\(classes.flatMap{ $0.ts(prefix: ("L", "I"), classes: classes) }.joined(separator: "\n")) | ||
const \(name) = new L_\(name)("\(name)"); | ||
""" | ||
} | ||
} | ||
|
||
private extension Lexicon.Graph.Node.Class.JSON { | ||
|
||
// TODO: make this more readable | ||
|
||
func ts(prefix: (class: String, protocol: String), classes: [Lexicon.Graph.Node.Class.JSON]) -> [String] { | ||
|
||
guard mixin == nil else { | ||
return [] | ||
} | ||
|
||
var lines: [String] = [] | ||
let T = id.idToClassSuffix | ||
let (L, I) = prefix | ||
|
||
if let protonym = protonym { | ||
lines += "type \(L)_\(T) = \(L)_\(protonym.idToClassSuffix)" | ||
return lines | ||
} | ||
|
||
lines += "class \(L)_\(T) extends \(L) implements \(I)_\(T) {" | ||
|
||
let supertype = supertype? | ||
.replacingOccurrences(of: "_", with: "__") | ||
.replacingOccurrences(of: ".", with: "_") | ||
.replacingOccurrences(of: "__&__", with: ", I_") | ||
|
||
if hasNoProperties { | ||
if supertype != nil { | ||
let superChildren = classes.filter {$0.id == supertype}.first?.children | ||
|
||
for child in superChildren ?? [] { | ||
lines += " \(child)!: \(L)_\(supertype!)_\(child);" | ||
} | ||
lines += "}" | ||
|
||
lines += "type \(I)_\(T) = I_\(supertype!);" | ||
} else { | ||
lines += "}" | ||
lines += "type \(I)_\(T) = I;" | ||
} | ||
} | ||
|
||
guard hasProperties else { | ||
return lines | ||
} | ||
|
||
for t in type ?? [] { | ||
let subClass = classes.filter{$0.id == t}.first | ||
for child in subClass?.children ?? [] { | ||
let id = "L.\(t).\(child)" | ||
lines += " \(child)!: \(id.idToClassSuffix);" | ||
} | ||
if let keys = subClass?.synonyms?.keys { | ||
for synonym in keys { | ||
let id = "L.\(t).\(synonym)" | ||
lines += " \(synonym)!: \(id.idToClassSuffix);" | ||
} | ||
} | ||
|
||
} | ||
|
||
for child in children ?? [] { | ||
let id = "\(id).\(child)" | ||
lines += " \(child) = new \(L)_\(id.idToClassSuffix)(`${this.__}.\(child)`);" | ||
} | ||
|
||
for (synonym, protonym) in (synonyms?.sortedByLocalizedStandard(by: \.key) ?? []) { | ||
lines += " \(synonym) = this.\(protonym);" | ||
} | ||
lines += "}" | ||
|
||
lines += "interface \(I)_\(T) extends \(I)\(supertype.map{ "_\($0)" } ?? "") {" | ||
|
||
for child in children ?? [] { | ||
let id = "\(id).\(child)" | ||
lines += " \(child): \(I)_\(id.idToClassSuffix);" | ||
} | ||
lines += "}" | ||
return lines | ||
} | ||
} | ||
|
||
private extension String { | ||
var idToClassSuffix: String { | ||
replacingOccurrences(of: "_", with: "__") | ||
.replacingOccurrences(of: ".", with: "_") | ||
.replacingOccurrences(of: "_&_", with: "_") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
interface I { } | ||
|
||
// L | ||
class L implements I { | ||
protected id: string; | ||
constructor(id: string) { | ||
this.id = id; | ||
} | ||
get ['__']() { | ||
return this.id; | ||
} | ||
} | ||
|
||
// MARK: generated types | ||
class L_test extends L implements I_test { | ||
one = new L_test_one(`${this.__}.one`); | ||
two = new L_test_two(`${this.__}.two`); | ||
type = new L_test_type(`${this.__}.type`); | ||
} | ||
interface I_test extends I { | ||
one: I_test_one; | ||
two: I_test_two; | ||
type: I_test_type; | ||
} | ||
class L_test_one extends L implements I_test_one { | ||
good!: L_test_type_odd_good; | ||
more = new L_test_one_more(`${this.__}.more`); | ||
} | ||
interface I_test_one extends I_test_type_odd { | ||
more: I_test_one_more; | ||
} | ||
class L_test_one_more extends L implements I_test_one_more { | ||
time = new L_test_one_more_time(`${this.__}.time`); | ||
} | ||
interface I_test_one_more extends I { | ||
time: I_test_one_more_time; | ||
} | ||
class L_test_one_more_time extends L implements I_test_one_more_time { | ||
one!: L_test_one; | ||
two!: L_test_two; | ||
type!: L_test_type; | ||
} | ||
type I_test_one_more_time = I_test; | ||
class L_test_two extends L implements I_test_two { | ||
no!: L_test_type_even_no; | ||
bad!: L_test_type_even_bad; | ||
timing = new L_test_two_timing(`${this.__}.timing`); | ||
} | ||
interface I_test_two extends I_test_type_even { | ||
timing: I_test_two_timing; | ||
} | ||
class L_test_two_timing extends L implements I_test_two_timing { | ||
} | ||
type I_test_two_timing = I; | ||
class L_test_type extends L implements I_test_type { | ||
even = new L_test_type_even(`${this.__}.even`); | ||
odd = new L_test_type_odd(`${this.__}.odd`); | ||
} | ||
interface I_test_type extends I { | ||
even: I_test_type_even; | ||
odd: I_test_type_odd; | ||
} | ||
class L_test_type_even extends L implements I_test_type_even { | ||
no = new L_test_type_even_no(`${this.__}.no`); | ||
bad = this.no.good; | ||
} | ||
interface I_test_type_even extends I { | ||
no: I_test_type_even_no; | ||
} | ||
type L_test_type_even_bad = L_test_type_even_no_good | ||
class L_test_type_even_no extends L implements I_test_type_even_no { | ||
good = new L_test_type_even_no_good(`${this.__}.good`); | ||
} | ||
interface I_test_type_even_no extends I { | ||
good: I_test_type_even_no_good; | ||
} | ||
class L_test_type_even_no_good extends L implements I_test_type_even_no_good { | ||
} | ||
type I_test_type_even_no_good = I; | ||
class L_test_type_odd extends L implements I_test_type_odd { | ||
good = new L_test_type_odd_good(`${this.__}.good`); | ||
} | ||
interface I_test_type_odd extends I { | ||
good: I_test_type_odd_good; | ||
} | ||
class L_test_type_odd_good extends L implements I_test_type_odd_good { | ||
} | ||
type I_test_type_odd_good = I; | ||
const test = new L_test("test"); |
28 changes: 28 additions & 0 deletions
28
Tests/TypeScriptStandAloneTests/TypeScriptStandAlone™.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
@_exported import Hope | ||
@_exported import Combine | ||
@_exported import Lexicon | ||
@_exported import TypeScriptStandAlone | ||
|
||
final class TypeScriptLexicon™: 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.ts".file().string() | ||
} | ||
|
||
func test_code() throws { | ||
/** | ||
@Test | ||
fun generator(){ | ||
assert(test.one.more.time.one.more.time.identifier == "test.one.more.time.one.more.time") | ||
assert(test.two.bad == test.two.no.good) | ||
assert(test.two.bad.identifier == "test.two.no.good") | ||
} | ||
*/ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// | ||
// github.com/screensailor 2022 | ||
// | ||
|
||
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() | ||
} | ||
} |