#FEATURE by exept
class: JavaScriptParser
added: #objectLiteralOrDescructuringExpression
changed:
#constOrVarDeclarationFor:isConst:
#primaryExpression
class: JavaScriptParser class
comment/format in: #examples
class: JavaScriptParser::JavaScriptMultiVariableNode
class definition
added:
#slots
#slots:
class: JavaScriptParser::JavaScriptMultiVariableNode class
added: #documentation
--- a/JavaScriptParser.st Fri Oct 18 16:42:23 2019 +0200
+++ b/JavaScriptParser.st Fri Oct 18 21:48:14 2019 +0200
@@ -151,6 +151,13 @@
privateIn:JavaScriptParser
!
+ParseNode subclass:#JavaScriptMultiVariableNode
+ instanceVariableNames:'slots'
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:JavaScriptParser
+!
+
ReturnNode subclass:#JavaScriptReturnNode
instanceVariableNames:'environmentToReturnFrom'
classVariableNames:''
@@ -465,6 +472,15 @@
}
'
[exEnd]
+ [exBegin]
+ JavaScriptParser parseExpression:'{a:10 , b:20}'
+ [exEnd]
+ [exBegin]
+ JavaScriptParser parseFunction:'function foo() { var foo = {a:10 , b:20}; }'
+ [exEnd]
+ [exBegin]
+ JavaScriptParser parseExpression:'function foo() { var {a,b} = foo(); }'
+ [exEnd]
"
!
@@ -2265,7 +2281,7 @@
|isConst isStatic isLet var varIndex initValueExpression locals value
firstInitializer lastInitializer initializer varNode pos1 pos2
- declareVariable varName|
+ declareVariable varName vars|
isStatic := isLet := false.
isConst := isConstIn.
@@ -2301,90 +2317,105 @@
[true] whileTrue:[
(tokenType == #Identifier) ifFalse:[
- self parseError:'''identifier'' expected.'.
- ^ nil.
- ].
-
- varName := tokenName.
- declareVariable :=
- [
- isConst ifTrue:[
- isStatic ifTrue:[
- var := self declareStaticConstant:varName.
- ] ifFalse:[
- var := self declareConstant:varName inEnvironment:anEnvironment.
- ].
- ] ifFalse:[
- isStatic ifTrue:[
- var := self declareStaticVariable:varName.
- ] ifFalse:[
- var := self declareVariable:varName inEnvironment:anEnvironment.
- ].
- ].
+ tokenType == ${ ifFalse:[
+ self parseError:'''identifier'' expected.'.
+ ^ nil.
].
-
- pos1 := tokenPosition.
- pos2 := tokenPosition + tokenName size - 1.
- self nextToken.
-
- tokenType == $= ifTrue:[
+ self nextToken.
+ vars := self objectLiteralOrDescructuringExpression.
+ vars class == JavaScriptMultiVariableNode ifFalse:[
+ self parseError:'''identifier'' or destruct expression expected.'.
+ ].
+ tokenType == $= ifFalse:[
+ self parseError:'''='' expected.'.
+ ].
self nextToken.
initValueExpression := self nonCommaExpression.
- declareVariable value.
- var expressionForSetup:initValueExpression.
- ((tokenType == $;) or:[tokenType == $,]) ifFalse:[
- self parseError:'Expected '';'' or '','' after init expression.'.
+ self isSyntaxHighlighter ifFalse:[
+ self parseError:'destruct expressions are not yet supported'
].
- (interactiveMode or:[isDoIt]) ifTrue:[
- "/ perform the assignment
- value := initValueExpression evaluateIn:anEnvironment.
- var value:value.
- locals isEmptyOrNil ifTrue:[
- anEnvironment _localVariables:(locals := IdentityDictionary new).
+ ] ifTrue:[
+ varName := tokenName.
+ declareVariable :=
+ [
+ isConst ifTrue:[
+ isStatic ifTrue:[
+ var := self declareStaticConstant:varName.
+ ] ifFalse:[
+ var := self declareConstant:varName inEnvironment:anEnvironment.
+ ].
+ ] ifFalse:[
+ isStatic ifTrue:[
+ var := self declareStaticVariable:varName.
+ ] ifFalse:[
+ var := self declareVariable:varName inEnvironment:anEnvironment.
+ ].
+ ].
].
- locals at:var name put:var.
- ] ifFalse:[
- "/ if already in the real statements section, do the assignment here...
- firstInitializer isNil ifTrue:[
- firstInitializer := lastInitializer := JavaScriptStatementNode new.
- ] ifFalse:[
- lastInitializer nextStatement: JavaScriptStatementNode new.
- lastInitializer := lastInitializer nextStatement.
+
+ pos1 := tokenPosition.
+ pos2 := tokenPosition + tokenName size - 1.
+ self nextToken.
+
+ tokenType == $= ifTrue:[
+ self nextToken.
+ initValueExpression := self nonCommaExpression.
+ declareVariable value.
+ var expressionForSetup:initValueExpression.
+ ((tokenType == $;) or:[tokenType == $,]) ifFalse:[
+ self parseError:'Expected '';'' or '','' after init expression.'.
].
- (anEnvironment notNil and:[anEnvironment isInnerFunction]) ifTrue:[
- varNode := JavaScriptVariableNode
- type:#BlockVariable
- name:var name
- token:var
- index:var index
- block:anEnvironment
- from:anEnvironment
+ (interactiveMode or:[isDoIt]) ifTrue:[
+ "/ perform the assignment
+ value := initValueExpression evaluateIn:anEnvironment.
+ var value:value.
+ locals isEmptyOrNil ifTrue:[
+ anEnvironment _localVariables:(locals := IdentityDictionary new).
+ ].
+ locals at:var name put:var.
] ifFalse:[
- (anEnvironment notNil and:[anEnvironment isJavaScriptClassNode]) ifTrue:[
- varNode := JavaScriptVariableNode
- type:(isStatic ifTrue:[#ClassVariable] ifFalse:[#InstanceVariable])
+ "/ if already in the real statements section, do the assignment here...
+ firstInitializer isNil ifTrue:[
+ firstInitializer := lastInitializer := JavaScriptStatementNode new.
+ ] ifFalse:[
+ lastInitializer nextStatement: JavaScriptStatementNode new.
+ lastInitializer := lastInitializer nextStatement.
+ ].
+ (anEnvironment notNil and:[anEnvironment isInnerFunction]) ifTrue:[
+ varNode := JavaScriptVariableNode
+ type:#BlockVariable
name:var name
token:var
- index:nil.
+ index:var index
+ block:anEnvironment
+ from:anEnvironment
] ifFalse:[
- varNode := JavaScriptVariableNode
- type:#MethodVariable
- name:var name
- token:var
- index:var index.
+ (anEnvironment notNil and:[anEnvironment isJavaScriptClassNode]) ifTrue:[
+ varNode := JavaScriptVariableNode
+ type:(isStatic ifTrue:[#ClassVariable] ifFalse:[#InstanceVariable])
+ name:var name
+ token:var
+ index:nil.
+ ] ifFalse:[
+ varNode := JavaScriptVariableNode
+ type:#MethodVariable
+ name:var name
+ token:var
+ index:var index.
+ ].
+ ].
+ varNode startPosition: pos1 endPosition: pos2.
+ lastInitializer expression:(initializer := (JavaScriptAssignmentNode variable:varNode expression:initValueExpression) parent: lastInitializer).
+ anEnvironment isNil ifTrue:[
+ ] ifFalse:[
+ anEnvironment isJavaScriptClassNode ifFalse:[
+ var expressionForSetup:nil.
+ ].
].
].
- varNode startPosition: pos1 endPosition: pos2.
- lastInitializer expression:(initializer := (JavaScriptAssignmentNode variable:varNode expression:initValueExpression) parent: lastInitializer).
- anEnvironment isNil ifTrue:[
- ] ifFalse:[
- anEnvironment isJavaScriptClassNode ifFalse:[
- var expressionForSetup:nil.
- ].
- ].
+ ] ifFalse:[
+ declareVariable value.
].
- ] ifFalse:[
- declareVariable value.
].
tokenType == $; ifTrue:[
@@ -4013,6 +4044,112 @@
"Modified: / 22-06-2019 / 01:45:50 / Claus Gittinger"
!
+objectLiteralOrDescructuringExpression
+ "opening brace has already been read
+
+ objectLiteral -> '{' [ slotName ':' literal { , slotName ':' literal } ] '}'
+ desctruct -> '{' [ slotName , slotName , ... '}'
+ "
+
+ |slots name anyNonConstant expr obj
+ jso jc mkarr pos1 pos2 node isLiteral isDestruct|
+
+ anyNonConstant := isLiteral := isDestruct := false.
+ pos1 := tokenLastEndPosition.
+
+ slots := OrderedCollection new.
+ [ tokenType == $} ] whileFalse:[
+ (tokenType == #Identifier) ifTrue:[
+ name := tokenName.
+ ] ifFalse:[
+ (tokenType == #String) ifTrue:[
+ name := tokenValue asSymbol.
+ ] ifFalse:[
+ self parseError:'Identifier expected'.
+ ]
+ ].
+ self nextToken.
+
+ tokenType == $: ifTrue:[
+ isDestruct ifTrue:[
+ self parseError:'"," expected in destruct'.
+ ].
+ isLiteral := true
+ ] ifFalse:[
+ tokenType == $, ifTrue:[
+ isLiteral ifTrue:[
+ self parseError:'":" expected in object literal'.
+ ].
+ isDestruct := true
+ ] ifFalse:[
+ tokenType == $} ifFalse:[
+ self parseError:(isLiteral
+ ifTrue:['":" expected']
+ ifFalse:[
+ isDestruct
+ ifTrue:['"," expected']
+ ifFalse:['":" or "," expected']]).
+ ].
+ isLiteral ifTrue:[
+ self parseError:'":" expected in object literal'.
+ ].
+ isDestruct := true
+ ].
+ ].
+
+ isLiteral ifTrue:[
+ self nextToken.
+ expr := self nonCommaExpression.
+ slots add:(name asSymbol -> expr).
+ expr isConstant ifFalse:[
+ anyNonConstant := true
+ ].
+ ] ifFalse:[
+ slots add:(name asSymbol).
+ ].
+
+ tokenType == $} ifFalse:[
+ tokenType == $, ifFalse:[
+ self parseError:'"," expected'.
+ ].
+ self nextToken.
+ ].
+ ].
+
+ self nextToken.
+ pos2 := tokenLastEndPosition.
+
+ isDestruct ifTrue:[
+ ^ JavaScriptMultiVariableNode new
+ slots:slots;
+ startPosition:pos1 endPosition:pos2;
+ yourself
+ ].
+
+ anyNonConstant ifTrue:[
+ "/ generate a structure dynamically
+ jso := VariableNode globalNamed:#JavaScriptObject.
+ jso startPosition:-1 endPosition:-1.
+
+ jc := JavaScriptConstantNode type:#Array value:(slots map:#key) asArray.
+ jc startPosition:-1 endPosition:-1.
+
+ mkarr := Parser genMakeArrayWith:(slots map:#value).
+ mkarr startPosition:pos1 endPosition:pos2.
+
+ node := MessageNode receiver:jso selector:#newWith:values: arg1:jc arg2:mkarr.
+ ] ifFalse:[
+ "/ generate a structure
+ obj := JavaScriptObject newWith:(slots map:#key) values:((slots map:#value) map:#value).
+ node := JavaScriptConstantNode type:#Object value:obj.
+ ].
+ node startPosition:pos1 endPosition:pos2.
+ ^ node
+
+ "Modified: / 16-07-2012 / 21:15:24 / cg"
+ "Modified: / 22-06-2019 / 01:45:50 / Claus Gittinger"
+!
+
powerExpression
"powerExpr -> unaryExpr ** unaryExpr
"
@@ -4134,7 +4271,10 @@
].
tokenType == ${ ifTrue:[
self nextToken.
- expr := self objectLiteral.
+ expr := self objectLiteralOrDescructuringExpression.
+ expr class == JavaScriptMultiVariableNode ifTrue:[
+ self halt.
+ ].
^ expr
].
@@ -8026,6 +8166,25 @@
^ visitor visitJavaScriptBinaryNode:self
! !
+!JavaScriptParser::JavaScriptMultiVariableNode class methodsFor:'documentation'!
+
+documentation
+"
+ the left side of a destructuring expression;
+ eg. { foo, bar } = ...
+"
+! !
+
+!JavaScriptParser::JavaScriptMultiVariableNode methodsFor:'accessing'!
+
+slots
+ ^ slots
+!
+
+slots:something
+ slots := something.
+! !
+
!JavaScriptParser::JavaScriptReturnNode methodsFor:'accessing'!
environmentToReturnFrom