jsy-lang / jsy-lang-docs

JSY is an indented (offside) JavaScript dialect. We believe indentation is better at describing code blocks.
https://jsy-lang.github.io
Other
9 stars 1 forks source link

JSY Language Documentation

JSY is an indented (offside) JavaScript dialect. We believe indentation is better at describing code blocks instead of painstakingly matching open/close sections {} () [].

Think modern JavaScript — ES6, ES2018 — indented similar to Python or CoffeeScript.

Inspired by Wisp, JSY primarily operates as a scanner-pass syntax transformation to change indented (offside) code into the corresponding open/close matching token code. Thus the internal scanning parser only has to be aware of /* comments */ and "string literals" rules to successfully transform code. Thus, as a JavaScript dialect, JSY automatically keeps pace with modern JavaScript editions!

Quick Start

Start with rollup-plugin-jsy-lite or use the playground!

Sample JSY code:

// JSY
const apiUrl = 'http://api.example.com'

class ExampleApi extends SomeBaseClass ::
  constructor( credentials ) ::
    const apiCall = async ( pathName, body ) => ::
      const res = await fetch @ `${apiUrl}/${pathName}`, @{}
        method: 'POST'
        headers: @{}
            'Content-Type': 'application/json'
        body: JSON.stringify @ body

      return await res.json()

    Object.assign @ this, @{}
      add: data => apiCall @ 'add', data
      modify: data => apiCall @ 'send', data
      retrieve: data => apiCall @ 'get', data

Reference

There are at-based (@), double colon-based (::), and keyword operators (if, for, while, etc.). All operators wrap until the indentation is equal to or farther out than the current line, similar to Python or CoffeeScript. We refer to this as the indented block. For example:

  // JSY
  function add( a, b ) ::
    return a + b

The double colon :: in the preceding example opens a brace { when used, then closes the matching brace } when the indentation level matches that of the line it was first used on. The indented block is the return statement.

Implicit Leading Commas

Commas are implicit at the first indent under any @-prefixed operator.

// JSY
console.log @
  "the"
  answer, "is"
  42

Translated to JavaScript:

// JavaScript
console.log(
   "the"
 , answer, "is"
 , 42 )

Explicit commas are respected.

// JSY
console.log @
    "or use"
  , "explicit commas"

Translated to JavaScript:

// JavaScript
console.log(
    "or use"
  , "explicit commas")

Operators

:: Double Colon – Code Blocks

The :: operator wraps the indented block in curly braces {«block»}.

function add( a, b ) ::
  return a + b

Translated to JavaScript:

function add( a, b ) {
  return a + b
}

@ At – Calling Functions

The @ operator on its own wraps the indented block in parentheses («block»), where commas are implicit.

// JSY
console.log @
  add @ 2, 3

Translated to JavaScript:

// JavaScript
console.log(
  add( 2, 3 )
)

@{} At Braces – Hashes

The @{} operator wraps the indented block in curly braces {«block»}, where commas are implicit.

// JSY
fetch @ 'http://api.example.com', @{}
  method: 'POST'
  headers: @{}
    'Content-Type': 'application/json'
  body: JSON.stringify @ body

Translated to JavaScript:

// JavaScript
fetch( 'http://api.example.com', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify( body )
})

@: At Colon – Calling with Hash

The @: operator wraps the indented block in parentheses wrapping curly braces ({«block»}), where commas are implicit.

// JSY
request @:
  url: 'http://api.example.com'
  method: 'POST'
  headers: @{}
    'Content-Type': 'application/json'
  body: JSON.stringify @ body

Translated to JavaScript:

// JavaScript
request({
  url: 'http://api.example.com',
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify( body )
})

@[] At Brackets - Lists

The @[] operator wraps the indented block in square brackets [«block»], where commas are implicit.

// JSY
const tri = @[]
  @[]  0.0,  1.0, 0.0
  @[] -1.0, -1.0, 0.0
  @[]  1.0, -1.0, 0.0

Translated to JavaScript:

// JavaScript
const tri = [
  [0.0, 1.0, 0.0],
  [-1.0, -1.0, 0.0],
  [1.0, -1.0, 0.0]
]

@# At Pound – Calling with a List

The @# operator wraps the indented block in parentheses wrapping square brackets ([«block»]), where commas are implicit.

