ソースを参照

Merge pull request #689 from matthias-springer/number-exp-syntax

Support exponential writing for numbers
Nicolas Petton 10 年 前
コミット
d24d3a9a2b
4 ファイル変更118 行追加53 行削除
  1. 2 1
      js/Compiler-Tests.js
  2. 49 48
      st/Compiler-Tests.st
  3. 64 3
      support/parser.js
  4. 3 1
      support/parser.pegjs

+ 2 - 1
js/Compiler-Tests.js

@@ -515,9 +515,10 @@ self._should_return_("foo ^ false",false);
 self._should_return_("foo ^ #{1->2. 3->4}",smalltalk.HashedCollection._from_([(1).__minus_gt((2)),(3).__minus_gt((4))]));
 self._should_return_("foo ^ #hello","hello");
 self._should_return_("foo ^ -123.456",(-123.456));
+self._should_return_("foo ^ -2.5e4",(-25000));
 return self}, function($ctx1) {$ctx1.fill(self,"testLiterals",{},smalltalk.CodeGeneratorTest)})},
 args: [],
-source: "testLiterals\x0a\x09self should: 'foo ^ 1' return: 1.\x0a\x09self should: 'foo ^ ''hello''' return: 'hello'.\x0a\x09self should: 'foo ^ #(1 2 3 4)' return: #(1 2 3 4).\x0a\x09self should: 'foo ^ {1. [:x | x ] value: 2. 3. [4] value}' return: #(1 2 3 4).\x0a\x09self should: 'foo ^ true' return: true.\x0a\x09self should: 'foo ^ false' return: false.\x0a\x09self should: 'foo ^ #{1->2. 3->4}' return: #{1->2. 3->4}.\x0a\x09self should: 'foo ^ #hello' return: #hello.\x0a\x09self should: 'foo ^ -123.456' return: -123.456",
+source: "testLiterals\x0a\x09self should: 'foo ^ 1' return: 1.\x0a\x09self should: 'foo ^ ''hello''' return: 'hello'.\x0a\x09self should: 'foo ^ #(1 2 3 4)' return: #(1 2 3 4).\x0a\x09self should: 'foo ^ {1. [:x | x ] value: 2. 3. [4] value}' return: #(1 2 3 4).\x0a\x09self should: 'foo ^ true' return: true.\x0a\x09self should: 'foo ^ false' return: false.\x0a\x09self should: 'foo ^ #{1->2. 3->4}' return: #{1->2. 3->4}.\x0a\x09self should: 'foo ^ #hello' return: #hello.\x0a\x09self should: 'foo ^ -123.456' return: -123.456.\x0a\x09self should: 'foo ^ -2.5e4' return: -25000.",
 messageSends: ["should:return:", "->"],
 referencedClasses: []
 }),

+ 49 - 48
st/Compiler-Tests.st

@@ -121,13 +121,6 @@ tearDown
 
 !CodeGeneratorTest methodsFor: 'testing'!
 
-should: aString return: anObject
-	^ self 
-		should: aString 
-		receiver: receiver 
-		return: anObject
-!
-
 should: aString receiver: anObject return: aResult
 	| method result |
 
@@ -136,6 +129,13 @@ should: aString receiver: anObject return: aResult
 	result := receiver perform: method selector.
 	anObject class removeCompiledMethod: method.
 	self assert: aResult equals: result
+!
+
+should: aString return: anObject
+	^ self 
+		should: aString 
+		receiver: receiver 
+		return: anObject
 ! !
 
 !CodeGeneratorTest methodsFor: 'tests'!
@@ -180,6 +180,12 @@ testDynamicDictionaryElementsOrdered
 ' return: #{'foo'->1. 'bar'->2}.
 !
 
+testGlobalVar
+	self should: 'foo ^ eval class' return: BlockClosure.
+	self should: 'foo ^ Math cos: 0' return: 1.
+	self should: 'foo ^ NonExistingVar' return: nil
+!
+
 testInnerTemporalDependentElementsOrdered
 	self should: 'foo
 	| x |
@@ -203,6 +209,10 @@ testInnerTemporalDependentElementsOrdered
 ' return: #{'foo'->1. 'bar'->2}.
 !
 
+testJSStatement
+	self should: 'foo <return 2+3>' return: 5
+!
+
 testLiterals
 	self should: 'foo ^ 1' return: 1.
 	self should: 'foo ^ ''hello''' return: 'hello'.
