Git Product home page Git Product logo

saltwater's Introduction

Saltwater

Build Status Join us on Discord

saltwater: the part of the sea causing lots of rust

A C compiler written in Rust, with a focus on good error messages.


This project is no longer maintained.


Running

swcc reads from standard in by default, so you can type in code directly. It's not interactive though, you have to hit Ctrl+D to indicate end of file (Ctrl+Z on Windows).

Use swcc --help for all options (or see below).

Running on Windows

You need to have cc on your PATH. You can either install mingw + gcc or MSVC. Other than that, it should work exactly the same as on Linux.

Homebrew

brew install saltwater

Unimplemented features

  • Defining functions taking variadic arguments. Note that calling variadic functions (like printf) is already supported.
  • Variable-length arrays (int a[n])
  • Multiple translation units (files)
  • Bitfields
  • Compiling on non-x86 platforms
  • Cross-compilation

Examples

$ cat tests/runner-tests/readme.c
// output: j is 6
int printf(const char *, ...);

typedef struct s *sp;

int i = 1;
int a[3] = {1, 2, 3};
float f = 2.5;

struct s {
  int outer;
} my_struct;

int g(int);

int main(void) {
  sp my_struct_pointer = &my_struct;
  const int c = my_struct_pointer->outer = 4;
  // should return 6
  int j = i + f*a[2] - c/g(1);
  printf("j is %d\n", j);
  return j;
}

int g(int i) {
  if (i < 0 || i >= 3) {
    return 0;
  }
  return a[i];
}
$ swcc tests/runner-tests/readme.c
$️ ./a.out
j is 6

Debug output

$ cat tests/runner-tests/cpp/if/defined.c
// code: 2

#define a
#define b

#if defined(a)
int i = 2;
#endif

#ifndef b
syntax error
#endif

# if defined b && defined(a)
    int main() { return i; }
#endif
$ swcc -E tests/runner-tests/cpp/if/defined.c
int i = 2 ; int main ( ) { return i ; }
$ echo 'int i = 1 + 2 ^ 3 % 5 / 2 & 1; int main(){}' | swcc --debug-ast
ast: int i = ((1) + (2)) ^ ((((3) % (5)) / (2)) & (1));
ast: int main(){
}
$ cat tests/runner-tests/hello_world.c
#include<stdio.h>
int main() {
    puts("Hello, world!");
}
$ swcc --debug-ir tests/runner-tests/hello_world.c
function u0:0() -> i32 system_v {
    gv0 = symbol colocated u1:3
    sig0 = (i64) -> i32 system_v
    fn0 = u0:26 sig0

block0:
    v0 = global_value.i64 gv0
    v1 = call fn0(v0)
    v2 = iconst.i32 0
    return v2
}
$ ./a.out
Hello, world!

All options

$ swcc --help
swcc 0.9.0
Joshua Nelson <[email protected]>
A C compiler written in Rust, with a focus on good error messages.
Homepage: https://github.com/jyn514/rcc/

usage: swcc [FLAGS] [OPTIONS] [<file>]

FLAGS:
        --debug-ast        If set, print the parsed abstract syntax tree (AST) in addition to compiling.
                            The AST does no type checking or validation, it only parses.
        --debug-hir        If set, print the high intermediate representation (HIR) in addition to compiling.
                            This does type checking and validation and also desugars various expressions.
        --debug-ir         If set, print the intermediate representation (IR) of the program in addition to compiling.
        --debug-lex        If set, print all tokens found by the lexer in addition to compiling.
        --jit              If set, will use JIT compilation for C code and instantly run compiled code (No files produced).
                            NOTE: this option only works if swcc was compiled with the `jit` feature.
    -h, --help             Prints help information
    -c, --no-link          If set, compile and assemble but do not link. Object file is machine-dependent.
    -E, --preprocess-only  If set, preprocess only, but do not do anything else.
                            Note that preprocessing discards whitespace and comments.
                            There is not currently a way to disable this behavior.
    -V, --version          Prints version information

OPTIONS:
        --color <when>       When to use color. May be "never", "auto", or "always". [default: auto]
    -o, --output <output>    The output file to use. [default: a.out]
        --max-errors <max>   The maximum number of errors to allow before giving up.
                             Use 0 to allow unlimited errors. [default: 10]
    -I, --include <dir>      Add a directory to the local include path (`#include "file.h"`).
                              Can be specified multiple times to add multiple directories.
    -D, --define <id[=val]>  Define an object-like macro.
                              Can be specified multiple times to add multiple macros.
                              `val` defaults to `1`.

