Permalink
Browse files

FIX: scoping problems for variables assigned inside 'efficiency compr…

…ehensions' ALL TESTS PASSING
  • Loading branch information...
shanebdavis committed Jan 9, 2019
1 parent 05ba97b commit f680223e0dbbb6063225079fbbccc89e7bfdd0c3
@@ -4,11 +4,18 @@ controlStatement:
{}
stnFactory: :ControlOperatorStn
pattern:
"ifUnlessWhileUntil _ expression:expressionWithOneLessBlock body:block elseBody:elseClause?"
"ifUnlessWhileUntil _ expression:expressionWithOneLessBlock body:block? elseBody:elseClause"
"ifUnlessWhileUntil _ expression:expression thenClause elseBody:elseClause?"
stnProps: ~>
operand: @ifUnlessWhileUntil.toString()
"ifUnless _ expression:expressionWithOneLessBlock body:block elseBody:elseClause?"
"ifUnless _ expression:expressionWithOneLessBlock body:block? elseBody:elseClause"
"ifUnless _ expression:expression thenClause elseBody:elseClause?"
stnProps: ~> operand: @ifUnless.toString()

{}
stnFactory: :WhileStn
pattern:
"whileUntil _ expression:expressionWithOneLessBlock body:block elseBody:elseClause?"
"whileUntil _ expression:expressionWithOneLessBlock body:block? elseBody:elseClause"
"whileUntil _ expression:expression thenClause elseBody:elseClause?"
stnProps: ~> operand: @whileUntil.toString()

{}
stnFactory: :TryStn
@@ -44,9 +51,10 @@ elseClause: "controlStructorClauseJoiner else _? lineOfStatementsOrBlock"

controlStructorClauseJoiner: "" _? end?

catch: /catch\b/
try: /try\b/
ifUnlessWhileUntil: /(if|unless|while|until)\b/
thenDo: /(then|do)\b/
when: /when\b/
else: /else\b/
catch: /catch\b/
try: /try\b/
whileUntil: /(while|until)\b/
ifUnless: /(if|unless)\b/
thenDo: /(then|do)\b/
when: /when\b/
else: /else\b/
@@ -51,10 +51,14 @@ UniqueIdentifierHandle = &UniqueIdentifierHandle
@identifiersInScope[identifier] = true
@identifiersUsed[identifier] = true

addIdentifierLet: (identifier) ->
if identifier
@addExplicitlyDeclared identifier

addIdentifierAssigned: (identifier, initializer, insideLet)->
if identifier
if insideLet
@addExplicitlyDeclared identifier
@addIdentifierLet identifier
else
throw new Error "bindUniqueIdentifier must be called AFTER all calls to addIdentifierAssigned" if @_boundUniqueIdentifiers
@identifiersInScope[identifier] = true
@@ -85,18 +89,20 @@ UniqueIdentifierHandle = &UniqueIdentifierHandle
@identifiersAssigned[identifier] = true if addToLets
identifier

requireScopeUpdated: ->
unless @_scopeUpdated
throw new Error "Scope must be fully updated. #{@className}"

getAvailableIdentifierName: (preferredName) ->
preferredName = normalizePerferredName preferredName
unless @_scopeUpdated
log.error ScopeStnMixin: getAvailableIdentifierName: "cannot be called before updateScope completes: #{@className}", new Error
@requireScopeUpdated()
blockedIndentifiers = @identifiersInSelfParentAndChildScopes
unless blockedIndentifiers[preferredName]
preferredName
else
{identifiersActiveInScope} = @
unless identifiersActiveInScope[preferredName]
preferredName
else
count = 0
name while identifiersActiveInScope[name = "#{preferredName}#{count+=1}"]
name
count = 0
name while blockedIndentifiers[name = "#{preferredName}#{count+=1}"]
name

addChildScope: (child) ->
unless child == @
@@ -107,10 +113,61 @@ UniqueIdentifierHandle = &UniqueIdentifierHandle
each uniqueIdentifierHandle in @_uniqueIdentifierHandles
uniqueIdentifierHandle.identifier

getAutoLets: ->
@bindAllUniqueIdentifiersRequested()
if @_identifiersAssigned && (identifiers = @requiredIdentifierLets).length > 0
"let #{identifiers.join ', '}"
getSourceNodeForAutoLetsWithStatements: (statementsStn, toSourceNodeOptions) ->
toSourceNodeOptions extract? returnAction

