Coder Social home page Coder Social logo

yoheimuta / go-protoparser Goto Github PK

View Code? Open in Web Editor NEW
162.0 6.0 20.0 381 KB

Yet another Go package which parses a Protocol Buffer file (proto2+proto3)

License: MIT License

Makefile 0.48% Go 99.52%
proto3 protobuf parser formatter golang-package golang protocol-buffers protobuf-parser proto2

go-protoparser's Introduction

go-protoparser GoDocCircleCIGo Report CardReleaseLicense

go-protoparser is a yet another Go package which parses a Protocol Buffer file (proto2+proto3).

  • Conforms to the exactly official spec.
  • Undergone rigorous testing. The parser can parses all examples of the official spec well.
  • Easy to use the parser. You can just call the Parse function and receive the Proto struct.

Installation

GO111MODULE=on go get github.com/yoheimuta/go-protoparser/v4

Example

A Protocol Buffer file versioned 3 which is an example of the official reference.

syntax = "proto3";
// An example of the official reference
// See https://developers.google.com/protocol-buffers/docs/reference/proto3-spec#proto_file
package examplepb;
import public "other.proto";
option java_package = "com.example.foo";
enum EnumAllowingAlias {
    option allow_alias = true;
    UNKNOWN = 0;
    STARTED = 1;
    RUNNING = 2 [(custom_option) = "this is a "
                                   "string on two lines"
                ];
}
message outer {
    option (my_option).a = true;
    message inner {   // Level 2
      int64 ival = 1;
    }
    repeated inner inner_message = 2;
    EnumAllowingAlias enum_field =3;
    map<int32, string> my_map = 4;
}
service HelloService {
  rpc SayHello (HelloRequest) returns (HelloResponse) {};
}

The Parsed result is a Go typed struct. The below output is encoded to JSON for simplicity.

