1.0.3 • Published 8 years ago

bizubee-compiler v1.0.3

Weekly downloads
3
License
MIT
Repository
github
Last release
8 years ago

Bizubee

Bizubee aims to preserve JavaScript semantics while simultaneously simplifying and extending JS. Bizubee syntax is largely inspired by CoffeeScript and Dart, though the semantics differ significanty from both. Unlike most compile-to-js languages, the Bizubee compiler targets modern JavaScript (currently ES2015), since transpilers like Traceur can further transpile for support in legacy browsers.

Additions and deviations from JS:

Line Breaks

Whereas in JS line breaks without semicolons lead to strange behavior, in Bizubee line breaks signify a new line unless they are found adjacent to binary operators and other special cases. This means semicolons are unnecessary, but still usable in Bizubee. So the following JS if statement

console.log("a is greater than b!");
console.log("a is greater than b again!");

can be written as

console.log("Hello,")
console.log("World!")

in Bizubee

Variables

In Bizubee the a var declaration is equivalent to the let declaration in modern JavaScript, and is therefore block scoped. A const declaration in Bizubee is also block scoped like in modern JS.

so

if true
	var a = 7
    const b = 6
    
console.log(a)
console.log(b)

prints undefined twice because a and b are both local to the block scope.

No Unnecessary Parentheses

In JS an if statement requires parentheses around the boolean expression, but in Bizubee

if (a > b) {
	console.log("a is greater than b")
}

can be written as

if a > b {
	console.log("a is greater than b")
}

Note that since the conditional is an expression it can still be wrapped in parentheses in Bizubee.

Similarly, a try-catch statement from JS that would be written as

try {
	doSomethingRisky();
} catch (e) {
	recoverFromError(e);
}

is instead written as

try {
	doSomethingRisky()
} catch e {
	recoverFromError(e)
}

The parentheses are also omitted from for-loops and while-loops.

Optional Indentation

try-catch, if-else, function blocks and other similar code blocks can use either curly brackets or indentation as block delimiters. For example in Bizubee

if a > b {
	console.log("a is greater than b")
} else {
	console.log("a is no greater than b")
}

is equivalent to

if a > b
	console.log("a is greater than b")
else
	console.log("a is no greater than b")

In a curly bracketed block indentation is ignored, though line breaks are still not. In indented blocks neither line breaks or indentation are ignored.

For-loops

The semantics of for loops in Bizubee differs significantly from JS For loops. There are only two types of for loops, for-in loops and for-on loops. The for-in loop is equivalent to the for-of loop in modern JS (as of ES2015). So

for i in range(0, 10) {
	console.log("i is now ${i}")
}

is equivalent to the folowing in JS

// Note that this code uses ES2015 JS features
for (let i of range(0, 10)) {
	console.log(`i is now ${i}`);
}

C style for loops and JS for-in loops have no special syntactic support, however the keys function from the "bizubee lib" library iterates over the keys of the object.

import {keys} from bizubee lib

const myObject = {
	a: 1
	b: 4
}

for key in keys(myObject)
	console.log(key)

Produces

a
b

For-on loops are the async equivalent of for-in loops. Whereas a for-in loop iterates over an iterator, a for-on loop iterates over an async-iterable. Async-iterables are useful for iterating over async sequences of data.

for packet on tcpConnection {
	processPacket(packet)
}

Functions

Bizubee functions come in 8 flavors, but the simplest function for is defined as

const divide = (numerator, denominator) -> {
	return numerator / denominator
}

whereas in JS this would be

const divide = function(numerator, denominator) {
	return numerator / denominator;
};

Just like Coffeescript (and ES2015 JS) there is also a fat arrow function type in Bizubee

@numerator = 5
@denominator = 10
const myDivide = () => {
	return @numerator / @denominator
}

which works just like in Coffeescript (and now JavaScript!), by preserving the this binding of the parent scope.

Just like ES2015, Bizubee has support for generator functions. A function is turned into a generator function by adding an asterix after the function's arrow (-> or =>) as in

const range = (start, end) -> * {
	var i = start
	while i != end {
		yield i
		i += 1
	}
}

