[PATCH] New syntax parser

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[PATCH] New syntax parser

Paolo Bonzini
This is the last missing brick to trigger the big conversion to the new
syntax (also a work of Daniele, with only a few minor fixes from me).
Actually since the parser is supposed to be backwards compatible I want
to check that it does convert the kernel sources correctly (in addition
to generating documentation from said sources).

The old parser is still accessible with "-f gst2" to gst-convert.

There are known bugs in the handling of comments, which mysteriously
disappear.  Well, I know why but I don't know how to fix them. :-P

In addition, another blocker for the 3.0 release surfaced, which is the
ability to store class variable initializers somewhere.  For now, I'm
adding an addClassVarName:value: method to which we'll add meat later.
The C parser should also call this same method so that initializers will
be handled properly.

Paolo

2007-08-08  Paolo Bonzini  <[hidden email]>
            Daniele Sciascia  <[hidden email]>

        * kernel/Class.st: Add #addClassVarName:value:.
        * scripts/Convert.st: Add GSTParser support.

packages/stinst/parser:
2007-08-08  Daniele Sciascia  <[hidden email]>

        * RBParser.st: Add #parseMethodInto:.
        * STFileParser.st: Support "self evaluate: nil".  Only skip "!"
        after evaluating.  Add #currentNamespace.
        * STLoader.st: Handle #addClassVarName:value:.
        * STLoaderObjs.st: Add #collectCategories.
        * GSTParser.st: New.


* looking for [hidden email]--2004b/smalltalk--devo--2.2--patch-503 to compare with
* comparing to [hidden email]--2004b/smalltalk--devo--2.2--patch-503
A  packages/stinst/parser/GSTParser.st
M  packages/stinst/parser/STFileParser.st
M  scripts/Convert.st
M  packages/stinst/parser/package.xml
M  packages/stinst/parser/RBParser.st
M  packages/stinst/parser/STLoader.st
M  packages/stinst/parser/STLoaderObjs.st
M  kernel/Class.st

* modified files

--- orig/kernel/Class.st
+++ mod/kernel/Class.st
@@ -96,14 +96,21 @@ superclass: aClass
 !Class methodsFor: 'accessing instances and variables'!
 
 addClassVarName: aString
-    "Add a class variable with the given name to the class pool dictionary"
+    "Add a class variable with the given name to the class pool dictionary."
     | sym |
     sym := aString asClassPoolKey.
-
     (self classPool includesKey: sym)
-        ifTrue: [ SystemExceptions.AlreadyDefined signalOn: aString ].
+        ifFalse: [ self classPool at: sym put: nil ].
 
-    self classPool at: sym put: nil
+    ^self classPool associationAt: sym
+!
+
+addClassVarName: aString value: valueBlock
+    "Add a class variable with the given name to the class pool dictionary,
+     and evaluate valueBlock as its initializer."
+    ^(self addClassVarName: aString)
+        value: valueBlock value;
+ yourself
 !
 
 bindingFor: aString


--- orig/packages/stinst/parser/RBParser.st
+++ mod/packages/stinst/parser/RBParser.st
@@ -292,6 +292,10 @@ parseMessagePattern
 parseMethod
     | methodNode |
     methodNode := self parseMessagePattern.
+    ^self parseMethodInto: methodNode!
+
+parseMethodInto: methodNode
+    tags := nil.
     self parseResourceTag.
     self addCommentsTo: methodNode.
     methodNode body: (self parseStatements: true).


--- orig/packages/stinst/parser/STFileParser.st
+++ mod/packages/stinst/parser/STFileParser.st
@@ -76,7 +76,7 @@ evaluate: node
     "This should be overridden because its result affects the parsing
      process: true means 'start parsing methods', false means 'keep
      evaluating'."
-    ^node statements size > 0 and: [ driver evaluate: node ]
+    ^node notNil and: [ node statements size > 0 and: [ driver evaluate: node ]]
 ! !
 
 !STFileParser methodsFor: 'utility'!
@@ -154,10 +154,11 @@ parseDoits
     [
  self atEnd ifTrue: [ ^false ].
  node := self parseStatements.
-        scanner stripSeparators.           "gobble doit terminating bang"
+        scanner stripSeparators.
  self evaluate: node
     ] whileFalse: [
-        self step            "gobble doit terminating bang"
+        (currentToken isSpecial and: [currentToken value == $!])
+    ifTrue: [ self step ]
     ].
     ^true
 !
@@ -258,6 +259,12 @@ evaluate: node
      process: true means 'start parsing methods', false means 'keep
      evaluating'. By default, always answer false."
     ^false
