Git Product home page Git Product logo

quest's Introduction

Quest

CI

A language based around extensibility and freedom

Features

Quest supports everything you'd expect from a programming language and more!

  • Simple, but powerful keyword-less syntax.
  • Fundamentally based on hashmaps, not classes.
  • Identifiers are first-class objects, just like everything else.
  • Attributes and methods can be added to anything (including primitives!).
  • Everything is fair game, including methods defined on primitives.

What's Quest

Quest is a "non-typed" language that is designed to allow for efficient code reuse. Similar to dynamically-typed languages in that types aren't relevant, Quest takes this a step further: There are no types (just key-value pairs).

Examples

See the examples folder for some examples of what Quest can do! Most of them expect that you've read at least

Basic Examples

# Text can either be single or double quotes: they're identical (like python).
where = 'world';
print('Hello, ' + where + '!'); # => Hello, world!

Local variables are actually just string keys on the current object. The following is identical to the previous example:

# `:0` is 'the current scope'.
:0.'where' = 'world';
print('Hello, ' + :0.'where' + '!'); # => Hello, world!

Functions, Classes, and Maps

In Quest, there are no named/anonymous functions—they're both simply Blocks, written as { ... }:

# Arguments are passed via local variables `_0`, `_1`, `_2`, etc.
# The last statement in a block is implicitly returned.
print('4 squared is: ', { _0 ** 2 }(4)); # => 4 squared is: 16

# You can assign anonymous functions to variables too. The `->` syntax
# can be used to name parameters.
square = n -> { n ** 2 };
print('4 squared is: ', square(4)); # => 4 squared is: 16

# You can even just straight-up add them to builtin classes:
# The `_0` argument is the the object that this method was called on, akin to
# `self` or `this` in other languages.
Number.square = n -> { n ** 2 };
print('4 squared is: ', 4.square());

Maps are created by simply returning the result of an executed block of code:

traffic_lights = {
	# A blank scope is created whenever a block is called. Again, `:0` is the
	# same as `this` / `self` in other languages. 
	:0.'red' = 'stop';
	:0.'green' = 'go';
	:0.'yellow' = 'go, if you can';

	:0 # Return the current scope
}();

print('Green means ', traffic_lights.'green'); # => Green means go

However, you rarely need to do this directly. The object function makes this a lot easier:

traffic_lights = object() {
	'red' = 'stop';
	'green' = 'go';
	'yellow' = 'go, if you can';
};

print('Green means ', traffic_lights.'green'); # => Green means go

Classes are actually just objects too: They're just a group of methods which are available for any object which makes the "class" its parent. There is no intrinsic concept of a "constructor" either, and is generally implemented by overloading the "call" (()) function and returning the new scope.

Person = object() {
	# Whenever something is called, the `()` attribute is run.
	# "Constructors" are really just defining a `()` attribute that overwrites `__parents__` and
	# returns `:0` as the last value.
	'()' = (class, first, last) -> {
		# You can have multiple parents (to allow for multiple inheritance and mixins),
		# However, here we don't need to have multiple parents.
		__parents__ = [class];

		:0.becomes(class); # However, this is generally used instead of the previous line as it's simpler.

		# The `first` and `last` variables are already defined in the current scope, so we don't
		# need to assign them!

		:0 # return the current scope, i.e. the new object
	};

	# Define the conversion to a text object
	@text = self -> { self.first + " " + self.last };
};
();

person = Person("John", "Doe");
print(person); # => "John Doe"

No Keywords

Sticking to the theme of extensibility and freedom, there aren't traditional "keywords." Traditional control-flow keywords (such as if, while, and return) are simply attributes defined on the Kernel object (which most objects inherit from). And traditional "definition" keywords (such as class and function-declaration keywords) aren't relevant.

factorial = n -> {
	# The if function executes whichever branch is chosen
	if(n <= 1, {
		1
	}, {
		n * factorial(n - 1)
	})
};

print("10! =", factorial(10)); # => 10! = 3628800

i = 0;
while ({ i < 5 }) {
	i += 1;
	print("i =", i);
};
# => i = 1
# => i = 2
# => i = 3
# => i = 4
# => i = 5

The return function

The return function is a bit different than other languages. Because there is no concept of "functions vs blocks", you must return to a specific scope:

make_dinner = {
	the_magic_word = prompt("what's the magic word? ");
	if(the_magic_word != "please", {
		print("You didn't say 'please'!");
		# `:0` is the current stackframe, `:1` is the stackframe above this
		# one in this case, that's the `make_dinner` stackframe. return `false`
		# from that stackframe.
		return(false, :1);
	});

	# Alternatively, you can use the shorthand of `false.return`, which returns
	# only a single level up.
	if(the_magic_word != "please", false.return);

	# Or even
	(the_magic_word == "please").else(false.return);

	collect_ingredients();
	prepare_stove();
	cook_food();
	set_table();

	print("food's ready!");
	true # return `true`
};

# the `if` function can also be used as a ternary operator.
print(if(make_dinner(), { "time to eat!" }, { "aww" }));

This also removes the need for continue and break keywords that so many other languages have:

i = 0;
while ({ i < 100 }) {
	i += 1;

	# Quest supports "truthy" values.
	if (i % 2) {
		# Return from the while loops's body's stackframe.
		# This is analogous to `continue`.
		return(:1);
	};

	print("i =", i);

	if (i == 8) {
		print("stopping.");
		# Return from the while loop's stackframe. 
		# This is analogous to `break`.
		return(:2);
	};
};

print("done");

# => i = 2
# => i = 4
# => i = 6
# => i = 8
# => stopping
# => done

No distinction between l- and r-values

(TODO: there's probably more I could do here to explain this better...)

Because there's no separate concept of an "identifier" in Quest (as all identifiers are really Texts), there's no true l- or r-value concept. Instead, they are implemented via attributes defined on Text: = and ().

Unlike most languages, = is actually an operator. Only Text has it defined by default (but like any other operator, anything can overload it.):

x = 5; # call the `Text::=` function implicitly
y.'='(6); # call the `Text::=` function explicitly

print(:0.x, :0.y); # => 5 6

# now you can assign numbers.
# however, you can only access them via `:0.XXX`.
Number.'=' = Text::'=';

3 = 4;
print(:0.3) # => 4

# Obviously this isn't that helpful for numbers, but it's how destructoring lists work!

(Minor note: a.b = c doesn't actually use the = operator; it's syntactic sugar for the .= operator—a.'.='(b,c)—and is accessible on every object that inherits from Pristine (which is everything, by default).)

Text also has the () method defined, where it simply looks up its value in the current scope: (Bare variables, eg foo, were added so 'foo'() wouldn't be necessary.)

'x' = 5;
	
print(x, 'x'(), :0.'x'); # => 5 5 5

Everything is fair game

Most runtime languages support some form of instance variables that can be added to objects. However, Quest takes this a step further, and allows everything to have attributes added/removed from them, including primitives like numbers. (For those language-savvy folks, every Quest object is a singleton object.)

# define the `square` method on Numbers in general.
Number.square = self -> { self ** 2 };

twelve = 12;
print(twelve.square()); # => 144

# define the `cube` method on this instance of 12.
twelve.cube = self -> { self ** 3 };
print(twelve.cube); # => 1728

# no other `12` in the program has access to the `cube` method.
print(12.__has_attr__('cube')); # => false

More

See the examples folder for more examples of what Quest can do!

There's also some stuff I've written up in the docs that goes more into depth.

Installation

  1. Clone the repo
  2. If you haven't already, install Rust and cargo
  3. Run $ cargo build to create the project
  4. ./quest [-h] [-f file] [-e script] [-- [args to pass to the quest program]]
    • Command-line arguments are passed in the __args__ method in the base script object.

If all arguments are omitted a REPL instance will be launched.

TODO

I should probably add more discussion of Quest's features.

Misc

EBNF

PROGRAM := <block-inner>

expr
 := <primary>
  | UNARY_OP <expr>
  | <expr> BINARY_OP <expr>
  | <expr> <block> # Function call
 ;

primary := <block> | <literal>;
block := '(' <block-inner> ')' | '[' <block-inner> ']' | '{' <block-inner> '}';
block-inner := (<line>;)* <line>?;
line := (<expr>,)* <expr>?;

literal := <ident> | <number> | <string>
ident := ...;
number := ...;
string := ...;

quest's People

Contributors

fennecdjay avatar sampersand 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

Watchers

 avatar  avatar  avatar  avatar

quest's Issues

Making `SharedCow` more uniform

Currently, there are a few ad-hoc implementations of the same general concept spread throughout (eg Parents).

If the SharedCow pattern could be emulated in more places, it definitely seems like it'd be a positive.

N.B: I'm not using the std::borrow::Cow intentionally: It doesn't allow for "ToOwned to be an Arc. The idea is we have a type T that implements Clone, and we either have two states:

  1. "Owned", where the owner can read-and-write to the data
  2. "Shared", where no one can write to the data—if someone wants to, they have to clone it to modify it.

While this does leave us with the possibility of a single instance of an object having "Shared" data, that doesn't seem to be that common.

Figure out the `&&` and `||` operators

These have been in limbo for a while, and a final verdict on them should be reached before Alpha.

Due to the lack of "special" operators in Quest, and that all values are eagerly evaluated (however see #24), their traditional "short-circuit" functionality isn't possible (well, it technically is if you do expr || { expr }, but that's just a huge code hack).

So, they've been operators for a while, but I should either create special semantics for them, or remove them in favor of if($t=EXPR, $t, { EXPR })

Create builtin importing

The way to "import" files right now is actually to evaluate their contents. That is, to import frac.qs, you do

# I/O isn't done yet
system("cat", "foo.qs").$eval();

This is both inefficient (it has to load the entire file, and then parse it), as well as quite possibly dangerous. So having a builtin way to do this would be nice, eg import("foo.qs") (or even just import("foo")).

Proposal to Improve Quest's Binary Operator semantics

Proposal to Improve Quest's Binary Operator semantics.

Currently in Quest there are Unary and Binary operators which collide in names. This is an issue for both those using and implementing Quest.

The-Problem

Operator Unary Binary issues
- -@ - It is not clear that -@ is for Unary minus. It is often incorrectly implemented
+ +@ + It is not clear that +@ is for Unary plus. It is often incorrectly implemented
! ! No Such Operator Inconsistent: Unlike the other Unary ops it does not get the @
~ ! No Such Operator Inconsistent: Unlike the other Unary ops it does not get the @

I brought this issue Up in the Quest Discord channel with the code Number."-" = Number::"+"; disp(---1).
@sampersand was not able to immediately see that my intent to do Number."-@" = Number::"+"; disp(---1).

Proposal

  1. Rename the +@ -@ + and - methods like this.
  2. Give + and - new implementations that call them

Renames

Old Name New Name
+ +/2
+@ +/1
- -/2
-@ -/1

this is inspired by this erlang and elixr syntax.

This would however make the method + and - not obsolete.
The new behavior for them would be to dispatch to their /1 and /2 counterparts.

Implementation

You can test this out by pasting unary.qs into your code.
A polyfill working in current Quest can be found in this gist

configuring the polyfill

unary_polyfill_config.typename is the name of the Unary class that implements variadic '-' and variadic '+'
unary_pollyfill_config.aliaes is what to rename to what, currently it uses the table given here
unary_polyfill_config.debug debug function. The debug function can be set to disp to show a debug that matches /[\+-]\[-?[0-9\.]+, -?[0-9\.]+\]/.

This table shows what is called on each operation

Expr Evaluated Functions Result
42 - 7 Unary::'-'(42, 7) Number::'-/2'(42, 7) 35
-101 Unary::'-'(101) Number::'-/1'(101) -101
2+2 Unary::'+'(2, 2) Number::'+/2'(2, 2) 4
+1337 Unary::'+'(1337) Number::'+/1' 1337

Non-eager evaluation

Hm.. it might be interesting to think about quest without eagerly evaluating everything?

This would work well with the block paradigm, and blocks wouldn't be so ubiquitous. But that would require a massive overhaul of, well, everything, so that might be a long-term goal, or just something to think on

Remove the `[]` and `{}` operators

Way back when, they were used for different ways to call blocks (e.g. foo ( ... ) vs foo [ ... ]), but I've sinced moved away from that.

However, they're still a vestige and should be removed.

Rething parsing?

Somewhat related to #21 , it might be prudent to rethink how we're parsing things.

Originally, I wanted everything to either be a literal, an operator, or a parenthesis—this included ; and , as operators! However this had a lot of issues:

  • It made calling functions a nightmare (you'd always have to remember to do sqrt(4,), because otherwise the first argument was converted to an array implicitly.
  • It was inefficient, as the ; operator had to be executed for every statement
  • There was no purpose for it asides from novelty—why on earth would you want to change how statement execution works? I guess you could try to hack on some type of GOTO functionality, but that's just dumb.

Now ; and , are their own tokens. However, I'm still coming across issues that arise from parsing, such as #8 and #21. While it was nifty to have =, ., and .= be operators, it might be time to bite the bullet and accept that it's not feasible to use them?

Alternatively, we could have special semantics for those operators? who knows.

I'd prefer to keep them in if at all possible, because they add a nice flavor of "you can do what!" to Quest that just would be missing without them.

Rethink how parsing occurs

Somewhat related to #21 , it might be prudent to rethink how we're parsing things.

Originally, I wanted everything to either be a literal, an operator, or a parenthesis—this included ; and , as operators! However this had a lot of issues:

  • It made calling functions a nightmare (you'd always have to remember to do sqrt(4,), because otherwise the first argument was converted to an array implicitly.
  • It was inefficient, as the ; operator had to be executed for every statement

Cleanup `Text::eval`

Currently, the evaluating text code is a bit of a mess. I'd like to clean it up.

Text::eval will evaluate the text in the scope of whatever object is passed to it. However, if no object is passed to it, it evaluates it in the current scope. Figure out a way to just let the current scope be passed by default?

For reference, the mess I'm talking about is (within exec/main.rs, though this might change if #1 gets solved):

Text::mapping().set_attr_lit("eval", RustFn::new("Text::eval", |this, args| {
	fn execute_text(text: String) -> quest_core::Result<Object> {
		quest_parser::Expression::parse_stream(BufStream::from(text).tokens())
			.map_err(|err| err.to_string())?
			.execute()
			.map_err(Into::into)
	}

	let this = this.try_downcast_ref::<Text>()?;

	if let Ok(binding) = args.arg(0) {
		Binding::new_stackframe(Some(binding.clone()), args, |_| execute_text(this.to_string()))
	} else {
		execute_text(this.to_string())
	}
}));

should `while` introduce a new scope _above_ the body?

If it did this, we could use return as a break statement. Currently, if you want to, you need to wrap the while in a block, eg:

$i = 0;
disp({
	while({ i < 10 }, {
		if(i == 5, { return(:2, i ** 2) })
	})
}()); # => 25

However, adding a new block would possibly confuse users who would think only one new block would need to be added

Non-UTF8 characters

For some reason, non-utf8 characters completely stop the parsing after the line they're on.
Example:

$¢=3;disp(¢);
disp(4); # never prints

and

disp("¢");
disp(4); # never prints

Add in stacktrace

Currently, Quest has no stacktrace when an exception occurs. Add this in?

Roadmap

  • Fairly simple, just add a field on the quest_core::Error type and read from Binding

Create Iterator

Create an Iterator mixin and include it on all the basic types

  • Create a base Iterator and the map method
  • Implement each for appropriate object types
    • Text—iterate over chars
    • List—iterate over list items
    • (?) Number—iterate over digits
  • Write out the rest of the iterator methods
  • Include it in the Kernel
  • Documentation!

Questions

  • Should we implement optimized versions of these methods for our iterators?
  • Do we want a way to iterate over keys? Maybe something like __for_each_key__()?

Kernel's `for` and `loop`

Should these both exist? Currently, they're both defined within Kernel (although for just panics because it's not implemented).

They can both easily be implemented in terms of while—e.g. while($true, { ... }) for loop.

Advantages

  • Makes us more like other languages, which means Quest is easier to pick up
  • Can probably marginally increase performance, but not by a whole lot.

Disadvantages

  • Moves away from the whole "Use the fundamental building blocks" theme I was going for.

Should Objects return `null` instead of raising an error

Should Objects return null instead of throwing an exception if an object that's indexed doesn't exist?

By returning null we allow for some nifty tricks like $MAX = if(_0, _0, 100), but at the same time it can hide errors in your implementation, especially since null responds to most things with null itself.

Add in I/O handling (including for network requests)

Currently, the only way to do anything with I/O is to use system, eg $foo_contents = system("cat foo")... not ideal.

It'd be best if we could make some object that represents all i/o interactions, or possibly somehow have Text pretend to be it? (That'd be preferred, as it'd keep the interface easier, but it might be too difficult to implement.)

Add in some form of "case" statements

Right now, Quest doesn't have an idea of case statements.

Instead, the best you can do is by simply indexing into a map, eg { $red = "stop!"; $green = "go!" }.$green }. We might want to make a case statement that would do this for us?

This is a long-shot too, as it doesn't seem all that useful.

Add Unit and E2E tests

Both of these are crucial for further development, and should be done by no later than alpha.

Roadmap

Quest Core

  • Unit tests
    • "Core" objects
    • Helper objects
    • Other things
  • E2E Testing
    • Ensure the Object:*_attr family of functions all work correctly
    • <find more stuff to include?>

Quest Parser

  • Unit Tests
    • Stream-to-token unit testing
      • Primaries
      • Parens
      • Tokens overall
    • Token-to-expression unit testing
      • Primaries map correctly
      • Blocks are parsed correctly
  • E2E Testing
    • ??

Quest Bin

  • Unit Tests
    • Command-line argument parsing
    • ??
  • E2E Testing
    • ??

E2E Overall

  • Write some test files and ensure they're executed properly?

Cleanup `ObjectType` macro code

Currently the macro is a hodgepodge of legacy code stuck together.

It should ideally be made as simple as possible, possibly with a add_attr method or something on ObjectType?

Probably relates to #1

Rethink `BoundFunction`s

The BoundFunction concept is a bit of a hack, and should probably be rethought before the Beta release.

It originally arose out of a need to not explicitly pass a this parameter everywhere (e.g. (my_list::$push)(my_list, 12)). So, I created the . operator—or rather, hijacked it from its previous purpose. It behaves exactly like the :: operator except: If the retrieved attribute is a RustFn, BoundFunction, or it's Debug starts with Object(Block (extremely hacky...), a new BoundFunction is created. blech

That mostly solved the problem, except calling functions without accessing them via a dot (e.g. foo(1, 2)) would still use the old method. So I effectively made it so whenever something's looked up normally as well, it effectively does __this__.<WHATEVER>.

While this makes the object oriented part of the language much more tolerable, it makes the functional aspect much more difficult. Now, if you want to pass functions around (e.g. the manual each or select implementations I made), you have to __get_attr__ them:

$Enumerator = {
	$map = {
		$ret = [];
		$fn = _1;
		_0.$each({ ret.$push(__get_attr__($fn)(_1)) });
		ret
	};

	$select = {
		$ret = [];
		$fn = _1;
		_0.$each({ if(__get_attr__($fn)(_1), { ret.$push(_1) }) });
		ret
	};

}();

While the stack literal syntax helped a bit (:0::$fn), it still is just dodging the core issue of the automatic conversions.

How does this get solved before the Beta release? My best guess would be to only keep the automatic BoundFunction thing for actual . uses—that is, foo(1,2) would no longer be __this__.$foo(1,2), but rather __this__::$foo(1,2).

This still has some holes, so I'm not sure what the best way to go about it is.

Add in REPL

Currently the REPL doens't actually exist.

This would be extremely useful for testing simple expressions and stuff.

It shouldn't be too hard to get a dumb REPL working; in the future, autocompletion/colors and stuff could be added to make it look better

Changing `$...` syntax to be evaluating text

Currently, variable literals are written as varname, whereas the $... syntax is another way to write out Text literals.

I propose we flip the two around, and have unquoted variable names be interpreted as text as well, and $... be reserved for variable literals. I thought originally it might be ok with the way I have it because BASH does it this way, but I realize now that basing language design off bash isn't the best idea 😛

Roadmap

  • Update the two literals within the parser
  • Run the (few) tests I have :/
  • Update example code

Advantages

  • Vastly removes visual clutter (reading/writing attributes is more common than evaluating them).
  • Reduces confusion. It makes more sense to access attributes by their names, and not with a random sigil in front. (Plus, the intent is more obvious when you say $memo.$n than memo.n)

Issues

  • What to do with stack literals? should those be still :# or $:# now?
  • We end up with somewhat-weird $disp syntax.
  • We lose the advantage of $+ = { ... } and have to write '+' = { ... }

Example

$Number.square = { _0 ** 2 };
$disp( 4.square() );

$Number.factorial = {
	memo = { :0 }();
	$memo.0 = 1;
	{
		if($memo.__has_attr__($_0), {
			$memo.$_0
		}, {
			$memo.$_0 = ($_0 - 1).factorial() * $_0
		})
	}
}();

$disp('5! =', 5.factorial());
$disp('10! = ', 10.factorial());

Implement a form of `try ... catch`

Currently, exceptions are just thrown all the way to the outermost layer (except for Return).

They should be accessible from within quest, and be catchable. This shouldn't be too hard to do.

Roadmap

  • Make quest_core::Error a ObjectType and implement basic methods
  • Make each of the error derivatives (eg KeyError) also a ObjectType
  • Have a way to catch errors (e.g. a function called catch)
    • To keep things simple now, we might just want to have it take two blocks, the first is what to execute, the second is the rescue block
    • Have a way to distinguish between errors (eg #14)
  • Have a way to throw errors (e.g. a function called throw)
    • Should we be able to throw any arbitrary object, or must they be an error type?
    • Should we hijack return and instead do return(KeyError("err")) and have the catch block try catching KeyErrors?

Should Lists have `get` and `set`

Should List (and to some extend, Text) be redesigned to have .0, .1, ... to access elements instead of get and set?

It's a bit weird that Lists are the only compound data type in quest, and it might make more sense to have them be represented as simply a map of numbered values. (Although storing them internally as a Vec would still be ideal, as it's much more efficient)

Remove `ArgsOld`

The ArgsOld struct is a vestige of times past, where a "self" parameter wasn't explicitly passed and always had to be checked.

Custom `type_name` function

Add a custom TypeName trait that can be used to reference types within Quest code.

Currently, std::any::type_name is used, but that also reveals internal implementation details (eg quest_core::types::Boolean), and so there should be a dedicated type

Quest attributes

Instead of manually defining qs_methodname on every object, why not instead make a MethodName trait?

This would reduce a lot of the fragility when it comes to traits and Literals. For example:

pub trait Inspect {
	const METHOD: Literal = "__inspect__";

	fn call(this: &Object, args: Args) -> Result<Text>;
}

Regex and Range literals

Should we have Regex and Range literals?

On one hand, it moves us further away from the elegance and simplicity of quest, but on the other it is quite useful.

I'm leaning towards Regex literals via /.../ syntax, but I'm not so sure about ranges: They can be represented/created with upto methods

Completely finish all documentation

  • Quest Documentation

    • README
    • Documentation for object types
      • Core Types
        • Boolean
        • List
        • Number
        • Null
        • Text
        • Kernel
        • Pristine
        • Basic
      • Mixins and helpers
        • Rustfn
        • BoundFunction
        • Comparable
        • Function
        • Scope
    • Figure out how to host docs somewhere other than RustDocs (Future?)
  • Rust Documentation

    • Document how the internals of things work
    • Link everything!

Make a "parent iterator" internally

Make an iterator that goes through the parents and keeps track of which ones we've seen so far (via Object::is_identical).

This will make a lot of other code easier, including things like #14 and the __keys__ method.

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.