{
  "Syntax": {
    "ProtobufVersion": "proto3",
    "ProtobufVersionQuote": "\"proto3\"",
    "Comments": null,
    "InlineComment": null,
    "Meta": {
      "Pos": {
        "Filename": "simple.proto",
        "Offset": 0,
        "Line": 1,
        "Column": 1
      },
      "LastPos": {
        "Filename": "",
        "Offset": 0,
        "Line": 0,
        "Column": 0
      }
    }
  },
  "ProtoBody": [
    {
      "Name": "examplepb",
      "Comments": [
        {
          "Raw": "// An example of the official reference",
          "Meta": {
            "Pos": {
              "Filename": "simple.proto",
              "Offset": 19,
              "Line": 2,
              "Column": 1
            },
            "LastPos": {
              "Filename": "",
              "Offset": 0,
              "Line": 0,
              "Column": 0
            }
          }
        },
        {
          "Raw": "// See https://developers.google.com/protocol-buffers/docs/reference/proto3-spec#proto_file",
          "Meta": {
            "Pos": {
              "Filename": "simple.proto",
              "Offset": 59,
              "Line": 3,
              "Column": 1
            },
            "LastPos": {
              "Filename": "",
              "Offset": 0,
              "Line": 0,
              "Column": 0
            }
          }
        }
      ],
      "InlineComment": null,
      "Meta": {
        "Pos": {
          "Filename": "simple.proto",
          "Offset": 151,
          "Line": 4,
          "Column": 1
        },
        "LastPos": {
          "Filename": "",
          "Offset": 0,
          "Line": 0,
          "Column": 0
        }
      }
    },
    {
      "Modifier": 1,
      "Location": "\"other.proto\"",
      "Comments": null,
      "InlineComment": null,
      "Meta": {
        "Pos": {
          "Filename": "simple.proto",
          "Offset": 170,
          "Line": 5,
          "Column": 1
        },
        "LastPos": {
          "Filename": "",
          "Offset": 0,
          "Line": 0,
          "Column": 0
        }
      }
    },
    {
      "OptionName": "java_package",
      "Constant": "\"com.example.foo\"",
      "Comments": null,
      "InlineComment": null,
      "Meta": {
        "Pos": {
          "Filename": "simple.proto",
          "Offset": 199,
          "Line": 6,
          "Column": 1
        },
        "LastPos": {
          "Filename": "",
          "Offset": 0,
          "Line": 0,
          "Column": 0
        }
      }
    },
    {
      "EnumName": "EnumAllowingAlias",
      "EnumBody": [
        {
          "OptionName": "allow_alias",
          "Constant": "true",
          "Comments": null,
          "InlineComment": null,
          "Meta": {
            "Pos": {
              "Filename": "simple.proto",
              "Offset": 269,
              "Line": 8,
              "Column": 5
            },
            "LastPos": {
              "Filename": "",
              "Offset": 0,
              "Line": 0,
              "Column": 0
            }
          }
        },
        {
          "Ident": "UNKNOWN",
          "Number": "0",
          "EnumValueOptions": null,
          "Comments": null,
          "InlineComment": null,
          "Meta": {
            "Pos": {
              "Filename": "simple.proto",
              "Offset": 300,
              "Line": 9,
              "Column": 5
            },
            "LastPos": {
              "Filename": "",
              "Offset": 0,
              "Line": 0,
              "Column": 0
            }
          }
        },
        {
          "Ident": "STARTED",
          "Number": "1",
          "EnumValueOptions": null,
          "Comments": null,
          "InlineComment": null,
          "Meta": {
            "Pos": {
              "Filename": "simple.proto",
              "Offset": 317,
              "Line": 10,
              "Column": 5
            },
            "LastPos": {
              "Filename": "",
              "Offset": 0,
              "Line": 0,
              "Column": 0
            }
          }
        },
        {
          "Ident": "RUNNING",
          "Number": "2",
          "EnumValueOptions": [
            {
              "OptionName": "(custom_option)",
              "Constant": "\"this is a string on two lines\""
            }
          ],
          "Comments": null,
          "InlineComment": null,
          "Meta": {
            "Pos": {
              "Filename": "simple.proto",
              "Offset": 334,
              "Line": 11,
              "Column": 5
            },
            "LastPos": {
              "Filename": "",
              "Offset": 0,
              "Line": 0,
              "Column": 0
            }
          }
        }
      ],
      "Comments": null,
      "InlineComment": null,
      "InlineCommentBehindLeftCurly": null,
      "Meta": {
        "Pos": {
          "Filename": "simple.proto",
          "Offset": 240,
          "Line": 7,
          "Column": 1
        },
        "LastPos": {
          "Filename": "simple.proto",
          "Offset": 454,
          "Line": 14,
          "Column": 1
        }
      }
    },
    {
      "MessageName": "outer",
      "MessageBody": [
        {
          "OptionName": "(my_option).a",
          "Constant": "true",
          "Comments": null,
          "InlineComment": null,
          "Meta": {
            "Pos": {
              "Filename": "simple.proto",
              "Offset": 476,
              "Line": 16,
              "Column": 5
            },
            "LastPos": {
              "Filename": "",
              "Offset": 0,
              "Line": 0,
              "Column": 0
            }
          }
        },
        {
          "MessageName": "inner",
          "MessageBody": [
            {
              "IsRepeated": false,
              "IsRequired": false,
              "IsOptional": false,
              "Type": "int64",
              "FieldName": "ival",
              "FieldNumber": "1",
              "FieldOptions": null,
              "Comments": null,
              "InlineComment": null,
              "Meta": {
                "Pos": {
                  "Filename": "simple.proto",
                  "Offset": 544,
                  "Line": 18,
                  "Column": 7
                },
                "LastPos": {
                  "Filename": "",
                  "Offset": 0,
                  "Line": 0,
                  "Column": 0
                }
              }
            }
          ],
          "Comments": null,
          "InlineComment": null,
          "InlineCommentBehindLeftCurly": {
            "Raw": "// Level 2",
            "Meta": {
              "Pos": {
                "Filename": "simple.proto",
                "Offset": 527,
                "Line": 17,
                "Column": 23
              },
              "LastPos": {
                "Filename": "",
                "Offset": 0,
                "Line": 0,
                "Column": 0
              }
            }
          },
          "Meta": {
            "Pos": {
              "Filename": "simple.proto",
              "Offset": 509,
              "Line": 17,
              "Column": 5
            },
            "LastPos": {
              "Filename": "simple.proto",
              "Offset": 564,
              "Line": 19,
              "Column": 5
            }
          }
        },
        {
          "IsRepeated": true,
          "IsRequired": false,
          "IsOptional": false,
          "Type": "inner",
          "FieldName": "inner_message",
          "FieldNumber": "2",
          "FieldOptions": null,
          "Comments": null,
          "InlineComment": null,
          "Meta": {
            "Pos": {
              "Filename": "simple.proto",
              "Offset": 570,
              "Line": 20,
              "Column": 5
            },
            "LastPos": {
              "Filename": "",
              "Offset": 0,
              "Line": 0,
              "Column": 0
            }
          }
        },
        {
          "IsRepeated": false,
          "IsRequired": false,
          "IsOptional": false,
          "Type": "EnumAllowingAlias",
          "FieldName": "enum_field",
          "FieldNumber": "3",
          "FieldOptions": null,
          "Comments": null,
          "InlineComment": null,
          "Meta": {
            "Pos": {
              "Filename": "simple.proto",
              "Offset": 608,
              "Line": 21,
              "Column": 5
            },
            "LastPos": {
              "Filename": "",
              "Offset": 0,
              "Line": 0,
              "Column": 0
            }
          }
        },
        {
          "KeyType": "int32",
          "Type": "string",
          "MapName": "my_map",
          "FieldNumber": "4",
          "FieldOptions": null,
          "Comments": null,
          "InlineComment": null,
          "Meta": {
            "Pos": {
              "Filename": "simple.proto",
              "Offset": 645,
              "Line": 22,
              "Column": 5
            },
            "LastPos": {
              "Filename": "",
              "Offset": 0,
              "Line": 0,
              "Column": 0
            }
          }
        }
      ],
      "Comments": null,
      "InlineComment": null,
      "InlineCommentBehindLeftCurly": null,
      "Meta": {
        "Pos": {
          "Filename": "simple.proto",
          "Offset": 456,
          "Line": 15,
          "Column": 1
        },
        "LastPos": {
          "Filename": "simple.proto",
          "Offset": 676,
          "Line": 23,
          "Column": 1
        }
      }
    },
    {
      "ServiceName": "HelloService",
      "ServiceBody": [
        {
          "RPCName": "SayHello",
          "RPCRequest": {
            "IsStream": false,
            "MessageType": "HelloRequest",
            "Meta": {
              "Pos": {
                "Filename": "simple.proto",
                "Offset": 716,
                "Line": 25,
                "Column": 16
              },
              "LastPos": {
                "Filename": "",
                "Offset": 0,
                "Line": 0,
                "Column": 0
              }
            }
          },
          "RPCResponse": {
            "IsStream": false,
            "MessageType": "HelloResponse",
            "Meta": {
              "Pos": {
                "Filename": "simple.proto",
                "Offset": 739,
                "Line": 25,
                "Column": 39
              },
              "LastPos": {
                "Filename": "",
                "Offset": 0,
                "Line": 0,
                "Column": 0
              }
            }
          },
          "Options": null,
          "Comments": null,
          "InlineComment": null,
          "Meta": {
            "Pos": {
              "Filename": "simple.proto",
              "Offset": 703,
              "Line": 25,
              "Column": 3
            },
            "LastPos": {
              "Filename": "simple.proto",
              "Offset": 757,
              "Line": 25,
              "Column": 57
            }
          }
        }
      ],
      "Comments": null,
      "InlineComment": null,
      "InlineCommentBehindLeftCurly": null,
      "Meta": {
        "Pos": {
          "Filename": "simple.proto",
          "Offset": 678,
          "Line": 24,
          "Column": 1
        },
        "LastPos": {
          "Filename": "simple.proto",
          "Offset": 759,
          "Line": 26,
          "Column": 1
        }
      }
    }
  ],
  "Meta": {
    "Filename": "simple.proto"
  }
}