// JSY
Promise.all @#
  fetch @ 'http://api.example.com/dosomething'
  fetch @ 'http://api.example.com/dosomethingelse'
  fetch @ 'http://api.example.com/dosomethingmore'

Translated to JavaScript:

// JavaScript
Promise.all([
  fetch('http://api.example.com/dosomething'),
  fetch('http://api.example.com/dosomethingelse'),
  fetch('http://api.example.com/dosomethingmore')
])

@=> At Arrow – Arrow Function Expression with No Arguments

The @=> operator wraps the indented block in parentheses and begins an arrow function (()=> «block»).

The @=>> operator wraps the indented block in parentheses and begins an async arrow function (async ()=> «block»).

// JSY
const call = fn => fn()

call @=> ::
  console.log @ 'Do cool things with JSY!'

Translated to JavaScript:

// JavaScript
const call = fn => fn()

call( () => {
  console.log('Do cool things with JSY!')
})

Asynchronous:

// JSY
const fn = @=>> await dothings()

Translated to JavaScript:

// JavaScript
const fn = async () => await dothings();

@:: At Block – Arrow Function Block with No Arguments

The @:: operator wraps the indented block in parentheses and begins an arrow function block (()=> { «block» }).

The @::> operator wraps the indented block in parentheses and begins an async arrow function block (async ()=> { «block» }).

// JSY
describe @ 'example test suite', @::
  it @ 'some test', @::>
    const res = await fetch @ 'http://api.example.com/dosomething'
    await res.json()

Translated to JavaScript:

// JavaScript
describe('example test suite', (() => {
  it('some test', (async () => {
    const res = await fetch('http://api.example.com/dosomething')
    await res.json()}) ) }) )

Arrow functions with Arguments

Keep in mind JSY does not change anything about the special case lambda expression with a single argument, for example:

// JSY
something @:
  evt: e => this.handle @ e

Would still require the "opener" @\ in order to declare more than one argument, eg.:

// JSY
something @:
  evt: @\ e, f => this.handle @ e, f

Other arrow expressions:

// JSY
// async
const fn = async e => await handle @ e

// multiple arguments
const fn_body = @\ a, b, c ::
  body

const fn_body = @\ ...args =>
  expression

// first argument object destructure
const fn_obj_body = @\: a, b, c ::
  body

const fn_obj_expr = @\: a, b, c =>
  expression

// first argument array destructure
const fn_arr_body = @\# a, b, c ::
  body

const fn_arr_expr = @\# a, b, c =>
  expression

Translated to JavaScript:

// JavaScript
// async
const fn = async e => await handle(e)

// multiple arguments
const fn_body = (( a, b, c ) => {
  body})

const fn_body = (( ...args ) =>
  expression)

// first argument object destructure
const fn_obj_body = (({ a, b, c }) => {
  body})

const fn_obj_expr = (({ a, b, c }) =>
  expression)

// first argument array destructure
const fn_arr_body = (([ a, b, c ]) => {
  body})

const fn_arr_expr = (([ a, b, c ]) =>
  expression)

::! and @! - Bang Immediately Invoked Expressions

The ::! operator wraps the indented block in a function, then invokes it {(() => {«block»})()} in a block with no return value.

The @! operator wraps the indented block in a function, then invokes it (() => {«block»})() as an assignable expression.

// JSY
const a_value = @!
  const a = 1
  const b = 2
  return a + b

::!
  console.log @ 'Starting server...'
  startServer @ '1337', ()=> console.log @ 'Server online.'

Translated to JavaScript:

// JavaScript
const a_value = ((() => {
  const a = 1
  const b = 2
  return a + b })())

{(()=>{
  console.log('Starting server...')
  startServer('1337', ()=> console.log('Server online.')) })()}

::!> and @!> - Bang Async Immediately Invoked Expressions

The ::!> operator wraps the indented block in an async function, then invokes it {(async ()=>{«block»})()} in a block with no return value.

The @!> operator wraps the indented block in an async function, then invokes it (async ()=>{«block»})() as an assignable expression.

// JSY
const promise_value = @!>
  const a = await Promise.resolve @ 1
  const b = await Promise.resolve @ 2
  return a + b

::!>
  console.log @ 'Starting server...'
  await new Promise @\ resolve ::
    startServer @ '1337', resolve

  console.log @ 'Server online.'

Translated to JavaScript:

// JavaScript
const promise_value = ((async () => {
  const a = await Promise.resolve(1)
  const b = await Promise.resolve(2)
  return a + b})())

{(async ()=>{
  console.log('Starting server...')
  await new Promise (( resolve ) => {
    startServer('1337', resolve) })

  console.log('Server online.') })()}

Generators

TODO

Keyword Operators

For keywords with expressions, when not followed by a paren, everything between the keyword and the double colon :: is captured as the keyword expression. When followed by a paren, parses as normal JavaScript.

No special parsing is done for keywords without expressions.

if/else, while, and do/while

// JSY
if a > b ::
  console.log @ 'JSY is the best!'
else if a < b ::
  console.log @ 'JSY rocks!'
else ::
  console.log @ 'JSY is still awesome!'

while 0 != q.length ::
  console.log @ q.pop()

do ::
  console.log @ 'It is a song that never ends...'
while 1

Translated to JavaScript:

// JavaScript
if (a > b) {
  console.log('JSY is the best!')
} else if (a < b) {
  console.log('JSY rocks!')
} else {
  console.log('JSY is still awesome!')
}

while (0 != q.length) {
  console.log(q.pop())
}

do {
  console.log("It is a song that never ends...");
} while (1);

for

// JSY
for let i = 0; i < 10; i++ ::
  console.log @: i

for const val of [1,'two',0xa] ::
  console.log @: val

for await const ea of someAsyncGenerator() ::
  console.log @: ea

Translated to JavaScript:

// JavaScript
for (let i = 0; i < 10; i++) {
  console.log({i})
}

for (const val of [1,'two',0xa]) {
  console.log({val})
}

for await (const ea of someAsyncGenerator()) {
  console.log({ea})
}

try, catch, and finally

// JSY
try ::
  if 0.5 > Math.random() ::
    throw new Error @ 'Oops!'
catch err ::
  console.error @ err
finally ::
  console.log @ 'Finally.'

Translated to JavaScript:

// JavaScript
try {
  if (0.5 > Math.random()) {
    throw new Error('Oops!')
  }
} catch (err) {
  console.error(err)
} finally {
  console.log('Finally.')
}

switch

// JSY
switch command ::
  case 'play':
    player.play()
    break
  case 'pause':
    player.pause()
    break
  default:
    player.stop().eject()

Translated to JavaScript:

// JavaScript
switch (command) {
  case 'play':
    player.play()
    break
  case 'pause':
    player.pause()
    break
  default:
    player.stop().eject()
}

Operators (Uncommon Use Cases)

Uncommon use cases for ::-prefixed operators require explicit commas, whereas the @-prefixed operators allow for the implicit use of commas.

Uncommon Operator Alias for Use Instead
::{} :: ::
::[] @[]
::() @
::@ ::() @
@() @ @

JSY Idioms

A few examples of why JSY is awesome.

Idiom #1 – Arrow Function Returning Hash

res.data.map @
  personData => @:
    fullName: `${ personData.info.fName } ${ personData.info.lName }`
    dateOfBirth: moment @ personData.info.dob

Idiom #2 – Arrow Function Compose

function double(x) :: return x + x
function add(x, y) :: return x + y

const clamp = (min, max, score) =>
  Math.max @ min,
    Math.min @ max, score

const calcScore = v => @
  v = double @ v
  v = add @ 7, v
  v = clamp @ 0, 100, v

Idiom #3 - Order of Operations Laziness

force += G * @ ( m1 * m2 ) / ( d * d )

JSY Integration with Other Tools

Using jsy-node with NodeJS

Command Line

$ npm install -g jsy-node
$ jsy-node some-script.jsy

Mocha

$ mocha --require jsy-node/all some-unittest.jsy

Ecosystem

Pure JavaScript Transpiler (stable)

Babel Transpiler (newer, beta)

Misc Babel-based Tools

Syntax Highlighters

Real Syntax Highlighters Needed!

Most JavaScript hightlighters work okay, but could certainly be better.

Projects using JSY

Thanks!

Special thanks to Robert Sirois for making this documentation happen!

Thanks to Brian Brown for inspiring, pushing, and supporting JSY's creation.

Thanks to Brandon Brown and Larry King for intensive use and feedback on JSY.

Licenses

Creative Commons License


Documentation is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

Examples and source code are licensed under BSD 2-Clause.