Giter VIP home page Giter VIP logo

c9s / r3 Goto Github PK

View Code? Open in Web Editor NEW
815.0 31.0 83.0 1.5 MB

libr3 is a high-performance path dispatching library. It compiles your route paths into a prefix tree (trie). By using the constructed prefix trie in the start-up time, you may dispatch your routes with efficiency

Home Page: http://c9s.github.com/r3/bench.html

License: MIT License

Shell 0.31% HTML 1.89% Makefile 0.54% C 93.57% C++ 1.24% Ruby 0.29% CMake 1.32% M4 0.84%

r3's Introduction

R3

Build Status

Coverage Status

R3 is an URL router library with high performance, thus, it's implemented in C. It compiles your R3Route paths into a prefix trie.

By using the prefix tree constructed in the start-up time, you can dispatch the path to the controller with high efficiency.

Requirement

Build Requirement

  • autoconf
  • automake
  • check
  • pkg-config

Runtime Requirement

  • pcre2
  • (optional) graphviz version 2.38.0 (20140413.2041)
  • (optional) libjson-c-dev

Pattern Syntax

/blog/post/{id}      use [^/]+ regular expression by default.
/blog/post/{id:\d+}  use `\d+` regular expression instead of default.

API

#include <r3/r3.h>

// create a router tree with 10 children capacity (this capacity can grow dynamically)
R3Node *n = r3_tree_create(10);

int route_data = 3;

// insert the R3Route path into the router tree
r3_tree_insert_path(n, "/bar", &route_data); // ignore the length of path

r3_tree_insert_pathl(n, "/zoo", strlen("/zoo"), &route_data );
r3_tree_insert_pathl(n, "/foo/bar", strlen("/foo/bar"), &route_data );

r3_tree_insert_pathl(n ,"/post/{id}", strlen("/post/{id}") , &route_data );

r3_tree_insert_pathl(n, "/user/{id:\\d+}", strlen("/user/{id:\\d+}"), &route_data );


// if you want to catch error, you may call the extended path function for insertion
int data = 10;
char *errstr = NULL;
R3Node *ret = r3_tree_insert_pathl_ex(n, "/foo/{name:\\d{5}", strlen("/foo/{name:\\d{5}"), NULL, &data, &errstr);
if (ret == NULL) {
    // failed insertion
    printf("error: %s\n", errstr);
    free(errstr); // errstr is created from `asprintf`, so you have to free it manually.
}


// let's compile the tree!
char *errstr = NULL;
int err = r3_tree_compile(n, &errstr);
if (err != 0) {
    // fail
    printf("error: %s\n", errstr);
    free(errstr); // errstr is created from `asprintf`, so you have to free it manually.
}


// dump the compiled tree
r3_tree_dump(n, 0);

// match a route
R3Node *matched_node = r3_tree_matchl(n, "/foo/bar", strlen("/foo/bar"), NULL);
if (matched_node) {
    int ret = *( (int*) matched_node->data );
}

// release the tree
r3_tree_free(n);

Capture Dynamic Variables

If you want to capture the variables from regular expression, you will need to create a match_entry object and pass the object to r3_tree_matchl function, the catched variables will be pushed into the match entry structure:

match_entry * entry = match_entry_create("/foo/bar");

// free the match entry
match_entry_free(entry);

And you can even specify the request method restriction:

entry->request_method = METHOD_GET;
entry->request_method = METHOD_POST;
entry->request_method = METHOD_GET | METHOD_POST;

When using match_entry, you may match the R3Route with r3_tree_match_entry function:

R3Node * matched_node = r3_tree_match_entry(n, entry);

Release Memory

To release the memory, you may call r3_tree_free(R3Node *tree) to release the whole tree structure, node*, edge*, route* objects that were inserted into the tree will be freed.

Routing with conditions

// create a router tree with 10 children capacity (this capacity can grow dynamically)
n = r3_tree_create(10);

