Giter Club home page Giter Club logo

ziglua's Introduction

Ziglua

shield showing current tests status Discord

A Zig package that provides a complete and lightweight wrapper around the Lua C API. Ziglua currently supports the latest releases of Lua 5.1, 5.2, 5.3, 5.4, and Luau and targets Zig master. Tagged versions of Ziglua are made for stable Zig releases.

Ziglua can be used in two ways, either

  • embedded to statically embed the Lua VM in a Zig program,
  • or as a shared module to create Lua libraries that can be loaded at runtime in other Lua-based software.

In both cases, Ziglua will compile Lua from source and link against your Zig code making it easy to create software that integrates with Lua without requiring any system Lua libraries.

Documentation

Docs are a work in progress and are automatically generated for each push to main. Most functions and public declarations are documented:

See docs.md for more general information on Ziglua and how it differs from the C API.

Example code is included in the examples directory.

  • Run an example with zig build run-example-<name>
  • Install an example with zig build install-example-<name>

Why use Ziglua?

In a nutshell, Ziglua is a simple wrapper around the C API you would get by using Zig's @cImport(). Ziglua aims to mirror the Lua C API as closely as possible, while improving ergonomics using Zig's features. For example:

  • Zig error unions to require failure state handling
  • Null-terminated slices instead of C strings
  • Type-checked enums for parameters and return values
  • Compiler-enforced checking of optional pointers
  • Better types in many cases (e.g. bool instead of int)
  • Comptime convenience functions to make binding creation easier

Nearly every function in the C API is exposed in Ziglua. Additional convenience functions like toAny and pushAny use comptime reflection to make the API easier to use.

Integrating Ziglua in your project

Find the archive url of the Ziglua version you want to integrate with your project. For example, the url for the commit 41a110981cf016465f72208c3f1732fd4c92a694 is https://github.com/natecraddock/ziglua/archive/41a110981cf016465f72208c3f1732fd4c92a694.tar.gz.

Then run zig fetch --save git+https://github.com/natecraddock/ziglua to add the most recent commit of ziglua to your build.zig.zon file.

Add a #<tag> to the url to use a specific tagged release like zig fetch --save git+https://github.com/natecraddock/ziglua#0.3.0

Then in your build.zig file you can use the dependency.

pub fn build(b: *std.Build) void {
    // ... snip ...

    const ziglua = b.dependency("ziglua", .{
        .target = target,
        .optimize = optimize,
    });

    // ... snip ...

    // add the ziglua module and lua artifact
    exe.root_module.addImport("ziglua", ziglua.module("ziglua"));

}

This will compile the Lua C sources and link with your project.

There are currently two additional options that can be passed to b.dependency():

  • .lang: Set the Lua language to build and embed. Defaults to .lua54. Possible values are .lua51, .lua52, .lua53, .lua54, and luau.
  • .shared: Defaults to false for embedding in a Zig program. Set to true to dynamically link the Lua source code (useful for creating shared modules).

For example, here is a b.dependency() call that and links against a shared Lua 5.2 library:

const ziglua = b.dependency("ziglua", .{
    .target = target,
    .optimize = optimize,
    .lang = .lua52,
    .shared = true,
});

The ziglua module will now be available in your code. Here is a simple example that pushes and inspects an integer on the Lua stack:

const std = @import("std");
const ziglua = @import("ziglua");

const Lua = ziglua.Lua;

pub fn main() anyerror!void {
    // Create an allocator
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();
    defer _ = gpa.deinit();

    // Initialize the Lua vm
    var lua = try Lua.init(&allocator);
    defer lua.deinit();

    // Add an integer to the Lua stack and retrieve it
    lua.pushInteger(42);
    std.debug.print("{}\n", .{try lua.toInteger(1)});
}

Contributing

Please make suggestions, report bugs, and create pull requests. Anyone is welcome to contribute!

I only use a subset of the Lua API through Ziglua, so if there are parts that aren't easy to use or understand, please fix it yourself or let me know!

