-
Notifications
You must be signed in to change notification settings - Fork 1
自動生成する変数名が既存の名前と衝突しないようにする #129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| import TypeScriptAST | ||
|
|
||
| struct NameProvider { | ||
| init() {} | ||
|
|
||
| private var used: Set<String> = [] | ||
|
|
||
| mutating func register(signature: TSFunctionDecl) { | ||
| for param in signature.params { | ||
| register(name: param.name) | ||
| } | ||
| } | ||
|
|
||
| mutating func register(name: String) { | ||
| used.insert(name) | ||
| } | ||
|
|
||
| mutating func provide(base: String) -> String { | ||
| if let name = provideIfUnused(name: base) { | ||
| return name | ||
| } | ||
|
|
||
| var i = 2 | ||
| while true { | ||
| let cand = "\(base)\(i)" | ||
| if let name = provideIfUnused(name: cand) { | ||
| return name | ||
| } | ||
| i += 1 | ||
| } | ||
| } | ||
|
|
||
| mutating func provideIfUnused(name: String) -> String? { | ||
| if used.contains(name) { | ||
| return nil | ||
| } | ||
| used.insert(name) | ||
| return name | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -269,6 +269,8 @@ private struct DecodeObjFuncGen { | |
| } | ||
|
|
||
| private func decodeAssociatedValues( | ||
| nameProvider: inout NameProvider, | ||
| names: inout [String: String], | ||
| caseElement: EnumCaseElementDecl, | ||
| json: any TSExpr | ||
| ) throws -> [TSVarDecl] { | ||
|
|
@@ -282,26 +284,37 @@ private struct DecodeObjFuncGen { | |
| expr = try generator.converter(for: value.interfaceType) | ||
| .callDecodeField(json: expr) | ||
|
|
||
| let varName = nameProvider.provide(base: TSKeyword.escaped(label)) | ||
| names[label] = varName | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 用意された名前は後で参照する時のために辞書に入れておく |
||
|
|
||
| return TSVarDecl( | ||
| kind: .const, name: TSKeyword.escaped(label), | ||
| kind: .const, name: varName, | ||
| initializer: expr | ||
| ) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private func buildCaseObject(caseElement: EnumCaseElementDecl) -> TSObjectExpr { | ||
| return TSObjectExpr(caseElement.associatedValues.map { (value) in | ||
| private func buildCaseObject( | ||
| names: [String: String], | ||
| caseElement: EnumCaseElementDecl | ||
| ) throws -> TSObjectExpr { | ||
| return TSObjectExpr(try caseElement.associatedValues.map { (value) in | ||
| let label = value.codableLabel | ||
| let varName = try names[label].unwrap(name: "var name") | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 用意した名前を辞書から引く。 |
||
| return TSObjectExpr.Field.named( | ||
| name: label, | ||
| value: TSIdentExpr(TSKeyword.escaped(label)) | ||
| value: TSIdentExpr(varName) | ||
| ) | ||
| }) | ||
| } | ||
|
|
||
| private func thenCode(caseElement: EnumCaseElementDecl) throws -> TSBlockStmt { | ||
| private func thenCode( | ||
| nameProvider: NameProvider, | ||
| caseElement: EnumCaseElementDecl | ||
| ) throws -> TSBlockStmt { | ||
| var nameProvider = nameProvider | ||
| var block: [any ASTNode] = [] | ||
|
|
||
| if !caseElement.associatedValues.isEmpty { | ||
|
|
@@ -313,9 +326,13 @@ private struct DecodeObjFuncGen { | |
| ) | ||
| ) | ||
| block.append(j) | ||
| nameProvider.register(name: "j") | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. enumについては |
||
| } | ||
|
|
||
| var names: [String: String] = [:] | ||
| block += try decodeAssociatedValues( | ||
| nameProvider: &nameProvider, | ||
| names: &names, | ||
| caseElement: caseElement, | ||
| json: TSIdentExpr("j") | ||
| ) | ||
|
|
@@ -327,7 +344,10 @@ private struct DecodeObjFuncGen { | |
| ), | ||
| .named( | ||
| name: caseElement.name, | ||
| value: buildCaseObject(caseElement: caseElement) | ||
| value: try buildCaseObject( | ||
| names: names, | ||
| caseElement: caseElement | ||
| ) | ||
| ) | ||
| ]) | ||
|
|
||
|
|
@@ -352,6 +372,8 @@ private struct DecodeObjFuncGen { | |
| func generate() throws -> TSFunctionDecl? { | ||
| guard let decl = try converter.decodeSignature() else { return nil } | ||
|
|
||
| var nameProvider = NameProvider() | ||
| nameProvider.register(signature: decl) | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 関数の仮引数名を登録しておく。 これは関数の実装部全体で有効なのでこのnameProviderをこの先で引き継ぐが、 そこはうまく局所的な生成関数に |
||
| var topStmt: (any TSStmt)? | ||
|
|
||
| func appendElse(stmt: any TSStmt) { | ||
|
|
@@ -377,7 +399,7 @@ private struct DecodeObjFuncGen { | |
| collect(at: ce.name) { | ||
| let ifSt = TSIfStmt( | ||
| condition: condCode(caseElement: ce), | ||
| then: try thenCode(caseElement: ce), | ||
| then: try thenCode(nameProvider: nameProvider, caseElement: ce), | ||
| else: nil | ||
| ) | ||
|
|
||
|
|
@@ -401,7 +423,11 @@ private struct EncodeObjFuncGen { | |
| var converter: EnumConverter | ||
| var type: EnumDecl | ||
|
|
||
| func encodeAssociatedValues(element: EnumCaseElementDecl) throws -> [TSVarDecl] { | ||
| func encodeAssociatedValues( | ||
| nameProvider: inout NameProvider, | ||
| names: inout [String: String], | ||
| element: EnumCaseElementDecl | ||
| ) throws -> [TSVarDecl] { | ||
| return try withErrorCollector { collect in | ||
| element.associatedValues.enumerated().compactMap { (i, value) in | ||
| collect(at: value.interfaceName ?? "_\(i)") { | ||
|
|
@@ -412,25 +438,36 @@ private struct EncodeObjFuncGen { | |
| expr = try generator.converter(for: value.interfaceType) | ||
| .callEncodeField(entity: expr) | ||
|
|
||
| let varName = nameProvider.provide(base: TSKeyword.escaped(value.codableLabel)) | ||
| names[value.codableLabel] = varName | ||
|
|
||
| return TSVarDecl( | ||
| kind: .const, name: TSKeyword.escaped(value.codableLabel), | ||
| kind: .const, name: varName, | ||
| initializer: expr | ||
| ) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func buildCaseObject(element: EnumCaseElementDecl) throws -> TSObjectExpr { | ||
| return TSObjectExpr(element.associatedValues.map { (value) in | ||
| func buildCaseObject( | ||
| names: [String: String], | ||
| element: EnumCaseElementDecl | ||
| ) throws -> TSObjectExpr { | ||
| return TSObjectExpr(try element.associatedValues.map { (value) in | ||
| let varName = try names[value.codableLabel].unwrap(name: "var name") | ||
| return TSObjectExpr.Field.named( | ||
| name: value.codableLabel, | ||
| value: TSIdentExpr(TSKeyword.escaped(value.codableLabel)) | ||
| value: TSIdentExpr(varName) | ||
| ) | ||
| }) | ||
| } | ||
|
|
||
| func caseBody(element: EnumCaseElementDecl) throws -> [any ASTNode] { | ||
| func caseBody( | ||
| nameProvider: NameProvider, | ||
| element: EnumCaseElementDecl | ||
| ) throws -> [any ASTNode] { | ||
| var nameProvider = nameProvider | ||
| var code: [any ASTNode] = [] | ||
|
|
||
| if !element.associatedValues.isEmpty { | ||
|
|
@@ -439,11 +476,17 @@ private struct EncodeObjFuncGen { | |
| initializer: TSMemberExpr(base: TSIdentExpr.entity, name: element.name) | ||
| ) | ||
| code.append(e) | ||
| nameProvider.register(name: "e") | ||
| } | ||
|
|
||
| code += try encodeAssociatedValues(element: element) | ||
| var names: [String: String] = [:] | ||
| code += try encodeAssociatedValues( | ||
| nameProvider: &nameProvider, | ||
| names: &names, | ||
| element: element | ||
| ) | ||
|
|
||
| let innerObject = try buildCaseObject(element: element) | ||
| let innerObject = try buildCaseObject(names: names, element: element) | ||
|
|
||
| let outerObject = TSObjectExpr([ | ||
| .named(name: element.name, value: innerObject) | ||
|
|
@@ -456,8 +499,14 @@ private struct EncodeObjFuncGen { | |
| func generate() throws -> TSFunctionDecl? { | ||
| guard let decl = try converter.encodeSignature() else { return nil } | ||
|
|
||
| var nameProvider = NameProvider() | ||
| nameProvider.register(signature: decl) | ||
|
|
||
| if type.caseElements.count == 1 { | ||
| decl.body.elements += try caseBody(element: type.caseElements[0]) | ||
| decl.body.elements += try caseBody( | ||
| nameProvider: nameProvider, | ||
| element: type.caseElements[0] | ||
| ) | ||
| return decl | ||
| } | ||
|
|
||
|
|
@@ -470,7 +519,12 @@ private struct EncodeObjFuncGen { | |
| collect(at: caseElement.name) { | ||
| `switch`.cases.append( | ||
| TSCaseStmt(expr: TSStringLiteralExpr(caseElement.name), elements: [ | ||
| TSBlockStmt(try caseBody(element: caseElement)) | ||
| TSBlockStmt( | ||
| try caseBody( | ||
| nameProvider: nameProvider, | ||
| element: caseElement | ||
| ) | ||
| ) | ||
| ]) | ||
| ) | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2から始めて衝突しなくなるまでインクリメントし続ける。