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) {
87 NSTextCheckingResult
*match
= matches
[0];
88 if ([match rangeAtIndex
:1].location
!= -1) { // integer
89 return [numf numberFromString
:token
];
90 } else if ([match rangeAtIndex
:2].location
!= -1) { // float
91 return [numf numberFromString
:token
];
92 } else if ([match rangeAtIndex
:3].location
!= -1) { // nil
93 return [NSNull alloc
];
94 } else if ([match rangeAtIndex
:4].location
!= -1) { // true
95 return [MalTrue alloc
]; // TODO
: intern
96 } else if ([match rangeAtIndex
:5].location
!= -1) { // false
97 return [MalFalse alloc
]; // TODO
: intern
98 } else if ([match rangeAtIndex
:6].location
!= -1) { // string
99 NSString
* str
= [token substringWithRange
:[match rangeAtIndex
:6]];
101 stringByReplacingOccurrencesOfString
:@"
\\\"" withString
:@"
\""
]
102 stringByReplacingOccurrencesOfString
:@"
\\n" withString
:@"
\n"
]
103 stringByReplacingOccurrencesOfString
:@"
\\\\" withString
:@"
\\"
];
104 return [token substringWithRange
:[match rangeAtIndex
:6]];
105 } else if ([match rangeAtIndex
:7].location
!= -1) { // keyword
106 return [NSString stringWithFormat
:@"
\u029e%@",
107 [token substringWithRange
:[match rangeAtIndex
:7]]];
108 } else if ([match rangeAtIndex
:8].location
!= -1) { // symbol
109 return [MalSymbol stringWithString
:token
];
116 // Only used locally
, so declare here
117 NSObject
* read_form
(Reader
* rdr
);
119 NSArray
* read_list
(Reader
* rdr
, char start
, char end) {
120 NSString
* token
= [rdr next
];
121 NSMutableArray
* ast
= [NSMutableArray array
];
123 if ([token characterAtIndex
:0] != start
) {
124 @throw
[NSString stringWithFormat
:@"expected
'%c'"
, start
];
126 while ((token
= [rdr peek
]) && ([token characterAtIndex
:0] != end)) {
127 [ast addObject
:read_form
(rdr
)];
130 @throw
[NSString stringWithFormat
:@"expected
'%c', got EOF"
, end];
136 NSObject
* read_form
(Reader
* rdr
) {
137 NSString
*token
= [rdr peek
];
138 switch ([token characterAtIndex
:0]) {
139 case '\'': [rdr next
];
140 return @
[[MalSymbol stringWithString
:@"quote"
],
142 case '`': [rdr next
];
143 return @
[[MalSymbol stringWithString
:@"quasiquote"
],
145 case '~': [rdr next
];
146 if ([token isEqualToString
:@"~@"
]) {
147 return @
[[MalSymbol stringWithString
:@"splice
-unquote"
],
150 return @
[[MalSymbol stringWithString
:@"unquote"
],
153 case '^': [rdr next
];
154 NSObject
* meta = read_form
(rdr
);
155 return @
[[MalSymbol stringWithString
:@"with
-meta"
],
158 case '@': [rdr next
];
159 return @
[[MalSymbol stringWithString
:@"deref"
],
164 @throw @"unexpected
')'"
;
166 return read_list
(rdr
, '(', ')');
170 @throw @"unexpected
']'"
;
172 return [MalVector fromArray
:read_list
(rdr
, '[', ']')];
176 @throw @"unexpected
'}'"
;
178 return hash_map
(read_list
(rdr
, '{', '}'));
180 return read_atom
(rdr
);
184 NSObject
* read_str
(NSString
*str
) {
185 NSArray
* tokens
= tokenize
(str
);
186 if ([tokens count
] == 0) { @throw
[NSException exceptionWithName
:@"ReaderContinue"
187 reason
:@"empty token"
189 //if ([tokens count
] == 0) { @throw
[[MalContinue alloc
] init
]; }
190 return read_form
([[Reader alloc
] initWithTokens
:tokens
]);