Created
April 1, 2012 18:52
-
-
Save dalehenrich/2277677 to your computer and use it in GitHub Desktop.
simple JSON parser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'From Pharo1.3 of 16 June 2011 [Latest update: #13315] on 23 March 2012 at 9:24:50 am'! | |
Object subclass: #MCFileTreeJsonParser | |
instanceVariableNames: 'stream' | |
classVariableNames: '' | |
poolDictionaries: '' | |
category: 'MonticelloFileTree-Core'! | |
!MCFileTreeJsonParser methodsFor: 'adding' stamp: 'dkh 3/1/2012 16:36:43'! | |
addProperty: anAssociation to: anObject | |
"Add the property anAssociation described with key and value to anObject. Subclasses might want to refine this implementation." | |
^ anObject | |
add: anAssociation; | |
yourself! ! | |
!MCFileTreeJsonParser methodsFor: 'adding' stamp: 'dkh 3/1/2012 16:36:43'! | |
addValue: anObject to: aCollection | |
"Add anObject to aCollection. Subclasses might want to refine this implementation." | |
^ aCollection copyWith: anObject! ! | |
!MCFileTreeJsonParser methodsFor: 'creating' stamp: 'dkh 3/1/2012 16:36:43'! | |
createArray | |
"Create an empty collection. Subclasses might want to refine this implementation." | |
^ Array new! ! | |
!MCFileTreeJsonParser methodsFor: 'creating' stamp: 'dkh 3/1/2012 16:36:43'! | |
createFalse | |
"Create the false literal. Subclasses might want to refine this implementation." | |
^ false! ! | |
!MCFileTreeJsonParser methodsFor: 'creating' stamp: 'dkh 3/1/2012 16:36:43'! | |
createNull | |
"Create the null literal. Subclasses might want to refine this implementation." | |
^ nil! ! | |
!MCFileTreeJsonParser methodsFor: 'creating' stamp: 'dkh 3/1/2012 16:36:43'! | |
createNumber: aString | |
"Create a number literal. Subclasses might want to refine this implementation." | |
^ aString asNumber! ! | |
!MCFileTreeJsonParser methodsFor: 'creating' stamp: 'dkh 3/1/2012 16:36:43'! | |
createObject | |
"Create an empty object. Subclasses might want to refine this implementation." | |
^ Dictionary new! ! | |
!MCFileTreeJsonParser methodsFor: 'creating' stamp: 'dkh 3/1/2012 16:36:43'! | |
createProperty: aKey with: aValue | |
"Create an empty attribute value pair. Subclasses might want to refine this implementation." | |
^ aKey -> aValue! ! | |
!MCFileTreeJsonParser methodsFor: 'creating' stamp: 'dkh 3/1/2012 16:36:43'! | |
createString: aString | |
"Create a string literal. Subclasses might want to refine this implementation." | |
^ aString! ! | |
!MCFileTreeJsonParser methodsFor: 'creating' stamp: 'dkh 3/1/2012 16:36:43'! | |
createTrue | |
"Create the true literal. Subclasses might want to refine this implementation." | |
^ true! ! | |
!MCFileTreeJsonParser methodsFor: 'private' stamp: 'dkh 3/1/2012 16:36:43'! | |
error: aString | |
"Raise a parse error labelled aString." | |
^ JSJsonSyntaxError signal: aString! ! | |
!MCFileTreeJsonParser methodsFor: 'private' stamp: 'dkh 3/1/2012 16:36:43'! | |
expect: aString | |
"Expects aString and consume input, throw an error otherwise." | |
^ (self match: aString) ifFalse: [ self error: aString , ' expected' ]! ! | |
!MCFileTreeJsonParser methodsFor: 'private' stamp: 'dkh 3/1/2012 16:36:43'! | |
match: aString | |
"Tries to match aString, consume input and answer true if successful." | |
| position | | |
position := stream position. | |
aString do: [ :each | | |
(stream atEnd or: [ stream next ~= each ]) ifTrue: [ | |
stream position: position. | |
^ false ] ]. | |
self whitespace. | |
^ true! ! | |
!MCFileTreeJsonParser methodsFor: 'private' stamp: 'dkh 3/1/2012 16:36:43'! | |
whitespace | |
"Strip whitespaces from the input stream." | |
[ stream atEnd not and: [ stream peek isSeparator ] ] | |
whileTrue: [ stream next ]! ! | |
!MCFileTreeJsonParser methodsFor: 'initialization' stamp: 'dkh 3/1/2012 16:36:43'! | |
initializeOn: aStream | |
self initialize. | |
stream := aStream! ! | |
!MCFileTreeJsonParser methodsFor: 'parsing' stamp: 'dkh 3/1/2012 16:36:43'! | |
parse | |
| result | | |
result := self whitespace; parseValue. | |
stream atEnd | |
ifFalse: [ self error: 'end of input expected' ]. | |
^ result! ! | |
!MCFileTreeJsonParser methodsFor: 'parsing' stamp: 'dkh 3/1/2012 16:36:43'! | |
parseArray | |
| result | | |
self expect: '['. | |
result := self createArray. | |
(self match: ']') | |
ifTrue: [ ^ result ]. | |
[ stream atEnd ] whileFalse: [ | |
result := self | |
addValue: self parseValue | |
to: result. | |
(self match: ']') | |
ifTrue: [ ^ result ]. | |
self expect: ',' ]. | |
self error: 'end of array expected'! ! | |
!MCFileTreeJsonParser methodsFor: 'parsing' stamp: 'dkh 3/1/2012 16:36:43'! | |
parseObject | |
| result | | |
self expect: '{'. | |
result := self createObject. | |
(self match: '}') | |
ifTrue: [ ^ result ]. | |
[ stream atEnd ] whileFalse: [ | |
result := self | |
addProperty: self parseProperty | |
to: result. | |
(self match: '}') | |
ifTrue: [ ^ result ]. | |
self expect: ',' ]. | |
self error: 'end of object expected'! ! | |
!MCFileTreeJsonParser methodsFor: 'parsing' stamp: 'dkh 3/1/2012 16:36:43'! | |
parseValue | |
| char | | |
stream atEnd ifFalse: [ | |
char := stream peek. | |
char = ${ | |
ifTrue: [ ^ self parseObject ]. | |
char = $[ | |
ifTrue: [ ^ self parseArray ]. | |
char = $" | |
ifTrue: [ ^ self parseString ]. | |
(char = $- or: [ char between: $0 and: $9 ]) | |
ifTrue: [ ^ self parseNumber ]. | |
(self match: 'true') | |
ifTrue: [ ^ self createTrue ]. | |
(self match: 'false') | |
ifTrue: [ ^ self createFalse ]. | |
(self match: 'null') | |
ifTrue: [ ^ self createNull ] ]. | |
self error: 'invalid input'! ! | |
!MCFileTreeJsonParser methodsFor: 'parsing-internal' stamp: 'dkh 3/1/2012 16:36:43'! | |
parseCharacter | |
| char | | |
(char := stream next) = $\ | |
ifFalse: [ ^ char ]. | |
(char := stream next) = $" | |
ifTrue: [ ^ char ]. | |
char = $\ | |
ifTrue: [ ^ char ]. | |
char = $/ | |
ifTrue: [ ^ char ]. | |
char = $b | |
ifTrue: [ ^ Character backspace ]. | |
char = $f | |
ifTrue: [ ^ Character newPage ]. | |
char = $n | |
ifTrue: [ ^ Character lf ]. | |
char = $r | |
ifTrue: [ ^ Character cr ]. | |
char = $t | |
ifTrue: [ ^ Character tab ]. | |
char = $u | |
ifTrue: [ ^ self parseCharacterHex ]. | |
self error: 'invalid escape character \' , (String with: char)! ! | |
!MCFileTreeJsonParser methodsFor: 'parsing-internal' stamp: 'dkh 3/1/2012 16:36:43'! | |
parseCharacterHex | |
| value | | |
value := self parseCharacterHexDigit. | |
3 timesRepeat: [ value := (value << 4) + self parseCharacterHexDigit ]. | |
^ Character codePoint: value! ! | |
!MCFileTreeJsonParser methodsFor: 'parsing-internal' stamp: 'dkh 3/1/2012 16:36:43'! | |
parseCharacterHexDigit | |
| digit | | |
stream atEnd ifFalse: [ | |
digit := stream next greaseInteger. | |
(digit between: "$0" 48 and: "$9" 57) | |
ifTrue: [ ^ digit - 48 ]. | |
(digit between: "$A" 65 and: "$F" 70) | |
ifTrue: [ ^ digit - 55 ]. | |
(digit between: "$a" 97 and: "$f" 102) | |
ifTrue: [ ^ digit - 87 ] ]. | |
self error: 'hex-digit expected'! ! | |
!MCFileTreeJsonParser methodsFor: 'parsing-internal' stamp: 'dkh 3/1/2012 16:36:43'! | |
parseNumber | |
| negated number | | |
negated := stream peek = $-. | |
negated ifTrue: [ stream next ]. | |
number := self parseNumberInteger. | |
(stream peek = $.) ifTrue: [ | |
stream next. | |
number := number + self parseNumberFraction ]. | |
(stream peek = $e or: [ stream peek = $E ]) ifTrue: [ | |
stream next. | |
number := number * self parseNumberExponent ]. | |
negated ifTrue: [ number := number negated ]. | |
^ self whitespace; createNumber: number! ! | |
!MCFileTreeJsonParser methodsFor: 'parsing-internal' stamp: 'dkh 3/1/2012 16:36:43'! | |
parseNumberExponent | |
| number negated | | |
number := 0. | |
negated := stream peek = $-. | |
(negated or: [ stream peek = $+ ]) ifTrue: [ stream next ]. | |
[ stream atEnd not and: [ stream peek isDigit ] ] | |
whileTrue: [ number := 10 * number + (stream next greaseInteger - 48) ]. | |
negated ifTrue: [ number := number negated ]. | |
^ 10 raisedTo: number! ! | |
!MCFileTreeJsonParser methodsFor: 'parsing-internal' stamp: 'dkh 3/1/2012 16:36:43'! | |
parseNumberFraction | |
| number power | | |
number := 0. | |
power := 1.0. | |
[ stream atEnd not and: [ stream peek isDigit ] ] whileTrue: [ | |
number := 10 * number + (stream next greaseInteger - 48). | |
power := power * 10.0 ]. | |
^ number / power! ! | |
!MCFileTreeJsonParser methodsFor: 'parsing-internal' stamp: 'dkh 3/1/2012 16:36:43'! | |
parseNumberInteger | |
| number | | |
number := 0. | |
[ stream atEnd not and: [ stream peek isDigit ] ] | |
whileTrue: [ number := 10 * number + (stream next greaseInteger - 48) ]. | |
^ number! ! | |
!MCFileTreeJsonParser methodsFor: 'parsing-internal' stamp: 'dkh 3/1/2012 16:36:43'! | |
parseProperty | |
| name value | | |
name := self parseString. | |
self expect: ':'. | |
value := self parseValue. | |
^ self createProperty: name with: value.! ! | |
!MCFileTreeJsonParser methodsFor: 'parsing-internal' stamp: 'dkh 3/1/2012 16:36:43'! | |
parseString | |
| result | | |
self expect: '"'. | |
result := WriteStream on: String new. | |
[ stream atEnd or: [ stream peek = $" ] ] | |
whileFalse: [ result nextPut: self parseCharacter ]. | |
^ self expect: '"'; createString: result contents! ! | |
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! | |
MCFileTreeJsonParser class | |
instanceVariableNames: ''! | |
!MCFileTreeJsonParser class methodsFor: 'instance creation' stamp: 'dkh 3/1/2012 16:36:43'! | |
new | |
self error: 'Instantiate the parser with a stream.'! ! | |
!MCFileTreeJsonParser class methodsFor: 'instance creation' stamp: 'dkh 3/1/2012 16:36:43'! | |
on: aStream | |
^ self basicNew initializeOn: aStream! ! | |
!MCFileTreeJsonParser class methodsFor: 'accessing' stamp: 'dkh 3/1/2012 16:36:43'! | |
parse: aString | |
^ self parseStream: aString readStream! ! | |
!MCFileTreeJsonParser class methodsFor: 'accessing' stamp: 'dkh 3/1/2012 16:36:43'! | |
parseStream: aStream | |
^ (self on: aStream) parse! ! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment