diff --git a/src/ast/__snapshots__/ast_test.snap b/src/ast/__snapshots__/ast_test.snap index 5965c23..d3bbe5e 100755 --- a/src/ast/__snapshots__/ast_test.snap +++ b/src/ast/__snapshots__/ast_test.snap @@ -3,20 +3,105 @@ ast.File{ Name: "some", Body: { - ast.TypeDeclaration{Id:"Status", Kind:"int"}, + ast.TypeDeclaration{ + BaseDeclaration: ast.BaseDeclaration{ + LeadingComments: { + {Value:" hello\n"}, + }, + TrailingComments: { + }, + }, + Id: "Status", + Kind: "int", + }, ast.ConstDeclaration{ - Declarators: { - {Kind:"Status", Id:"Todo", Value:"iota"}, - {Kind:"", Id:"Done", Value:""}, - {Kind:"", Id:"Pending", Value:""}, - {Kind:"", Id:"InProgress", Value:""}, + BaseDeclaration: ast.BaseDeclaration{}, + Declarators: { + { + BaseDeclaration: ast.BaseDeclaration{ + LeadingComments: { + {Value:" 代办\n"}, + }, + TrailingComments: { + {Value:" 59todo\n"}, + }, + }, + Kind: "Status", + Id: "Todo", + Value: "iota", + }, + { + BaseDeclaration: ast.BaseDeclaration{ + LeadingComments: { + {Value:" 已完成\n"}, + }, + TrailingComments: { + }, + }, + Kind: "", + Id: "Done", + Value: "", + }, + { + BaseDeclaration: ast.BaseDeclaration{ + LeadingComments: { + }, + TrailingComments: { + }, + }, + Kind: "", + Id: "Pending", + Value: "", + }, + { + BaseDeclaration: ast.BaseDeclaration{ + LeadingComments: { + }, + TrailingComments: { + }, + }, + Kind: "", + Id: "InProgress", + Value: "", + }, + }, + }, + ast.TypeDeclaration{ + BaseDeclaration: ast.BaseDeclaration{ + LeadingComments: { + }, + TrailingComments: { + }, }, + Id: "Sex", + Kind: "string", }, - ast.TypeDeclaration{Id:"Sex", Kind:"string"}, ast.ConstDeclaration{ - Declarators: { - {Kind:"Sex", Id:"Female", Value:"female"}, - {Kind:"Sex", Id:"Male", Value:"male"}, + BaseDeclaration: ast.BaseDeclaration{}, + Declarators: { + { + BaseDeclaration: ast.BaseDeclaration{ + LeadingComments: { + }, + TrailingComments: { + }, + }, + Kind: "Sex", + Id: "Female", + Value: "female", + }, + { + BaseDeclaration: ast.BaseDeclaration{ + LeadingComments: { + }, + TrailingComments: { + {Value:"hhh\n"}, + }, + }, + Kind: "Sex", + Id: "Male", + Value: "male", + }, }, }, }, diff --git a/src/ast/ast.go b/src/ast/ast.go index d0ccec5..4fda53a 100644 --- a/src/ast/ast.go +++ b/src/ast/ast.go @@ -11,20 +11,29 @@ type AstGenerator struct { Tokens []token.Token index int currentToken token.Token + usedComments map[token.Token]bool } func (a *AstGenerator) nextToken(reportErrorWhenIsNull bool) (token.Token, error) { - a.index += 1 + for i := a.index + 1; ; i++ { + if i >= len(a.Tokens) { + if reportErrorWhenIsNull { + a.reportTokenError() + } - if a.index >= len(a.Tokens) { - if reportErrorWhenIsNull { - a.reportTokenError() + return token.Token{}, errors.New("Overflow") } - return token.Token{}, errors.New("Overflow") - } + tok := a.Tokens[i] - a.currentToken = a.Tokens[a.index] + if tok.Type == token.LineComment { + continue + } + + a.index = i + a.currentToken = a.Tokens[a.index] + break + } return a.currentToken, nil } @@ -75,9 +84,51 @@ func (a *AstGenerator) initFile() File { return file } +func (a *AstGenerator) resolveComments(node *BaseDeclaration, leading bool) { + currentToken := a.currentToken + comments := []Comment{} + + if leading { + for i := a.index - 1; i >= 0; i-- { + tok := a.Tokens[i] + + if tok.Type == token.LineComment && !a.usedComments[tok] { + comment := Comment{} + comment.Value = tok.Value + comments = append([]Comment{comment}, comments...) + a.usedComments[tok] = true + } else { + break + } + } + + node.LeadingComments = comments + } else { + for i := a.index + 1; i < len(a.Tokens); i++ { + tok := a.Tokens[i] + + if tok.Start[0] != currentToken.Start[0] { + break + } + + if tok.Type == token.LineComment { + comments = []Comment{{Value: tok.Value}} + a.usedComments[tok] = true + break + } + + break + } + + node.TrailingComments = comments + } +} + func (a *AstGenerator) readTypeDeclaration() TypeDeclaration { d := TypeDeclaration{} + a.resolveComments(&d.BaseDeclaration, true) + next, _ := a.nextToken(true) d.Id = next.Value @@ -89,6 +140,8 @@ func (a *AstGenerator) readTypeDeclaration() TypeDeclaration { d.Kind = String } + a.resolveComments(&d.BaseDeclaration, false) + return d } @@ -107,6 +160,7 @@ func (a *AstGenerator) readConstDeclaration() ConstDeclaration { break } + a.resolveComments(&decl.BaseDeclaration, true) a.match(token.Identifier) prev := a.currentToken decl.Id = a.currentToken.Value @@ -125,11 +179,13 @@ func (a *AstGenerator) readConstDeclaration() ConstDeclaration { if a.currentToken.Type == token.StringValue || a.currentToken.Type == token.IntValue || a.currentToken.Type == token.IOTA { decl.Value = a.currentToken.Value + a.resolveComments(&decl.BaseDeclaration, false) declarators = append(declarators, decl) } else { a.reportTokenError() } } else { + a.resolveComments(&decl.BaseDeclaration, false) declarators = append(declarators, decl) if a.currentToken.Type == token.RightParentheses { break @@ -141,7 +197,7 @@ func (a *AstGenerator) readConstDeclaration() ConstDeclaration { a.matchNextLine() } - return ConstDeclaration{declarators} + return ConstDeclaration{Declarators: declarators} } func (a *AstGenerator) match(t token.TokenType) { @@ -153,6 +209,10 @@ func (a *AstGenerator) match(t token.TokenType) { func (a *AstGenerator) matchNextLine() { next := a.Tokens[a.index+1] + if next.Type == token.LineComment { + next = a.Tokens[a.index+2] + } + if next.Type == token.Semicolon { a.nextToken(true) return @@ -192,5 +252,5 @@ func (a *AstGenerator) Gen() File { } func NewAstGenerator(tokens []token.Token) AstGenerator { - return AstGenerator{Tokens: tokens, index: -1} + return AstGenerator{Tokens: tokens, index: -1, usedComments: map[token.Token]bool{}} } diff --git a/src/ast/ast_test.go b/src/ast/ast_test.go index bb18fb0..53fd94e 100644 --- a/src/ast/ast_test.go +++ b/src/ast/ast_test.go @@ -15,7 +15,7 @@ func TestNormal(t *testing.T) { a := path.Join(wd, "../test-cases/normal.go") parser := token.NewParser(a) tokens := parser.Parse() - ast := AstGenerator{Tokens: tokens, index: -1} + ast := NewAstGenerator(tokens) result := ast.Gen() diff --git a/src/ast/type.go b/src/ast/type.go index 02fe0ce..5566d6a 100644 --- a/src/ast/type.go +++ b/src/ast/type.go @@ -7,16 +7,28 @@ const ( Int = "int" ) +type BaseDeclaration struct { + LeadingComments []Comment + TrailingComments []Comment +} + type TypeDeclaration struct { + BaseDeclaration Id string Kind TypeKind } type ConstDeclaration struct { + BaseDeclaration Declarators []ConstDeclarator } +type Comment struct { + Value string +} + type ConstDeclarator struct { + BaseDeclaration Kind string Id string Value string diff --git a/src/generator/__snapshots__/generator_test.snap b/src/generator/__snapshots__/generator_test.snap index 0932793..542f577 100755 --- a/src/generator/__snapshots__/generator_test.snap +++ b/src/generator/__snapshots__/generator_test.snap @@ -3,13 +3,17 @@ namespace some { export enum Sex { Female = 'female', - Male = 'male', + Male = 'male', //hhh + } export enum Status { - Todo = 0, + // 已完成 Done = 1, - Pending = 2, InProgress = 3, + Pending = 2, + // 代办 + Todo = 0, // 59todo + } } diff --git a/src/generator/generator.go b/src/generator/generator.go index 8e39c9f..94c20c7 100644 --- a/src/generator/generator.go +++ b/src/generator/generator.go @@ -8,10 +8,17 @@ import ( "github.com/anc95/golang-enum-to-ts/src/ast" ) -func getEnum(f ast.File) map[string]map[string]interface{} { +type EnumValue struct { + Comments ast.BaseDeclaration + Value interface{} +} + +type EnumValueMap map[string]map[string]EnumValue + +func getEnum(f ast.File) EnumValueMap { body := f.Body enumType := make(map[string]string) - enumValue := make(map[string]map[string]interface{}) + enumValueMap := make(EnumValueMap) for i := 0; i < len(body); i++ { decl := body[i] @@ -32,11 +39,13 @@ func getEnum(f ast.File) map[string]map[string]interface{} { if x.Kind != "" { kind = x.Kind - if enumValue[kind] == nil { - enumValue[kind] = map[string]interface{}{} + if enumValueMap[kind] == nil { + enumValueMap[kind] = map[string]EnumValue{} } } + enumValue := EnumValue{Comments: x.BaseDeclaration} + if iotaFlag { iotaValue += 1 } @@ -45,39 +54,41 @@ func getEnum(f ast.File) map[string]map[string]interface{} { if x.Value == "iota" { useIota = true iotaFlag = true - enumValue[kind][x.Id] = iotaValue + enumValue.Value = iotaValue } else { useIota = false if kind == "int" || enumType[kind] == "int" { val, _ := strconv.Atoi(x.Value) - enumValue[kind][x.Id] = val + enumValue.Value = val prevValue = val + } else { + enumValue.Value = x.Value + prevValue = x.Value } - - enumValue[kind][x.Id] = x.Value - prevValue = x.Value } } else { if useIota { - enumValue[kind][x.Id] = iotaValue + enumValue.Value = iotaValue } else { - enumValue[kind][x.Id] = prevValue + enumValue.Value = prevValue } } + + enumValueMap[kind][x.Id] = enumValue } } } - return enumValue + return enumValueMap } func GenerateTS(f ast.File) string { - enumValue := getEnum(f) + enumValueMap := getEnum(f) result := "" keys := make([]string, 0) - for k, _ := range enumValue { + for k, _ := range enumValueMap { keys = append(keys, k) } sort.Strings(keys) @@ -85,17 +96,37 @@ func GenerateTS(f ast.File) string { for _, key := range keys { ret := "" - for k, v := range enumValue[key] { + keys := make([]string, 0) + for k, _ := range enumValueMap[key] { + keys = append(keys, k) + } + sort.Strings(keys) + + for _, k := range keys { + v := enumValueMap[key][k] + if ret != "" { ret += "\n" } + leadingComments, trailingComments := v.Comments.LeadingComments, v.Comments.TrailingComments + statement := "" - switch v.(type) { + switch v.Value.(type) { case int: - ret += fmt.Sprintf(" %s = %d,", k, v) + statement += fmt.Sprintf(" %s = %d,", k, v.Value) case string: - ret += fmt.Sprintf(" %s = '%s',", k, v) + statement += fmt.Sprintf(" %s = '%s',", k, v.Value) + } + + for i := 0; leadingComments != nil && i < len(leadingComments); i++ { + statement = fmt.Sprintf(" //%s", leadingComments[i].Value) + statement } + + for i := 0; trailingComments != nil && i < len(trailingComments); i++ { + statement += fmt.Sprintf(" //%s", trailingComments[i].Value) + } + + ret += statement } ret = fmt.Sprintf(" export enum %s {\n", key) + ret diff --git a/src/test-cases/normal.go b/src/test-cases/normal.go index 8974f75..5dd0484 100644 --- a/src/test-cases/normal.go +++ b/src/test-cases/normal.go @@ -4,7 +4,9 @@ package some type Status int const ( - Todo Status = iota + // 代办 + Todo Status = iota // 59todo + // 已完成 Done Pending InProgress @@ -14,7 +16,7 @@ type Sex string const ( Female Sex = "female" - Male Sex = "male" + Male Sex = "male" //hhh ) func Abctext() { diff --git a/src/token/__snapshots__/token_test.snap b/src/token/__snapshots__/token_test.snap index ab00f85..9f5e051 100755 --- a/src/token/__snapshots__/token_test.snap +++ b/src/token/__snapshots__/token_test.snap @@ -49,143 +49,167 @@ Start: {5, 6}, End: {5, 6}, }, + { + Value: " 代办\n", + Type: "LineComment", + Start: {6, 2}, + End: {6, 10}, + }, { Value: "Todo", Type: "Identifier", - Start: {6, 1}, - End: {6, 4}, + Start: {7, 1}, + End: {7, 4}, }, { Value: "Status", Type: "Identifier", - Start: {6, 6}, - End: {6, 11}, + Start: {7, 6}, + End: {7, 11}, }, { Value: "=", Type: "Assignment", - Start: {6, 13}, - End: {6, 13}, + Start: {7, 13}, + End: {7, 13}, }, { Value: "iota", Type: "IOTA", - Start: {6, 15}, - End: {6, 18}, + Start: {7, 15}, + End: {7, 18}, + }, + { + Value: " 59todo\n", + Type: "LineComment", + Start: {7, 21}, + End: {7, 29}, + }, + { + Value: " 已完成\n", + Type: "LineComment", + Start: {8, 2}, + End: {8, 13}, }, { Value: "Done", Type: "Identifier", - Start: {7, 1}, - End: {7, 4}, + Start: {9, 1}, + End: {9, 4}, }, { Value: "Pending", Type: "Identifier", - Start: {8, 1}, - End: {8, 7}, + Start: {10, 1}, + End: {10, 7}, }, { Value: "InProgress", Type: "Identifier", - Start: {9, 1}, - End: {9, 10}, + Start: {11, 1}, + End: {11, 10}, }, { Value: ")", Type: "RightParentheses", - Start: {10, 0}, - End: {10, 0}, + Start: {12, 0}, + End: {12, 0}, }, { Value: "type", Type: "Type", - Start: {12, 0}, - End: {12, 3}, + Start: {14, 0}, + End: {14, 3}, }, { Value: "Sex", Type: "Identifier", - Start: {12, 5}, - End: {12, 7}, + Start: {14, 5}, + End: {14, 7}, }, { Value: "string", Type: "StringType", - Start: {12, 9}, - End: {12, 14}, + Start: {14, 9}, + End: {14, 14}, }, { Value: "const", Type: "Const", - Start: {14, 0}, - End: {14, 5}, + Start: {16, 0}, + End: {16, 5}, }, { Value: "(", Type: "LeftParentheses", - Start: {14, 6}, - End: {14, 6}, + Start: {16, 6}, + End: {16, 6}, }, { Value: "Female", Type: "Identifier", - Start: {15, 1}, - End: {15, 6}, + Start: {17, 1}, + End: {17, 6}, }, { Value: "Sex", Type: "Identifier", - Start: {15, 8}, - End: {15, 10}, + Start: {17, 8}, + End: {17, 10}, }, { Value: "=", Type: "Assignment", - Start: {15, 12}, - End: {15, 12}, + Start: {17, 12}, + End: {17, 12}, }, { Value: "female", Type: "StringValue", - Start: {15, 14}, - End: {15, 21}, + Start: {17, 14}, + End: {17, 21}, }, { Value: "Male", Type: "Identifier", - Start: {16, 1}, - End: {16, 4}, + Start: {18, 1}, + End: {18, 4}, }, { Value: "Sex", Type: "Identifier", - Start: {16, 8}, - End: {16, 10}, + Start: {18, 8}, + End: {18, 10}, }, { Value: "=", Type: "Assignment", - Start: {16, 12}, - End: {16, 12}, + Start: {18, 12}, + End: {18, 12}, }, { Value: "male", Type: "StringValue", - Start: {16, 14}, - End: {16, 19}, + Start: {18, 14}, + End: {18, 19}, + }, + { + Value: "hhh\n", + Type: "LineComment", + Start: {18, 22}, + End: {18, 26}, }, { Value: ")", Type: "RightParentheses", - Start: {17, 0}, - End: {17, 0}, + Start: {19, 0}, + End: {19, 0}, }, { Value: "func Abctext() {\n\t//dadsad\n}\n", Type: "Unknown", - Start: {19, 0}, - End: {21, 1}, + Start: {21, 0}, + End: {23, 1}, }, } ---