+!
+
+currentNamespace
+    ^Namespace current
+!
+
 ! !
 
 RBScanner subclass: #STFileScanner


--- orig/packages/stinst/parser/STLoader.st
+++ mod/packages/stinst/parser/STLoader.st
@@ -154,6 +154,9 @@ initialize
 
  toEvaluate: #addClassVarName:
  perform: #doSend:selector:arguments:;
+
+ toEvaluate: #addClassVarName:value:
+ perform: #doAddClassVarName:selector:arguments:;
 
  toEvaluate: #instanceVariableNames:
  perform: #doSend:selector:arguments:
@@ -266,6 +269,15 @@ doSend: receiver selector: selector argu
     ^false
 !
 
+doAddClassVarName: receiver selector: selector arguments: argumentNodes
+    | class classVarName value |
+    class := self resolveClass: receiver.
+    classVarName := argumentNodes first value asString.
+    value := argumentNodes last.
+    class addClassVarName: classVarName value: value.
+    ^false
+!
+
 doImport: receiver selector: selector arguments: argumentNodes
     | class namespace |
     receiver isMessage ifTrue: [ ^false ].


--- orig/packages/stinst/parser/STLoaderObjs.st
+++ mod/packages/stinst/parser/STLoaderObjs.st
@@ -532,10 +532,21 @@ superclass
 methodDictionary
     methods isNil ifTrue: [ methods := LookupTable new ].
     ^methods
-!
+!
 
 methodDictionary: aDictionary
     methods := aDictionary
