Giter VIP home page Giter VIP logo

pip's Introduction

Pip

Pip is an interpreted, imperative code-golf language. See the GitHub wiki for an introduction to the language, or the documentation site for a language reference.

Why Pip?

Unlike most golfing languages, but like many practical languages, Pip is an imperative language with infix operators. It also uses plain ASCII instead of a custom codepage. These features make it a great introduction to golflangs for users of imperative languages like Python, JavaScript, and Perl.

In a survey of Code Golf StackExchange submissions, Pip scored slightly better than GolfScript and CJam, but not quite as good as Pyth.

Usage

You can run Pip at the following online interpreters:

  • Attempt This Online typically supports the latest release of Pip (but possibly not the latest commit). (Thanks to pxeger for adding Pip support to ATO!)
  • Do Stuff Online supports the latest commit of Pip. Note that DSO runs the interpreter client-side, so it will hang your browser if your code takes a long time to run. (Thanks to emanresu A for adding Pip support to DSO!)
  • Try It Online supports version 0.18, aka Pip Classic, which is a few years out of date. (Thanks to Dennis for adding Pip support to TIO!)
  • Replit hosts the latest commit of Pip, with the downside that it uses a command-line interface and doesn't allow permalinking. Clicking the run button will drop you into the Pip REPL. If you want to run command-line Pip, you can exit the REPL with ;quit and then run ./pip.py with whatever arguments you like. (Thanks to razetime for the idea of hosting Pip on Replit!)

You can also clone the Pip repository and run it from the command line. Pip is implemented in Python 3. The main interpreter is the pip.py file. It should run on most systems with Python 3 installed simply by invoking pip.py in the directory where you put it (for *nix systems, use ./pip.py). You may also wish to modify the PATH environment variable to include the path to Pip, so that you can invoke it from anywhere. Typical invocation patterns:

pip.py [flags] path/to/codefile.pip [args]

pip.py [flags] -e 'code' [args]

pip.py -R (REPL mode)

pip.py (interactive mode)

Execute pip.py --help for more detailed information.

What does the name refer to?

This fellow, of course.

Actually, the name "Pip" originated as a recursive acronym, though exactly what it stands for is open to debate. For some possibilities, see The Tao of Pip. The name was also chosen for its connotations of smallness.

Pip is not to be confused with pip.

pip's People

Contributors

dloscutoff avatar lyxal avatar pxeger 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

Watchers

 avatar  avatar

pip's Issues

Warn when using negative index with infinite Range

If x is an infinite range (e.g. x: 2,()), indexing or slicing it with a negative number (e.g. x@-2, x@<-3, xS5) should generate a warning if warnings are turned on. Currently, it returns nil (probably the right call), but it does so without any kind of warning message.

Empty program handled incorrectly

Running an empty program (for example, pip.py -e "") should trivially succeed and do nothing. However, currently, it prints a usage message and exits.

The cause appears to be some lines in pip.py that check the truthiness of code when they should check code is not None.

Add a REPL

A REPL environment would be less capable in some ways than a full program, but it could provide a nice supplemental way of messing around with simple Pip code.

Indexing a Scalar with a List of Patterns raises TypeError

For example,

Y"Hello, World!"
y@[+XW `\W`]

should return [["Hello";"World"];[",";" ";"!"]], but instead gives

Implementation error: evaluate([Operator(@,AT,2,44,L), Name(y), [Operator(LIST,LIST,1,46,L), [Operator(+,POS,1,39,None), Name(XW)], `\W`]]) raised TypeError ss 'ptypes.Pattern'> to index Scal

which is a really fascinating error message. =^P

Make the pip() function more usable