Bizubee also supports async functions using a ~ after the function's arrow.

An async function that fetches some html via HTTP and returns it parsed might look like

const getHTMLTree = (url) -> ~ {
	var string = await getStringFromUrl(url)
	return parseHTML(string)
}

The await operator works on any object following the Promise/A+ spec. When await is encountered, function execution blocks until the promise argument is resolved, then the function continues with the resolution value taking the place of the await expression, if the promise is rejected an error is thrown which can be handled via usual error handling techniques. What gets returned by an async function call is not the return value of the function but rather a promise that resolves to the return value.

The 3rd function variation is the async-generator, a function is defined as an async generator by placing a ~* to the right of the arrow, for example one could write an async generator that reads stock valuations for some company, and yields changes.

const getStockPriceChanges = (company)-> ~* {
	var previous
	var priceURL = "${baseURL}?company=${company}"
	while true
		var price = await getNextPrice(priceURL)
		if defined(previous)
			yield price - previous
		previous = price
}

Calling an async-generator returns an async iterator, this means one can iterate over the price changes above with a for-on loop

for change on getStockPriceChanges('AOL')
	doSomethingWithChange(change)

Note that for-on loops can only exist within async functions, in fact the above example is just sugar over

const asyncIterator = getStockPriceChanges('AOL')
while true
	const controller = await asyncIterator.next()
	if controller.done
		break
	const change = controller.value
	
	doSomethingWithChange(change)

Notice the await in the desugared example.

Function Declarations

Much like JavaScript, Bizubee supports function declarations, so

myFunc() -> {
	doSomething()
}

is equivalent to

const myFunc = () -> {
	doSomething()
}

However, all functions declarations are bubbled to the top of their scope, so functionally they behave like JavaScript's function declarations in that

myFunc()

myFunc() -> {
  doSomething()
}

will run, where as

myFunc()

const myFunc = () -> {
  doSomething()
}

will throw an error.

Modules

Module semantics in bizubee is an ever expanding subset of the ES2015 module specification

Exports

Variable and constant declarations, function, and class declarations can be exported with their values also being accessible in the current file.

export var a = 1, b = 4		# exports names a and b
export const c = 3, d = 5	# exports names c and d

export someFunc() -> {		# exports name someFunc
	return c + b
}

export class SomeClass {	# exports name SomeClass
	constructor(a) -> {
    	@someProp = a
    }
}

console.log(someFunc()) # prints 8 cause names are accessible in file too

One can also export names explicitly

var a = 1, b = 4
const c = 3, d = 5

someFunc() -> {
	return c + b
}


export {a, b, c, d, someFunc} # exports names of all variables declared

One can optionally set the default value of the export, if no value is provided, a module object with all the names as properties is exported as the default value

export var 1 = 5

export func() -> {
	doSomething()
}

export default () -> {		# default value for export
	doSomethingCool()
}

Imports

Exported values can be accessed in the following manner

# import names from someFile
import {a, b} from ./someFile	# if we export a, b from file 'someFile'

# import aliased names
import {c as myName, d as myOtherName} from ./someFile

# import default values from someFile
import defaultValue from ./someFile

const c = doSomethingWith(b, myName)

defaultValue(c) 		# if defaultValue is a function

Note that "./someFile" is imported multiple times, but the file is evaluated only once, with its module object cached, so each import is based on the same module object.

Usage

	$ bizubee -c bizubee/file/path.bz 				# to compile file
	$ bizubee bizubee/file/path.bz                 	# to execute file
	$ bizubee bizubee/file/path.bz <arguments>     	# to execute file with arguments
	$ bizubee <options> bizubee/file/path.bz <arguments>    # to add runtime args

Options

    -c,	--compile   	Compile bisubee file and all dependencies into single file
	-t,	--target    	Specify target for file compilation output (defaults to <filename>.js)
	-m,	--mapfile   	Specify custom mapfile name when compiling
	-v,	--version   	Show version of bizubee
	-h,	--help      	Shows this list of commands and information

Note that with static compilation (-c), all dependencies are resolved statically and dumped into a single file. This is generally the prefered options to use when targeting the browser, whereas execution can be used for other platforms.