int route_data = 3;

// insert the R3Route path into the router tree
r3_tree_insert_routel(n, METHOD_GET | METHOD_POST, "/blog/post", sizeof("/blog/post") - 1, &route_data );

char *errstr = NULL;
int err = r3_tree_compile(n, &errstr);
if (err != 0) {
    // fail
    printf("error: %s\n", errstr);
    free(errstr); // errstr is created from `asprintf`, so you have to free it manually.
}


// in your http server handler

// create the match entry for capturing dynamic variables.
match_entry * entry = match_entry_create("/blog/post");
entry->request_method = METHOD_GET;


R3Route *matched_R3Route = r3_tree_match_route(n, entry);
matched_route->data; // get the data from matched route

// free the objects at the end
match_entry_free(entry);
r3_tree_free(n);

Slug

A slug is a placeholder, which captures the string from the URL as a variable. Slugs will be compiled into regular expression patterns.

Slugs without patterns (like /user/{userId}) will be compiled into the [^/]+ pattern.

To specify the pattern of a slug, you may write a colon to separate the slug name and the pattern:

"/user/{userId:\\d+}"

The above R3Route will use \d+ as its pattern.

Optimization

Simple regular expressions are optimized through a regexp pattern to opcode translator, which translates simple patterns into small & fast scanners.

By using this method, r3 reduces the matching overhead of pcre2 library.

Optimized patterns are: [a-z]+, [0-9]+, \d+, \w+, [^/]+, [^-]+ or .*.

Slugs without specified regular expression will be compiled into the [^/]+ pattern. therefore, it's optimized too.

Complex regular expressions will still use libpcre2 to match URL (partially).

Performance

The routing benchmark from stevegraham/rails' PR stevegraham/rails#1:

             omg    10462.0 (±6.7%) i/s -      52417 in   5.030416s

And here is the result of the router journey:

             omg     9932.9 (±4.8%) i/s -      49873 in   5.033452s

r3 uses the same R3Route path data for benchmarking, and here is the benchmark:

            3 runs, 5000000 iterations each run, finished in 1.308894 seconds
            11460057.83 i/sec

The Route Paths Of Benchmark

The R3Route path generator is from stevegraham/rails#1:

#!/usr/bin/env ruby
arr    = ["foo", "bar", "baz", "qux", "quux", "corge", "grault", "garply"]
paths  = arr.permutation(3).map { |a| "/#{a.join '/'}" }
paths.each do |path|
    puts "r3_tree_insert_path(n, \"#{path}\", NULL);"
end

Function prefix mapping

Function Prefix Description
r3_tree_* Tree related operations, which require a node to operate a whole tree
r3_node_* Single node related operations, which do not go through its own children or parent.
r3_edge_* Edge related operations
r3_route_* Route related operations, which are needed only when the tree is defined by routes
match_entry_* Match entry related operations, a match_entry is just like the request parameters

Rendering Routes With Graphviz

The r3_tree_render_file API let you render the whole R3Route trie into a image.

To use graphviz, you need to enable graphviz while you run configure:

./configure --enable-graphviz

Here is the sample code of generating graph output:

R3Node * n = r3_tree_create(1);

r3_tree_insert_path(n, "/foo/bar/baz",  NULL);
r3_tree_insert_path(n, "/foo/bar/qux",  NULL);
r3_tree_insert_path(n, "/foo/bar/quux",  NULL);
r3_tree_insert_path(n, "/foo/bar/corge",  NULL);
r3_tree_insert_path(n, "/foo/bar/grault",  NULL);
r3_tree_insert_path(n, "/garply/grault/foo",  NULL);
r3_tree_insert_path(n, "/garply/grault/bar",  NULL);
r3_tree_insert_path(n, "/user/{id}",  NULL);
r3_tree_insert_path(n, "/post/{title:\\w+}",  NULL);

