Git Product home page Git Product logo

citrine's Introduction

Citrine

Welcome to the Citrine Programming Language Project.

Citrine is a general-purpose, localised scripting language. It aims to foster readable and maintainable code, while remaining simple and easy to learn by focusing on syntactical and conceptual minimalism:

☞ greeting ≔ ‘Hello country!’.

greeting country: ‘Great Britain’.

✎ write: greeting.

Hello Great Britain!

Citrine is wholly free and open-source software, released under the 2-clause BSD License (see the file LICENSE).

For more information (including more examples and an on-line demo), please see the official website.

Installation

Citrine is cross-platform and should run on a variety of operating systems, but the installation procedure for some is currently lacking in support. Help to improve the situation is very welcome.

Please make sure you are using the latest official release of Citrine, in order to have the newest features and bug fixes.

Using a precompiled binary

The easiest way to get started is by using a precompiled binary, currently available on the official website for the following systems:

  • GNU/Linux
  • Microsoft Windows
  • OpenBSD

Compiling the source code

Source code for the latest official release may likewise be acquired from the official website. If you choose to compile it yourself, there’s an installation script, mk.sh, included to automate the process. You may run it as follows:

./mk.sh

This should generate a separate binary for every supported language in the bin directory, in a subdirectory matching your system. Now you can install Citrine with all of the compiled binaries using make:

make install

You can adjust the path the files get installed to by setting the variable prefix, if the default value doesn’t work well for your setup:

make prefix='/usr/local' install

Compiling only some languages

Compiling binaries for all the languages can take a while. If you know for sure that you won’t be needing all of those, you can choose to only compile binaries for a subset of the languages instead, by passing their codes as arguments to mk.sh:

./mk.sh en nl  #Compile English and Dutch only.

Without arguments, mk.sh compiles all the languages listed in the i18nsel directory. By default, this is only a symbolic link to the actual directory i18n, where all of the available languages are really stored.

Manual compilation

In case mk.sh doesn’t work for you, here’s an explanation of how to compile Citrine manually, but still using make:

First, a binary for every language is compiled separately. Every time you run make on the appropriate makefile, the environment variable ISO of your shell should contain the ISO code of the language you want to compile. Likewise, the variable OS should contain an identifier of your operating system. For example:

ISO='hi' OS='Haiku' make -f makefile all

Given the above, make would fetch Hindi vocabularies from the language’s files in i18n/hi, compile them into a binary named ctr, and copy the binary to the Haiku system’s directory at bin/Haiku, with the language’s code attached to the filename.

Now’s a good opportunity to use the binary to compose a dictionary of translations between two languages. Just point Citrine to each language’s dictionary.h file to make a one-way translation dictionary:

./ctr -g i18n/nl/dictionary.h i18n/hi/dictionary.h > dict/nlhi.dict
./ctr -g i18n/hi/dictionary.h i18n/nl/dictionary.h > dict/hinl.dict

These two commands would produce two dictionaries in the dict directory: one for translating from Dutch to Hindi, and another for translating from Hindi to Dutch.

Having done all this, you can clean up the files produced during compilation and move on to the next language:

make -f makefile clean

Once you’ve compiled all the languages you want, you will find the binaries in bin. You can start using them right away, but you might want to install them under your system’s standard binary path first. You also probably want to install the included font that gives Citrine’s symbols a nicer look in your text editor:

mv bin/Haiku/ctr* ~/.local/bin
mv fonts/Citrine.ttf ~/.local/fonts

And with this, you should be done!

Contributing

If you’d like to contribute to the project, you can get in touch using e-mail or GitHub. Forms of contribution include, but are not limited to:

Citrine and its plugins are written in the C programming language. The Citrine Project is apolitical.

CitrineCI

citrine's People

Contributors

aaveshdev avatar denniscgc avatar gabordemooij avatar jadedctrl avatar janus avatar madcapjake avatar noracodes avatar shinriyo avatar takano32 avatar temurumaru 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