+!
+
+collectCategories
+    | categories |
+    self methodDictionary isNil ifTrue: [ ^#() ].
+
+    categories := Set new.
+    self methodDictionary do:
+ [ :method | categories add: (method methodCategory) ].
+
+    ^categories asSortedCollection
 ! !
 
 !PseudoBehavior methodsFor: 'printing'!


--- orig/packages/stinst/parser/package.xml
+++ mod/packages/stinst/parser/package.xml
@@ -17,6 +17,7 @@
   <filein>STLoader.st</filein>
   <filein>SqueakParser.st</filein>
   <filein>SIFParser.st</filein>
+  <filein>GSTParser.st</filein>
   <filein>Exporter.st</filein>
 
   <test>
@@ -39,8 +40,9 @@
   <file>STSymTable.st</file>
   <file>RewriteTests.st</file>
   <file>SqueakParser.st</file>
+  <file>STFileParser.st</file>
   <file>SIFParser.st</file>
+  <file>GSTParser.st</file>
   <file>Exporter.st</file>
-  <file>STFileParser.st</file>
   <file>ChangeLog</file>
 </package>


--- orig/scripts/Convert.st
+++ mod/scripts/Convert.st
@@ -383,7 +383,8 @@ Eval [
     filter := [ :class | true ].
     converter := SyntaxConverter new.
     formats := Dictionary from: {
- 'gst' -> STInST.STFileInParser.
+ 'gst2' -> STInST.STFileInParser.
+ 'gst' -> STInST.GSTFileInParser.
  'squeak' -> STInST.SqueakFileInParser.
  'sif' -> STInST.SIFFileInParser
     }.



* added files

--- /dev/null
+++ mod/packages/stinst/parser/GSTParser.st
@@ -0,0 +1,334 @@
+"======================================================================
+|
+|   GNU Smalltalk syntax parser
+|
+|
+ ======================================================================"
+
+
+"======================================================================
+|
+| Copyright 2007 Free Software Foundation, Inc.
+| Written by Daniele Sciascia.
+|
+| This file is part of the GNU Smalltalk class library.
+|
+| The GNU Smalltalk class library is free software; you can redistribute it
+| and/or modify it under the terms of the GNU Lesser General Public License
+| as published by the Free Software Foundation; either version 2.1, or (at
+| your option) any later version.
+|
+| The GNU Smalltalk class library is distributed in the hope that it will be
+| useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
+| General Public License for more details.
+|
+| You should have received a copy of the GNU Lesser General Public License
+| along with the GNU Smalltalk class library; see the file COPYING.LIB.
+| If not, write to the Free Software Foundation, 59 Temple Place - Suite
+| 330, Boston, MA 02110-1301, USA.  
+|
+ ======================================================================"
+
+STInST.STFileInParser subclass: GSTFileInParser [
+    | class |
+
+    parseStatements [
+        | returnPosition statements node |
+
+        (currentToken isSpecial and: [currentToken value == $!])
+   ifTrue: [ ^RBSequenceNode statements: #() ].
+
+        statements := OrderedCollection new.
+
+        (currentToken isSpecial and: [currentToken value == $^])
+            ifTrue: [returnPosition := currentToken start.  
+                     self step.
+                     node := RBReturnNode return: returnPosition value: self parseAssignment.
+                     self addCommentsTo: node.
+                     statements add: node]
+            ifFalse: [node := self parseAssignment.
+                      self addCommentsTo: node.
+                      statements add: node].
+
+        (currentToken isSpecial and: [self skipToken: $[])
+            ifTrue: [self parseDeclaration: node. ^nil].
+
+        ^RBSequenceNode statements: statements
+    ]
+
+    parseDeclaration: node [
+        node isMessage ifTrue: [
+            (node selectorParts first value = 'subclass:')
+                ifTrue: [self parseClass: node. ^self].
+                
+            ((node receiver name = 'Namespace')
+                and: [node selectorParts first value = 'current:' ])
+                    ifTrue: [self parseNamespace: node. ^self].
+            
+            (node selectorParts first value = 'extend')
+                ifTrue: [self parseClassExtension: node. ^self]].
+        
+        node isVariable
+            ifTrue: [(node name = 'Eval')
+                        ifTrue: [self parseEval. ^self]].
+        
+        self parserError: 'expected Eval, Namespace or class definition'
+    ]
+    
+    parseEval [
+        | stmts |
+        stmts := self parseStatements: false.
+        self skipExpectedToken: $].
+        self evaluate: stmts.
+    ]
+    
+    parseNamespace: node [  
+        | namespace fullNamespace |
+        namespace := RBVariableNode
+    named: self driver currentNamespace name asString.
+        fullNamespace := RBVariableNode
+    named: (self driver currentNamespace nameIn: Smalltalk).
+
+        self evaluateMessageOn: namespace
+             selector: #addSubspace:
+             argument: node arguments first name asSymbol.
+          
+        self evaluateStatement: node.      
+        self parseSmalltalk.
+        self skipExpectedToken: $].
+
+        "restore previous namespace"
+        node parent: nil.
+        node arguments: { fullNamespace }.
+        self evaluateStatement: node
+    ]
+    
+    parseClassExtension: node [
+        class := node receiver.
+        self parseClassBody.
+        class := nil
+    ]
+    
+    parseClass: node [
+        self evaluateMessageOn: (node receiver)
+             selector: #subclass:
+             argument: (node arguments first name asSymbol).
+            
+        class := node arguments first.
+        self parseClassBody.
+        class := nil.
+    ]
+    
+    parseClassBody [
+        [ self skipToken: $] ]
+            whileFalse: [ self parseClassBodyElement ]
+    ]
+    
+    parseClassBodyElement [
+        | node classNode |
+        
+        "look for class tag"
+        (currentToken value = #< and: [self nextToken isKeyword])
+            ifTrue: [self parseClassTag. ^self].
+        
+        "look for class variable"
+        (currentToken isIdentifier and: [self nextToken isAssignment])
+            ifTrue: [self parseClassVariable. ^self].
+            
+        "class side"
+        ((currentToken isIdentifier
+            and: [self nextToken isIdentifier])
+            and: [self nextToken value = 'class'])
+                ifTrue: [classNode := RBVariableNode identifierToken: currentToken.
+                         self step.
+    
+                         (classNode = class)
+                            ifTrue: ["look for class method"
+                                     (self nextToken value = #>>)
+                                        ifTrue: [self step. self step.
+                                                 self parseMethodSourceOn: (self makeClassOf: classNode).
+                                                 ^self ].
+                                            
+                                     "look for metaclass"
+                                     (self nextToken value = $[)
+                                        ifTrue: [self parseMetaclass.
+                                                 ^self ].
+                                        
+                                     self parserError: 'invalid class body element'].
+                          
+                         "look for overriding class method"
+                         self step.
+                         (currentToken value = #>>)
+                            ifTrue: ["TODO: check that classNode is a superclass of the current class"
+                                     self step.
+                                     self parseMethodSourceOn: (self makeClassOf: classNode).
+                                     ^self].
+                          
+                          self parserError: 'invalid class body element' ].
+                        
+        "look for overriding method"
+        (currentToken isIdentifier and: [self nextToken value = #>>])
+            ifTrue: ["check that classNode is a superclass of the current class!!!"    
+                     classNode := RBVariableNode identifierToken: currentToken.
+                     self step. self step.
+                     self parseMethodSourceOn: classNode.
+                     ^self].
+              
+        node := self parseMessagePattern.
+        
+        "look for method"
+        (self skipToken: $[)
+            ifTrue: [self parseMethodSource: node. ^self].
+        
+        "look for instance variables"
+        (node selectorParts first value = #|)
+            ifTrue: [self parseInstanceVariables: node. ^self].
+            
+        self parserError: 'invalid class body element'
+    ]
+    
+    parseClassTag [
+        | selector argument stmt |
+        
+        self skipExpectedToken: #<.
+        
+        (currentToken isKeyword)
+            ifTrue: [selector := currentToken value asSymbol. self step]
+            ifFalse: [self parserError: 'expected keyword'].
+        
+        argument := self parsePrimitiveObject.
+        self skipExpectedToken: #>.
+        
+        argument isVariable
+            ifTrue: [stmt := RBMessageNode
+                         receiver: class
+                         selector: selector
+                         arguments: { argument }.
+                     self evaluateStatement: stmt]
+            ifFalse: [self evaluateMessageOn: class
+                           selector: selector
+                           argument: argument token value]
+    ]
+    
+    parseClassVariable [
+        | node stmt name |
+        
+        node := self parseAssignment.
+        node isAssignment
+            ifFalse: [self parserError: 'expected assignment'].
+        
+        self skipExpectedToken: $. .
+
+        name := RBLiteralNode value: (node variable name asSymbol).
+        node := self makeSequenceNode: node.
+        node := RBBlockNode body: node.
+        
+        stmt := RBMessageNode
+                receiver: class
+                selector: #addClassVarName:value:
+                arguments: { name . node }.
+
+        self evaluateStatement: stmt.
+    ]
+    
+    parseMetaclass [
+        | tmpClass |    
+        
+        self step. self step.
+        tmpClass := class.
+        class := self makeClassOf: class.
+        self parseClassBody.
+        class := tmpClass
+    ]
+    
+    parseMethodSource: patternNode [
+        self parseMethodSource: patternNode on: class
+    ]
+    
+    parseMethodSourceOn: classNode [
+        | patternNode |
+        
+        patternNode := self parseMessagePattern.
+        self skipExpectedToken: $[.
+        self parseMethodSource: patternNode on: classNode.
+    ]
+    
+    parseMethodSource: patternNode on: classNode [
+        "TODO: parse category tag inside methods"
+        | methodNode source start stop |
+        
+        start := patternNode selectorParts first start - 1.
+        methodNode := self parseMethodInto: patternNode.
+        stop := currentToken start - 1.
+        self skipExpectedToken: $].
+        source := scanner stream copyFrom: start to: stop.
+        source := MappedCollection collection: source map: (1 - start to: stop).
+        methodNode source: source.
+        
+        self evaluateMessageOn: classNode
+             selector: #methodsFor:
+             argument: nil.
+        
+        self compile: methodNode
+    ]
+    
+    parseInstanceVariables: node [
+        | vars |
+            
+        vars := (node arguments at: 1) name.
+        [currentToken isIdentifier]
+            whileTrue: [vars := vars , ' ' , currentToken value.
+        
+                        self step ].      
+        self skipExpectedToken: #|.
+        
+        self evaluateMessageOn: class
+             selector: #instanceVariableNames:
+             argument: vars.
+    ]
+    
+    evaluateMessageOn: rec selector: sel argument: arg [
+        | stmt |
+          
+        stmt := RBMessageNode
+            receiver: rec
+            selector: sel
+            arguments: { RBLiteralNode value: arg }.
+        
+        self evaluateStatement: stmt.
+    ]
+    
+    evaluateStatement: node [
+        ^self evaluate: (self makeSequenceNode: node).
+    ]
+    
+    makeSequenceNode: stmt [
+        | seq |
+ seq := RBSequenceNode
+            leftBar: nil
+            temporaries: #()
+            rightBar: nil.
+        seq statements: { stmt }.
+        seq periods: #().
+ ^seq
+    ]
+    
+    makeClassOf: node [
+        ^RBMessageNode
+        receiver: node
+        selector: #class
+        arguments: #()
+    ]
+
+    skipToken: tokenValue [
+        (currentToken value = tokenValue)
+            ifTrue: [self step. ^true]
+            ifFalse: [^false]
+    ]
+    
+    skipExpectedToken: tokenValue [
+        (self skipToken: tokenValue)
+            ifFalse: [self parserError: ('expected ' , tokenValue asSymbol)]
+    ]
+]


_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk