Skip to content

Commit

Permalink
closes #181; made pipe use & to reference input
Browse files Browse the repository at this point in the history
  • Loading branch information
satyr committed Oct 20, 2012
1 parent 67592e1 commit ee47572
Show file tree
Hide file tree
Showing 11 changed files with 147 additions and 104 deletions.
79 changes: 52 additions & 27 deletions lib/ast.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
var Node, Negatable, Block, Atom, Literal, Var, Key, Index, Chain, Call, List, Obj, Prop, Arr, Unary, Binary, Assign, Import, Of, Existence, Fun, Class, Super, Parens, Splat, Jump, Throw, Return, While, For, Try, Switch, Case, If, Label, Cascade, JS, Util, Vars, DECLS, ref$, UTILS, LEVEL_TOP, LEVEL_PAREN, LEVEL_LIST, LEVEL_COND, LEVEL_OP, LEVEL_CALL, PREC, TAB, ID, SIMPLENUM, slice$ = [].slice, replace$ = ''.replace;
var Node, Negatable, Block, Atom, Literal, Var, Key, Index, Chain, Call, List, Obj, Prop, Arr, Unary, Binary, Assign, Import, Of, Existence, Fun, Class, Super, Parens, Splat, Jump, Throw, Return, While, For, Try, Switch, Case, If, Label, Pipe, JS, Util, Vars, DECLS, ref$, UTILS, LEVEL_TOP, LEVEL_PAREN, LEVEL_LIST, LEVEL_COND, LEVEL_OP, LEVEL_CALL, PREC, TAB, ID, SIMPLENUM, slice$ = [].slice, replace$ = ''.replace;
(Node = function(){
throw Error('unimplemented');
}).prototype = {
Expand Down Expand Up @@ -295,10 +295,6 @@ exports.Block = Block = (function(superclass){
(ref$ = this.lines).splice.apply(ref$, [this.neck(), 0].concat(slice$.call(arguments)));
return this;
};
prototype.pipe = function(it){
this.lines.push(Assign(Var('_'), this.lines.pop()), it);
return this;
};
prototype.unwrap = function(){
if (this.lines.length === 1) {
return this.lines[0];
Expand Down Expand Up @@ -517,10 +513,10 @@ exports.Literal = Literal = (function(superclass){
this.carp('stray star');
break;
case '&':
if (!(val = o.cascadee)) {
this.carp('stray cascadee');
if (!(val = o.ref)) {
this.carp('stray reference');
}
val.referred = true;
this.cascadee || (val.erred = true);
}
return val;
};
Expand Down Expand Up @@ -3125,38 +3121,67 @@ exports.Label = Label = (function(superclass){
};
return Label;
}(Node));
exports.Cascade = Cascade = (function(superclass){
Cascade.displayName = 'Cascade';
var prototype = extend$(Cascade, superclass).prototype, constructor = Cascade;
function Cascade(target, body){
this.target = target;
this.body = body;
exports.Pipe = Pipe = (function(superclass){
Pipe.displayName = 'Pipe';
var prototype = extend$(Pipe, superclass).prototype, constructor = Pipe;
function Pipe(input, output, cascade){
this.input = input;
this.output = output;
this.cascade = cascade;
}
prototype.children = ['target', 'body'];
prototype.show = function(){
return this.cascade && '=>';
};
prototype.children = ['input', 'output'];
prototype.terminator = '';
prototype.delegate(['isCallable', 'isArray', 'isString', 'isRegex'], function(it){
return this.output[it]();
});
prototype.getJump = function(it){
return this.output.getJump(it);
};
prototype.makeReturn = function(ret){
this.ret = ret;
return this;
};
prototype.compileNode = function(o){
var ref, t, b;
this.temps = [ref = o.scope.temporary('x')];
t = ref + ' = ' + this.target.compile(o, LEVEL_LIST);
var level, input, output, ref, x$, code, out;
level = o.level;
input = this.input, output = this.output, ref = this.ref;
if (this.cascade && ('ret' in this || level && !this['void'])) {
output.add((x$ = Literal('&'), x$.cascadee = true, x$));
}
if ('ret' in this) {
this.body.add(Var(ref).makeReturn(this.ret));
output = output.makeReturn(this.ret);
}
if (ref) {
output = Assign(this.cascade
? Arr()
: JS(ref), output);
} else {
o.level && !this['void'] && this.body.add(Var(ref));
this.temps = [ref = o.scope.temporary('x')];
}
o.cascadee = new String(ref);
b = this.body.compile(o, o.level && LEVEL_PAREN);
o.cascadee.referred || this.carp('unreferred cascadee');
if (o.level) {
return "(" + t + ", " + b + ")";
if (input instanceof Pipe) {
input.ref = ref;
} else {
return t + ";\n" + b;
input = Assign(JS(ref), input);
}
o.level && (o.level = LEVEL_PAREN);
code = input.compile(o);
o.ref = new String(ref);
out = Block(output).compile(o);
o.ref.erred || this.carp("unreferred " + (this.cascade ? 'cascad' : 'pip') + "ee");
if (!level) {
return code + "" + input.terminator + "\n" + out;
}
code += ", " + out;
if (level > LEVEL_PAREN) {
return "(" + code + ")";
} else {
return code;
}
};
return Cascade;
return Pipe;
}(Node));
exports.JS = JS = (function(superclass){
JS.displayName = 'JS';
Expand Down
6 changes: 3 additions & 3 deletions lib/grammar.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ bnf = {
}), o('LET CALL( ArgList OptComma )CALL Block', function(){
return Chain(Call['let']($3, $6));
}), o('WITH Expression Block', function(){
return Chain(new Cascade($2, $3));
return Chain(new Pipe($2, $3, true));
})
],
List: [
Expand Down Expand Up @@ -77,7 +77,7 @@ bnf = {
],
Line: [
o('Expression'), o('Expression Block', function(){
return new Cascade($1, $2);
return new Pipe($1, $2, true);
}), o('PARAM( ArgList OptComma )PARAM <- Expression', function(){
return Call.back($2, $6, $5 === '<~');
}), o('COMMENT', function(){
Expand Down Expand Up @@ -121,7 +121,7 @@ bnf = {
? Binary($2.slice(1), $1, $3).invert()
: Binary($2, $1, $3);
}), o('Expression |> Expression', function(){
return Block($1).pipe($3);
return new Pipe($1, $3);
}), o('Chain !?', function(){
return Existence($1.unwrap(), true);
}), o('PARAM( ArgList OptComma )PARAM -> Block', function(){
Expand Down
10 changes: 5 additions & 5 deletions lib/lexer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1133,11 +1133,11 @@ function addImplicitIndentation(tokens){
}
}
function addImplicitParentheses(tokens){
var i, brackets, token, ref$, endi, tpair, tag, prev, skipBlock, seenSwitch;
var i, brackets, token, endi, ref$, tpair, tag, prev, skipBlock, seenSwitch;
i = 0;
brackets = [];
while (token = tokens[++i]) {
if (token[1] === 'do' && ((ref$ = tokens[i + 1]) != null ? ref$[0] : void 8) === 'INDENT') {
if (token[1] === 'do' && tokens[i + 1][0] === 'INDENT') {
endi = indexOfPair(tokens, i + 1);
if (tokens[endi + 1][0] === 'NEWLINE' && ((ref$ = tokens[endi + 2]) != null ? ref$[0] : void 8) === 'WHILE') {
token[0] = 'DO';
Expand All @@ -1162,6 +1162,9 @@ function addImplicitParentheses(tokens){
continue;
}
}
if (tag === 'BITWISE' && token[1] === '&' && !of$(tokens[i + 1][0], ARG)) {
tag = token[0] = 'LITERAL';
}
if (!(((ref$ = prev[0]) === 'FUNCTION' || ref$ === 'LET') || prev.spaced && able(tokens, i, true))) {
continue;
}
Expand All @@ -1170,9 +1173,6 @@ function addImplicitParentheses(tokens){
tpair[0] = ')CALL';
continue;
}
if (tag === 'BITWISE' && token[1] === '&' && !(token.spaced && of$(tokens[i + 1][0], ARG))) {
tag = token[0] = 'LITERAL';
}
if (!(of$(tag, ARG) || !token.spaced && (tag === '+-' || tag === '^'))) {
continue;
}
Expand Down
6 changes: 3 additions & 3 deletions lib/parser.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

76 changes: 45 additions & 31 deletions src/ast.co
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,6 @@ class exports.Block extends Node
@lines.splice @neck!, 0, ...arguments
this

pipe: -> @lines.push Assign(Var \_; @lines.pop!), it; this

unwrap: -> if @lines.length is 1 then @lines.0 else this

# Removes trailing comment nodes.
Expand Down Expand Up @@ -324,8 +322,8 @@ class exports.Literal extends Atom
return "(function(){\n#TAB#{o.indent}debugger;\n#{o.indent}}())"
case \* then @carp 'stray star'
case \&
@carp 'stray cascadee' unless val = o.cascadee
val.referred = true
@carp 'stray reference' unless val = o.ref
@cascadee or val.erred = true
val

#### Var
Expand Down Expand Up @@ -565,9 +563,9 @@ class exports.Chain extends Node
expandSlice: (o, assign) ->
{tails} = this; i = -1; while tail = tails[++i] then if tail.key?items
tail.carp 'calling a slice' if tails[i+1] instanceof Call
tails.splice 0 i+1
|> _.pop!key.toSlice o, Chain(@head, _)unwrap!, tail.symbol, assign
|> @head = _ <<< {@front}
_ = tails.splice 0 i+1
_ = _.pop!key.toSlice o, Chain(@head, _)unwrap!, tail.symbol, assign
@head = _ <<< {@front}
i = -1
this

Expand Down Expand Up @@ -843,9 +841,9 @@ class exports.Unary extends Node
case \new then it.isCallable! or it.carp 'invalid constructor'
case \do
# `do f?` => `f?()`
Parens if it instanceof Existence and not it.negated
_ = Parens if it instanceof Existence and not it.negated
then Chain(it)add Call! else Call.make it
|> return (_ <<< {@front, @newed})compile o
return (_ <<< {@front, @newed})compile o
case \delete
@carp 'invalid delete' if it instanceof Var or not it.isAssignable!
return @compilePluck o if o.level and not @void
Expand Down Expand Up @@ -965,13 +963,13 @@ class exports.Binary extends Node

compileExistence: (o) ->
if @op is \!?
If(Existence @first; @second) <<< {@cond, @void or not o.level}
|> return _.compileExpression o
_ = If(Existence @first; @second) <<< {@cond, @void or not o.level}
return _.compileExpression o
if @void or not o.level
Binary \&& Existence(@first, true), @second
|> return (_ <<< {+void})compileNode o
@first.cache o, true
|> If(Existence _.0; _.1)addElse(@second)compileExpression o
_ = Binary \&& Existence(@first, true), @second
return (_ <<< {+void})compileNode o
_ = @first.cache o, true
If(Existence _.0; _.1)addElse(@second)compileExpression o

# `x instanceof [A, B]` => `x instanceof A || x instanceof B`
compileAnyInstanceOf: (o, items) ->
Expand All @@ -983,8 +981,8 @@ class exports.Binary extends Node
compileMinMax: (o) ->
lefts = @first .cache o, true
rites = @second.cache o, true
Binary @op.charAt!, lefts.0, rites.0
|> If _, lefts.1 .addElse rites.1 .compileExpression o
_ = Binary @op.charAt!, lefts.0, rites.0
If _, lefts.1 .addElse rites.1 .compileExpression o

compileMethod: (o, klass, method, arg) ->
args = [@second]concat arg || []
Expand Down Expand Up @@ -1910,27 +1908,43 @@ class exports.Label extends Node
then o.indent += TAB; @compileBlock o, it
else it.compile o

#### Cascade
# A construction that allows `&`-references to target expression.
class exports.Cascade extends Node
(@target, @body) ->
#### Pipe
# Passes input to output via `&`-references.
class exports.Pipe extends Node
(@input, @output, @cascade) ->

show: -> @cascade and \=>

children: <[ target body ]>
children: <[ input output ]>

terminator: ''

::delegate <[ isCallable isArray isString isRegex ]> -> @output[it]!

getJump: -> @output.getJump it

makeReturn: (@ret) -> this

compileNode: (o) ->
@temps = [ref = o.scope.temporary \x]
t = ref + ' = ' + @target.compile o, LEVEL_LIST
compileNode: ({level}:o) ->
{input, output, ref} = this
if @cascade and (\ret in this or level and not @void)
output.add (Literal \& => &cascadee = true)
if \ret in this
then @body.add Var(ref)makeReturn @ret
else o.level and not @void and @body.add Var ref
o.cascadee = new String ref
b = @body.compile o, o.level && LEVEL_PAREN
o.cascadee.referred or @carp 'unreferred cascadee'
if o.level then "(#t, #b)" else "#t;\n#b"
output.=makeReturn @ret
if ref
then output = Assign (if @cascade then Arr! else JS ref), output
else @temps = [ref = o.scope.temporary \x]
if input instanceof Pipe
then input <<< {ref}
else input = Assign JS(ref), input
o.level &&= LEVEL_PAREN
code = input.compile o
o.ref = new String ref
out = Block output .compile o
o.ref.erred or @carp "unreferred #{ if @cascade then \cascad else \pip }ee"
return "#code#{input.terminator}\n#out" unless level
code += ", #out"
if level > LEVEL_PAREN then "(#code)" else code

#### JS
# Embedded JavaScript snippets.
Expand Down
7 changes: 4 additions & 3 deletions src/grammar.co
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ bnf =

o 'LET CALL( ArgList OptComma )CALL Block' -> Chain Call.let $3, $6

o 'WITH Expression Block' -> Chain new Cascade $2, $3
o 'WITH Expression Block' -> Chain new Pipe $2, $3, true

# An array or object
List:
Expand Down Expand Up @@ -114,7 +114,8 @@ bnf =
Line:
o \Expression

o 'Expression Block' -> new Cascade $1, $2
# Cascade without `with`
o 'Expression Block' -> new Pipe $1, $2, true

o 'PARAM( ArgList OptComma )PARAM <- Expression'
, -> Call.back $2, $6, $5 is \<~
Expand Down Expand Up @@ -168,7 +169,7 @@ bnf =
*if \! is $2.charAt 0 then Binary $2.slice(1), $1, $3 .invert!
else Binary $2 , $1, $3

o 'Expression |> Expression' -> Block $1 .pipe $3
o 'Expression |> Expression' -> new Pipe $1, $3

o 'Chain !?' -> Existence $1.unwrap!, true

Expand Down
2 changes: 1 addition & 1 deletion src/lang-co.co
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ tint \co [
: t(?:h(?:is|at)|o|il)
| f(?:rom|allthrough)
| e(?:val)?
| it | arguments | by | constructor | prototype | superclass | _
| it | arguments | by | constructor | prototype | superclass
) #kwend //]
[\glb // ^ (?
: Array | Boolean | Date | Error | Function | JSON | Math | Number
Expand Down
Loading

0 comments on commit ee47572

Please sign in to comment.