Thank you to the Lua team for creating such a great language!

ziglua's People

Contributors

bfredl avatar davidgranstrom avatar deanoc avatar efjimm avatar hryx avatar natecraddock avatar ntbbloodbath avatar nurpax avatar oakmac avatar ryleelyman avatar t1nk3r1 avatar theoparis avatar visendev 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

ziglua's Issues

Fix allocator interface

The current method of making allocators is a bit messy and could be error prone.

  • no longer store an allocator in the Lua struct

exportFn fails during semantic analysis

I've tried to build a simple shared module using the exportFn in a comptime block. Using zig version v0.11.0-dev.1638+7199d7c77 it fails with the following error:

/home/galli/repos/git.sr.ht/~hubidubi/river-lua/zig/lib/ziglua/src/ziglua-5.4/lib.zig:2021:13: error: TODO implement exporting arbitrary Value objects
    @export(declaration, .{ .name = "luaopen_" ++ name, .linkage = .Strong });

Since there is a TODO, I'm assuming this will be fixed at some point. But git blame tells me this TODO has been there for over 2 years, so who knows when that will happen.

build.zig:

const std = @import("std");
const ziglua = @import("lib/ziglua/build.zig");

pub fn build(b: *std.Build) void {
    // Standard release options allow the person running `zig build` to select
    // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const lib = b.addSharedLibrary(.{
        .name = "test",
        .root_source_file = .{ .path = "src/main.zig" },
        .target = target,
        .optimize = optimize,
    });
    lib.addModule("ziglua", ziglua.compileAndCreateModule(b, lib, .{ .shared = true }));
    lib.install();
}

main.zig:

const std = @import("std");
const ziglua = @import("ziglua");

usingnamespace ziglua;

fn hello(lua: *ziglua.Lua) i32 {
    _ = lua;
    std.debug.print("hello world\n", .{});
    return 1;
}

fn module(lua: *ziglua.Lua) i32 {
    var fns = [_]ziglua.FnReg{ .{ .name = "hello", .func = ziglua.wrap(hello) }, .{ .name = "", .func = null } };
    lua.newLib(fns[0..]);
    return 1;
}

comptime {
    ziglua.exportFn("hello", module);
}

My current fix is to do the exporting and wrapping by hand, but maybe I'm missing something:

export fn luaopen_module(L: *ziglua.LuaState) callconv(.C) c_int {
    var fns = [_]ziglua.FnReg{ .{ .name = "hello", .func = ziglua.wrap(hello) }, .{ .name = "", .func = null } };

    var lua = ziglua.Lua{ .state = L };
    lua.newLib(fns[0..]);

    return 1;
}

Define apicheck function to use a Zig panic

From a quick glance at the Lua sources, it may be possible to override the use of assert.h for the API check with a custom function. If so, this would make api checks much more easy to debug with zig panics and stack traces.

luajit: opening standard library fails

I know luajit support is in it's infancy but wanted to report that opening the standard libraries with it fails. I can swap out for 5.2 through 5.4 and openLibs or openBase work fine, but luajit fails:

/home/tim/.cache/zig/p/1220bc58b81db27e4a04f61703e0929fa3ec780a24698483aee1b349dee2cf9dd96a/src/lib.zig:2536:16: error: member function expected 1 argument(s), found 2
        try lua.loadFile(file_name, .binary_text);
            ~~~^~~~~~~~~