Usage

See also _example/dump.

func run() int {
	flag.Parse()

	reader, err := os.Open(*proto)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to open %s, err %v\n", *proto, err)
		return 1
	}
	defer reader.Close()

	got, err := protoparser.Parse(reader)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to parse, err %v\n", err)
		return 1
	}

	gotJSON, err := json.MarshalIndent(got, "", "  ")
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to marshal, err %v\n", err)
	}
	fmt.Print(string(gotJSON))
	return 0
}

func main() {
	os.Exit(run())
}

Users

Motivation

There exists the similar protobuf parser packages in Go.

But I could not find the parser which just return a parsing result well-typed value. A parser which supports a visitor pattern is useful to implement like linter, but it may be difficult to use. It can be sufficient for most parsing situations to just return a parsing result well-typed value. This is easier to use.

Contributing

  • Fork it
  • Create your feature branch: git checkout -b your-new-feature
  • Commit changes: git commit -m 'Add your feature'
  • Pass all tests
  • Push to the branch: git push origin your-new-feature
  • Submit a pull request

License

The MIT License (MIT)

Acknowledgement

Thank you to the proto package: https://github.com/emicklei/proto

I referred to the package for the good proven design, interface and some source code.

go-protoparser's People

Contributors

ehrktia avatar geraldywy avatar jtxyz avatar paulsonoflars avatar perrydunn avatar shaldengeki avatar wwuck avatar yoheimuta avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