char *errstr = NULL;
int err;
err = r3_tree_compile(n, &errstr);
if (err != 0) {
    // fail
    printf("error: %s\n", errstr);
    free(errstr); // errstr is created from `asprintf`, so you have to free it manually.
}

r3_tree_render_file(n, "png", "check_gvc.png");
r3_tree_free(n);

Imgur

Or you can even export it with dot format:

digraph g {
	graph [bb="0,0,205.1,471"];
	node [label="\N"];
	"{root}"	 [height=0.5,
		pos="35.097,453",
		width=0.97491];
	"#1"	 [height=0.5,
		pos="35.097,366",
		width=0.75];
        ....

Graphviz Related Functions

int r3_tree_render_file(const R3Node * tree, const char * format, const char * filename);

int r3_tree_render(const R3Node * tree, const char *layout, const char * format, FILE *fp);

int r3_tree_render_dot(const R3Node * tree, const char *layout, FILE *fp);

int r3_tree_render_file(const R3Node * tree, const char * format, const char * filename);

JSON Output

You can render the whole tree structure into json format output.

Please run configure with the --enable-json option.

Here is the sample code to generate JSON string:

json_object * obj = r3_node_to_json_object(n);

const char *json = r3_node_to_json_pretty_string(n);
printf("Pretty JSON: %s\n",json);

const char *json = r3_node_to_json_string(n);
printf("JSON: %s\n",json);

Use case in PHP

not implemented yet

// Here is the paths data structure
$paths = [
    '/blog/post/{id}' => [ 'controller' => 'PostController' , 'action' => 'item'   , 'method'   => 'GET' ] ,
    '/blog/post'      => [ 'controller' => 'PostController' , 'action' => 'list'   , 'method'   => 'GET' ] ,
    '/blog/post'      => [ 'controller' => 'PostController' , 'action' => 'create' , 'method' => 'POST' ]  ,
    '/blog'           => [ 'controller' => 'BlogController' , 'action' => 'list'   , 'method'   => 'GET' ] ,
];
$rs = r3_compile($paths, 'persisten-table-id');
$ret = r3_dispatch($rs, '/blog/post/3' );
list($complete, $route, $variables) = $ret;

// matched conditions aren't done yet
list($error, $message) = r3_validate($route); // validate R3Route conditions
if ( $error ) {
    echo $message; // "Method not allowed", "...";
}

Install

sudo apt-get install check libpcre2 libpcre2-dev libjemalloc-dev libjemalloc1 build-essential libtool automake autoconf pkg-config
sudo apt-get install graphviz-dev graphviz  # if you want graphviz
./autogen.sh
./configure && make
sudo make install

And we support debian-based distro now!

sudo apt-get install build-essential autoconf automake libpcre2-dev pkg-config debhelper libtool check
mv dist-debian debian
dpkg-buildpackage -b -us -uc
sudo gdebi ../libr3*.deb

Run Unit Tests

./configure --enable-check
make check

Enable Graphviz

./configure --enable-graphviz

With jemalloc

./configure --with-malloc=jemalloc

ubuntu PPA

The PPA for libr3 can be found in https://launchpad.net/~r3-team/+archive/libr3-daily.

Binding For Other Languages

Node.js

Ruby

License

This software is released under MIT License.

r3's People

Contributors

bfontaine avatar bjosv avatar brendanashworth avatar c9s avatar caasi avatar cindylinz avatar czchen avatar dependabot[bot] avatar dthadi3 avatar fishgege avatar gasol avatar guachesuede avatar kanru avatar karantin2020 avatar mattn avatar membphis avatar msteinert avatar omerzimp avatar pangyre avatar peterdavehello avatar phynalle avatar rickysu avatar ronmi avatar sjones608 avatar thedrow avatar tonytonyjan avatar whitglint avatar yohanboniface 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  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

r3's Issues

Error with configure.ac

When I run ./configure I get the following error:

./configure: line 17189: syntax error near unexpected token have_check="no"' ./configure: line 17189:$as_echo "$as_me: WARNING: Check not found; cannot run unit tests!" >&2;} have_check="no"'

r3 as standalone dependency

Hi there,

I'm intending to use r3 as a dependency in my project, but I don't want to force it upon end users to install it globally for #include <r3.h> (or any other dependencies, such as PCRE). Do you have any advice for making it a standalone dependency?

Thanks.

Improve route slug parser

  • Save slug name in the structure
  • Add slugs member to route struct so we can iterate the slugs in the matched route.

Can't match root uri "/"

It's ok to insert a new route for "/".

but when I try to match uri "/", r3 told me that nothing matched.

Can't make check

On OSX 10.9, run make check follow REAME's steps will fail. Error message:

check_slug.c:9:10: fatal error: 'check.h' file not found

PHP extension for R3

I plan to work with PHP extension in a couple of weeks.

I think we should separate r3 and r3-php into two repository in the future, But for the sake of API stability We are still committing the changes into single repository.

It's hard to contribute since generated files are not in .gitignore

create mode 100644 Makefile
create mode 100644 aclocal.m4
Git output for git add --all:

create mode 100644 autom4te.cache/output.0
 create mode 100644 autom4te.cache/output.1
 create mode 100644 autom4te.cache/requests
 create mode 100644 autom4te.cache/traces.0
 create mode 100644 autom4te.cache/traces.1
 create mode 100644 config.log
 create mode 100644 r3.pc
 create mode 100644 src/.deps/edge.Plo
 create mode 100644 src/.deps/gvc.Plo
 create mode 100644 src/.deps/list.Plo
 create mode 100644 src/.deps/node.Plo
 create mode 100644 src/.deps/str.Plo
 create mode 100644 src/.deps/token.Plo
 create mode 100644 src/.libs/libr3.lai
 create mode 120000 src/.libs/libr3.so
 create mode 120000 src/.libs/libr3.so.0
 create mode 100755 src/.libs/libr3.so.0.0.0
 create mode 100644 src/Makefile
 create mode 100644 stamp-h1
 create mode 100644 tests/.deps/bench.Po
 create mode 100644 tests/.deps/check_gvc.Po
 create mode 100644 tests/.deps/check_slug.Po
 create mode 100644 tests/.deps/check_tree.Po
 create mode 100644 tests/Makefile

Export slug related functions

  1. int r3_parse_slugs( char * path, char ** slugs )
    this function parses the slug names from the path.
    this function returns the length of the slugs. each slug is a null-terminated strings.
int list_len = r3_parse_slugs("/user/{id}", token_list);
  1. r3_apply_slugs( char * path, ...)

    this function applies the variables into the slugs by the ordering. so an URL generator could be implemented.

        r3_apply_slugs("/user/{id}", 1, "123");
        r3_apply_slugs("/user/{id}/{title}", 2, "123", "title");

error code support

let r3_tree_compile_patterns return errno if error occurs, for example:

result_t r3_tree_compile_patterns(node * n)

Option to insert route with Content-Type as part of arguments

Hi ,
Very much impressed by the implementation and performance of this library. I was looking for a API like r3_tree_insert_routel_ex that can take Content-Type as part of arguments while insert the route.
Let me know if we have any such API for my requirements :

For Ex:
tree.insert_routel(METHOD_GET | METHOD_POST, "/blog/post", "application/json",
sizeof("/blog/post") - 1, &route_data);

... find the route :
r3::MatchEntry entry("/blog/post");
entry.set_request_method(METHOD_GET);
entry.set_content_type("application/json");

r3::Route matched_route = tree.match_route(entry);

Regards,
Badari

API stability

When preparing 1.3.3 for debian, I found that two APIs r3_route_create and r3_tree_insert_pathl are removed. Also, several APIs like zcalloc, zfree are exported in 1.3.1. Since library API change requires transitions, is there any plan to provide stable API and hide private functions from exporting in next version?

make check fails

The following is content of tests/test-suite.log when make check fails.

==================================
   r3 1.0: tests/test-suite.log
==================================

# TOTAL: 1
# PASS:  0
# SKIP:  0
# XFAIL: 0
# FAIL:  1
# XPASS: 0
# ERROR: 0

.. contents:: :depth: 2

FAIL: check_tree
================

Running suite(s): blah
PCRE compilation failed at offset 2: missing ), pattern: ^(
77%: Checks: 9, Failures: 2, Errors: 0
check_tree.c:63:F:testcase:test_compile:0: Failure 'NULL == r3_node_find_edge_str(n, "/", strlen("/") )' occured
check_tree.c:611:F:testcase:benchmark_str:0: Failure 'm == NULL' occured

multiple methods for a route

Hi,

Thanks for the awesome library. However, I was implementing a Go http mux based on r3 and found some strange behavior:

I found that inserting routes with different method but the same path seems to work only once. Is this the intended behavior?

If it is, then router implementations based on r3 will have to roll out their own method dispatchers on the leaf nodes and insert catch-it-all (METHOD_GET|METHOD_POST|METHOD ....) routes on every paths registered because there are no API to remove/replace existing routes. This makes routes kind of meaningless compared to paths, IMO.

Example behavior here:
https://gist.github.com/freehaha/514038ec448667fc22e8

Installing problem in osx

It says libtoolize is not installed when executing autogen.sh. But I already have libtool installed. Any suggestions?

matched_entry->data 跑掉

現在是

  • match 第一個 add 的 path 時 ->data 會亂跑
  • match 有一個 capture 的時候 ->data 會變成 NULL

License mismatch

The files in src has Distributed under terms of the MIT license.. However, the COPYING is for GPL-3.

could be memory leak

r3_tree_free should release the whole tree, including node*, edge* and route*, but the code below allocates my memory over 100 MB. As shown in the code, I create a tree, add 1000 paths, and then free the tree, repeat 1000 times. I wonder if I did something wrong?

for(int i = 0; i < 1000; ++i){
  node *tree = r3_tree_create(1000);
  for(int j = 0; j < 1000; ++j){
    char buf[10];
    sprintf(buf, "/%d", j);
    r3_tree_insert_path(tree, buf, NULL);
  }
  r3_tree_free(tree); // it should release the whole tree structure: node*, edge*, route*
}

Crash on r3_tree_free

I'm using the libr3-dev 1.3.4-1 that comes along ubuntu.

*** Error in `python3': free(): invalid next size (normal): 0x000000000141a9e0 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f7aa51cf7e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x7fe0a)[0x7f7aa51d7e0a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7f7aa51db98c]
/usr/lib/x86_64-linux-gnu/libr3.so.0(r3_tree_free+0x76)[0x7f7aa3e76006]
/usr/lib/x86_64-linux-gnu/libr3.so.0(r3_edge_free+0x1a)[0x7f7aa3e7756a]
/usr/lib/x86_64-linux-gnu/libr3.so.0(r3_tree_free+0x26)[0x7f7aa3e75fb6]
/usr/lib/x86_64-linux-gnu/libr3.so.0(r3_edge_free+0x1a)[0x7f7aa3e7756a]
/usr/lib/x86_64-linux-gnu/libr3.so.0(r3_tree_free+0x26)[0x7f7aa3e75fb6]
/usr/lib/x86_64-linux-gnu/libr3.so.0(r3_edge_free+0x1a)[0x7f7aa3e7756a]
/usr/lib/x86_64-linux-gnu/libr3.so.0(r3_tree_free+0x26)[0x7f7aa3e75fb6]
/usr/lib/x86_64-linux-gnu/libr3.so.0(r3_edge_free+0x1a)[0x7f7aa3e7756a]
/usr/lib/x86_64-linux-gnu/libr3.so.0(r3_tree_free+0x26)[0x7f7aa3e75fb6]
/usr/lib/x86_64-linux-gnu/libr3.so.0(r3_edge_free+0x1a)[0x7f7aa3e7756a]
/usr/lib/x86_64-linux-gnu/libr3.so.0(r3_tree_free+0x26)[0x7f7aa3e75fb6]
/usr/lib/x86_64-linux-gnu/libr3.so.0(r3_edge_free+0x1a)[0x7f7aa3e7756a]
/usr/lib/x86_64-linux-gnu/libr3.so.0(r3_tree_free+0x26)[0x7f7aa3e75fb6]
/usr/lib/x86_64-linux-gnu/libr3.so.0(r3_edge_free+0x1a)[0x7f7aa3e7756a]
/usr/lib/x86_64-linux-gnu/libr3.so.0(r3_tree_free+0x26)[0x7f7aa3e75fb6]
/home/iceboy/ws/router-benchmark/_r3.cpython-35m-x86_64-linux-gnu.so(+0x2241)[0x7f7aa40a8241]
python3[0x587b71]
python3[0x577813]
python3(PyDict_SetItem+0x268)[0x582268]
python3(PyImport_Cleanup+0x1da)[0x51af2a]
python3(Py_Finalize+0x5e)[0x5fea5e]
python3(Py_Main+0x644)[0x63e9c4]
python3(main+0xe1)[0x4cfe41]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f7aa5178830]
python3(_start+0x29)[0x5d5f29]

Code causing crash:
https://github.com/iceb0y/router-benchmark/commit/2d7ede0e60d50a6ac7d59bade92ad0c57ac4018b

tree_compile error didn't happend sometimes

with

my $t = Router::R3->new(
    '/a/{a:(}' => 1
);

I got:

creating R3 routing tree fail: PCRE compilation failed at offset 7: missing ), pattern: ^/a/(()

(good)

with

my $t = Router::R3->new(
    '/post4/{idx:\d{3}}/{idy}' => 6,
    '/post4/{idx:\d{3}}/{idy:(}' => 7,
);

I got

(nothing)

handle return codes, prevent segfaults

looks like you're not checking return codes for a lot of function calls. this will cause issues in a real-world environment.

at first glance, the following in "list.c" need to report/handle failures:

  • line#15: zmalloc failing will cause segfaults here
  • line#19: failing to create a mutex will cause nasal demons in every other list_ function
  • line#30: no lock validation
  • lines 39 & 40: no unlock/destroy validation (may segfault)
  • line#49: no lock validation; may cause this operation to be non-thread-safe
  • line#51: zmalloc failing will cause segfaults

...

if you like, i can fork and fix a few of these :)

c++ support for r3 can't compile on gcc-4.1.2

when compiling the example simple_cpp.cpp, the typedef from pcre header causes a compile error:

In file included from ../include/r3.hpp:11,
                 from simple_cpp.cpp:4:
../include/r3.h:50: error: declaration of ‘pcre_extra* _node::pcre_extra’
/usr/local/include/pcre.h:385: error: changes meaning of ‘pcre_extra’ from ‘typedef struct pcre_extra pcre_extra’
In file included from simple_cpp.cpp:4:

the code snippet from pcre.h:385:

typedef struct pcre_extra {
  unsigned long int flags;        /* Bits for which fields are set */
  void *study_data;               /* Opaque data from pcre_study() */
  unsigned long int match_limit;  /* Maximum number of calls to match() */
  void *callout_data;             /* Data passed back in callouts */
  const unsigned char *tables;    /* Pointer to character tables */
  unsigned long int match_limit_recursion; /* Max recursive calls to match() */
  unsigned char **mark;           /* For passing back a mark pointer */
  void *executable_jit;           /* Contains a pointer to a compiled jit code */
} pcre_extra;

Namespace `node`

It's a little surprising that a common name like node is not namespaced. It'd be nice to call it r3_node or something.

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.