if (statementsStn extract statements)[0]?.type == :Assignment && @haveAutoLets
autoLetIdentifiers = object @requiredIdentifierLets with true
assignedAutoLetStns = []
find statement, i in statements
statement extract type, propName
if type == :Assignment && autoLetIdentifiers[propName] && (!returnAction || i < statements.length - 1)
assignedAutoLetStns.push statement
autoLetIdentifiers[propName] = false
false
else true # 'found' a non-match; stop

if 0 < numAssignsConsumed = assignedAutoLetStns.length
letSourceNodes =
array v, k in autoLetIdentifiers when v with k into
array v in assignedAutoLetStns with v.toSourceNode()

[]
"let "

array letSourceNode, i in letSourceNodes into out = []
out.push ', ' if i > 0
letSourceNode

if statements.length - numAssignsConsumed > 0
[]
"; "
statementsStn.toSourceNodeWithCustomChildren
statements.slice numAssignsConsumed, statementsStn.length
toSourceNodeOptions
else
";"

else
[]
@autoLetsForSourceNode
statementsStn.toSourceNode toSourceNodeOptions

else
statementsStn.toSourceNode toSourceNodeOptions


@getter
autoLetsForSourceNode: -> lets + "; " if lets = @getAutoLets()

haveAutoLets: ->
@bindAllUniqueIdentifiersRequested()
@_identifiersAssigned && @requiredIdentifierLets.length > 0

autoLets: ->
@bindAllUniqueIdentifiersRequested()
if @_identifiersAssigned && (identifiers = @requiredIdentifierLets).length > 0
"let #{identifiers.join ', '}"

getBareInitializers: ->
@bindAllUniqueIdentifiersRequested()
@@ -119,6 +176,7 @@ UniqueIdentifierHandle = &UniqueIdentifierHandle
if identifiers.length > 0
"#{identifiers.join '; '}"

# return-value ignored
updateScope: (@scope)->
@bindAllUniqueIdentifiersRequested()
@scope.addChildScope @
@@ -158,6 +216,23 @@ UniqueIdentifierHandle = &UniqueIdentifierHandle
identifier


identifiersInParentScopes: (out = {})->
@ extract scope
while scope
mergeInto out, scope.identifiersInScope
scope = if scope.scope != scope then scope.scope else null

out

identifiersInChildScopes: (out = {})->
each childScope in @_childScopes into out
mergeInto out, childScope.identifiersInScope
childScope.getIdentifiersInChildScopes out

identifiersInSelfParentAndChildScopes: ->
@getIdentifiersInParentScopes @getIdentifiersInChildScopes merge @identifiersInScope

# includes parents
identifiersActiveInScope: ->
out = merge @_identifiersInScope
{scope} = @
@@ -2,10 +2,13 @@ import &StandardImport

class ForInControlStn extends &BaseStn

@getter
varStn: -> @children[0]
fromStn: -> @children[1]

toSourceNode: ->
[varStn, fromStn] = @children
@createSourceNode
"let " if @props.let
varStn.toSourceNode()
@varStn.toSourceNode()
" in "
fromStn.toSourceNode expression: true
@fromStn.toSourceNode expression: true
@@ -1,5 +1,5 @@
(insideForParens, body) ->
&ControlOperatorStn
&WhileStn
operand: "for"
insideForParens
body
@@ -11,6 +11,10 @@ class FunctionDefinitionArgStn extends &BaseStn
@defaultValue = children[1]

@getter
identifierStn: ->
if @isSimpleIdentifier
@target

argumentName: -> @target.name
isSimpleIdentifier: -> @target.type == :Identifier
explicitIdentifier: -> @target?.explicitIdentifier
@@ -23,7 +27,6 @@ class FunctionDefinitionArgStn extends &BaseStn
@target.toSourceNode()
[] " = " @defaultValue.toSourceNode() if @defaultValue


generatePreBodyStatementStn: ->
if @assignThisProperty
{IdentifierStn, AssignmentStn, ThisStn, ReferenceStn} = &StnRegistry
@@ -32,6 +35,3 @@ class FunctionDefinitionArgStn extends &BaseStn
AssignmentStn
ThisStn identifierStn
identifierStn