citrine's Issues

Removed redundant object

It seems to me that variable argument2 is redundant.
ctr_object* ctr_string_skip(ctr_object* myself, ctr_argument* argumentList) {
ctr_argument* argument1;
/* ctr_argument* argument2; _/
if (myself->value.svalue->vlen < argumentList->object->value.nvalue) return ctr_build_string("",0);
argument1 = CTR_CREATE_ARGUMENT();
/_argument2 = CTR_CREATE_ARGUMENT(); _/
argument1->object = argumentList->object;
/_argument1->next = argument2; _/
/_argument2->object */
argument1->next->object = ctr_build_number_from_float(myself->value.svalue->vlen - argumentList->object->value.nvalue);
return ctr_string_from_length(myself, argument1);
}

Recipe for target 'install' failed

When attempting to run make install when setting up citrine, it fails after attempting the cp ./ctr /usr/bin/ctr step, due to cp failing with the message cp: cannot stat './ctr': No such file or directory.

It does successfully create the binaries in the respective bin/"$OS" folder, so it's not a major issue, but still.

I'm running the command from the repository's root folder (the one containing the makefile), and I'm running Ubuntu 18.04 through WSL on Windows 10 - $OS and $ISO are unset by default, so I've tried running with those manually set to various values and successfully built for all combinations of (Linux/OpenBSD) and (us/nl/ro) I've tried, apart from that final cp step which always fails (even when running as root).

Idea for Threading/concurrency API