In theory, running pip("code", ["arglist"]) from within Python code should work nicely; and indeed, it does work nicely from within IDLE. But in any other context (Python's built-in REPL, other Python code), the fact that the function calls sys.exit(1) when it runs into a fatal error is unexpected and a bit tricky to deal with. A better approach would be to raise a normal exception (maybe FatalError) and let the caller handle it. When running pip.py as the main program, one approach might be to catch the error in the if __name__ == "__main__" section and do sys.exit(1) there.

In a similar vein, sys.exit(0) could instead be return, which would play a lot nicer with anyone trying to call pip() from their own code.

Finally, there are some parts of the current pip() function that ought to be refactored and called something else. The argparse code could probably go in its own function, for starters; and any code that assumes we're coming from a command-line invocation probably belongs outside of pip(), which might be called from some other context.

Add list flattening operator(s)

I think two types of flattening would be useful: one that flattens a single level (equivalent to $AL) and one that flattens all levels.

Slicing ,0 results in nonempty range

(,0)@<5 should result in an empty Range, just like ""@<5 is an empty string and []@<5 is an empty List. Instead, it gives (),5. Seems like maybe the code is getting confused between an upper value of 0 and an upper value of nil.

Add a bytecount to verbose mode

In addition to the tokenized & parsed forms, it would be nice if -v told you how many bytes (& characters) your code was.

Nil should equal itself

Currently,

()=()    0
()!=()   1
()Q()    0
()NE()   1
()==()   1

I don't see why nil should compare as not equal to itself in any context, even though it's not a number/string. Nil should be equal to itself and not equal to anything else.

The more interesting question: what about <=, >=, LE, and GE? I suppose the most consistent return value would be true, since a=b would seem to imply a<=b.

(Since nil is not equal to any other value and is not comparable, all of the comparison operators except != & NE should return false when given nil and any other value.)

Don't allow underscores when doing base conversion

It turns out that Python 3 allows underscores in strings representing base-N numbers: int("a_b_c", 16) results in 2748, the same as int("abc", 16) does. This is undesirable behavior for Pip; underscores in the left argument of FB should give a result of nil, just like any other character that isn't a digit in the given base. I think the implementation of FROMBASE will have to check for underscores explicitly.

Ideas for new operators

This issue is a place to brainstorm potentially useful new operators. Most of these, if they are implemented, will become 2-uppercase-letter operators; some will be 2-byte symbolic operators. Feel free to make suggestions in the comments. Also, if you see an idea you like, feel free to implement it and make a pull request!

  • Chop: split an iterable into n chunks of (roughly) equal size; unary version splits into two chunks (CH, or possibly >< because it's the converse of <>)
  • Flatten List (FL for flattening one level, FA for FlattenAll)
  • Map and flatten (MF)
  • Filter-enumerate: filters an iterable, but the first argument to the function is the index of the element and the second is the element itself (FE)
  • Filter-unpack: filters an iterable, but the arguments to the function are the elements of each element rather than the element itself (FU)
  • Filter negated: keeps elements for which the function returns a falsey value (FN)
  • Filter and join (FJ)
  • Filter and flatten (FF)
  • Filter indexes: like FE, but returns a list of indexes instead of a list of elements (FX)
  • Take-while and drop-while (TW and DW)
  • Fold on function (FO for [left] Fold, FR for FoldRight)
  • Iterate function (IW for Iterate While truthy, IU for Iterate Until truthy, IQ for Iterate while uniQue)
  • Pad with spaces (LS for LeftSpace, RS for RightSpace, BS for BothSpace; or PL for PadLeft, PR for PadRight, PC for PadCentered)
  • Left-pad with zeros (LZ for LeftZero; or ZP for ZeroPad)
  • Base-conversion operator that takes a width and left-pads with zeros
  • Pad with arbitrary character
  • Wrap in parentheses (WP? or PN or PT for PareNThesize?)
  • Bit-length (how many bits are in the binary representation of a number) (BL)
  • Rotate iterable left/right
  • Title-case (capitalize first letter of each run of letters) (TC)
  • Initial caps (capitalize first letter of string) (IC)
  • Round up, down, toward zero, to nearest (>| for round up, |< for round down, RZ for RoundZero, RN for RoundNearest)
  • Keyed versions of MN and MX, analogous to SK (NK for MinKey, MK for MaxKey)
  • String versions of MN and MX, analogous to SS (MS isn't available, so maybe LS for LeastString and GS for GreatestString)
  • Sort in descending order (DN for DescendingNumeric, DS for DescendingString)
  • Sort keyed in descending order (DK)
  • Trim list (does the same thing as TM but works on Lists instead of Scalars) (TL)
  • Join on list/element (concatenate list or insert element between consecutive elements of list) (JL and JE)
  • Wrap in list (unary, binary, and maybe ternary versions) (WL or maybe WS for WrapSingleton, WP for WrapPair, WT for WrapTriple--maybe WL has unary and binary versions, but then there's also WS that's only unary?)
  • Cartesian power (CW, or maybe CE by analogy to E for Exponential)
  • Power set, i.e. list of all subsets (PS)
  • A version of Z that, given a list of strings, pads with spaces and returns a list of strings rather than a list of lists of characters (ZJ for ZipJoin, by quasi-analogy to MJ)
  • Versions of zip and weave that cycle the shorter list(s) (ZC and WC)
  • A version of HV that rounds up instead of down (HU)
  • Non-cyclically-indexing version of @ (returns nil when out of bounds; possibly extends iterable when out of bounds as an lvalue) (maybe EA for ElementAt, IA for ItemAt, or AI for AtIndex?)
  • One-based indexing (\@, with a unary version that gets the last element)
  • Random shuffle (SH)
  • Various triangular matrices
  • Reversed identity matrix (RY, by analogy with EY?)
  • Euclidean norm (ED for EuclideanDistance, or HY for HYpotenuse?)
  • Dot product (DP)
  • Convolution (CV, maybe also VT for ConvolutionTrimmed to the size of the bigger input)
  • Log base 2 and base 10 (LB for LogBinary, LD for LogDecimal)

Set I/O formatting options in the code, not with flags

Context

WHEREAS, when Pip was first created, its command-line flags were intended to count as +1 byte each, according to the then-current scoring rules of Code Golf StackExchange;

WHEREAS, Code Golf StackExchange no longer adds flags to the bytecount;

WHEREAS, however, using free flags to obtain a different output format is considered unsporting by several CGCC members;

WHEREAS, after considering their arguments as regards other languages, I have come to agree that setting the I/O format ought to cost bytes;

BE IT RESOLVED, THAT all Pip flags affecting input and output behavior be removed, and syntax be added to perform the function of each such flag in 1 byte of code.

Length-related operators crash on infinite Range

The operators #, #=, #<, and #> do not check if their arguments include an infinite Range, which means they can crash the interpreter with an uncaught ValueError. Handle this case gracefully.

Option to run the program on each line of stdin

Inspired by Perl's -n and -p flags, supposing we had one or two flags that ran an implicit loop over all lines of stdin and called the given code as a function on each line? I'm thinking something like this:

T (a:q) == ()
 P {

  ; Your code here

 } V (g PE a)

corresponding to perl -p and the same thing without the P for perl -n.

  • It loops over all lines of stdin and runs your code against each one.
  • The final expression in your code is autoprinted. You can specify whatever list format you like via command-line argument.
  • Inside your code, the line of stdin is a; the actual command-line arguments are still accessible as b through e and also @>g.
  • Global variables retain their values from one iteration to the next (unless you don't want them to, in which case you can lead with WG).
  • Your code can call itself recursively using f.

One question: what actual flags do we use, since -n and -p (and -P) are already taken--as are -e (for Each) and -l (for Line)?

Exploit allows modification of increment amount

In the current implementation, certain operators return the value scalarOne as a shortcut for Scalar("1"). Unfortunately, since this is pass-by-reference and since Scalars can be modified by assigning to their elements...

YSG1
y@0:3
[SG1 t t+:1 ++t]Jk

should output 1, 10, 11, 12 but outputs 3, 10, 11, 14.

Useful initial values for j and p

The variables j and p currently don't have useful values like the other single lowercase letters. Figure out some.

(Also, if a more useful value for a currently assigned lowercase letter suggests itself, I'm open to changing it. In particular, I'm not sure how useful k and w are at the moment.)

Loss of precision when dividing large integers

1000000000000000064//2 should give 500000000000000032, but instead it gives 500000000000000000. I suspect some kind of int -> float -> int conversion is going on behind the scenes to cause the loss of precision. Interestingly, this issue does not affect HV.

"Reverse operands" meta-operator

Sometimes, it would be useful to reverse the operands of a binary operator. For example, to yank an expression and then use it as the lower bound of a Range:

(Ya+b),c

There is currently no way to save the parentheses, unless it's possible to rearrange the program so the yank happens first and then the expression is y,c.

If ~ were a meta-operator that swapped an operator's left and right operands, we could do

c,~Ya+b

potentially saving a byte.

TBD: What does the meta-operator do when applied to a ternary operator?

Make RA behave as expected for multiple, size-changing replacements

When RA is given a List of indices as its second argument, it performs a replacement at each index. However, this can be problematic if a replacement is a different length from what it replaces.

Example: First, consider z RA [3 7] '_. This replaces the characters at indices 3 and 7 in the lowercase alphabet (d and h) with underscores. The result is abc_efg_ijklmnopqrstuvwxyz. But now, consider z RA [3 7] "___". This should replace the same two characters (d and h) with three underscores each; however, the replacement of d changes the length of the string, and now index 7 is no longer h but f: abc___e___ghijklmnopqrstuvwxyz. Similarly, z RA [3 7] "" should delete d and h but instead deletes i: abcefghjklmnopqrstuvwxyz.

The best solution is to sort the list of indices in descending order. By doing the rightmost replacements first, we can preserve the correct indices for characters to the left until we are ready to perform those replacements. Caveat: we'll need to handle indices that are Ranges, too, and I think it will be impossible to handle these perfectly because of the possibility of overlapping indices. TBD exactly how to sort when Ranges are involved.

Interpreter crash when weaving a List containing nil

In unary WV, if the argument List contains nil, the intended behavior is to replace nil with an empty string; but the implementation doesn't accomplish that. It then tries to take the len of nil and dies with a TypeError.

The * metaoperator doesn't work in lambdas

For example, A*_ should be a function that returns a list of the charcodes of its argument, equivalent to {A*a}. Instead, the interpreter tries to iterate over _, fails because a Block isn't iterable, and crashes on an unhandled TypeError.

Change delimiter for escaped strings

After having \"escaped strings\" in the language for a while, I'm starting to think it may be better to use an unescaped quote character for the closing delimiter, and escape quotes inside the string instead:

Old: \"double "quotes" unescaped\"
New: \"double \"quotes\" escaped"

Situations for using escaped strings:

  • There's a double quote in the string. If there is only one double quote, the change breaks even; if there are multiple double quotes, the change increases the byte-count by one for each double quote beyond the first one.
  • Variable interpolation. For this use, the change would save one byte every time.

So the question is, which is more common--variable interpolation, or strings with multiple double quotes in them? Right now, the answer seems to be "neither"... quotes in string literals are rare, but variable interpolation isn't used much because there's usually an equally short way to do things:

\"Hello, \y.\"
"Hello, 0."R0y
"Hello, ".y.'.

Shaving a byte off the escaped-string syntax would make interpolation more attractive:

\"Hello, \y."
"Hello, 0."R0y
"Hello, ".y.'.

The situation is even better with multiple variables:

\"I like \a, \b, and spam."
"I like ".a.k.b.", and spam."
"I like 0, 1, and spam."R,2[ab]

Meanwhile, most escaped strings containing double quotes already have an equally short alternative (ignoring precedence):

\"This string contains "quotes"\"
ST`This string contains "quotes"`

In summary, I don't think there's much of importance to lose, and perhaps something to gain. But more deliberation is in order before making a final decision.

Change precedence of unary functional operators

Currently, unary FI and V are higher-precedence than their binary counterparts, which makes unary FI much less useful. It would be nice to be able to do FI{...}Mg and have it mean the same as _FI{...}Mg, i.e. FI({...}Mg), rather than the current (FI{...})Mg, which doesn't mean anything. If the goal is to filter before mapping, {...}MFIg works no matter what the precedence of FI is.

Proposal: make unary FI and V the same precedence as unary RE (just lower than binary FI, M, et al).

Implement destructuring assignments

An assignment operation like [abc]:,3 should result in a=0 & b=1 & c=2.

To accomplish this, lvals in a list will need to stay as lvals until an operation needs to be performed on them. Then, the : operator will need to be changed to accept a list as its lhs and perform the appropriate assignments.

If possible, it would be nice if nested lists worked too: [[ab][cd]]:CG2 should result in a=[0;0] & b=[0;1] & c=[1;0] & d=[1;1].

Correct repr for Blocks

The repr of a Block should be a string that, when evaluated, returns a Block equivalent to the original. It doesn't have to be exactly the same; in particular, extra parentheses are tolerable (though a version that uses minimal parentheses would be neat).

The str of a Block should probably be the same as the repr.

Some examples of acceptable outputs:

RP _  -> "{a}"
RP $+_/#_  -> "{$+a/#a}" or "{$+(a/#a)}" or even "{($+((a)/(#(a))))}
RP B*{a+3}  -> "{b*(a+3)}"  Note: the parentheses are NOT optional for this one!
RP {[a 1 2]}  -> "{[a1 2]}" or "{[a;1;2]}" or "{[(a)(1)(2)]}"
RP {Fi,#aa@i*:b-ia}  -> "{Fi,#aa@i*:b-ia}" or "{F i , # a a @ i *: b - i a}" or "{Fi;,(#a);{(a@i)*:(b-i);}a;}"

Fix scanning of Pattern literals (again)

The recent "fix" to Pattern literals broke more than it fixed... I think the regex should be

`([^`\\]|\\.)*`

That is, the contents should be 0 or more repetitions of EITHER any character that's not a backtick or a backslash, OR a backslash followed by any one character.

New single-letter operators

Several letters are available or could become available with some rearranging:

  • Letters that are currently unassigned: D, H
  • Letters that are binary operators but could have unary versions as well: M, N, Q
  • Letters that are currently statements/other syntax elements but could potentially be repurposed as operators, with the syntactical use being respelled: E, S, U

Operators that deserve 1-byte versions: ++, --, **, //

The question is, how to match these two sets up?

Disallow weird exploit with Pattern literals

Normally, you'd expect `\` to be a scanner error because it's an unterminated Pattern literal--the second backtick is escaped by the backslash and so can't be the closing delimiter. And it is a scanner error... unless this is the last Pattern literal in the file, in which case Pip happily reads it as the Pattern \, the same as if you concatenated '\ to an empty Pattern.

This behavior is inconsistent, which could be a source of programmer surprise (nearly always bad), and it doesn't even help for golfing because the result is not a well-formed regular expression (definitely bad). Therefore it is a bug and should be fixed.

Decrease precedence of some highest-precedence operators

It doesn't make sense for unary operators like # and AB to be higher-precedence than binary @ and @<. It's highly unlikely that I want #a@-1 to mean "the last digit of the length of a," and much more likely that I want "the length of the last element of a." Therefore, the following operators should be decreased in precedence to the same level as U and D (between binary @ and binary FB):

  • #
  • A and C
  • AB and SG
  • Unary FB

(I'm going to leave ++ and -- where they are for now, since options for increment and decrement already exist at the new precedence level. TBD whether to keep them there, move them to the new precedence level, or get rid of them entirely.)

New flag to eval arguments before running program

Inputting arrays would be a lot easier if arguments were eval'd, as in some other golfing languages. This seems like a good use case for a flag: sometimes it's useful to input as Scalars, other times it'd be better to eval first.

Error when iterating over range from nil to nil

Iterating over an infinite range (one with nil as its upper bound) normally produces a warning and an infinite loop. However, when the lower bound of the range is also nil, the interpreter crashes with an unhandled TypeError. It looks like Range's __iter__ is missing a "if lower bound is nil, start iterating at 0" check.

Indexed empty iterables as lvalues

Trying to index into an empty Scalar, List, or Range as an rvalue gives a warning and returns nil, which makes sense. However, trying to index into an empty iterable as an lvalue gives an unhandled ZeroDivisionError. Examples:

@x:5
l@-1:["some" ["value"]]
l@4PB'!

The culprit is the line index %= len(self._value) in both Scalar and List's __setitem__ implementation. It should be easy enough to test for this case and handle it more gracefully. The question is, how should we handle it?

  • Warn and do nothing?
  • Append the value to the sequence? Does it matter if the index is greater than 0?

Give info about operators in REPL

Typing ;help followed by an operator could give information on the operator: its name, arity, precedence, and associativity. With a little extra development, something similar could be done for global variables, and possibly for other language elements.

Error with TD operator

RP 469569899237030134113125 TD 256

should output

[99;111;111;108;44;32;110;105;99;101]

but instead it outputs

[99;111;111;108;44;32;112;0;0;101]

Implement destructuring in For loop headers

For loops should support a syntax equivalent to destructuring assignments: F[xy]l{...}, on each iteration, takes the next item in l and assigns its first element to x and its second element to y before executing the loop body.

Complex numbers

Complex numbers could be implemented as another way of interpreting a Scalar numerically: before checking if a prefix of the string looks like a float or an int, check for a prefix that looks like (number)+/-(number)j or (number)j. Do whatever operation needs to be done on the value using the Python complex type, and cast any complex result back to a Scalar before returning.

(Technically, this would break backwards compatibility, since right now 2 * "3+4j" returns 6 rather than "6+8j"--though I don't recall ever having written a program where that change would make a difference.)

Some questions: Do we interpret a string like "1-j" as a complex number? What about "-j", or just "j"? What about "5jumpingjacks"? Do we use j as a variable preinitialized to the complex unit, or as part of imaginary-number literals like in Python? How should we write the repr of complex numbers?

Pip types are no longer hashable

The program UQ[1 2 3] crashes with an implementation error: "TypeError: unhashable type: 'Scalar'." Same thing happens with a List containing Lists, Ranges, or Patterns. This looks like either a regression error or a problem caused by a newer Python version, since UQ works as expected on TIO.

Add block comment syntax

Some possibilities:

  • (; ... ;)
  • {; ... ;}
  • ;< ... >;
  • ;* ... *;

If possible, it would be nice to have nestable block comments, so that you could easily comment out any section of code even if it already contains a block comment. I think that might be harder to implement, though, since the scanner currently just uses regex and Python's regex flavor can't do nested structures.

Global variables that hold the main program's arguments

Often, a program will need to reference one of the command-line arguments inside a curly-brace function. The args are assigned to local variables a through e (and g), so the only way to access them from inside a function is to explicitly assign them to global variables before calling the function. If they were auto-assigned to global variables, we could save that extra step.

Proposal: global variables \a through \e should hold the first five arguments to the main program; \g should hold the list of all arguments; and \f (for completeness) should hold the main program as a function.

Make cyclic slicing logic consistent

We have four operators (@<, @>, H, and S) that do some kind of slicing, and all of them behave a little differently when the absolute value of the index is greater than the length of the iterable. Analyze, decide on a consistent behavior, and implement it across the board. (Don't forget to test @ with a Range argument, too.)

Floating point numbers and scientific notation

When the magnitude gets small (or big) enough, Python's floating point numbers use scientific notation for their str. However, Pip Scalars don't recognize scientific notation, which causes problems. For example, 5E-10 + 1 should be 1.0000001024. But 5E-10 actually comes out as 1.024e-07, which Pip reads in a numeric context as 1.024, and thus 5E-10 + 1 returns 2.024. Try it online!

Possible solutions:

  • Add code in Scalar.__init__, when given a float argument, that checks whether the str will contain scientific notation and rewrites it in a way that doesn't use scientific notation
  • Add code in Scalar.toNumber that properly handles scientific notation

"Cannot index into Range" when indexing variable

Y,t y@4

should give 4, but instead gives nil with a warning "Cannot index into Range". Tested with several different ranges, as well as @< and @>. This only happens when indexing into a variable that has a value of type Range. Indexing directly, as in (,t 4), works as expected.

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.