Git Product home page Git Product logo

hummingbird's Introduction

Hummingbird

Build Status

This is an experimental language.

For the previous JavaScript implementation which compiled to JavaScript or binary-via-LLVM check out the legacy-2015 branch.

Getting started on macOS

One-time setup:

# Install LLVM v8
brew install llvm@8

When starting work in a new shell:

# Set up your environment so that the llvm-sys crate can discover Homebrew's
# installation of llvm@8.
source script/env-mac

Building and testing:

cargo build
cargo test

License

Released under the Modified BSD License. See LICENSE for details.

hummingbird's People

Contributors

dirk avatar horak 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

hummingbird's Issues

Classes

How do we go about this? This is what I was expecting:

class SkinnedMesh extends THREE.Mesh {
  var idMatrix: THREE.Matrix4 = SkinnedMesh.defaultMatrix()
  var bones: Array<Any> = []
  var boneMatrices: Array<Three.Matrix4> = []
  var idMatrix: THREE.Matrix4 = SkinnedMesh.defaultMatrix()

  init(geometry: Matrix, materials: Matrix) {
    super(geometry, materials) # translates to => super.init(geometry, materials)
  }

  update(camera: Any) {
    # use camera
    super() # translates to => super.update()
  }

  get boneCount() {
    # Computed property, can be returned with mesh.boneCount
    return this.bones.length
  }

  set matrixType(matrixType: String) {
    # Setter for matrix type
    this.idMatrix = SkinnedMesh[matrixType]()
  }

  static defaultMatrix() {
    # Creates a class method
    # http://odetocode.com/blogs/scott/archive/2015/02/02/static-members-in-es6.aspx
    return new THREE.Matrix4()
  }

}

Translates to:

ES6

class SkinnedMesh extends THREE.Mesh {
  constructor(geometry, materials) {
    super(geometry, materials);

    this.idMatrix = SkinnedMesh.defaultMatrix();
    this.bones = [];
    this.boneMatrices = [];
    //...
  }
  update(camera) {
    //...
    super.update();
  }
  get boneCount() {
    return this.bones.length;
  }
  set matrixType(matrixType) {
    this.idMatrix = SkinnedMesh[matrixType]();
  }
  static defaultMatrix() {
    return new THREE.Matrix4();
  }
}

ES5

"use strict";

var _createClass = (function () { function defineProperties(target, props) { for (var key in props) { var prop = props[key]; prop.configurable = true; if (prop.value) prop.writable = true; } Object.defineProperties(target, props); } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

var _get = function get(object, property, receiver) { var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc && desc.writable) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };

var _inherits = function (subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; };

var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };

var SkinnedMesh = (function (_THREE$Mesh) {
  function SkinnedMesh(geometry, materials) {
    _classCallCheck(this, SkinnedMesh);

    _get(Object.getPrototypeOf(SkinnedMesh.prototype), "constructor", this).call(this, geometry, materials);

    this.idMatrix = SkinnedMesh.defaultMatrix();
    this.bones = [];
    this.boneMatrices = [];
    //...
  }

  _inherits(SkinnedMesh, _THREE$Mesh);

  _createClass(SkinnedMesh, {
    update: {
      value: function update(camera) {
        //...
        _get(Object.getPrototypeOf(SkinnedMesh.prototype), "update", this).call(this);
      }
    },
    boneCount: {
      get: function () {
        return this.bones.length;
      }
    },
    matrixType: {
      set: function (matrixType) {
        this.idMatrix = SkinnedMesh[matrixType]();
      }
    }
  }, {
    defaultMatrix: {
      value: function defaultMatrix() {
        return new THREE.Matrix4();
      }
    }
  });

  return SkinnedMesh;
})(THREE.Mesh);

Implement basic closure syntax

func () with (a, b) { ... } defines a closing function that only has access to the outer linkages a and b. The type-system will enforce this behavior and disallow any other closing of linkages into the function's scope.

Specification

Including the special CommonMark syntax addition:

Lorem ipsum.

<spec name="basic-variables">
``hb
var a: String = "1"
let b: Integer = 2
``
``js
var a = "1";
var b = 2;
``
</spec>

Lorem ipsum.

Double-backticks in the above example should be triple-backticks in actual specification documentation.

`let` already has semantics in javascript

let allows you to declare variables, limiting its scope to the block, statement, or expression on which it is used. This is unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.

Block scope with let

The let keyword can also be used to define variables inside a block.

if (x > y) {
  let gamma = 12.7 + y;
  i = gamma * x;
}

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

Add browser builds and source-map support

Use a build system (Gulp?) to bundle all the files together into a single file that can be loaded in a browser. Also take parser metadata in the AST and generate a source-map for the output JavaScript.

  • Browser builds implemented with browserify as of 177cd4d
  • Source-map support

Why use this over TypeScript?

I think we should be able to explain why this would be superior to TypeScript (and if it isn't, try to determine how we can improve on what TypeScript and AtScript already provide).

Decide on exception handling

Need to figure out a couple things in this:

  1. How do exceptions fit into the type system? Do we want to enforce that they're all subclasses of a certain parent class (Error, Exception, etc.)?
  2. What are the possible ways to catch exceptions? Do we want to do exception-type-specific catching (ie. only catch SpecialException) natively in the language?
  3. What should the syntax for exceptions look like?

Init needs space between keyword and ()

class Gonzalez {
  var dude: Integer

  init() {
    this.dude = 2
  }
  func compute() {
    return "YO"
  }
}

var ryan = new Gonzalez()

Produces an error:

SyntaxError: Expected " ", "#", "%", "*", "+", "-", "/", ";", "<", "==", ">", "\n", "\t" or "||" but "{" found.
  at line 4, column 10

Converting init() to init () fixes the issue.

Type downcasting and overwriting

This needs to be figured out before we can move forward with implementing unions.

A downcast takes an instance of a union of types (eg. 'Union<Number, Void>) and reduces it to an instance of just one of those types (eg.'Number such as "1" in JavaScript or 'Void which is "null" in JavaScript). This is inspired by the downcasting system in Swift.

An overwrite takes the type of any expression and overwrites it with a new type.

Downcasting use case

The downcast is when you're working within the type-system and want it to keep an eye on you. For example it would be very useful for dealing with results from some failable external resource:

let result = User.findByID(1) # => 'Union<Error, User>
if result isa Error {
  console.log("Error finding user: "+(result as Error).toString())
  return
}
let user = result as User
# do stuff with the user

The thing to note here is that the type-system is still watching over your shoulder, so if you tried to do a result as SpaceInvader it would throw an error since SpaceInvader is not in Union<Error, User>.

Overwriting use case

Overwriting is for when you're dealing with external libraries or input and need to manually clue in the type-system to what's going on.

let result = OutsideLibrary.someWeirdCall() # => 'Any
let data = result is ComplexData # => 'ComplexData

Block as argument or block as result type signature

When defining a function that takes a block or returns a block, what should the type signature for the block look like in the function definition?

Swift does this:

func jediTrainer () -> ((String, Int) -> String) {
  func train(name: String, times: Int) -> (String) {
    return "\(name) has been trained in the Force \(times) times"
  }
  return train
}
let train = jediTrainer()
train("Obi Wan", 3)

I don't see any reason for us to do anything differently. Only issue I can see is that this could lead to overlong function signatures.

Design standard library

Includes general conventions as well as specific APIs necessary to support writing for environments outside the browser/Node.js/io.js.

Notes

All of the standard library will be under the std module. This name is short, readable, and its presence will help prevent naming conflicts for any 3rd-party modules (ie. packages) that may duplicate and/or go beyond the functionality of the standard library.

Standard library

  • std
    • core: This is included automatically by the compiler without the need for an import.
      • types: These provide the functions used by the compiler for common operations (ie. string concatenation, non-trivial mathematical operations, etc.)
        • integer, float, boolean, string
    • io: Basic input-output primitives (streams)
    • fs: File-system operations (opening, modifying, statuses, directories, path manipulation, etc.)
    • net: Basic networking (sockets)
      • dns, http, etc.
    • crypto: Cryptography (hashing, encrypting, etc.)
    • os: General operating system-related features
      • unix, darwin, etc.: Platform-specific features and APIs
    • data: Modules for working with data
      • zlib, bz2, csv, etc.
    • time: Working with time
    • runtime: APIs related to working with the program itself while it's running
      • gc: Sometimes you need to suspend/trigger the garbage collector manually!

Create bootstrapping file for built-in types

Like for console:

  var consoleType = new types.Object(this.root.getLocal('Object'))
  consoleType.intrinsic = true
  consoleType.name      = 'BuiltinConsole'
  // Create the `log(...)` function and add it to the console's type
  var consoleLogFunction = new types.Function(rootObject, [this.root.getLocal('Integer')])
  consoleType.properties['log'] = consoleLogFunction 
  // Create a faux instance of the console and add it to the root scope
  var consoleInstance = new types.Instance(consoleType)
  this.root.setLocal('console', consoleInstance)

Write README

  • What is Hummingbird
  • How to install
  • How to contribute

Multiple files, modules, require, etc.

We eventually need to get to the issue of how we want to handle multiple files. There are, as I see it, a few approaches to this:

1. Copy Node.js

This is the most brittle approach, however it also ties in easiest with our JavaScript target since all source files can be compiled directly to corresponding JavaScript files.

The big downside to this approach is that require and module.exports provide zero type information, so any uses of values from a required(...) module (internal or external) would have no type information (just a ton of Any types).

We could perhaps develop a hybrid system on top of this. Where Hummingbird files would be loaded with a different keyword/syntax and those files would expose their exported values via a complementary interface. For example:

// Importing Node module and local Hb module
var express = require("express")
var something = import("./something.hb")

let Foo = "bar"
func baz () -> String { return "#yolo" }

// Exporting something in this file
// (compiled down to module.exports)
export Foo, baz

2. Roll our own with different design

Node enforces strict boundaries between files, but we don't necessarily need to follow the same path. I'm very open to a "unifying" system that compiles files together into a unified space instead of the deep tree of modules that Node builds.

The disadvantage of this approach is that we don't always get the same file-to-file compilation path as the former. The latter is that we can decide exactly how we want to design our system free from any bounds imposed by Node.js or any other JavaScript runtime environment. Although this means more work for us and less closely hewing to the JavaScript target, I'm in favor of it for the freedom that it gives us to design our system, APIs, and ability to target other runtimes (PyPy, native, etc.).

A trivial, contrived example off the top of my head:

// Only available when targeting Node.js compilation
var express = require_node("express")
// Loading from Hb package system and local file
var something = import("something")
var other = import("./other")

let Foo = "bar"

export Foo as FOO

Final note: import() vs export

In these examples I use import() with parentheses and export without. The reasoning for this is that, in my mind, importing is an imperative action: you are telling the compiler to do something right now. In contrast, exporting is a declarative action: you are telling the compiler something about this file. This is just a thought and I am in no way attached to these designs.

Build website

  • Introduction (excerpt of README)
  • Specification (need to generate nice HTML from the specification file)
  • Manual/documentation (need to determine structure of this)

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.