/home/tim/.cache/zig/p/1220bc58b81db27e4a04f61703e0929fa3ec780a24698483aee1b349dee2cf9dd96a/src/lib.zig:2652:5: note: function declared here
    fn loadFile51(lua: *Lua, file_name: [:0]const u8) !void {
    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Simplify the structure of the tests

Currently each version of Lua/Luau that Ziglua supports has tests and bindings for that version. This results in a lot of duplicated code (which isn't too bad because Lua is a stable target). But as time has gone on, this leads to a lot of duplicated effort in the following cases

  • making large updates to Ziglua, like documentation format
  • breaking changes in Zig versions

This is annoying enough that I think it is worth it to find a way to simplify things down to just one of each ziglua.zig and tests.zig files.

I attempted this in the past with usingnamespace "mixins", but it was really messy. I want to attempt this again, but this time using conditional compilation based on the target version of Lua.

Fix unnecessary uses of sentinel slices

For values returned from Lua, using sentinel slices (specifically [:0]const u8) makes sense. It accurately reflects the data returned, and can be easily used in Zig. For values passed into Lua, this often does not make sense. Most "strings" in Zig aren't null terminated, and it can require an extra allocation to pass a string to Lua this way.

It appears I got a bit excited and used [:0]const u8 far too often for function parameters. These should be audited and changed to []const u8 wherever possible.

Proposed fixes to memory management issues with parsing

Here's an issue I've realized we have with the current parsing setup

Currently, when parsing a struct with lots of different arrays and pointers from a lua table, freeing that memory could get quite complex. The toAny and autoCall functions should be modified to return a ziglua.Parsed struct which contains an arena allocator which can be used to free the memory easily.

We can also add a toAnyLeaky and autoCallLeaky that leaves memory deallocation to be done manually as happens in the current implementation (as a toAny call may not necessarily require a memory allocation).

new to zig and don't know how to use.

Hi, I'm new to zig and want to find a way to make Lua C module in zig, I found this great repo but I don't know how to use it.

I'm using zig 0.11 because it's the latest release on its GitHub page and Homebrew package manager. But it seems do not support zig fetch.

After some dig I realize I should write up the build.zig.zon and calculate the hash myself, is that right? but I cannot find any easy way to make up them.

So is it the right time to use zig? or I just wait 0.12 to release? or maybe I should build zig 0.12 myself instead of using official release?

zig is looking like a great language, but it's too hard to use :-(

Ziglua 1.0.0

This lists the changes I want to make before releasing a 1.0.0. At a high level, I want Ziglua to be in a state that is more easily maintained, and generally be feature complete enough to reflect what future versions would include. This means supporting Lua 5.1-5.4, LuaJIT, and Luau well, good documentation, and well organized code.

The list of tasks here are roughly in the order in which I will make changes

Tasks

Still under consideration

Lua 5.4 - 'cimport' has no member named 'lua_objlen'

Hello there.

lib/ziglua/src/lib.zig:1345:17: error: root struct of file 'cimport' has no member named 'lua_objlen'
        return c.lua_objlen(lua.state, index);

Not sure if it's a bug or me being fresh to Lua, but it looks like there was a change in Lua5.2+ and there is no lua_objlen function anymore. Insted there is lua_len, which pushes the result onto the stack.

Lua5.1 Docs

size_t lua_objlen (lua_State *L, int index);
Returns the "length" of the value at the given acceptable index: for strings, this is the string length; for tables, this is the result of the length operator ('#'); for userdata, this is the size of the block of memory allocated for the userdata; for other values, it is 0.

Lua(5.2, 5.3, 5.4) Docs

void lua_len (lua_State *L, int index);
Returns the length of the value at the given index. It is equivalent to the '#' operator in Lua (see §3.4.7) and may trigger a metamethod for the "length" event (see §2.4). The result is pushed on the stack.

Remove `Ex` functions

Currently the Ziglua API is split between the Ex (Extended) versions of functions that return values, and functions that always return values. I want to remove the Ex functions. It isn't hard to write _ = at the call site, and this will be less to maintain.

Experimenting with Discord

I just added a Discord server link to the readme. Here is another link.

Maybe no one will join, but I figure it doesn't hurt to open up a second communication channel. Some people would be more likely to offer suggestions and feedback in a chat rather than opening an issue. Additionally, I hope people share cool things they build with Ziglua! This won't be a massive community, but I think it would be fun to have a small group discussing and sharing about cool Zig & Lua things.

I understand that there is some frustration in the open source world about using Discord for chats. This is an experiment and we will see if anyone joins.

Never mutated error on latest Zig version

Recent Zig versions (0.12.0-dev.1754+2a3226453 on my system) have introduced an error message for unmutated vars.

Error message

ZIGLUA_PATH\src\ziglua-5.4\lib.zig:354:13: error: local variable is never mutated
        var allocator_ptr = allocator.create(Allocator) catch return error.Memory;
            ^~~~~~~~~~~~~
ZIGLUA_PATH\src\ziglua-5.4\lib.zig:354:13: note: consider using 'const'
ZIGLUA_PATH\src\ziglua-5.4\lib.zig:2125:17: error: local variable is never mutated
            var message = std.mem.span(@as([*:0]const u8, @ptrCast(msg)));
                ^~~~~~~
ZIGLUA_PATH\src\ziglua-5.4\lib.zig:2125:17: note: consider using 'const'

ZigLua Integration Error: Missing build.zig

I added ziglua to my project according to the steps in the documentation. When I ran zig build run, I got the following:

warning: FileNotFound: C:\Users\xxx\AppData\Local\zig\p\1220d2378a3a95e1f48b1a73b6a65af96d853cc17993c7aad61b6403f7fc611eed71\build.zig
error: FileNotFound

I looked under this directory and found that this is the source code of lua5.1. It is a dependency of build.zig.zon in ziglua. It is a C language project and does not have a build.zig file. How can I correctly integrate ziglua into my project?

The zig version I am using is 0.11.0.

Look at TODO/NOTE comments

There are a lot of TODO/NOTE comments in ziglua that could either be fixed now, or be rewritten as GitHub issues for better tracking

Convenience functions taking advantage of zig's comptime

I think it would be nice if this library could provide some functionality that would automate some of the tedious work writing lua bindings.

For example,
theoretically, it should be possible to provide an api something like this

fn autoCall(lua: *Lua, comptime ReturnType: type, function_name: []const u8, args: anytype) !ReturnType;

const result = try lua.call(i32, "add", .{1, 2});

It should be possible to iterate over the args at comptime to make sure only types that can be represented in lua are passed. Then an inline for loop can be used to iterate over the args and call the appropriate lua_push function.

Another example would be get and set functions for lua globals

fn get(lua: *Lua, comptime T: type, name: []const u8) !T;
fn set(lua: *Lua, name: []const u8, value: anytype) !void;

It might also be possible to automatically write the bindings for a zig function to be called by lua at comptime?

I'm not entirely sure how this would work. Here's some sort of pseudo code of what I was thinking about. But some more thought about how to make this work within the current limits of zigs comptime would still be needed. You would probably need to then generate a second interface function that has the proper pop functions present so that it can properly call the zig function.

Not really sure but these are just some ideas

fn autoSetFunction(lua: *Lua, name: []const u8, comptime function: anytype) !void {
     const type_info = @typeInfo(@TypeOf(function));
     if(type_info != .Fn) @compileError("you must pass a function pointer");
     
     //create an interface function based off of the parameter types
     inline for(type_info.function.params) |parameter| {
         switch(parameter.type) {
             i32, u32 => //etc
     }
     
     //then pass the interface function to lua
     lua.pushFunction(...);
}

Remove newState, newStateLibc, and setAllocF

It isn't clear if these functions are of any use. After the changes from #40, removing these functions is not required, but it could still make things cleaner.

The one benefit from removing these functions is the Lua.allocator() function can never fail by making it impossible to create a Lua state without a Zig allocator from Ziglua.

Redo the debug interface

After merging #52, most of the combined library code is relatively clean. One area I would like to improve is the debug library.

I am not sure on the how or the what, but this is a reminder to take a look and see if we can build a more "ziggy" interface on top of what Lua provides. Bonus points if it also hides some of the differences between Lua versions

Make the Lua struct an opaque

Follow up from #40

This will remove the state field from the Lua struct and make the type an opaque. This makes the code simpler and shows the intent of the Lua struct more clearly.

How to install `liblua.a`?

liblua.a is available in the zig-cache/ directory after running ziglua.compileAndCreateModule in my project’s build.zig. Can ziglua possibly install liblua.a into zig-out/? This would help in doing Zig-Go interop — I’d like Go to link against this liblua.a. Perhaps there’s a way of doing this from my project’s build.zig

Errno compile error

First of all thanks for this library, it saved me a ton of work!

i have an error compiling on Ubuntu -

zig build-lib lua Debug native: error: error(compilation): clang failed with stderr: In file included from /home/connorrigby/workspace/connorrigby/coneboy/lib/ziglua/lib/lua-5.4/src/loslib.c:13:
In file included from /usr/include/errno.h:28:
In file included from /usr/include/bits/errno.h:26:
/usr/include/linux/errno.h:1:10: fatal error: 'asm/errno.h' file not found

I'm not really sure what's going on here, but this file seems to exist at /include/asm-generic/errno.h on my machine. Any pointers would be appreciated.

Proper way to reuse c headers from the exposed lua library

In addition to using the @import("ziglua") module from zig code, I need to compile additional C extensions which depend on the headers for the correct lua version as exposed by ziglua.

After some trial and error I found this to work:

    my_module.addImport("ziglua", ziglua.module("ziglua"));
    // addImport already links by itself. but we need headers as well..
    my_module.linkLibrary(ziglua.artifact("lua"));

This feels a bit hacky as it relies on the artifact name "lua" which might be seen as internal to ziglua (not part of the exported module), and also, as addImport by itself is enough to add the .o files for the lua library, linkLibrary would add the same objects a second time?
This should "safely" be handled gracefully by most linkers, but it would be best to avoid this if possible.

Any thoughts about what the proper way would be?

Expand documentation

Now that I am finally consistently using Ziglua I am beginning to be annoyed at the terse documentation. It forces me to jump between the Lua docs and the Ziglua docs frequently. Now, Ziglua will never fully document the entire reference manual, but we can do better.

Update the docs to contain all needed information from the Lua docs. This includes the possible errors, push/pop from stack information, etc.

Completing this is a prerequisite for future changes. Adding or fixing code across the 4 Lua versions is tedious and while this will be a lot of work, it will pay off and make future work much easier.

Improve error documentation

Instead of inferring error sets with !, each function that can fail should use a specific error set or error set union

access to Allocator inside wrapped Zig functions

When calling a wrapped Zig function from within Lua, lua.allocator seems to be always null:

pub const Player = struct {
    score: i32,

    pub fn init() Player {
        return Player{
            .score = 0,
        };
    }

    pub fn incScore(self: @This()) void {
        self.score += 1;
    }
};

fn newPlayer(lua: *Lua) i32 {
    std.debug.print("newPlayer zig\n", .{});
    if (lua.allocator) |a| {
        const p = a.create(Player) catch unreachable;
        const udata = lua.newUserdata(*Player);
        udata.* = p;
    } else {
        std.debug.print("lua allocator is null?\n", .{});
        return 0;
    }
    return 1;
}

pub fn register(lua: *Lua) void {
    lua.pushFunction(ziglua.wrap(newPlayer), "newPlayer");
    lua.setGlobal("newPlayer");
}

I think it's due to how wrapZigFn creates a new Lua context:

/// Wrap a ZigFn in a CFn for passing to the API
fn wrapZigFn(comptime f: ZigFn) CFn {
    return struct {
        fn inner(state: ?*LuaState) callconv(.C) c_int {
            // this is called by Lua, state should never be null
            var lua: Lua = .{ .state = state.? };
            return @call(.always_inline, f, .{&lua});
        }
    }.inner;
}

I suppose it should somehow setup the allocator field too.

IMO having a working allocator within the wrapped functions would be useful, so that I can go ahead and use whatever allocator is passed through the lua state.

Remove vendored libraries and rely on the Zig package manager

My understanding is that the Zig package manager will eventually support pulling down non-zig code and using it in the build process. Maybe this is already possible?

But this would remove all of the files in ./lib. Doing this would make it much easier to update subprojects like Luau or LuaJIT, which have more frequent updates. It also makes it more clear that Ziglua actually does rely on Lua, and that we haven't tampered with the source code.

We could rely on the upstream project for everything except Lua 5.1. #2 reported a CVE that some distros have patched in Lua 5.1, but that Lua hasn't patched. So I would make a lua repo that contains the full history of Lua, and then that patch.

Remove the need for ziglua.wrap()

I originally wrote this so the API functions would accept the CFn versions of functions assuming this would be more flexible.

In practice I think this is more confusing and more work. The public API functions should just accept the ZigFn versions and wrap internally.

Error creating shared library on Windows 11

I attempted to create a shared library, but encountered an error when running zig build. I’m currently using Windows 11 and zig version 0.12.0-dev.3212+40e64245f.

zig build                       
install
└─ install zlo
   └─ zig build-lib zlo Debug native 1 errors
error: lld-link: ___chkstk_ms was replaced
error: the following command failed with 1 compilation errors:
//build.zig
pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    const lib = b.addSharedLibrary(.{
        .name = "zlo",
        .root_source_file = .{ .path = "src/root.zig" },
        .target = target,
        .optimize = optimize,
    });
    const ziglua = b.dependency("ziglua", .{
        .target = target,
        .optimize = optimize,
        .lang = .lua53,
        .shared = true,
    });
    lib.root_module.addImport("ziglua", ziglua.module("ziglua"));
    b.installArtifact(lib);
}
//src/root.zig
const ziglua = @import("ziglua");
fn zlo(lua: *ziglua.Lua) i32 {
    lua.newLib(&.{
        .{ .name = "add", .func = ziglua.wrap(add) },
    });
    return 1;
}
fn add(lua: *ziglua.Lua) i32 {
    const a = lua.toInteger(1) catch 0;
    const b = lua.toInteger(2) catch 0;
    lua.pushInteger(a + b);
    return 1;
}
comptime {
    _ = ziglua.exportFn("zlo", zlo);
}

Expose lua version to ziglua module and rename

The build option version to set the Lua version to build with should be exposed publicly in ziglua. This would allow users of ziglua to utilize conditional compilation and target multiple versions of lua.

With this, I think version isn't as clear as it could be. Especially with the recent Luau support and LuaJIT coming soon.

I propose renaming the option to lang. This would make usage look like this

Running tests

$ zig build test -Dlang=lua51

Dependency

const ziglua = b.dependency("ziglua", .{
    .target = target,
    .optimize = optimize,
    .lang = .luau,
});

Variable

if (ziglua.lang == .lua52) {
    // do something
}

Luau support

Some conversations on #19 show interest in Luau support.

It should be possible, though I haven't attempted yet. It is written in C++ which may cause issues.

Zig package manager

It isn't yet clear how the new Zig package manager will end up. But I want to start brainstorming how to best package Ziglua.

It sounds like there will be a build.zig.zon file to store dependencies. Maybe we could store the links to the lua tar.gz files (so I don't have to store them in the repo here)

  • the biggest downside of this is that there are now patches (the Lua 5.1 CVE patch). But I could just store a single file, or maybe even write a basic Zig script to patch? Not sure yet.

Ideally, ziglua would be pure zig code + patches to keep the repo small.

Combine all lib.zig files

Now that all of the tests are combined into a single file (#33) for a while, I think it will be useful to do the same to the module code. This will make it much easier to make changes that apply to all supported language targets.

Fix slices

Some slices are []const u8 when they should indicate they are null terminated [:0] const u8

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.