ARGS:
    <file>    The file to read C source from. "-" means stdin (use ./- to read a file called '-').
              Only one file at a time is currently accepted. [default: -]

Testing

cargo test
# optionally, you can fuzz the compiler
# it may be more helpful to just `grep -R unimplemented src`, though

# libFuzzer/AFL
tests/fuzz.sh

# Honggfuzz:
# Running Honggfuzz locally requires some parameters to use it at its full potential,
# so it is probably a good idea to have a look here: https://github.com/rust-fuzz/honggfuzz-rs/blob/master/README.md
# and here: https://github.com/google/honggfuzz/blob/master/docs/USAGE.md
# we suggest the following:
HFUZZ_RUN_ARGS="--tmout_sigvtalrm --exit_upon_crash" tests/hfuzz.sh

FAQ

See FAQ.md

Implementation Defined Behavior

See IMPLEMENTATION_DEFINED.md

Contributing

See CONTRIBUTING.md. This also includes reporting bugs.

saltwater's People

Contributors

1011x avatar byter09 avatar elfeiin avatar hdamron17 avatar jyn514 avatar kixiron avatar playxe avatar pythongirl325 avatar repnop avatar ryankeleti avatar seekingmeaning avatar stupremee avatar thomcc 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

saltwater's Issues

Forward struct declarations are handled wrong

// set up malloc
typedef long unsigned int size_t;
void* malloc (size_t size);

// make a typedef to a _forward declaration_ of s, not the struct itself
typedef struct s *S;

// define s
struct s {
        int i;
};

S returns_struct() {
        // this still thinks s hasn't been defined
        S my_struct = malloc(sizeof(struct s));
        my_struct->i = 1;
        return my_struct;
}
<stdin>:8:14: error: no member named 'i' in 'struct s'

When we see the definition for s, we need to update the entry for S.

multiplication crashes on pointers

$ cargo run
int a[]; int main() { return a * 1; }
thread 'main' panicked at 'Type::sign can only be called on integral types', src/parse/expr.rs:1149:18

The fix is to do type checking in Expr::multiplicative_expr before calling binary_promote, that solved this for additive_expr

Lexer crashes on unsigned exponent

parse_num just keeps coming back to haunt me, I might split it up into integer and float parsing.

0E0u
thread 'main' panicked at 'internal error: entered unreachable code: parse_num should never return something besides Token::Int when called with allow_float: false', src/lex.rs:433:18

The proper thing to do is to not allow unsigned exponents.

Returns are broken again

int main() {
        int i;
        if (1) return 1;    
        else i = 2;
}
thread 'main' panicked at 'you cannot add an instruction to a block already filled', /home/joshua/.cargo/registry/src/github.com-1ecc6299db9ec823/cranelift-frontend-0.42.0/src/frontend.rs:413:13

Infinite loop on invalid top-level declaration

