Permalink
Browse files

implemented _lef_ (left-hand-side reference) as `<>` (#180)

  • Loading branch information...
1 parent 7753f7d commit f4f0119d2189ab65f31107755aa6468196fc543d @satyr committed Jan 10, 2013
Showing with 143 additions and 58 deletions.
  1. +60 −25 lib/ast.js
  2. +6 −3 lib/lexer.js
  3. +48 −26 src/ast.co
  4. +9 −4 src/lexer.co
  5. +20 −0 test/assignment.co
View
@@ -470,7 +470,7 @@ exports.Literal = Literal = (function(superclass){
};
prototype.isCallable = function(){
var ref$;
- return (ref$ = this.value) === 'this' || ref$ === 'eval' || ref$ === '&';
+ return (ref$ = this.value) === 'this' || ref$ === 'eval' || ref$ === '&' || ref$ === '<>';
};
prototype.isString = function(){
return 0 <= '\'"'.indexOf((this.value + "").charAt());
@@ -479,7 +479,8 @@ exports.Literal = Literal = (function(superclass){
return (this.value + "").charAt() === '/';
};
prototype.isComplex = function(){
- return this.isRegex() || this.value === 'debugger';
+ var ref$;
+ return this.isRegex() || ((ref$ = this.value) === 'debugger' || ref$ === '<>');
};
prototype.varName = function(){
if (/^\w+$/.test(this.value)) {
@@ -511,7 +512,8 @@ exports.Literal = Literal = (function(superclass){
}
break;
case '*':
- this.carp('stray *');
+ case '<>':
+ this.carp("stray " + val);
break;
case '&':
if (!(val = o.ref)) {
@@ -1780,7 +1782,7 @@ exports.Assign = Assign = (function(superclass){
return this.access && this;
};
prototype.compileNode = function(o){
- var left, ref$, i$, len$, op, right, reft, code, name, lvar, del, that, res;
+ var left, ref$, i$, len$, op, right, minmax, lefs, reft, ref, x0$, code, name, lvar, del, that, res;
left = this.left.expandSlice(o, true).unwrap();
if (!this.right) {
left.isAssignable() || left.carp('invalid unary assign');
@@ -1801,18 +1803,35 @@ exports.Assign = Assign = (function(superclass){
return this.compileDestructuring(o, left);
}
left.isAssignable() || left.carp('invalid assign');
- if (this.logic) {
- return this.compileConditional(o, left);
- }
op = this.op, right = this.right;
- if (op === '<?=' || op === '>?=') {
- return this.compileMinMax(o, left, right);
- }
if (op === '**=' || op === '*=' && right.isString() || (op === '-=' || op === '/=') && right.isMatcher()) {
- ref$ = Chain(left).cacheReference(o), left = ref$[0], reft = ref$[1];
- right = Binary(op.slice(0, -1), reft, right);
+ right = Binary(op.slice(0, -1), Literal('<>'), right);
op = ':=';
}
+ minmax = op === '<?=' || op === '>?=';
+ lefseek(right, lefs = []);
+ if (lefs[0]) {
+ ref$ = Chain(left).cacheReference(o), left = ref$[0], reft = ref$[1];
+ if (minmax || this.logic) {
+ ref$ = left.cache(o), left = ref$[0], ref = ref$[1], this.temps = ref$[2];
+ ref = ref.compile(o);
+ } else if (lefs[1]) {
+ ref$ = reft.cache(o, false, LEVEL_CALL), lefs.shift().value = ref$[0], ref = ref$[1];
+ this.temps = [ref];
+ } else {
+ ref = reft.compile(o);
+ }
+ for (i$ = 0, len$ = lefs.length; i$ < len$; ++i$) {
+ x0$ = lefs[i$];
+ x0$.value = ref;
+ }
+ }
+ if (minmax) {
+ return this.compileMinMax(o, left, right, reft);
+ }
+ if (this.logic) {
+ return this.compileConditional(o, left, right, reft);
+ }
(right = right.unparen()).ripName(left = left.unwrap());
code = name = (left.front = true, left).compile(o, LEVEL_LIST);
if (lvar = left instanceof Var) {
@@ -1837,22 +1856,38 @@ exports.Assign = Assign = (function(superclass){
}
return code;
};
- prototype.compileConditional = function(o, left){
- var ref$, lefts, morph;
+ function lefseek(node, lefs){
+ if (node.lefsought) {
+ return;
+ }
+ node.lefsought = true;
+ if (node instanceof Assign) {
+ node = node.left;
+ }
+ if (node instanceof Fun || node.value === '<>' && lefs.push(node)) {
+ return;
+ }
+ node.eachChild(function(it){
+ return lefseek(it, lefs);
+ });
+ }
+ prototype.compileConditional = function(o, left, right, reft){
+ var ref$, x0$;
if (left instanceof Var && ((ref$ = this.logic) === '?' || ref$ === '!?') && this.op === '=') {
o.scope.declare(left.value, left);
}
- lefts = Chain(left).cacheReference(o);
o.level += LEVEL_OP < o.level;
- morph = Binary(this.logic, lefts[0], (this.logic = false, this.left = lefts[1], this));
- return (morph['void'] = this['void'], morph).compileNode(o);
- };
- prototype.compileMinMax = function(o, left, right){
- var lefts, rites, test, put, ref$;
- lefts = Chain(left).cacheReference(o);
+ reft || (ref$ = Chain(left).cacheReference(o), left = ref$[0], reft = ref$[1]);
+ x0$ = Binary(this.logic, left, (this.logic = false, this.left = reft, this));
+ x0$ = (x0$['void'] = this['void'], x0$);
+ return x0$.compileNode(o);
+ };
+ prototype.compileMinMax = function(o, left, right, reft){
+ var ref$, rites, test, put;
+ reft || (ref$ = Chain(left).cacheReference(o), left = ref$[0], reft = ref$[1]);
rites = right.cache(o, true);
- test = Binary(this.op.replace('?', ''), lefts[0], rites[0]);
- put = Assign(lefts[1], rites[1], ':=');
+ test = Binary(replace$.call(this.op, '?', ''), left, rites[0]);
+ put = Assign(reft, rites[1], ':=');
if (this['void'] || !o.level) {
return Parens(Binary('||', test, put)).compile(o);
}
@@ -3162,7 +3197,7 @@ exports.Pipe = Pipe = (function(superclass){
return this;
};
prototype.compileNode = function(o){
- var level, input, output, prog1, ref, x0$, x1$, code, out;
+ var level, input, output, prog1, ref, x0$, ref$, key$, code, out;
level = o.level;
input = this.input, output = this.output, prog1 = this.prog1, ref = this.ref;
output = Block(output).chomp();
@@ -3173,7 +3208,7 @@ exports.Pipe = Pipe = (function(superclass){
output = output.makeReturn(this.ret);
}
if (ref) {
- prog1 || (x1$ = output.lines, x1$.push(Assign(Var(ref), x1$.pop())));
+ prog1 || ((ref$ = ref$ = output.lines)[key$ = ref$.length - 1] = Assign(Var(ref), ref$[key$]));
} else {
ref = o.scope.reference();
}
View
@@ -652,6 +652,9 @@ exports.doLiteral = function(code, index){
}
tag = 'UNARY';
break;
+ case '<>':
+ this.token('LITERAL', '<>', true);
+ return 2;
case '&':
if (!able(this.tokens)) {
this.token('LITERAL', '&', true);
@@ -792,7 +795,7 @@ exports.interpolate = function(str, idx, end){
return parts.size = pos + i + end.length, parts;
case '#':
c1 = str.charAt(i + 1);
- id = (c1 === '@' || c1 === '&') && c1 || (ID.lastIndex = i + 1, ID).exec(str)[1];
+ id = (c1 === '@' || c1 === '&') && c1 || c1 === '<' && '>' === str.charAt(i + 2) && '<>' || (ID.lastIndex = i + 1, ID).exec(str)[1];
if (!(id || c1 === '{')) {
continue;
}
@@ -811,7 +814,7 @@ exports.interpolate = function(str, idx, end){
if (id === '@') {
id = 'this';
}
- if (id === 'this' || id === '&') {
+ if (id === 'this' || id === '&' || id === '<>') {
tag = 'LITERAL';
} else {
id = camelize(id);
@@ -1448,7 +1451,7 @@ KEYWORDS_SHARED = ['true', 'false', 'null', 'this', 'void', 'super', 'return', '
KEYWORDS_UNUSED = ['enum', 'interface', 'package', 'private', 'protected', 'public', 'static', 'yield'];
KEYWORDS = KEYWORDS_SHARED.concat(KEYWORDS_UNUSED);
ID = /((?!\s)[a-z_$\xAA-\uFFDC](?:(?!\s)[\w$\xAA-\uFFDC]|-[a-z])*)([^\n\S]*:(?![:=]))?|/ig;
-SYMBOL = /[-+*\/%&|^:]=|\.{1,3}|([-+&|@:])\1|[-~=|]>|[!=]==?|<(?:<(?:=|<{0,2})|[-~]|\[(?:[\s\S]*?\]>)?)|>>>?=?|[<>]\??=?|!\?|\*\*=?|[^\s#]?/g;
+SYMBOL = /[-+*\/%&|^:]=|\.{1,3}|([-+&|@:])\1|[-~=|]>|[!=]==?|<(?:<(?:=|<{0,2})|[-~>]|\[(?:[\s\S]*?\]>)?)|>>>?=?|[<>]\??=?|!\?|\*\*=?|[^\s#]?/g;
SPACE = /[^\n\S]*(?:#.*)?/g;
MULTIDENT = /(?:\s*#.*)*(?:\n([^\n\S]*))+/g;
SIMPLESTR = /'[^\\']*(?:\\[\s\S][^\\']*)*'|/g;
View
@@ -93,7 +93,7 @@
if (base = this)[name = @aSource] instanceof Existence
base.=[name]; name = \it
unless base[name]value is \that
- base[name] = Assign Var(\that), base[name]
+ base[name] = Assign Var(\that), <>
function hasThat
it.value is \that or if it.aSource
then hasThat that if it[that]
@@ -246,7 +246,7 @@ class exports.Block extends Node
return @compileExpressions o, level if level
o.block = this; tab = o.indent
codes = for node of @lines
- node = node.unfoldSoak o or node
+ node = <>unfoldSoak o or <>
continue unless code = (node <<< {+front})compile o, level
node.isStatement! or code += node.terminator
tab + code
@@ -302,11 +302,11 @@ class exports.Literal extends Atom
return JS "#value" true if value.js
return new Super if value is \super
- isEmpty : -> @value of <[ null void ]>
- isCallable : -> @value of <[ this eval & ]>
+ isEmpty : -> @value of <[ null void ]>
+ isCallable : -> @value of <[ this eval & <> ]>
isString : -> 0 <= '\'"'indexOf "#{@value}"charAt!
isRegex : -> "#{@value}"charAt! is \/
- isComplex : -> @isRegex! or @value is \debugger
+ isComplex : -> @isRegex! or @value of <[ debugger <> ]>
varName: -> if /^\w+$/test @value then \$ + @value else ''
@@ -321,8 +321,8 @@ class exports.Literal extends Atom
@carp 'invalid use of ' + @value if level is LEVEL_CALL
case \debugger then if level
return "(function(){\n#TAB#{o.indent}debugger;\n#{o.indent}}())"
- case \*
- @carp 'stray *'
+ case \* \<>
+ @carp "stray #val"
case \&
@carp 'stray &' unless val = o.ref
@cascadee or val.erred = true
@@ -407,7 +407,7 @@ class exports.Chain extends Node
else if not @tails.1 and it.key?name is \prototype
@head.sproto = true
if delete it.vivify
- @head = Assign Chain(@head, @tails.splice 0, 9e9), that!, \= \||
+ @head = Assign Chain(<>, @tails.splice 0, 9e9), that!, \=, \||
this
# __Chain__ can be unwrapped as its inner node, if there are no subnodes.
@@ -462,14 +462,14 @@ class exports.Chain extends Node
# `a().b`
if base.isComplex!
ref = o.scope.temporary!
- base = Chain Assign Var(ref), base
+ base = Chain Assign Var(ref), <>
bref = Var(ref) <<< {+temp}
- # `a{}`
+ # `a()`
return [base, bref] unless name
# `a[b()]`
if name.isComplex!
ref = o.scope.temporary \key
- name = Index Assign Var(ref), name.key
+ name = Index Assign Var(ref), <>key
nref = Index Var(ref) <<< {+temp}
[base.add name; Chain bref || base.head, [nref or name]]
@@ -704,7 +704,7 @@ class exports.Obj extends List
else
"#{ key = node.compile o }: #key"
# Canonicalize the key, e.g.: `0.0` => `0`
- ID.test key or key = do Function "return #key"
+ ID.test key or key = do Function "return #<>"
node.carp "duplicate property \"#key\"" unless dic"#key." ^= 1
code = "{#{ code and code + \\n + @tab }}"
rest and code = Import(JS code; Obj rest)compile o <<< indent: @tab
@@ -1075,15 +1075,27 @@ class exports.Assign extends Node
left.=first
return @compileDestructuring o, left if left.items
left.isAssignable! or left.carp 'invalid assign'
- return @compileConditional o, left if @logic
{op, right} = this
- return @compileMinMax o, left, right if op of <[ <?= >?= ]>
if op is \**=
or op is \*= and right.isString!
or op of <[ -= /= ]> and right.isMatcher!
- [left, reft] = Chain(left)cacheReference o
- right = Binary op.slice(0 -1), reft, right
+ right = Binary(op.slice 0 -1; Literal \<>; <>)
op = \:=
+ minmax = op of [\<?= \>?=]
+ lefseek right, lefs = []
+ if lefs.0
+ [left, reft] = Chain left .cacheReference o
+ if minmax or @logic
+ [left, ref, @temps] = left.cache o
+ ref.=compile o
+ else if lefs.1
+ [lefs.shift!value, ref] = reft.cache o, false, LEVEL_CALL
+ @temps = [ref]
+ else
+ ref = reft.compile o
+ for lefs => &value = ref
+ return @compileMinMax o, left, right, reft if minmax
+ return @compileConditional o, left, right, reft if @logic
(right.=unparen!)ripName left.=unwrap!
code = name = (left <<< {+front})compile o, LEVEL_LIST
if lvar = left instanceof Var
@@ -1106,20 +1118,30 @@ class exports.Assign extends Node
code = "(#code)" if that > (if del then LEVEL_PAREN else LEVEL_LIST)
code
- compileConditional: (o, left) ->
+ !function lefseek node, lefs
+ return if node.lefsought
+ node.lefsought = true
+ node.=left if node instanceof Assign
+ return if node instanceof Fun
+ or node.value is \<> and lefs.push node
+ node.eachChild -> lefseek it, lefs
+
+ compileConditional: (o, left, right, reft) ->
if left instanceof Var and @logic of <[ ? !? ]> and @op is \=
o.scope.declare left.value, left
- lefts = Chain(left)cacheReference o
# Deal with `a && b ||= c`.
o.level += LEVEL_OP < o.level
- morph = Binary @logic, lefts.0, @<<<{-logic, left: lefts.1}
- (morph <<< {@void})compileNode o
-
- compileMinMax: (o, left, right) ->
- lefts = Chain(left)cacheReference o
+ reft or [left, reft] = Chain left .cacheReference o
+ # Transform to `left || reft = right`.
+ Binary @logic, left, @<<<{-logic, left: reft}
+ |> & <<< {@void}
+ |> &compileNode o
+
+ compileMinMax: (o, left, right, reft) ->
+ reft or [left, reft] = Chain left .cacheReference o
rites = right.cache o, true
- test = Binary @op.replace(\? ''), lefts.0, rites.0
- put = Assign lefts.1, rites.1, \:=
+ test = Binary @op - \?, left, rites.0
+ put = Assign reft, rites.1, \:=
# `a <?= b` => `a <= b || a = b `
return Parens(Binary \|| test, put)compile o if @void or not o.level
# `r = a <?= b` => `r = if a <= b then a else a = b`
@@ -1949,7 +1971,7 @@ class exports.Pipe extends Node
if \ret in this
output.=makeReturn @ret
if ref
- then prog1 or with output.lines => &push Assign Var(ref), &pop!
+ then prog1 or output.lines[*-1] = Assign Var(ref), <>
else ref = o.scope.reference!
if input instanceof Pipe
then input <<< {ref}
View
@@ -425,6 +425,9 @@ exports import
else break
return 1
tag = \UNARY
+ case \<>
+ @token \LITERAL \<> true
+ return 2
case \&
unless able @tokens
@token \LITERAL \& true
@@ -522,7 +525,9 @@ exports import
return parts <<< size: pos + i + end.length
case \#
c1 = str.charAt i+1
- id = c1 of <[ @ & ]> and c1 or (ID <<< lastIndex: i+1)exec str .1
+ id = c1 of <[ @ & ]> and c1
+ or c1 is \< and \> is str.charAt i+2 and \<>
@vendethiel
vendethiel Dec 1, 2013 Contributor

and \<> ?

@vendethiel
vendethiel Dec 1, 2013 Contributor

Indeed, thanks.
I'm currently removing auto-infusing IIFEs for LS, do you want me to PR coco with that too ?

@satyr
satyr Dec 1, 2013 Owner

...How is that relevant to this line? Anyway, PR is always welcome as long as it's reasonable.

@vendethiel
vendethiel Dec 1, 2013 Contributor

Not relevant ? This just happened to be something I was working on.

+ or (ID <<< lastIndex: i+1)exec str .1
continue unless id or c1 is \{
case \\ then ++i; fallthrough
default continue
@@ -532,7 +537,7 @@ exports import
if id
{length} = id
id = \this if id is \@
- if id of <[ this & ]>
+ if id of <[ this & <> ]>
tag = \LITERAL
else
id = camelize id
@@ -988,8 +993,8 @@ SYMBOL = //
| ([-+&|@:])\1 # crement / logic / `prototype` / `arguments`
| [-~=|]> # (bound-)function / inlinedent / pipe
| [!=]==? # equality
-| <(?: <(?:=|<{0,2}) | [-~]
- | \[(?:[\s\S]*?\]>)? ) # left shift / import / backcall / words
+| <(?: <(?:=|<{0,2}) | [-~>]
+ | \[(?:[\s\S]*?\]>)? ) # left shift / import / backcall / lef / words
| >>>?=? # rite shift
| [<>]\??=? # {less,greater}-than(-or-equal-to) / min/max
| !\? # inexistence
Oops, something went wrong.

0 comments on commit f4f0119

Please sign in to comment.