@@ -212,7 +222,8 @@ testLiterals
 	self should: 'foo ^ false' return: false.
 	self should: 'foo ^ #{1->2. 3->4}' return: #{1->2. 3->4}.
 	self should: 'foo ^ #hello' return: #hello.
-	self should: 'foo ^ -123.456' return: -123.456
+	self should: 'foo ^ -123.456' return: -123.456.
+	self should: 'foo ^ -2.5e4' return: -25000.
 !
 
 testLocalReturn
@@ -233,6 +244,10 @@ testMessageSends
 	self should: 'foo ^ 1 to: 5 by: 2' return: #(1 3 5)
 !
 
+testMultipleSequences
+	self should: 'foo | a b c | a := 2. b := 3. c := a + b. ^ c * 6' return: 30
+!
+
 testMutableLiterals
 	"Mutable literals must be aliased in cascades.
 	See https://github.com/amber-smalltalk/amber/issues/428"
@@ -250,6 +265,10 @@ testNestedIfTrue
 	self should: 'foo true ifTrue: [ false ifTrue: [ ^ 1 ] ]' return: receiver.
 !
 
+testNestedSends
+	self should: 'foo ^ (Point x: (Point x: 2 y: 3) y: 4) asString' return: (Point x: (2@3) y: 4) asString
+!
+
 testNonLocalReturn
 	self should: 'foo [ ^ 1 ] value' return: 1.
 	self should: 'foo [ ^ 1 + 1 ] value' return: 2.
@@ -276,6 +295,17 @@ testSendReceiverAndArgumentsOrdered
 ' return: {Array. 2}.
 !
 
+testSuperSend
+	self 
+		should: 'foo ^ super isBoolean' 
+		receiver: true
+		return: false
+!
+
+testThisContext
+	self should: 'foo ^ [ thisContext ] value outerContext == thisContext' return: true
+!
+
 testifFalse
 	self should: 'foo true ifFalse: [ ^ 1 ]' return: receiver.
 	self should: 'foo false ifFalse: [ ^ 2 ]' return: 2.
@@ -341,35 +371,6 @@ testifTrueIfFalse
 	
 	self should: 'foo ^ false ifTrue: [ 2 ] ifFalse: [ 1 ]' return: 1.
 	self should: 'foo ^ true ifTrue: [ 2 ] ifFalse: [ 1 ]' return: 2.
-!
-
-testGlobalVar
-	self should: 'foo ^ eval class' return: BlockClosure.
-	self should: 'foo ^ Math cos: 0' return: 1.
-	self should: 'foo ^ NonExistingVar' return: nil
-!
-
-testJSStatement
-	self should: 'foo <return 2+3>' return: 5
-!
-
-testMultipleSequences
-	self should: 'foo | a b c | a := 2. b := 3. c := a + b. ^ c * 6' return: 30
-!
-
-testNestedSends
-	self should: 'foo ^ (Point x: (Point x: 2 y: 3) y: 4) asString' return: (Point x: (2@3) y: 4) asString
-!
-
-testSuperSend
-	self 
-		should: 'foo ^ super isBoolean' 
-		receiver: true
-		return: false
-!
-
-testThisContext
-	self should: 'foo ^ [ thisContext ] value outerContext == thisContext' return: true
 ! !
 
 CodeGeneratorTest subclass: #InliningCodeGeneratorTest
@@ -388,10 +389,6 @@ CodeGeneratorTest subclass: #InterpreterTest
 
 !InterpreterTest methodsFor: 'parsing'!
 
-parse: aString forClass: aClass
-	^ self analyze: (self parse: aString) forClass: aClass
-!
-
 analyze: aNode forClass: aClass
 	(SemanticAnalyzer on: aClass) visit: aNode.
 	^ aNode
@@ -399,6 +396,10 @@ analyze: aNode forClass: aClass
 
 parse: aString
 	^ Smalltalk current parse: aString
+!
+
+parse: aString forClass: aClass
+	^ self analyze: (self parse: aString) forClass: aClass
 ! !
 
 !InterpreterTest methodsFor: 'private'!
@@ -426,19 +427,19 @@ interpret: aString receiver: anObject withArguments: aDictionary
 
 !InterpreterTest methodsFor: 'testing'!
 