$ cargo run
(a(
<stdin>:1:3: warning: type specifier missing, defaults to int
<stdin>:1:3: warning: type specifier missing, defaults to int
<stdin>:1:3: warning: type specifier missing, defaults to int
...

Logical and/or return booleans

Expected behavior:

$ cargo run
int main(int argc, char **argv) {
    return 0 || argc;
}
$ ./a.out three args; echo $?
3

Actual behavior: returns 1
Fix: need to implement arguments to EBBs

Scope

int i = 1;                                                                                   
int main() { int i = 2; return i; }                                                          
<stdin>:2:19: error: redefinition of 'i'

Infinite loop on invalid input

Whenever Parser::panic is called there's an infinite loop. Not sure why, it has clear termination conditions.

(osh) ~/.../rust/compiler ▶️ timeout 5s cargo run <<< 'int i, main() {} '
    Finished dev [unoptimized + debuginfo] target(s) in 0.06s
     Running `target/debug/compiler`
(osh) ~/.../rust/compiler ▶️ echo $?
124

Remove LogicalNot

According to the C11 standard, !x is exactly the same as x == 0 for all scalar x. It makes more sense to treat this as syntactic sugar than to try to compile it seperately in ir.rs.

Catch illegal types

  • arrays of functions: int f[]()
  • functions returning arrays: int f()[]
  • function returning function: int f()()
  • ... more?

Forward declarations are still broken

struct s my_s;
struct s { int i; };
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `Some(1)`,
 right: `Some(0)`', /home/joshua/.cargo/registry/src/github.com-1ecc6299db9ec823/faerie-0.11.0/src/artifact/decl.rs:453:5

Need to update all types in scope, not just typedefs :( this won't be hard but it'll be really slow

`impl Display for Type` messes up pointers

Pointers should be between declaration specifiers and declarators but instead are printed before both, i.e. the current *int () should be int *(). This is hard because it requires introspecting the types, it might be easier to print an english-like description the way cdecl does it

Pointers

int i, *p;
int main() { p = &i; return *p; }
thread 'main' panicked at 'not yet implemented: Ref(Expr { expr: Id(Symbol { id: "i", ctype: Int(true), qualifiers: Qualifiers { volatile: false, c_const: false }, storage_class: Extern, init: false }), ctype: Int(true), constexpr: false, lval: true, location: Location { line: 2, column: 20, file: "<stdin>" } })', src/ir.rs:411:17

Implicit lval conversions

char *s = "hi";
<stdin>:1:11: error: cannot implicitly convert 'char[3]' to '*char'. help: use an explicit cast: (*char)

The following degrade to pointers when used in an expression context:

  • Arrays
  • Functions

Additionally, ids are automatically derefence (this is implemented).

Waiting on #19 since this is only an issue for degrading to pointers.

Multiple declarations in one statement are not parsed properly

int main() {
        int i, j;
        j = 1;
}
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/libcore/option.rs:347:21

Immediate cause: src/ir.rs doesn't think j is in scope.
Root cause: Parser::compound_statement does not look at self.pending for declarations.
Proposed fix: Get rid of self.pending altogether, it's hacky and I never liked it. Replace it by making Stmt::Decl a Vec<Declaration> instead of a Box. That also allows getting rid of Option for Parser::declaration.

Allow self-referential structs

typedef struct self_referential { struct self_referential *q; } *SELF;
int main() {
        SELF p;
        SELF q = p->q;
}
<stdin>:4:22: error: cannot implicitly convert 'struct self_referential *' to 'struct self_referential *'. help: use an explicit cast: (struct self_referential *)

Statements

int main() { int i = 5, j = 0; while (i >= 0) { j += 1; i -= 1; } }
thread 'main' panicked at 'not yet implemented: almost every statement', src/ir.rs:296:18
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Floats are not stored properly in static memory

Steps to reproduce:

float f = 2;
int main() {
    return f;
}

Expected behavior: exits with return code 2
Actual behavior: exits with return code 0

The issue is that the casts in the constant folding don't have a way to change the type of the token. Cast(Token::Double(2.0), Float) gets turned into Token::Double(2.0). Then when Expr::into_bytes calls Token::ctype() it gets back Double instead of Float, so Token::into_bytes stores the full precision instead of single-precision. When it's loaded from memory at runtime, it only loads the first 4 bytes, which are garbage when interpreted as a float.

The fix is to finally implement types for literals, which we need for #4 anyway.

Variadic args

Need to go through and look at https://github.com/CraneStation/cranelift/issues/212 more closely. It looks like cranelift decided this needs to be done by the frontend.

This is important because no one can call printf without it.

Resources:

Allow casting char *const to char*

char *const c;
int main() {
        char *s = c;
}
<stdin>:3:13: error: cannot implicitly convert 'char *const ' to 'char *'. help: use an explicit cast: (char *)

Add comments to IR

Not sure how to do this.

Steps to replicate:

$ cargo run -- --debug-asm
int main() {
    int i = 1;
    return i;
}

Current behavior:

function u0:0() -> i32 system_v {
    ss0 = explicit_slot 4

ebb0:
    v0 = iconst.i32 1
    stack_store v0, ss0
    v1 = stack_addr.i64 ss0
    v2 = load.i32 v1
    return v2
}

Expected behavior:

function u0:0() -> i32 system_v {
    ss0 = explicit_slot 4

ebb0:
    v0 = iconst.i32 1   ; int i = 1
    stack_store v0, ss0
    v1 = stack_addr.i64 ss0  ; return i
    v2 = load.i32 v1
    return v2
}

Function Pointers

$ cargo run
int f();
int main() {
        int (*fp)() = f;
        return fp();
}
<stdin>:3:24: error: cannot implicitly convert 'int ()' to '*int ()'. help: use an explicit cast: (*int ())
$ cargo run
int f();
int main() {
        int (*fp)() = &f;
        return (*fp)();
}
thread 'main' panicked at 'not yet implemented: address of function', src/ir.rs:645:32

Support preprocessing

Section 6.10:

  • 6.10.1: #if / #ifdef / #ifndef / #elif / #else / defined conditional compilation (added in #184)
  • 6.10.2: #include headers
  • 6.10.3: #define macros and substitutions
    • object-like macros
    • 6.10.3.1 function-like macros
      • including __VA_ARGS__
    • 6.10.3.2 and # and ## operators
    • 6.10.3.5 and undef
  • 6.10.4: #line control (can be ignored for now, waiting on #152, brendanzab/codespan#157)
  • 6.10.5: #error directives
  • 6.10.6, 6.10.9: pragma directives (can be ignored altogether)
    • #pragma
    • _Pragma ()
  • 6.10.7: # on its own (ignored)
  • 6.10.8: predefined macros
    • conditional features (6.10.8.3):
      • __STDC_NO_ATOMICS__
      • __STDC_NO_COMPLEX__
      • __STDC_NO_THREADS__
      • __STDC_NO_VLA__

Typedefs

typedef int i;
thread 'main' panicked at 'not yet implemented: typedefs', src/parse/decl.rs:85:13

Fun fact: 'typedef' doesn't have to come first in the declaration:

$ clang -x c -
int unsigned typedef i;
$

Make Location faster

Right now, almost every token requires a copy of the filename and it's really expensive in terms of performance.

Short term plan

Make filename a global

Mid term plan

Use Rc basically everywhere. Alternatively, store a single offset from the start of the file and calculate lines and columns lazily.

Pie in the sky architecture astronaut plan

Use multiple threads for lexer, parser, and codegen. Store a single offset from the start of the file, so Location is now copy. Handle errors in a fourth thread, which is now the only one that needs to know the filename -> no longer a global. Use a channel to send semantic errors so they no longer disrupt parsing (see #32).

It's a long shot, but it would solve a lot of different problems really elegantly without having to rewrite the internals that much, only the wrappers.

Can't use enums as variables

enum e { A, B, C };
int f(enum e);
int main() {
    enum e my_e = A;
    f(my_e);
}
int f(enum e) {
    return 0;
}
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/libcore/option.rs:347:21

Catch errors for structs that aren't defined

struct s my_s;
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `Some(1)`,
 right: `Some(0)`', /home/joshua/.cargo/registry/src/github.com-1ecc6299db9ec823/faerie-0.11.0/src/artifact/decl.rs:453:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

Note: this is because I'm passing an align of 0 to faerie, which it doesn't like.

Aggregate dynamic initialization

int main() { int a[3] = {1, 2, 3}; }
thread 'main' panicked at 'not yet implemented: aggregate dynamic initialization', src/ir.rs:211:48

Note: the way LLVM implements this is by storing the array in static memory and then loading it into the stack when the function is called. It also loads 8 bytes at a time regardless of the type of the array (at least on x86_64).

Rewrite number parsing for lexer

Proposed architecture:

main loop:

  • '.' => parse_float()
  • digit => parse_num()

parse_num:

  1. Parse radix
  2. parse_int(radix)
  3. check for integer suffixes (if so, return)
  4. parse_float(radix)

parse_float(radix):

  1. Parse fraction
  2. Optionally, parse exponent
  3. check for float suffixes

and then parse_int is super simple

Report multiple errors at once

Current behavior:

int f(int) {
    return;
}
<stdin>:1:18: error: missing parameter name in function definition (parameter 0 of type 'int')
1 error generated

Expected behavior:

int f(int) {
    return;
}
<stdin>:1:5: error: missing parameter name in function definition (parameter 0 of type 'int')
<stdin>:2:3: error: function 'f' does not return a value
2 errors generated

I have a version locally that changes main and lib to report all errors, but because the architecture wasn't designed around this, it gets thrown off by semantic errors and cuts off parsing in the middle of a grammar expression. The example above ends up going into an infinite loop because it doesn't consume the '}'.

To fix this requires distinguishing between syntactic and semantic errors, which is not going to be fun. Ideally it would be separated into a new pass, but that would be a massive headache to do at this point.

Infer size of array from initializers

int a[] = {1, 2, 3};
thread 'main' panicked at 'not yet implemented: inferring size of unbounded array from initializer', src/ir.rs:904:21

This should be pretty easy to add.

Support float suffices

int main() {
    return 2.5f * 2;
}

Expected: returns 5
Actual: <stdin>:2:16: error: expected ';', got 'f'

if with return and no else does not compile

int main() {
        if (1) { 
                return 1;
        }
}
thread 'main' panicked at 'should not have a compile error: VerifierErrors([VerifierError { location: inst1, message: "invalid ebb reference ebb2" }])', src/libcore/result.rs:999:5

Compare an explicit return, which works fine:

int main() {
        if (1) { 
                return 1;
        }
        return 0;
}
asm
function u0:0() -> i32 system_v {
ebb0:
    v0 = iconst.i32 1
    brz v0, ebb2
    jump ebb1

ebb1:
    v1 = iconst.i32 1
    return v1
}
backtrace
thread 'main' panicked at 'should not have a compile error: VerifierErrors([VerifierError { location: inst1, message: "invalid ebb reference ebb2" }])', src/libcore/result.rs:999:5
stack backtrace:
   0: backtrace::backtrace::libunwind::trace
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.29/src/backtrace/libunwind.rs:88
   1: backtrace::backtrace::trace_unsynchronized
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.29/src/backtrace/mod.rs:66
   2: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:47
   3: std::sys_common::backtrace::print
             at src/libstd/sys_common/backtrace.rs:36
   4: std::panicking::default_hook::{{closure}}
             at src/libstd/panicking.rs:200
   5: std::panicking::default_hook
             at src/libstd/panicking.rs:214
   6: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:477
   7: std::panicking::continue_panic_fmt
             at src/libstd/panicking.rs:384
   8: rust_begin_unwind
             at src/libstd/panicking.rs:311
   9: core::panicking::panic_fmt
             at src/libcore/panicking.rs:85
  10: core::result::unwrap_failed
             at /rustc/eae3437dfe991621e8afdc82734f4a172d7ddf9b/src/libcore/macros.rs:18
  11: core::result::Result<T,E>::expect
             at /rustc/eae3437dfe991621e8afdc82734f4a172d7ddf9b/src/libcore/result.rs:827
  12: compiler::ir::LLVMCompiler::compile_func
             at src/ir.rs:248
  13: compiler::ir::compile
             at src/ir.rs:77
  14: compiler::compile
             at src/lib.rs:72
  15: compiler::real_main
             at src/main.rs:63
  16: compiler::main
             at src/main.rs:109
  17: std::rt::lang_start::{{closure}}
             at /rustc/eae3437dfe991621e8afdc82734f4a172d7ddf9b/src/libstd/rt.rs:64
  18: std::rt::lang_start_internal::{{closure}}
             at src/libstd/rt.rs:49
  19: std::panicking::try::do_call
             at src/libstd/panicking.rs:296
  20: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:82
  21: std::panicking::try
             at src/libstd/panicking.rs:275
  22: std::panic::catch_unwind
             at src/libstd/panic.rs:394
  23: std::rt::lang_start_internal
             at src/libstd/rt.rs:48
  24: std::rt::lang_start
             at /rustc/eae3437dfe991621e8afdc82734f4a172d7ddf9b/src/libstd/rt.rs:64
  25: main
  26: __libc_start_main
  27: _start
AST
FunctionBody(
                        [
                            Locatable {
                                data: If(
                                    Expr {
                                        expr: Literal(
                                            Int(
                                                1,
                                            ),
                                        ),
                                        ctype: Int(
                                            true,
                                        ),
                                        constexpr: true,
                                        lval: false,
                                        location: Location {
                                            line: 2,
                                            column: 14,
                                            file: "<stdin>",
                                        },
                                    },
                                    Locatable {
                                        data: Compound(
                                            [
                                                Locatable {
                                                    data: Return(
... snip ...

Overflow in const fold

  • addition (0x7fffffffffffffffL + 1, -0x7fffffffffffffffL + -2)
  • subtraction (-0x7fffffffffffffffL - 2, 0x7fffffffffffffffL - -1)
  • multiplication (0x7fffffffffffffffL * 2, (-0x7fffffffffffffffL - 1) * -1)
  • division (1/0, (-0x7fffffffffffffffL - 1) / -1)
  • modulus (1%0, (-0x7fffffffffffffffL - 1) % -1)
  • shift left (1 << 33)
  • negation(-(-0x7fffffffffffffffL - 1L))

Can't assign struct reference

struct s {
    int i, j, k;
} s;
int main() {
        int i = s.i = s.j = s.k = 1;
        struct s *sp = &s;
}
<stdin>:6:25: error: cannot implicitly convert 'struct s *' to 'struct s *'. help: use an explicit cast: (struct s *)

The right way to fix this is to implement Type::partial_eq by hand.

Finish up codegen for expressions

  • Struct/union members (a.b) (part of #21)
  • Pre/post increment/decrement (++a, i--)
  • sizeof (should be easy, just call Type::sizeof internally)
  • references (part of #19)
  • logical expressions (||, &&) - 558122e
  • ternaries (c ? a : b) - only implemented for constants

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.