\\// || //\ //\\ \// \// a dumb toy js variant for my own use only. PHILOSOPHIE =========== * above all and in every case, the transformation from source to js should: * be obvious * remain largely readable to those only familiar with the base language * transform inline (no change in line numbering) * never require additional procedure definition (corollary to previous) * be commonly useful * changes to base javascript must in every case improve drastically one or more of and never degrade any of * readability by, for instance, the removal of junk characters * performance by, for instance, compiling succinct comprehensions to bare loops * consistency by, for instance, fixing cases where javascript is a mess ENABLED ES PROPOSALS ==================== * |> FORWARD PIPE (using f# es proposal) * INCOMPLETE: affects line numbering. * ?. OPTIONAL CHAINING (using loose = true) * ?? NULLISH COALESCE (using loose = true) * :: FUNCTION BIND * DO EXPRESSIONS IMPLEMENTED =========== * COMPREHENSIONS implemented with bare for loops for perf. [ x * 2 for x in xs ] yields a value-returning comprehension [ `${idx}: ${x}` for x, idx in xs ] second-arg key [ x for { x } in xs ] destructuring works [ x for x in xs if x > 4 ] guard; unmatched values omitted [ y - 1 for y in x.ys for x in xs ] flat comprehension; nesting reads left to right [ y - 1 for y in ys for { ys } in xs ] destructuring still works [ [ y - 1 for y in x.ys ] for x in xs ] (nonflat comprehension) [ do { … } for x in xs ] use do if you need a block [ v for v of obj ] object-to-array comprehension { k: v for _, k of obj } object-to-object comprehension _ terms are ignored { [k + 1]: x for x in xs } array-to-object comprehension use brackets around expressions { unknown: x for x in xs } implied literal, though why would you do this? * IMPROVE: messy var naming scope * IMPROVE: do expr won't work for obj comprehensions * INCOMPLETE: affects line numbering. * ACCESS AS FUNCTION copied (improved?) from livescript. xs.map(.length) sugar: dangling accessors compile to one-arg functions xs.map(.length - 1) within paren, allow subsequent operators xs.map(.get('subarray').map(.length)) const f = .map(.y) sugar: const f = e => e.map(d => d.y) xs.map(.length |> f) sugar: xs.map(x => f(x.length)) * INCOMPLETE: affects line numbering. * GUARD STATEMENT copied from swift, ish. guard const x = maybeGet(); guard const y = maybeGet() else throw new Error('no y!'); guard const z = getZ(), y = getY(), x = getX(); guard a; guard whatever() else panic(); the else-less case return;s in the else case the else block is run. given break; continue; return; or throw; in else no further transformation is performed. otherwise, the else block is implicitly terminated with return; * BUG: spread destructuring does not work. * BUG: guard a; outputs an extra ; artifact. * IMPROVE: allow use of guard as binding name. * INCOMPLETE: affects line numbering. * DECLARATION EXISTENTIAL badly copied from swift's if let with adaptations due to scoping problems. const x = getX() { something(x); } let y = getY() { something(y); } * CONSIDER: use tempvar to solve scoping and get true if const? * INCOMPLETE: affects line numbering. * ? PREFIX EXISTENCE OPERATOR because prefix was easier to implement. sugar: ?a to (a == null) sugar: !?a to (a != null) * LAMBDA SIGNATURES very minor improvements. => 42 no need for () x => x * 2 like vanilla | x, y => x + y the pipe allows clean multi-arg separation without parens | x, y -> proc(x, y) multi-parameter * NON-RETURNING LAMBDAS very minor improvement. (it doesn't =equal= the value, so ->) x -> proc(x) sugar: x => { proc(x); } * INCOMPLETE: affects line numbering. * is isnt and or NAMED OPERATORS sugar: === !== && || * THROWAWAY UNDERSCORES because i don't care. f(| _ _ x => x) sugar: _ terms are ignored in arglists * IMPROVE: messy var naming scope. * OPTIONAL CLASSBODY because why. class x; sugar: class x {} class x extends y; sugar: class x extends y {} PROBABLY ======== planned. not final. * UNARY/BINARY OPERATORS AS FUNCTIONS pairs.map(+) sugar: bare unary operators compile to two-arg functions pairs.map(+ 2) sugar: the right operand may be filled pairs.filter(!?) sugar: why isn't this in livescript? * EXPLICIT INLINING const f = inline x => x * 72; probably too difficult to implement in reality? MAYBE ===== these would be "probably" but they're just not that life-changing. * DOTTED KEYS { options.app: app } sugar: { options: { app } } * NESTED BLOCK COMMENTS /* /* x */ */ duh (or ugh take your pick) * >> COMPOSE sugar: f >> g to (x => f(g(x))) * ++ CONCATENATOR sugar: a ++ b to a.concat(b) * <+ APPENDOR sugar: a <+ b to a.push(...b) MAYBE NOT ========= rough wishlist in order of decreasing likelihood. * BACKCALLS? x <= proc(2, 4) sugar: as in livescript * DELETE EXPRESSIONS const x = delete a.b sugar: delete returns its value if used where it would be useful * IF EXPRESSIONS const x = if … sugar: transforms to ternary (likely usurped by do exprs) * TRY EXPRESSIONS const x = try f_(); you only get one expression? * COMMA-OPTIONAL TERMS f(x y) commas in arglists and arrays are optional (also objs if newline/shorthand) (extremely unlikely to happen)