1 #import
<Foundation
/Foundation.h
>
5 // Only used here
, so define interface locally
6 @interface Reader
: NSObject
8 - (id
)initWithTokens
:(NSArray
*)toks
;
17 @implementation Reader
22 - (id
)initWithTokens
:(NSArray
*)toks
{
32 return [self initWithTokens
:@
[]];
37 return _tokens
[_position
-1];
41 if ([_tokens count
] > _position
) {
42 return _tokens
[_position
];
51 NSArray
* tokenize
(NSString
*str
) {
52 NSRegularExpression
*regex
= [NSRegularExpression
53 regularExpressionWithPattern
:@"
[\\s
,]*(~@|
[\\[\\]{}()'`~^@]|\"(?:[\\\\].|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}()'\"`@
,;]+)"
57 NSArray
*matches
= [regex
60 range
:NSMakeRange
(0, [str
length])];
62 NSMutableArray
* tokens
= [NSMutableArray array
];
63 for (NSTextCheckingResult
*match in matches
) {
64 NSString
* mstr
= [str substringWithRange
:[match rangeAtIndex
:1]];
65 if ([mstr characterAtIndex
:0] == ';') { continue
; }
66 [tokens addObject
:mstr
];
71 NSObject
* read_atom
(Reader
* rdr
) {
72 NSRegularExpression
*regex
= [NSRegularExpression
73 regularExpressionWithPattern
:@"
(^
-?
[0-9]+$
)|
(^
-?
[0-9][0-9.
]*$
)|
(^nil$
)|
(^true$
)|
(^false$
)|^
\"((?
:[\\\\].|
[^
\\\\\"])*)\"$|^
\"(.
*)$|
:(.
*)|
(^
[^
\"]*$
)"
76 NSNumberFormatter
*numf
= [[NSNumberFormatter alloc
] init
];
77 numf.numberStyle
= NSNumberFormatterDecimalStyle
;
79 NSString
*token
= [rdr next
];
81 NSArray
*matches
= [regex
84 range
:NSMakeRange
(0, [token
length])];
86 if ([matches count
] > 0) {
88 NSTextCheckingResult
*match
= matches
[0];
89 if ([match rangeAtIndex
:1].location
< -1ULL/2) { // integer
90 return [numf numberFromString
:token
];
91 } else if ([match rangeAtIndex
:2].location
< -1ULL/2) { // float
92 return [numf numberFromString
:token
];
93 } else if ([match rangeAtIndex
:3].location
< -1ULL/2) { // nil
94 return [NSNull alloc
];
95 } else if ([match rangeAtIndex
:4].location
< -1ULL/2) { // true
96 return [MalTrue alloc
]; // TODO
: intern
97 } else if ([match rangeAtIndex
:5].location
< -1ULL/2) { // false
98 return [MalFalse alloc
]; // TODO
: intern
99 } else if ([match rangeAtIndex
:6].location
< -1ULL/2) { // string
100 NSString
* str
= [token substringWithRange
:[match rangeAtIndex
:6]];
102 stringByReplacingOccurrencesOfString
:@"
\\\\" withString
:@"
\u029e"
]
103 stringByReplacingOccurrencesOfString
:@"
\\\"" withString
:@"
\""
]
104 stringByReplacingOccurrencesOfString
:@"
\\n" withString
:@"
\n"
]
105 stringByReplacingOccurrencesOfString
:@"
\u029e" withString
:@"
\\"
];
106 } else if ([match rangeAtIndex
:7].location
< -1ULL/2) { // string
107 @throw @"read_atom
: expected
'\"', got EOF"
;
108 } else if ([match rangeAtIndex
:8].location
< -1ULL/2) { // keyword
109 return [NSString stringWithFormat
:@"
\u029e%@",
110 [token substringWithRange
:[match rangeAtIndex
:8]]];
111 } else if ([match rangeAtIndex
:9].location
< -1ULL/2) { // symbol
112 return [MalSymbol stringWithString
:token
];
116 @throw @"read_atom
: invalid token"
;
119 // Only used locally
, so declare here
120 NSObject
* read_form
(Reader
* rdr
);
122 NSArray
* read_list
(Reader
* rdr
, char start
, char end) {
123 NSString
* token
= [rdr next
];
124 NSMutableArray
* ast
= [NSMutableArray array
];
126 if ([token characterAtIndex
:0] != start
) {
127 @throw
[NSString stringWithFormat
:@"expected
'%c'"
, start
];
129 while ((token
= [rdr peek
]) && ([token characterAtIndex
:0] != end)) {
130 [ast addObject
:read_form
(rdr
)];
133 @throw
[NSString stringWithFormat
:@"expected
'%c', got EOF"
, end];
139 NSObject
* read_form
(Reader
* rdr
) {
140 NSString
*token
= [rdr peek
];
141 switch ([token characterAtIndex
:0]) {
142 case '\'': [rdr next
];
143 return @
[[MalSymbol stringWithString
:@"quote"
],
145 case '`': [rdr next
];
146 return @
[[MalSymbol stringWithString
:@"quasiquote"
],
148 case '~': [rdr next
];
149 if ([token isEqualToString
:@"~@"
]) {
150 return @
[[MalSymbol stringWithString
:@"splice
-unquote"
],
153 return @
[[MalSymbol stringWithString
:@"unquote"
],
156 case '^': [rdr next
];
157 NSObject
* meta = read_form
(rdr
);
158 return @
[[MalSymbol stringWithString
:@"with
-meta"
],
161 case '@': [rdr next
];
162 return @
[[MalSymbol stringWithString
:@"deref"
],
167 @throw @"unexpected
')'"
;
169 return read_list
(rdr
, '(', ')');
173 @throw @"unexpected
']'"
;
175 return [MalVector fromArray
:read_list
(rdr
, '[', ']')];
179 @throw @"unexpected
'}'"
;
181 return hash_map
(read_list
(rdr
, '{', '}'));
183 return read_atom
(rdr
);
187 NSObject
* read_str
(NSString
*str
) {
188 NSArray
* tokens
= tokenize
(str
);
189 if ([tokens count
] == 0) { @throw
[NSException exceptionWithName
:@"ReaderContinue"
190 reason
:@"empty token"
192 //if ([tokens count
] == 0) { @throw
[[MalContinue alloc
] init
]; }
193 return read_form
([[Reader alloc
] initWithTokens
:tokens
]);