Given the ability to easily pass around executable code, I would think a threading API would be relatively easy to implement (or, at least, to conceptualize; I don't know much about threading in C.)

For example:

myThread := Thread new.
myThread on: 'start:' do: { number |
    Pen write: number, write: "Doing work...", brk.
    me yield.
    Pen write: number, write: "Doing more work...", brk.
}
var myThread01 := myThread new.
var myThread02 := myThread new.
myThread01 start: 1.
myThread02 start: 2.

The other option, as I see it, is to have each Thread or inherited object spawn a new thread when the message start is past, in effect acting as a thread factory.

Thoughts?

getting rid of magic number `40` from `ctr_build_number_from_string`

@janus says in
#28

What it the reason for using 40 instead of length.

/**
 * @internal
 * BuildNumberFromString
 */
ctr_object* ctr_build_number_from_string(char* str, ctr_size length) {
        char* numCStr;
        ctr_object* numberObject = ctr_internal_create_object(CTR_OBJECT_TYPE_OTNUMBER);
        /* turn string into a C-string before feeding it to atof */
        numCStr = (char*) calloc(40, sizeof(char));
        memcpy(numCStr, str, length);
        numberObject->value.nvalue = atof(numCStr);
        numberObject->link = CtrStdNumber;
        return numberObject;
}

This issue is re-issued above issue with pretty format.

[Proposal] String Interpolation

String interpolation is an awesome feature a lot of morden languages implement. I really enjoy using it and I think it makes code easier to read. Basically, it looks like this:

Pen write: 'Welcome #{name}'.

The syntax shown above is Ruby's, ES6 uses a slightly different style:

Pen write: 'Welcome ${name}'.

Syntax is debatable of course, my question is whether it should be implemented. Yay or nay?

Add support for Glyph clusters

Some groups of characters are to be counted as 1. Need some kind of engine/lib to take care of this or make it configurable at least.

`ctr_build_string function` needs some works for test 48

I checked that function and I started wondering why it does not free the argument string after memcpy. This looks strange and I tried to free , I got error while on test 48. I am still a bit confuse while C string after converting it to citrine object would still be active and live in subsequent code. And I am not able to figure out much from test 48, please explain it a bit for me.

tests/test0216.ctr

tests/test0216.ctr
‘SINGLE_LANGUAGE’.
Program tidiness: 0.
Program memory: 180000000.
☞ a ≔ Map new.
a property: a.
✎ write: a, stop.

Noticed a repetition below..

The string "new" called twice any special reason for this. When I commented one out my code still works.

ctr_internal_create_func(CtrStdObject, ctr_build_string("new", 3), &ctr_object_make); ctr_internal_create_func(CtrStdObject, ctr_build_string("equals:", 7), &ctr_object_equals); ctr_internal_create_func(CtrStdObject, ctr_build_string("=",1), &ctr_object_equals); ctr_internal_create_func(CtrStdObject, ctr_build_string("on:do:", 6), &ctr_object_on_do); ctr_internal_create_func(CtrStdObject, ctr_build_string("respondTo:", 10), &ctr_object_respond); ctr_internal_create_func(CtrStdObject, ctr_build_string("respondTo:with:", 15), &ctr_object_respond); ctr_internal_create_func(CtrStdObject, ctr_build_string("respondTo:with:and:", 19), &ctr_object_respond); ctr_internal_create_func(CtrStdObject, ctr_build_string("type", 4), &ctr_object_type); /*ctr_internal_create_func(CtrStdObject, ctr_build_string("new", 3), &ctr_object_make);*/ ctr_internal_create_func(CtrStdObject, ctr_build_string("isNil", 5), &ctr_object_is_nil); ctr_internal_create_func(CtrStdObject, ctr_build_string("myself", 6), &ctr_object_myself); ctr_internal_object_add_property(CtrStdWorld, ctr_build_string("Object", 6), CtrStdObject, 0);

citrine-lang.org landing page

Won't it be nice to show the user how to run the program. Something like "$ ctr hello.ctr"
A slightly over-engineered 'hello world' program:

Butler := Object new.
Butler on: 'greet:' do: { name |
Pen write: 'Welcome ' + name.
}.
james := Butler new.
james greet: 'visitor'.

This program will generate the following output:
Welcome visitor

Unit tests run out of order

On Ubuntu 17.01 running in VirtualBox in a freshly cloned copy of master,
the unit tests run in an undefined order
See the attached log.
test.log

Calling ctr_dparse_parse functions almost back to back

I just noticed this statement "program = ctr_dparse_parse(prg);" used two times... within a block and the first call's result not used. Any reason for this?
int main(int argc, char* argv[]) {
char* prg;
ctr_tnode* program;
ctr_argc = argc;
ctr_argv = argv;
ctr_malloc_chunk_pointer = 0;
ctr_mode_compile = 0;
ctr_mode_load = 0;
ctr_cli_read_args(argc, argv);
if (ctr_mode_compile) {
prg = ctr_internal_readf(ctr_mode_input_file);
ctr_malloc_mode = 0;
ctr_malloc_measured_size_addressbook = sizeof(ctr_ast_header);/* adds up to normal addressbook size */
program = ctr_dparse_parse(prg);
program = NULL;
ctr_malloc_mode = 1;
ctr_malloc_chunk_pointer = 0;
ctr_malloc_chunk = 0;
program = ctr_dparse_parse(prg);
ctr_serializer_serialize(program);
free(ctr_malloc_chunk);
free(prg);
exit(0);
}

ctr_build_string_from_cstring

I noticed sentinel '\0' in some places .... Are they not redundant?
ctr_build_string_from_cstring("Boolean\0");
ctr_build_string_from_cstring("Expected on: argument to be of type string.")

Create own console

Original: On Windows: prevent console from opening on running example

This is because of how Windows implements popen() which is used in "Program command:".
Probably need to implement an alternative for Windows and re-route STDIN and STDERR.

Turns out this is more a feature than a bug: i.e. having an own console.
Because if we hide the console you cannot use the basic write functions anymore.
So rule should be:

  • If Media Plugin then:
  • Hide console
  • If write/ask/flush/stop (during Media Plugin) then open own console
  • Close own console if app quits

String interpolation doesn't work at the end of string

catcall := 'meow'.
Pen write: 'The cat goes $$catcall'.

Crashes with this bug:

Parse error, unexpected ( ( t/020-get.ctr: 20 )
Expected ).

Actually, sometimes it seems to segfault instead of printing that error.

Not sure if this is related but the line number doesn't match too.

ctr_build_number_from_string

What it the reason for using 40 instead of length.

ctr_object* ctr_build_number_from_string(char* str, ctr_size length) {
    char* numCStr;
    ctr_object* numberObject = ctr_internal_create_object(CTR_OBJECT_TYPE_OTNUMBER);
    /* turn string into a C-string before feeding it to atof */
    numCStr = (char*) calloc(40, sizeof(char));
    memcpy(numCStr, str, length);
    numberObject->value.nvalue =  atof(numCStr);
    numberObject->link = CtrStdNumber;
    free(numCStr);
    return numberObject;
}

Segfault on argument index out of range.

If, say Command argCount is 3, Command argument: 3 causes a segfault. This is the expected behavior, but it might be nice to change it as part of version 0.6 error handling.

Use arc4random() instead of rand()

OpenBSD uses a much safe and cryporgraphically secure randomization technique.
This feature is to make sure both Linux and OpenBSD can benefit from this technology when scripting in Citrine.

http://man.openbsd.org/arc4random.3

Linux user can install the required dependency using:

sudo apt-get install libbsd0 libbsd-dev

ctr_object* ctr_string_html_escape

I looked at the above function and I started thinking that calling realloc when we have not exhausted memory block already created by calling malloc is not efficient in time . I think this algorithm should be made to be a bit faster by either calling **realloc ** after we have exhausted the memory block already created by malloc or while mallocing set the required memory size to be more than the actual string length to start with. If this view is accepted I will send a patch thereafter.
Finally, after calling malloc I would expect that we should check if there is enough memory or not before proceeding. This is lacking.

Unexpected behaviour with "learn" message and inheritance.

Using learn to define an alias for some message on an object a causes objects that inherit from a to call the parent's definition of that behaviour when the alias is used rather than its own.

For example, after Object learn: 'is nil' means: 'Nil?', Nil is nil calls Object Nil? (returning False) instead of the expected Nil Nil? (which would return True).

Or, for another example, the following code prints False.

☞ Thing := Object new.
Thing on: 'number' do: { ↲ 2. }.

Object learn: 'to number' means: 'number'.

✎ write: (Thing new number) = (Thing new to number).

Test 67 fails sometimes

might be memory issue/leak,
sometimes lastIndexOf returns 9 instead of 5. please research...

Enforce pipe rules

Right now you may use \ and | to separate params in a block from the block body.
The Lexer should enforce use of \ only for blocks without any parameters and forbid the use of | for empty param sections.

mk bash and make install

I can't truly figure out the difference between ./mk.bash and make install except cp ./ctr /usr/bin/ctr and object files littering folder. Is there something I am missing?

git clone --depth=50 https://github.com/gabordemooij/citrine.git gabordemooij/citrine
cd gabordemooij/citrine
git fetch origin
git checkout -qf FETCH_HEAD
./mk.bash
make install

n times: not working

I have a program

3 times: {\ Pen write: 'Test.'.}.

Which, when run with the ctr, produces no output. Am I doing something wrong, or is the times: functionality not implemented yet?

Error building on macOS

I ran into a problem while trying to build on macOS High Sierra. When running ./mk.sh I get the following:

using Linux Makefile.
rm -rf siphash.o utf8.o memory.o util.o base.o collections.o file.o system.o world.o lexer.o parser.o walker.o translator.o citrine.o ctr
cc -mtune=native -Wall -D forLinux -DlangUS -c siphash.c
cc -mtune=native -Wall -D forLinux -DlangUS -c utf8.c
cc -mtune=native -Wall -D forLinux -DlangUS -c memory.c
cc -mtune=native -Wall -D forLinux -DlangUS -c util.c
cc -mtune=native -Wall -D forLinux -DlangUS -c base.c
base.c:19:10: fatal error: 'bsd/stdlib.h' file not found
#include <bsd/stdlib.h>
         ^~~~~~~~~~~~~~
1 error generated.
make: *** [base.o] Error 1
./mk.sh: line 20: ./ctr: No such file or directory
./mk.sh: line 21: ./ctr: No such file or directory
./mk.sh: line 22: ./ctr: No such file or directory
rm -rf siphash.o utf8.o memory.o util.o base.o collections.o file.o system.o world.o lexer.o parser.o walker.o translator.o citrine.o ctrnl
cc -mtune=native -Wall -DforLinux -DlangNL -c siphash.c
cc -mtune=native -Wall -DforLinux -DlangNL -c utf8.c
cc -mtune=native -Wall -DforLinux -DlangNL -c memory.c
cc -mtune=native -Wall -DforLinux -DlangNL -c util.c
cc -mtune=native -Wall -DforLinux -DlangNL -c base.c
base.c:19:10: fatal error: 'bsd/stdlib.h' file not found
#include <bsd/stdlib.h>
         ^~~~~~~~~~~~~~
1 error generated.
make: *** [base.o] Error 1

I was able to get it to build successfully by modifying line 6, changing it from

if [ "$OS" = "OpenBSD" -o "$OS" = "FreeBSD" ]; then

to

if [ "$OS" = "OpenBSD" -o "$OS" = "FreeBSD" -o "$OS" = "Darwin" ]; then

diagonal movement never arrives at destination

☞ media ≔ Media new.
☞ plane ≔ Image new: ‘plane1.png’.

plane on: ‘destination:’ do: {
(⛏ x? > 100) yes: {
⛏ move to x: 100 y: 300.
⛏ image: ‘plane1b.png’.
}, else: {
⛏ move to x: 400 y: 350.
}.
}.

media on: ‘start’ do: {
plane x: 100 y: 300,
move to x: 400 y: 300,
speed: 1.

}.

media screen: ‘bg.png’.

test119

Noticed this error while running nightly. Looks like the last test119 is having issue with my versio

EXPECTED:
search=table&restaurant=pizzaria&menu=spaghetti&menu=macaroni&course[main]=canneloni&course[dessert]=tiramisu I was looking for a free table in a pizzaria, but the waiter told me all tables have been corrupted by spaghetti, macaroni and canneloni code.

BUT GOT:
search=table&restaurant=pizzaria&menu=spaghetti&menu=macaroni&course[main]=canneloni&course[dessert]=tiramisu Error, key not found: [Request].

Failed to compile from source

It have been a long while since I played around with citrine, but today I updated my local repo and ran ./mk.bash. However, it didn't produce the expected result. Instead I got the below:

using Linux Makefile.
gcc -mtune=native -Wall -c siphash.c
gcc -mtune=native -Wall -c utf8.c
gcc -mtune=native -Wall -c memory.c
gcc -mtune=native -Wall -c util.c
gcc -mtune=native -Wall -c base.c
gcc -mtune=native -Wall -c collections.c
gcc -mtune=native -Wall -c file.c
gcc -mtune=native -Wall -c system.c
gcc -mtune=native -Wall -c world.c
world.c: In function ‘ctr_internal_object_delete_property’:
world.c:142:15: warning: variable ‘oldMapItem’ set but not used [-Wunused-but-set-variable]
ctr_mapitem* oldMapItem;
^
gcc -mtune=native -Wall -c lexer.c
lexer.c: In function ‘ctr_clex_emit_error’:
lexer.c:70:2: warning: format not a string literal and no format arguments [-Wformat-security]
printf( message );
^
gcc -mtune=native -Wall -c parser.c
parser.c: In function ‘ctr_cparse_emit_error_unexpected’:
parser.c:25:2: warning: format not a string literal and no format arguments [-Wformat-security]
printf( message );
^
parser.c:27:2: warning: format not a string literal and no format arguments [-Wformat-security]
printf( hint );
^
parser.c: In function ‘ctr_cparse_expr’:
parser.c:511:8: warning: unused variable ‘t’ [-Wunused-variable]
int t = ctr_clex_tok();
^
parser.c: In function ‘ctr_cparse_receiver’:
parser.c:467:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
gcc -mtune=native -Wall -c walker.c
gcc -mtune=native -Wall -c citrine.c
gcc siphash.o utf8.o memory.o util.o base.o collections.o file.o system.o world.o lexer.o parser.o walker.o citrine.o -rdynamic -lm -ldl -o ctr
lexer.o: In function ctr_clex_tok': lexer.c:(.text+0x71c): undefined reference toctr_clex_is_delimiter'
lexer.c:(.text+0x771): undefined reference to ctr_clex_is_delimiter' lexer.c:(.text+0x7c6): undefined reference toctr_clex_is_delimiter'
collect2: error: ld returned 1 exit status
makefile:14: recipe for target 'ctr' failed
make: *** [ctr] Error 1

Citrine gammar

I am drive to understand citrine parser, I reproduced the grammar. However, I missed out a couple of things like block does not have parameters(someone here should help me figure this out). And I would like to hear your view and comments.

program ::-> statement dot {statement}
statement ::-> expression| return_token return | end
end ::-> endofprogram
return ::-> expression
expression ::-> receiver re_object
re_object ::->  assignment_token assignment| messages
assignment ::->  expression
receiver ::->  nil |  true |  false | number | string | ref | block | popen
ref ::-> [my_ref | var_ref ] name
block ::-> "{" { ref } "|" block_main "." "}" 
block_main ::-> return_token expression | expression
popen ::-> expression ")"
messages ::->  [REF | CHAIN ] message messages | empty
message ::->  bin expression | colon expression {expression} | unary_message
unary_message ::-> name
nil ::-> nil_object
true ::-> true_object
false ::-> false_object
string ::-> string_object
number ::-> number_object
block ::-> block_object
my_ref ::-> "my" 
var_ref ::-> "var" 
popen ::-> "(" expression ")"
assignment_token ::-> ":="
return_token ::-> "^"
bin ::-> "+"|"-"|"/"|"*"|"<"|">"|"=" 
colon ::-> ":"
dot ::-> "."
name ::-> [A-Za-z][A-Za-z]*

So cleaning needed

@gabordemooij

Thanks for this great source code, I was able to get the basic idea. It is cleanly written and in a natural way. However, the code below looks great but I was thinking that it would be great to fail fast by having the IF statement before memcpy function.

void ctr_serializer_serialize(ctr_tnode* t) {
FILE f;
f = fopen(ctr_mode_compile_save_as,"wb");
memcpy( ctr_malloc_chunk, ctr_default_header, sizeof(ctr_ast_header));/
append to addressbook, new header */
if (!f) { printf("Unable to open file!"); exit(1); }
fwrite(ctr_malloc_chunk, sizeof(char), ctr_malloc_measured_size_code+ctr_malloc_measured_size_addressbook, f);
fclose(f);
}

cgi exists already?

I read document how to write template like PHP.
But, I couldn't find how to run server.
So, cgi function does not exist yet?

Serialising AST header

Please could you explain to me the reason behind serializing the header. It still floating above my head.

Small memory leak in ctr_internal_plugin_find

The buffer realPathModName as allocated by realpath is never freed (see here)

Do you want to wait and let me fix it on my branch (it will probably take a while before that one is ready to be merged), or do you want to fix it yourself?

Harfbuzz support

To support language that require sharping, like Hindi or Arabic, we need to support the Harfbuzz engine. This is supported by SDL but we need to compile against a later version to enable FontScripts.

Socket functionality

Should socket functionality be implemented in the language, since POSIX allow it to work in basically the same way across UNIX systems, or should it be a plugin? If it should be built in, could I have some pointers on how to create new built in objects? Thanks.

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.