getFunctionPreBodyStatementsJs: ->
"this.#{@target.toJs()} = #{@target.toJs()}" if @assignThisProperty
@@ -22,7 +22,15 @@ class StatementsStn extends &BaseStn

out

toSourceNodeWithCustomChildren: (children, options) ->
oldChildren = @children
@children = children
out = @toSourceNode options
@children = oldChildren
out

@getter
statements: -> @children
compileTimeValue: ->
if @children.length == 1
@children[0].compileTimeValue
@@ -1,5 +1,74 @@
(test, body) ->
&ControlOperatorStn
operand: "while"
test
body
import &StandardImport

class WhileStn extends &ScopeStnMixin &BaseStn

constructor: (props, children) ->
super
@operand = props.operand ? :while
if @labeledChildren.expression
@expression = @labeledChildren.expression
@body = @labeledChildren.body ? StnRegistry.UndefinedStn()
else
@expression = children[0]
@body = children[1] ? StnRegistry.UndefinedStn()

unless @body.type == :Statements
@body = StnRegistry.StatementsStn @body
.parent = @

@validate()

@getter
autoLetsForSourceNode: -> lets + "; " if lets = @getAutoLets()
whileReturnTempVar: -> @_whileReturnTempVar ?= @scope.uniqueIdentifier
unaryOperator: -> if @operand == :until then "!"
jsKeyword: -> if @operand == :until then :while else @operand

validate: ->
switch @operand
when :while :until :for then true
else
throw new Error "INTERNAL: invalid control-operator: #{formattedInspect @operand}"

toSourceNode: (options = {})->
options extract expression, returnValueIsIgnored, noParens
@ extract operand, jsKeyword, unaryOperator

@createSourceNode
if expression
if returnValueIsIgnored
@doSourceNode
jsKeyword
" ("
unaryOperator
@expression.toSourceNode noParens: true, expression: true, dotBase: !!unaryOperator
") {"
@getSourceNodeForAutoLetsWithStatements @body
# @autoLetsForSourceNode
# @body.toSourceNode()
"};"

else
tempVarIdentifier = @whileReturnTempVar
@doSourceNode
jsKeyword
" ("
unaryOperator
@expression.toSourceNode noParens: true, expression: true, dotBase: !!unaryOperator
") {"
@getSourceNodeForAutoLetsWithStatements @body, returnAction: "#{tempVarIdentifier} ="
# @autoLetsForSourceNode
# @body.toSourceNode returnAction: "#{tempVarIdentifier} ="
"}; return #{tempVarIdentifier};"

else
[]
jsKeyword
" ("
unaryOperator
@expression.toSourceNode noParens: true, expression: true, dotBase: !!unaryOperator
") {"
@getSourceNodeForAutoLetsWithStatements @body
# @autoLetsForSourceNode
# @body.toSourceNode()
"}"
@@ -201,10 +201,12 @@ class ComprehensionStn extends &ScopeStnMixin &BaseStn
byId = IdentifierStn preferredIdentifier: :by

intoId = IdentifierStn preferredIdentifier: :into unless comprehensionType == 'each' && toClause && !intoClause
iId = IdentifierStn preferredIdentifier: if fromObjectClause then :k else :i
iId = IdentifierStn
preferredIdentifier: if fromObjectClause then :k else :i
addToLets: !fromObjectClause

unless variableDefinition
variableDefinition = [] IdentifierStn preferredIdentifier: :v, addToLets: false
variableDefinition = [] IdentifierStn preferredIdentifier: :v #, addToLets: false

[valueId] = variableDefinition

@@ -355,19 +357,18 @@ class ComprehensionStn extends &ScopeStnMixin &BaseStn

StatementsStn
if variableDefinition?.length > 0
LetStn
if fromObjectClause
if fromObjectClause
AssignmentStn
valueId.identifierStn ? valueId
AccessorStn
fromId
keyValueStn
else
array v, i in variableDefinition
AssignmentStn
valueId
AccessorStn
fromId
keyValueStn
else
array v, i in variableDefinition
AssignmentStn
v
if !toClause && i == 0 then AccessorStn fromId, iId.getValueStn()
else iId
v.identifierStn ? v
if !toClause && i == 0 then AccessorStn fromId, iId.getValueStn()
else iId

if whenClause
IfStn whenClause, invokeWithClauseAndPush
Oops, something went wrong.

0 comments on commit f680223

Please sign in to comment.