go-protoparser's Issues

Use Bazel for builds?

Hello!

I use Bazel to build a bunch of my personal projects, and at work as well. I like it a lot: https://bazel.build

In particular, Bazel support for golang is pretty good. There's a neat tool called gazelle that auto-manages things for you: https://github.com/bazelbuild/bazel-gazelle

I'd like to use your parser in projects that use Bazel for builds, and will probably end up bazel-ifying it in a fork. Mostly this involves adding BUILD.bazel files, and a WORKSPACE file. Would you be open to accepting this upstream? I'm happy to do the work of converting your builds, so that your CI builds get the benefits of Bazel caching.

MessageBody does not have parser.Extensions field, so UnorderedInterpret returns error when parse a proto message with "extensions"

type MessageBody struct {
Fields []*parser.Field
Enums []*Enum
Messages []*Message
Options []*parser.Option
Oneofs []*parser.Oneof
Maps []*parser.MapField
Groups []*parser.GroupField
Reserves []*parser.Reserved
Extends []*parser.Extend
EmptyStatements []*parser.EmptyStatement
}

could you please add a new field Extensions []*parser.Extension to the MessageBody struct and the switch statement? Thanks.

Parser panics when double semi-colon in service option

test case

Here is a simple reprod:

syntax = "proto3";
import "google/protobuf/descriptor.proto";

extend google.protobuf.ServiceOptions  {
  string service_description = 51000;
}

service MyService{
  option(service_description) = "description";;
}

note that there is a mistake in the file, with a double semicolon after description.

actual behaviour

when running protolint on this file, we got a crash:


protolint lint test.proto
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x18 pc=0x527104]

goroutine 1 [running]:
github.com/yoheimuta/go-protoparser/v4/parser.(*Service).Accept(0xc0001b45a0, {0xb52a20, 0xc000008bd0})
        /home/runner/go/pkg/mod/github.com/yoheimuta/go-protoparser/[email protected]/parser/service.go:90 +0x64
github.com/yoheimuta/go-protoparser/v4/parser.(*Proto).Accept(0xc000222750?, {0xb52a20, 0xc000008bd0})
        /home/runner/go/pkg/mod/github.com/yoheimuta/go-protoparser/[email protected]/parser/proto.go:26 +0x69
github.com/yoheimuta/protolint/linter/visitor.RunVisitorAutoDisable({0xb53b88?, 0xc000224a80?}, 0xc000222750, {0xa7185b, 0x10}, 0x5?)
        /home/runner/work/protolint/protolint/linter/visitor/hasExtendedVisitor.go:55 +0x119
github.com/yoheimuta/protolint/linter/visitor.RunVisitor(...)
        /home/runner/work/protolint/protolint/linter/visitor/hasExtendedVisitor.go:28
github.com/yoheimuta/protolint/internal/addon/rules.QuoteConsistentRule.Apply({{{0xa68c01?, 0x15?}}, 0xc00001e82d?, 0xa?}, 0xc00019e600?)
        /home/runner/work/protolint/protolint/internal/addon/rules/quoteConsistentRule.go:60 +0xa7
github.com/yoheimuta/protolint/internal/linter.(*Linter).Run(0xc00000b6e0?, 0xc000177758, {0xc000228180?, 0x7, 0x0?})
        /home/runner/work/protolint/protolint/internal/linter/linter.go:33 +0x10e
github.com/yoheimuta/protolint/internal/cmd/subcmds/lint.(*CmdLint).runOneFile(0xc0001c4380, {{0xc00001e810, 0x27}, {0xc00001e82d, 0xa}})
        /home/runner/work/protolint/protolint/internal/cmd/subcmds/lint/cmdLint.go:131 +0x151
github.com/yoheimuta/protolint/internal/cmd/subcmds/lint.(*CmdLint).run(0xc0001c4380)
        /home/runner/work/protolint/protolint/internal/cmd/subcmds/lint/cmdLint.go:100 +0xd6
github.com/yoheimuta/protolint/internal/cmd/subcmds/lint.(*CmdLint).Run(0xc0001c4380)
        /home/runner/work/protolint/protolint/internal/cmd/subcmds/lint/cmdLint.go:77 +0x53
github.com/yoheimuta/protolint/internal/cmd.doLint({0xc0000360e0?, 0x100000000?, 0x0?}, {0xb46980, 0xc00005e028}, {0xb46980, 0xc00005e030})
        /home/runner/work/protolint/protolint/internal/cmd/cmd.go:110 +0x2d1
github.com/yoheimuta/protolint/internal/cmd.doSub({0xc0000360d0?, 0x0?, 0x1d8e0249df8?}, {0xb46980?, 0xc00005e028?}, {0xb46980?, 0xc00005e030?})
        /home/runner/work/protolint/protolint/internal/cmd/cmd.go:64 +0xac
github.com/yoheimuta/protolint/internal/cmd.Do({0xc0000360d0?, 0x60?, 0x0?}, {0xb46980?, 0xc00005e028?}, {0xb46980?, 0xc00005e030?})
        /home/runner/work/protolint/protolint/internal/cmd/cmd.go:49 +0x78
main.main()
        /home/runner/work/protolint/protolint/cmd/protolint/main.go:11 +0x5e

protoc correctly handles this by reporting a syntax error.

Expected behaviour:

protolint should not panic, and rather output a syntax error like protoc

Custom Option Type map fails to parse

Hi!

Awesome application, thanks for building this. I hit a minor issue today:

We have enums defined which have custom options pointing to another message for documentation purposes:

e.g. something like this


  REQUEST = 1 [(event_additional_data) = {field_number: 101}];

This fails during parsing with:

failed to parse, err found "\"{\"(Token=14, Pos=some.proto:39:54)" but expected [enumValueOption] at /home/hfjn/go/pkg/mod/github.com/yoheimuta/go-protoparser/[email protected]/parser/enum.go:280:found "{" but expected [;]

After checking the code I assume the enum type currently only supports String types?

Thanks!

Support option aggregate syntax

Thanks for producing protolint!

Please could the aggregate syntax be supported for (custom) options?

message FooOptions {
  optional int32 opt1 = 1;
  optional string opt2 = 2;
}

extend google.protobuf.FieldOptions {
  optional FooOptions foo_options = 1234;
}

// usage:
message Bar {
  optional int32 a = 1 [(foo_options).opt1 = 123, (foo_options).opt2 = "baz"];
  // alternative aggregate syntax (uses TextFormat):
  optional int32 b = 2 [(foo_options) = { opt1: 123 opt2: "baz" }];
  // for completeness, split with (optional) commas:
  optional int32 c = 3 [(foo_options) = {
    opt1: 123,
    opt2: "baz",
  }];
}

Unfortunately this is not part of the language spec but is implemented in protoc...

Installation instructions in readme is invalid

Readme says to do:
go get github.com/yoheimuta/go-protoparser/v4
But this returns an error:

package github.com/yoheimuta/go-protoparser/v4: cannot find package "github.com/yoheimuta/go-protoparser/v4" in any of...

Multiline string literals error

String literals that are split between multiple lines in proto annotations cause an error in go-protoparser. Running the parser on _testdata/simple.proto with the following modification

enum EnumAllowingAlias {
    option allow_alias = true;
    UNKNOWN = 0;
    STARTED = 1;
    RUNNING = 2 [(custom_option) = "this is a "
                                   "string on two lines"
                ];
}

results in this error

failed to parse, err found "\""(Token=11, Pos=simple.proto:12:36) but expected []] at //go/src/github.com/yoheimuta/go-protoparser/parser/enum.go:293:found "\"" but expected [;]

Despite this syntax being accepted by the compiler. It's not clear from the language spec what the proper syntax for multiline literals is, however.

Comments are assigned to the wrong fields

I'm trying to parse comments from messages formatted according to the google protobuf style guide.

In the following example:

message SearchRequest {
  string query = 1;
  int32 page_number = 2;  // Which page number do we want?
  int32 result_per_page = 3;  // Number of results to return per page.
}

I would expect the field page_number to contain the comment "// Which page number do we want?", but instead I'm getting an empty string.

In the same way, I'd expect result_per_page to contain "// Number of results to return per page", but instead I get the comment of the field above it.

I had a quick look at your unit tests (ie: parser/message_test.go), and it seems you're following a different style.

message outer {
  // option
  option (my_option).a = true;
  // message
  message inner {   // Level 2
    int64 ival = 1;
  }
  // field
  repeated inner inner_message = 2;
  // enum
  enum EnumAllowingAlias {
    option allow_alias = true;
  }
  EnumAllowingAlias enum_field =3;
  // map
  map<int32, string> my_map = 4;
  // oneof
  oneof foo {
    string name = 5;
    SubMessage sub_message = 6;
  }
  // reserved
  reserved "bar";
}

I understand people have different styles, but if you claim it "Conforms to the exactly official spec" I would expect comments to be formatted as the official spec too.

Can we file this as a bug?

I'd be glad to help.

Support `option (.foo.bar) = {...}`

Hi, thanks for providing an awesome linter and library.
I'm introducing the linter to my project. And it raises an exception that found "." but expected [TIDENT]. Use -v for more details.

It fails at parsing like the following statement.

option (.foo.bar) = {…}

It seems invalid, according to https://developers.google.com/protocol-buffers/docs/reference/proto3-spec#option, but protobuf allows this syntax to change the name resolution algorithm.
cf. https://github.com/protocolbuffers/protobuf/blob/b696347f80d324c624e65a0b7e3ea5dac7ec5a41/src/google/protobuf/compiler/parser_unittest.cc#L2126-L2141

Could you support this format?

Godoc Links don't work

Hi,

I was going through the readme, and found that the godoc links don't work, thought of letting you know

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.