-should: aString return: anObject
-	^ self 
-		should: aString
-		receiver: receiver
-		return: anObject
-!
-
 should: aString receiver: anObject return: aResult
 	receiver := anObject.
 	
 	^ self 
 		assert: (self interpret: aString receiver: receiver withArguments: #{})
 		equals: aResult
+!
+
+should: aString return: anObject
+	^ self 
+		should: aString
+		receiver: receiver
+		return: anObject
 ! !
 
 TestCase subclass: #ScopeVarTest

+ 64 - 3
support/parser.js

@@ -50,6 +50,7 @@ smalltalk.parser = (function(){
         "symbol": parse_symbol,
         "bareSymbol": parse_bareSymbol,
         "number": parse_number,
+        "numberExp": parse_numberExp,
         "hex": parse_hex,
         "float": parse_float,
         "integer": parse_integer,
@@ -947,11 +948,14 @@ smalltalk.parser = (function(){
         var pos0;
         
         pos0 = clone(pos);
-        result0 = parse_hex();
+        result0 = parse_numberExp();
         if (result0 === null) {
-          result0 = parse_float();
+          result0 = parse_hex();
           if (result0 === null) {
-            result0 = parse_integer();
+            result0 = parse_float();
+            if (result0 === null) {
+              result0 = parse_integer();
+            }
           }
         }
         if (result0 !== null) {
@@ -972,6 +976,63 @@ smalltalk.parser = (function(){
         return result0;
       }
       
+      function parse_numberExp() {
+        var cacheKey = "numberExp@" + pos.offset;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = clone(cachedResult.nextPos);
+          return cachedResult.result;
+        }
+        
+        var result0, result1, result2;
+        var pos0, pos1;
+        
+        pos0 = clone(pos);
+        pos1 = clone(pos);
+        result0 = parse_float();
+        if (result0 === null) {
+          result0 = parse_integer();
+        }
+        if (result0 !== null) {
+          if (input.charCodeAt(pos.offset) === 101) {
+            result1 = "e";
+            advance(pos, 1);
+          } else {
+            result1 = null;
+            if (reportFailures === 0) {
+              matchFailed("\"e\"");
+            }
+          }
+          if (result1 !== null) {
+            result2 = parse_integer();
+            if (result2 !== null) {
+              result0 = [result0, result1, result2];
+            } else {
+              result0 = null;
+              pos = clone(pos1);
+            }
+          } else {
+            result0 = null;
+            pos = clone(pos1);
+          }
+        } else {
+          result0 = null;
+          pos = clone(pos1);
+        }
+        if (result0 !== null) {
+          result0 = (function(offset, line, column, n) {return parseFloat(n.join(""));})(pos0.offset, pos0.line, pos0.column, result0);
+        }
+        if (result0 === null) {
+          pos = clone(pos0);
+        }
+        
+        cache[cacheKey] = {
+          nextPos: clone(pos),
+          result:  result0
+        };
+        return result0;
+      }
+      
       function parse_hex() {
         var cacheKey = "hex@" + pos.offset;
         var cachedResult = cache[cacheKey];

+ 3 - 1
support/parser.pegjs

@@ -21,14 +21,16 @@ bareSymbol         = val:(selector / binarySelector / node:string {return node._
                              ._position_((line).__at(column))
                              ._value_(val);
                   }
-number         = n:(hex / float / integer) {
+number         = n:(numberExp / hex / float / integer) {
                      return smalltalk.ValueNode._new()
                             ._position_((line).__at(column))
                             ._value_(n);
                  }
+numberExp      = n:((float / integer) "e" integer) {return parseFloat(n.join(""));}
 hex            = neg:[-]? "16r" num:[0-9a-fA-F]+ {return parseInt((neg + num.join("")), 16);}
 float          = neg:[-]?digits:[0-9]+ "." dec:[0-9]+ {return parseFloat((neg + digits.join("") + "." + dec.join("")), 10);}
 integer        = neg:[-]?digits:[0-9]+ {return (parseInt(neg+digits.join(""), 10));}
+
 literalArray   = "#(" rest:literalArrayRest {return rest;}
 bareLiteralArray   = "(" rest:literalArrayRest {return rest;}
 literalArrayRest   = ws lits:(lit:(parseTimeLiteral / bareLiteralArray / bareSymbol) ws {return lit._value();})* ws ")" {