12 Commits

12 changed files with 244 additions and 71 deletions

View File

@@ -1,48 +1,66 @@
package main package main
import "os" import "os"
import "fmt"
import "strings" import "strings"
import "path/filepath" import "path/filepath"
import "git.tebibyte.media/sashakoshka/go-cli"
import "git.tebibyte.media/sashakoshka/goparse" import "git.tebibyte.media/sashakoshka/goparse"
import "git.tebibyte.media/sashakoshka/hopp/generate" import "git.tebibyte.media/sashakoshka/hopp/generate"
func main() { func main() {
name := os.Args[0] flagOutput := cli.NewInputFlag('o', "output", "The output file", "", cli.ValString)
if len(os.Args) != 3 { flagPackageName := cli.NewInputFlag('p', "package-name", "The package name of the file", "", cli.ValString)
fmt.Fprintf(os.Stderr, "Usage: %s SOURCE DESTINATION\n", name) command := cli.New("Compile PDL files to program source code",
flagOutput,
flagPackageName)
command.Syntax = "FILE [OPTIONS]..."
command.ParseOrExit(os.Args)
if len(command.Args) != 1 {
command.Usage()
os.Exit(2) os.Exit(2)
} }
source := os.Args[1] source := command.Args[0]
destination := os.Args[2] destination := flagOutput.Value
if destination == "" {
destination = "protocol.go"
}
input, err := os.Open(source) input, err := os.Open(source)
handleErr(1, err) handleErr(command, 1, err)
defer input.Close() defer input.Close()
protocol, err := generate.ParseReader(source, input) protocol, err := generate.ParseReader(source, input)
handleErr(1, err) handleErr(command, 1, err)
absDestination, err := filepath.Abs(destination) packageName := flagPackageName.Value
handleErr(1, err) if packageName == "" {
packageName := cleanPackageName(strings.ReplaceAll( absDestination, err := filepath.Abs(destination)
strings.ToLower(filepath.Base(absDestination)), handleErr(command, 1, err)
" ", "_")) base := filepath.Base(absDestination)
destination = filepath.Join(os.Args[2], "generated.go") if scrounged, ok := scroungeForPackageName(base); ok {
packageName = scrounged
} else {
packageName = strings.ReplaceAll(
strings.ToLower(base),
" ", "_")
}
}
packageName = cleanPackageName(packageName)
output, err := os.Create(destination) output, err := os.Create(destination)
handleErr(1, err) handleErr(command, 1, err)
generator := generate.Generator { generator := generate.Generator {
Output: output, Output: output,
PackageName: packageName, PackageName: packageName,
} }
_, err = generator.Generate(protocol) _, err = generator.Generate(protocol)
handleErr(1, err) handleErr(command, 1, err)
fmt.Fprintf(os.Stderr, "%s: OK\n", name) command.Println(output, "OK")
} }
func handleErr(code int, err error) { func handleErr(command *cli.Cli, code int, err error) {
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "%s: %v\n", os.Args[0], parse.Format(err)) command.Errorln(parse.Format(err))
os.Exit(code) os.Exit(code)
} }
} }
@@ -61,3 +79,32 @@ func cleanPackageName(str string) string {
} }
return string(buffer[:j]) return string(buffer[:j])
} }
func scroungeForPackageName(dir string) (string, bool) {
entries, err := os.ReadDir(dir)
if err != nil { return "", false}
for _, entry := range entries {
if !entry.Type().IsRegular() { continue }
file, err := os.Open(filepath.Join(dir, entry.Name()))
if err != nil { continue }
defer file.Close()
// FIXME: it is entirely possible that the only file will have
// a shitload of doc comments preceeding the package name, and
// those comments are usually huge so this is bad
buffer := [512]byte { }
n, _ := file.Read(buffer[:])
text := string(buffer[:n])
packageIndex := strings.Index(text, "package")
if packageIndex < 0 { continue }
text = text[packageIndex:]
newlineIndex := strings.Index(text, "\n")
if packageIndex > 0 { text = text[:newlineIndex] }
fields := strings.Fields(text)
if len(fields) < 2 { continue }
return fields[1], true
}
return "", false
}

View File

@@ -25,6 +25,7 @@ PDL allows defining a protocol using HOPP and TAPE.
| F64 | FP | 7 | | F64 | FP | 7 |
| F128[^2] | FP | 15 | | F128[^2] | FP | 15 |
| F256[^2] | FP | 31 | | F256[^2] | FP | 31 |
| Bool | SI | |
| String | SBA/LBA | * | UTF-8 string | String | SBA/LBA | * | UTF-8 string
| Buffer | SBA/LBA | * | Byte array | Buffer | SBA/LBA | * | Byte array
| []\<TYPE\> | OTA | * | Array of any type[^1] | []\<TYPE\> | OTA | * | Array of any type[^1]
@@ -52,6 +53,7 @@ structures. They are separated by whitespace.
| RBrace | `}` | A right curly brace. | RBrace | `}` | A right curly brace.
| LBracket | `[` | A left square bracket. | LBracket | `[` | A left square bracket.
| RBracket | `]` | A right square bracket. | RBracket | `]` | A right square bracket.
| Comment | `\/\/.*$` | A doc comment starting with a double-slash.
## Syntax ## Syntax
@@ -68,18 +70,27 @@ an Ident token respectively. A message consists of the method code (Method), the
message name (Ident), and the message's root type. This is usually a table, but message name (Ident), and the message's root type. This is usually a table, but
can be anything. can be anything.
Messages, types, and table fields can all have doc comments preceding them,
which are used to generate documentation for the protocol. The syntax is the
same as Go's (for now). Comments aren't allowed anywhere else.
Here is an example of all that: Here is an example of all that:
``` ```
// Connect is sent from the client to the server as the first message of an
// authenticated transaction.
M0000 Connect { M0000 Connect {
0000 Name String, 0000 Name String,
0001 Password String, 0001 Password String,
} }
// UserList is sent from the server to the client in response to a Connect
// message.
M0001 UserList { M0001 UserList {
0000 Users []User, 0000 Users []User,
} }
// User holds profile information about a single user.
User { User {
0000 Name String, 0000 Name String,
0001 Bio String, 0001 Bio String,
@@ -99,7 +110,7 @@ Below is an EBNF description of the language.
<field> -> <key> <ident> <type> <field> -> <key> <ident> <type>
<type> -> <ident> <type> -> <ident>
| "[" "]" <type> | "[" "]" <type>
| "{" (<field> ",")* [<field>] "}" | "{" (<comment>* <field> ",")* [<comment>* <field>] "}"
<message> -> <method> <ident> <type> <message> -> <comment>* <method> <ident> <type>
<typedef> -> <ident> <type> <typedef> -> <comment>* <ident> <type>
``` ```

View File

@@ -112,13 +112,20 @@ func (this *Generator) Generate(protocol *Protocol) (n int, err error) {
return n, nil return n, nil
} }
func (this *Generator) generateTypedef(name string, typ Type) (n int, err error) { func (this *Generator) generateTypedef(name string, typedef Typedef) (n int, err error) {
typ := typedef.Type
// type definition // type definition
nn, err := this.iprintf( if typedef.Doc == "" {
"\n// %s represents the protocol data type %s.\n", nn, err := this.iprintf(
name, name) "\n// %s represents the protocol data type %s.\n",
n += nn; if err != nil { return n, err } name, name)
nn, err = this.iprintf("type %s ", name) n += nn; if err != nil { return n, err }
} else {
nn, err := this.iprintf("\n%s\n", this.formatComment(typedef.Doc))
n += nn; if err != nil { return n, err }
}
nn, err := this.iprintf("type %s ", name)
n += nn; if err != nil { return n, err } n += nn; if err != nil { return n, err }
nn, err = this.generateType(typ) nn, err = this.generateType(typ)
n += nn; if err != nil { return n, err } n += nn; if err != nil { return n, err }
@@ -208,10 +215,16 @@ func (this *Generator) generateTypedef(name string, typ Type) (n int, err error)
// generateMessage generates the structure, as well as encoding decoding // generateMessage generates the structure, as well as encoding decoding
// functions for the given message. // functions for the given message.
func (this *Generator) generateMessage(method uint16, message Message) (n int, err error) { func (this *Generator) generateMessage(method uint16, message Message) (n int, err error) {
nn, err := this.iprintf( if message.Doc == "" {
"\n// %s represents the protocol message M%04X %s.\n", nn, err := this.iprintf(
message.Name, method, message.Name) "\n// %s represents the protocol message M%04X %s.\n",
nn, err = this.iprintf("type %s ", this.resolveMessageName(message.Name)) message.Name, method, message.Name)
n += nn; if err != nil { return n, err }
} else {
nn, err := this.iprintf("\n%s\n", this.formatComment(message.Doc))
n += nn; if err != nil { return n, err }
}
nn, err := this.iprintf("type %s ", this.resolveMessageName(message.Name))
n += nn; if err != nil { return n, err } n += nn; if err != nil { return n, err }
nn, err = this.generateType(message.Type) nn, err = this.generateType(message.Type)
n += nn; if err != nil { return n, err } n += nn; if err != nil { return n, err }
@@ -1090,7 +1103,9 @@ func (this *Generator) generateTypeTableDefined(typ TypeTableDefined) (n int, er
for _, key := range slices.Sorted(maps.Keys(typ.Fields)) { for _, key := range slices.Sorted(maps.Keys(typ.Fields)) {
field := typ.Fields[key] field := typ.Fields[key]
nn, err := this.iprintf("%s ", field.Name) nn, err := this.iprintf("%s\n", this.formatComment(field.Doc))
n += nn; if err != nil { return n, err }
nn, err = this.iprintf("%s ", field.Name)
n += nn; if err != nil { return n, err } n += nn; if err != nil { return n, err }
nn, err = this.generateType(field.Type) nn, err = this.generateType(field.Type)
n += nn; if err != nil { return n, err } n += nn; if err != nil { return n, err }
@@ -1175,17 +1190,21 @@ func (this *Generator) iprintf(format string, args ...any) (n int, err error) {
return fmt.Fprintf(this.Output, this.indent() + format, args...) return fmt.Fprintf(this.Output, this.indent() + format, args...)
} }
func (this *Generator) formatComment(comment string) string {
return "// " + strings.ReplaceAll(comment, "\n", "\n" + this.indent() + "// ")
}
func (this *Generator) resolveMessageName(message string) string { func (this *Generator) resolveMessageName(message string) string {
return "Message" + message return "Message" + message
} }
func (this *Generator) resolveTypeName(name string) (Type, error) { func (this *Generator) resolveTypeName(name string) (Type, error) {
if typ, ok := this.protocol.Types[name]; ok { if typedef, ok := this.protocol.Types[name]; ok {
if typ, ok := typ.(TypeNamed); ok { if typ, ok := typedef.Type.(TypeNamed); ok {
return this.resolveTypeName(typ.Name) return this.resolveTypeName(typ.Name)
} }
return typ, nil return typedef.Type, nil
} }
return nil, fmt.Errorf("no type exists called %s", name) return nil, fmt.Errorf("no type exists called %s", name)
} }

View File

@@ -83,11 +83,13 @@ func init() {
}, },
}, },
} }
exampleProtocol.Types["User"] = TypeTableDefined { exampleProtocol.Types["User"] = Typedef {
Fields: map[uint16] Field { Type: TypeTableDefined {
0x0000: Field { Name: "Name", Type: TypeString { } }, Fields: map[uint16] Field {
0x0001: Field { Name: "Bio", Type: TypeString { } }, 0x0000: Field { Name: "Name", Type: TypeString { } },
0x0002: Field { Name: "Followers", Type: TypeInt { Bits: 32 } }, 0x0001: Field { Name: "Bio", Type: TypeString { } },
0x0002: Field { Name: "Followers", Type: TypeInt { Bits: 32 } },
},
}, },
} }
} }

View File

@@ -15,6 +15,7 @@ const (
TokenRBrace TokenRBrace
TokenLBracket TokenLBracket
TokenRBracket TokenRBracket
TokenComment
) )
var tokenNames = map[parse.TokenKind] string { var tokenNames = map[parse.TokenKind] string {
@@ -26,6 +27,7 @@ var tokenNames = map[parse.TokenKind] string {
TokenRBrace: "RBrace", TokenRBrace: "RBrace",
TokenLBracket: "LBracket", TokenLBracket: "LBracket",
TokenRBracket: "RBracket", TokenRBracket: "RBracket",
TokenComment: "Comment",
} }
func Lex(fileName string, reader io.Reader) (parse.Lexer, error) { func Lex(fileName string, reader io.Reader) (parse.Lexer, error) {
@@ -81,6 +83,18 @@ func (this *lexer) nextInternal() (token parse.Token, err error) {
} }
} }
unexpected := func() error {
if unicode.IsPrint(this.rune) {
return parse.Errorf (
this.pos(), "unexpected rune '%c'",
this.rune)
} else {
return parse.Errorf (
this.pos(), "unexpected rune %U",
this.rune)
}
}
defer func () { defer func () {
newPos := this.pos() newPos := this.pos()
newPos.End -- // TODO figure out why tf we have to do this newPos.End -- // TODO figure out why tf we have to do this
@@ -133,14 +147,21 @@ func (this *lexer) nextInternal() (token parse.Token, err error) {
token.Kind = TokenRBracket token.Kind = TokenRBracket
appendRune() appendRune()
if this.eof { err = nil; return } if this.eof { err = nil; return }
case unicode.IsPrint(this.rune): // Comment
err = parse.Errorf ( case this.rune == '/':
this.pos(), "unexpected rune '%c'", token.Kind = TokenComment
this.rune) appendRune()
if this.eof { return }
if this.rune != '/' {
err = unexpected()
return
}
for this.rune != '\n' {
appendRune()
if this.eof { err = nil; return }
}
default: default:
err = parse.Errorf ( err = unexpected()
this.pos(), "unexpected rune %U",
this.rune)
} }
return return

View File

@@ -6,14 +6,21 @@ import "git.tebibyte.media/sashakoshka/goparse"
func TestLex(test *testing.T) { func TestLex(test *testing.T) {
lexer, err := Lex("test.pdl", strings.NewReader(` lexer, err := Lex("test.pdl", strings.NewReader(`
// User holds profile information about a single user.
M0001 User { M0001 User {
0000 Name String, 0000 Name String,
// dog water comment
// Users is asdkjsagkj why
//
// wow
0001 Users []User, 0001 Users []User,
0002 Followers U32, 0002 Followers U32,
}`)) }`))
if err != nil { test.Fatal(parse.Format(err)) } if err != nil { test.Fatal(parse.Format(err)) }
correctTokens := []parse.Token { correctTokens := []parse.Token {
tok(TokenComment, "// User holds profile information about a single user."),
tok(TokenMethod, "0001"), tok(TokenMethod, "0001"),
tok(TokenIdent, "User"), tok(TokenIdent, "User"),
tok(TokenLBrace, "{"), tok(TokenLBrace, "{"),
@@ -21,6 +28,10 @@ func TestLex(test *testing.T) {
tok(TokenIdent, "Name"), tok(TokenIdent, "Name"),
tok(TokenIdent, "String"), tok(TokenIdent, "String"),
tok(TokenComma, ","), tok(TokenComma, ","),
tok(TokenComment, "// dog water comment"),
tok(TokenComment, "// Users is asdkjsagkj why"),
tok(TokenComment, "// "),
tok(TokenComment, "// wow"),
tok(TokenKey, "0001"), tok(TokenKey, "0001"),
tok(TokenIdent, "Users"), tok(TokenIdent, "Users"),
tok(TokenLBracket, "["), tok(TokenLBracket, "["),

View File

@@ -1,6 +1,7 @@
package generate package generate
import "io" import "io"
import "strings"
import "strconv" import "strconv"
import "git.tebibyte.media/sashakoshka/goparse" import "git.tebibyte.media/sashakoshka/goparse"
@@ -21,7 +22,7 @@ func Parse(lx parse.Lexer) (*Protocol, error) {
func defaultProtocol() Protocol { func defaultProtocol() Protocol {
return Protocol { return Protocol {
Messages: make(map[uint16] Message), Messages: make(map[uint16] Message),
Types: map[string] Type { }, Types: map[string] Typedef { },
} }
} }
@@ -47,18 +48,28 @@ func (this *parser) parse() error {
} }
func (this *parser) parseTopLevel() error { func (this *parser) parseTopLevel() error {
err := this.ExpectDesc("message or typedef", TokenMethod, TokenIdent) doc := ""
if err != nil { return err } for {
if this.EOF() { return nil } err := this.ExpectDesc("message or typedef", TokenMethod, TokenIdent, TokenComment)
if err != nil { return err }
if this.EOF() { return nil }
if this.Kind() == TokenComment {
if doc != "" { doc += "\n" }
doc += this.parseComment(this.Value())
this.Next()
} else {
break
}
}
switch this.Kind() { switch this.Kind() {
case TokenMethod: return this.parseMessage() case TokenMethod: return this.parseMessage(doc)
case TokenIdent: return this.parseTypedef() case TokenIdent: return this.parseTypedef(doc)
} }
panic("bug") panic("bug")
} }
func (this *parser) parseMessage() error { func (this *parser) parseMessage(doc string) error {
err := this.Expect(TokenMethod) err := this.Expect(TokenMethod)
if err != nil { return err } if err != nil { return err }
method, err := this.parseHexNumber(this.Value(), 0xFFFF) method, err := this.parseHexNumber(this.Value(), 0xFFFF)
@@ -72,12 +83,13 @@ func (this *parser) parseMessage() error {
if err != nil { return err } if err != nil { return err }
this.protocol.Messages[uint16(method)] = Message { this.protocol.Messages[uint16(method)] = Message {
Name: name, Name: name,
Doc: doc,
Type: typ, Type: typ,
} }
return nil return nil
} }
func (this *parser) parseTypedef() error { func (this *parser) parseTypedef(doc string) error {
err := this.Expect(TokenIdent) err := this.Expect(TokenIdent)
if err != nil { return err } if err != nil { return err }
name := this.Value() name := this.Value()
@@ -85,7 +97,10 @@ func (this *parser) parseTypedef() error {
if err != nil { return err } if err != nil { return err }
typ, err := this.parseType() typ, err := this.parseType()
if err != nil { return err } if err != nil { return err }
this.protocol.Types[name] = typ this.protocol.Types[name] = Typedef {
Doc: doc,
Type: typ,
}
return nil return nil
} }
@@ -117,6 +132,7 @@ func (this *parser) parseType() (Type, error) {
case "Buffer": return TypeBuffer { }, this.Next() case "Buffer": return TypeBuffer { }, this.Next()
case "Table": return TypeTable { }, this.Next() case "Table": return TypeTable { }, this.Next()
case "Any": return TypeAny { }, this.Next() case "Any": return TypeAny { }, this.Next()
case "Bool": return TypeBool { }, this.Next()
} }
return this.parseTypeNamed() return this.parseTypeNamed()
case TokenLBracket: case TokenLBracket:
@@ -157,12 +173,22 @@ func (this *parser) parseTypeTable() (TypeTableDefined, error) {
Fields: make(map[uint16] Field), Fields: make(map[uint16] Field),
} }
for { for {
err := this.ExpectDesc("table field", TokenKey, TokenRBrace) doc := ""
if err != nil { return TypeTableDefined { }, err } for {
err := this.ExpectDesc("table field", TokenKey, TokenRBrace, TokenComment)
if err != nil { return TypeTableDefined { }, err }
if this.Kind() == TokenComment {
if doc != "" { doc += "\n" }
doc += this.parseComment(this.Value())
this.Next()
} else {
break
}
}
if this.Is(TokenRBrace) { if this.Is(TokenRBrace) {
break break
} }
key, field, err := this.parseField() key, field, err := this.parseField(doc)
if err != nil { return TypeTableDefined { }, err } if err != nil { return TypeTableDefined { }, err }
typ.Fields[key] = field typ.Fields[key] = field
err = this.Expect(TokenComma, TokenRBrace) err = this.Expect(TokenComma, TokenRBrace)
@@ -178,7 +204,7 @@ func (this *parser) parseTypeTable() (TypeTableDefined, error) {
return typ, nil return typ, nil
} }
func (this *parser) parseField() (uint16, Field, error) { func (this *parser) parseField(doc string) (uint16, Field, error) {
err := this.Expect(TokenKey) err := this.Expect(TokenKey)
if err != nil { return 0, Field { }, err } if err != nil { return 0, Field { }, err }
key, err := this.parseHexNumber(this.Value(), 0xFFFF) key, err := this.parseHexNumber(this.Value(), 0xFFFF)
@@ -192,6 +218,7 @@ func (this *parser) parseField() (uint16, Field, error) {
if err != nil { return 0, Field { }, err } if err != nil { return 0, Field { }, err }
return uint16(key), Field { return uint16(key), Field {
Name: name, Name: name,
Doc: doc,
Type: typ, Type: typ,
}, nil }, nil
} }
@@ -206,3 +233,7 @@ func (this *parser) parseHexNumber(input string, maxValue int64) (int64, error)
} }
return number, nil return number, nil
} }
func (this *parser) parseComment(input string) string {
return strings.TrimPrefix(strings.TrimPrefix(input, "//"), " ")
}

View File

@@ -9,6 +9,7 @@ func TestParse(test *testing.T) {
correct := defaultProtocol() correct := defaultProtocol()
correct.Messages[0x0000] = Message { correct.Messages[0x0000] = Message {
Name: "Connect", Name: "Connect",
Doc: "Connect is sent from the client to the server as the first message of an\nauthenticated transaction.",
Type: TypeTableDefined { Type: TypeTableDefined {
Fields: map[uint16] Field { Fields: map[uint16] Field {
0x0000: Field { Name: "Name", Type: TypeString { } }, 0x0000: Field { Name: "Name", Type: TypeString { } },
@@ -18,36 +19,50 @@ func TestParse(test *testing.T) {
} }
correct.Messages[0x0001] = Message { correct.Messages[0x0001] = Message {
Name: "UserList", Name: "UserList",
Doc: "UserList is sent from the server to the client in response to a Connect\nmessage.",
Type: TypeTableDefined { Type: TypeTableDefined {
Fields: map[uint16] Field { Fields: map[uint16] Field {
0x0000: Field { Name: "Users", Type: TypeArray { Element: TypeNamed { Name: "User" } } }, 0x0000: Field { Name: "Users", Type: TypeArray { Element: TypeNamed { Name: "User" } } },
}, },
}, },
} }
correct.Types["User"] = TypeTableDefined { correct.Types["User"] = Typedef {
Fields: map[uint16] Field { Doc: "User holds profile information about a single user.",
0x0000: Field { Name: "Name", Type: TypeString { } }, Type: TypeTableDefined {
0x0001: Field { Name: "Bio", Type: TypeString { } }, Fields: map[uint16] Field {
0x0002: Field { Name: "Followers", Type: TypeInt { Bits: 32 } }, 0x0000: Field { Name: "Name", Type: TypeString { } },
0x0001: Field { Name: "Bio", Type: TypeString { } },
0x0002: Field { Name: "Followers", Type: TypeInt { Bits: 32 } },
0x0003: Field { Name: "Bouncy", Type: TypeBool { } },
},
}, },
} }
correct.Types["Anything"] = TypeAny { } correct.Types["Anything"] = Typedef {
Type: TypeAny { },
}
test.Log("CORRECT:", &correct) test.Log("CORRECT:", &correct)
got, err := ParseReader("test.pdl", strings.NewReader(` got, err := ParseReader("test.pdl", strings.NewReader(`
// Connect is sent from the client to the server as the first message of an
// authenticated transaction.
M0000 Connect { M0000 Connect {
0000 Name String, 0000 Name String,
// Password is where you put your secrets, your shameful secrets
0001 Password String, 0001 Password String,
} }
// UserList is sent from the server to the client in response to a Connect
// message.
M0001 UserList { M0001 UserList {
0000 Users []User, 0000 Users []User,
} }
// User holds profile information about a single user.
User { User {
0000 Name String, 0000 Name String,
0001 Bio String, 0001 Bio String,
0002 Followers U32, 0002 Followers U32,
0003 Bouncy Bool,
} }
Anything Any Anything Any

View File

@@ -7,11 +7,17 @@ import "crypto/md5"
type Protocol struct { type Protocol struct {
Messages map[uint16] Message Messages map[uint16] Message
Types map[string] Type Types map[string] Typedef
} }
type Message struct { type Message struct {
Name string Name string
Doc string
Type Type
}
type Typedef struct {
Doc string
Type Type Type Type
} }
@@ -43,6 +49,12 @@ func (typ TypeFloat) String() string {
return fmt.Sprintf("F%d", typ.Bits) return fmt.Sprintf("F%d", typ.Bits)
} }
type TypeBool struct { }
func (TypeBool) String() string {
return "Bool"
}
type TypeString struct { } type TypeString struct { }
func (TypeString) String() string { func (TypeString) String() string {
@@ -84,6 +96,7 @@ func (typ TypeTableDefined) String() string {
type Field struct { type Field struct {
Name string Name string
Doc string
Type Type Type Type
} }

1
go.mod
View File

@@ -3,6 +3,7 @@ module git.tebibyte.media/sashakoshka/hopp
go 1.23.0 go 1.23.0
require ( require (
git.tebibyte.media/sashakoshka/go-cli v0.1.3
git.tebibyte.media/sashakoshka/go-util v0.9.1 git.tebibyte.media/sashakoshka/go-util v0.9.1
git.tebibyte.media/sashakoshka/goparse v0.2.0 git.tebibyte.media/sashakoshka/goparse v0.2.0
) )

2
go.sum
View File

@@ -1,3 +1,5 @@
git.tebibyte.media/sashakoshka/go-cli v0.1.3 h1:tSkWjyx2JrGu6KotbXWSTKSYGGS1D4O3qwCrRoZuwbs=
git.tebibyte.media/sashakoshka/go-cli v0.1.3/go.mod h1:JFA3wSdRkXxa4iQJWHfe3DokiG7Dh2XUJBzPmuVlbuY=
git.tebibyte.media/sashakoshka/go-util v0.9.1 h1:eGAbLwYhOlh4aq/0w+YnJcxT83yPhXtxnYMzz6K7xGo= git.tebibyte.media/sashakoshka/go-util v0.9.1 h1:eGAbLwYhOlh4aq/0w+YnJcxT83yPhXtxnYMzz6K7xGo=
git.tebibyte.media/sashakoshka/go-util v0.9.1/go.mod h1:0Q1t+PePdx6tFYkRuJNcpM1Mru7wE6X+it1kwuOH+6Y= git.tebibyte.media/sashakoshka/go-util v0.9.1/go.mod h1:0Q1t+PePdx6tFYkRuJNcpM1Mru7wE6X+it1kwuOH+6Y=
git.tebibyte.media/sashakoshka/goparse v0.2.0 h1:uQmKvOCV2AOlCHEDjg9uclZCXQZzq2PxaXfZ1aIMiQI= git.tebibyte.media/sashakoshka/goparse v0.2.0 h1:uQmKvOCV2AOlCHEDjg9uclZCXQZzq2PxaXfZ1aIMiQI=

View File

@@ -252,8 +252,8 @@ func decodeAnyOrError(decoder *Decoder, destination reflect.Value, tag Tag) (n i
// not we receive an empty any value) actually makes it fucking // not we receive an empty any value) actually makes it fucking
// work. go figure(). // work. go figure().
// //
// (the map allocation functionality in skeletonPointer has been // (the map allocation functionality in skeletonPointer was
// removed) // removed after this comment was written)
value := reflect.MakeMapWithSize(reflect.TypeOf(dummyMap), lengthCast) value := reflect.MakeMapWithSize(reflect.TypeOf(dummyMap), lengthCast)
destination.Set(value) destination.Set(value)
destination = value destination = value