• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// [The "BSD licence"]
2// Copyright (c) 2006-2007 Kay Roepke
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions
7// are met:
8// 1. Redistributions of source code must retain the above copyright
9//    notice, this list of conditions and the following disclaimer.
10// 2. Redistributions in binary form must reproduce the above copyright
11//    notice, this list of conditions and the following disclaimer in the
12//    documentation and/or other materials provided with the distribution.
13// 3. The name of the author may not be used to endorse or promote products
14//    derived from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27#import "DebugEventSocketProxy.h"
28#import "Token+DebuggerSupport.h"
29#include <string.h>
30
31static NSData *newlineData = nil;
32static unsigned lengthOfUTF8Ack = 0;
33
34@implementation DebugEventSocketProxy
35
36+ (void) initialize
37{
38	if (!newlineData) newlineData = [@"\n" dataUsingEncoding:NSUTF8StringEncoding];
39	if (!lengthOfUTF8Ack) lengthOfUTF8Ack = [[@"ack\n" dataUsingEncoding:NSUTF8StringEncoding] length];
40}
41
42- (id) init
43{
44	return [self initWithGrammarName:nil debuggerPort:DEFAULT_DEBUGGER_PORT];
45}
46
47- (id) initWithGrammarName:(NSString *)aGrammarName debuggerPort:(NSInteger)aPort
48{
49	self = [super init];
50	if (self) {
51		serverSocket = -1;
52		[self setGrammarName:aGrammarName];
53		if (aPort == -1) aPort = DEFAULT_DEBUGGER_PORT;
54		[self setDebuggerPort:aPort];
55	}
56	return self;
57}
58
59- (void) dealloc
60{
61	if (serverSocket != -1)
62		shutdown(serverSocket,SHUT_RDWR);
63	serverSocket = -1;
64	[debuggerFH release];
65    [self setGrammarName:nil];
66    [super dealloc];
67}
68
69/* Java stuff
70public void handshake() throws IOException {
71    if ( serverSocket==nil ) {
72        serverSocket = new ServerSocket(port);
73        socket = serverSocket.accept();
74        socket.setTcpNoDelay(true);
75        OutputStream os = socket.getOutputStream();
76        OutputStreamWriter osw = new OutputStreamWriter(os, "UTF8");
77        out = new PrintWriter(new BufferedWriter(osw));
78        InputStream is = socket.getInputStream();
79        InputStreamReader isr = new InputStreamReader(is, "UTF8");
80        in = new BufferedReader(isr);
81        out.println("ANTLR "+ DebugEventListener.PROTOCOL_VERSION);
82        out.println("grammar \""+ grammarFileName);
83        out.flush();
84        ack();
85    }
86}
87
88- (void) commence
89{
90    // don't bother sending event; listener will trigger upon connection
91}
92
93- (void) terminate
94{
95    [self transmit:@"terminate";
96    [out close];
97    try {
98        [socket close];
99    }
100    catch (IOException *ioe) {
101        ioe.printStackTrace(System.err);
102    }
103}
104
105- (void) ack
106{
107    try {
108        in.readLine();
109    }
110    catch (IOException ioe) {
111        ioe.printStackTrace(System.err);
112    }
113}
114
115protected void transmit(String event) {
116    out.println(event);
117    out.flush();
118    ack();
119}
120*/
121
122- (void) waitForDebuggerConnection
123{
124	if (serverSocket == -1) {
125		serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
126
127		NSAssert1(serverSocket != -1, @"Failed to create debugger socket. %s", strerror(errno));
128
129		int yes = 1;
130		setsockopt(serverSocket, SOL_SOCKET, SO_KEEPALIVE|SO_REUSEPORT|SO_REUSEADDR|TCP_NODELAY, (void *)&yes, sizeof(NSInteger));
131
132		struct sockaddr_in server_addr;
133		bzero(&server_addr, sizeof(struct sockaddr_in));
134		server_addr.sin_family = AF_INET;
135		server_addr.sin_port = htons([self debuggerPort]);
136		server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
137		NSAssert1( bind(serverSocket, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) != -1, @"bind(2) failed. %s", strerror(errno));
138
139		NSAssert1(listen(serverSocket,50) == 0, @"listen(2) failed. %s", strerror(errno));
140
141		NSLog(@"ANTLR waiting for debugger attach (grammar %@)", [self grammarName]);
142
143		debuggerSocket = accept(serverSocket, &debugger_sockaddr, &debugger_socklen);
144		NSAssert1( debuggerSocket != -1, @"accept(2) failed. %s", strerror(errno));
145
146		debuggerFH = [[NSFileHandle alloc] initWithFileDescriptor:debuggerSocket];
147		[self sendToDebugger:[NSString stringWithFormat:@"ANTLR %d", DebugProtocolVersion] waitForResponse:NO];
148		[self sendToDebugger:[NSString stringWithFormat:@"grammar \"%@", [self grammarName]] waitForResponse:NO];
149	}
150}
151
152- (void) waitForAck
153{
154	NSString *response;
155	@try {
156		NSData *newLine = [debuggerFH readDataOfLength:lengthOfUTF8Ack];
157		response = [[NSString alloc] initWithData:newLine encoding:NSUTF8StringEncoding];
158		if (![response isEqualToString:@"ack\n"]) @throw [NSException exceptionWithName:@"DebugEventSocketProxy" reason:@"illegal response from debugger" userInfo:nil];
159	}
160	@catch (NSException *e) {
161		NSLog(@"socket died or debugger misbehaved: %@ read <%@>", e, response);
162	}
163	@finally {
164		[response release];
165	}
166}
167
168- (void) sendToDebugger:(NSString *)message
169{
170	[self sendToDebugger:message waitForResponse:YES];
171}
172
173- (void) sendToDebugger:(NSString *)message waitForResponse:(BOOL)wait
174{
175	if (! debuggerFH ) return;
176	[debuggerFH writeData:[message dataUsingEncoding:NSUTF8StringEncoding]];
177	[debuggerFH writeData:newlineData];
178	if (wait) [self waitForAck];
179}
180
181- (NSInteger) serverSocket
182{
183    return serverSocket;
184}
185
186- (void) setServerSocket: (NSInteger) aServerSocket
187{
188    serverSocket = aServerSocket;
189}
190
191- (NSInteger) debuggerSocket
192{
193    return debuggerSocket;
194}
195
196- (void) setDebuggerSocket: (NSInteger) aDebuggerSocket
197{
198    debuggerSocket = aDebuggerSocket;
199}
200
201- (NSString *) grammarName
202{
203    return grammarName;
204}
205
206- (void) setGrammarName: (NSString *) aGrammarName
207{
208    if (grammarName != aGrammarName) {
209        [aGrammarName retain];
210        [grammarName release];
211        grammarName = aGrammarName;
212    }
213}
214
215- (NSInteger) debuggerPort
216{
217    return debuggerPort;
218}
219
220- (void) setDebuggerPort: (NSInteger) aDebuggerPort
221{
222    debuggerPort = aDebuggerPort;
223}
224
225- (NSString *) escapeNewlines:(NSString *)aString
226{
227	NSMutableString *escapedText;
228	if (aString) {
229		escapedText = [NSMutableString stringWithString:aString];
230		NSRange wholeString = NSMakeRange(0,[escapedText length]);
231		[escapedText replaceOccurrencesOfString:@"%" withString:@"%25" options:0 range:wholeString];
232		[escapedText replaceOccurrencesOfString:@"\n" withString:@"%0A" options:0 range:wholeString];
233		[escapedText replaceOccurrencesOfString:@"\r" withString:@"%0D" options:0 range:wholeString];
234	} else {
235		escapedText = [NSMutableString stringWithString:@""];
236	}
237	return escapedText;
238}
239
240#pragma mark -
241
242#pragma mark DebugEventListener Protocol
243- (void) enterRule:(NSString *)ruleName
244{
245	[self sendToDebugger:[NSString stringWithFormat:@"enterRule %@", ruleName]];
246}
247
248- (void) enterAlt:(NSInteger)alt
249{
250	[self sendToDebugger:[NSString stringWithFormat:@"enterAlt %d", alt]];
251}
252
253- (void) exitRule:(NSString *)ruleName
254{
255	[self sendToDebugger:[NSString stringWithFormat:@"exitRule %@", ruleName]];
256}
257
258- (void) enterSubRule:(NSInteger)decisionNumber
259{
260	[self sendToDebugger:[NSString stringWithFormat:@"enterSubRule %d", decisionNumber]];
261}
262
263- (void) exitSubRule:(NSInteger)decisionNumber
264{
265	[self sendToDebugger:[NSString stringWithFormat:@"exitSubRule %d", decisionNumber]];
266}
267
268- (void) enterDecision:(NSInteger)decisionNumber
269{
270	[self sendToDebugger:[NSString stringWithFormat:@"enterDecision %d", decisionNumber]];
271}
272
273- (void) exitDecision:(NSInteger)decisionNumber
274{
275	[self sendToDebugger:[NSString stringWithFormat:@"exitDecision %d", decisionNumber]];
276}
277
278- (void) consumeToken:(id<Token>)t
279{
280	[self sendToDebugger:[NSString stringWithFormat:@"consumeToken %@", [self escapeNewlines:[t description]]]];
281}
282
283- (void) consumeHiddenToken:(id<Token>)t
284{
285	[self sendToDebugger:[NSString stringWithFormat:@"consumeHiddenToken %@", [self escapeNewlines:[t description]]]];
286}
287
288- (void) LT:(NSInteger)i foundToken:(id<Token>)t
289{
290	[self sendToDebugger:[NSString stringWithFormat:@"LT %d %@", i, [self escapeNewlines:[t description]]]];
291}
292
293- (void) mark:(NSInteger)marker
294{
295	[self sendToDebugger:[NSString stringWithFormat:@"mark %d", marker]];
296}
297- (void) rewind:(NSInteger)marker
298{
299	[self sendToDebugger:[NSString stringWithFormat:@"rewind %d", marker]];
300}
301
302- (void) rewind
303{
304	[self sendToDebugger:@"rewind"];
305}
306
307- (void) beginBacktrack:(NSInteger)level
308{
309	[self sendToDebugger:[NSString stringWithFormat:@"beginBacktrack %d", level]];
310}
311
312- (void) endBacktrack:(NSInteger)level wasSuccessful:(BOOL)successful
313{
314	[self sendToDebugger:[NSString stringWithFormat:@"endBacktrack %d %d", level, successful ? 1 : 0]];
315}
316
317- (void) locationLine:(NSInteger)line column:(NSInteger)pos
318{
319	[self sendToDebugger:[NSString stringWithFormat:@"location %d %d", line, pos]];
320}
321
322- (void) recognitionException:(RecognitionException *)e
323{
324#warning TODO: recognition exceptions
325	// these must use the names of the corresponding Java exception classes, because ANTLRWorks recreates the exception
326	// objects on the Java side.
327	// Write categories for Objective-C exceptions to provide those names
328}
329
330- (void) beginResync
331{
332	[self sendToDebugger:@"beginResync"];
333}
334
335- (void) endResync
336{
337	[self sendToDebugger:@"endResync"];
338}
339
340- (void) semanticPredicate:(NSString *)predicate matched:(BOOL)result
341{
342	[self sendToDebugger:[NSString stringWithFormat:@"semanticPredicate %d %@", result?1:0, [self escapeNewlines:predicate]]];
343}
344
345- (void) commence
346{
347	// no need to send event
348}
349
350- (void) terminate
351{
352	[self sendToDebugger:@"terminate"];
353	@try {
354		[debuggerFH closeFile];
355	}
356	@finally {
357#warning TODO: make socket handling robust. too lazy now...
358		shutdown(serverSocket,SHUT_RDWR);
359		serverSocket = -1;
360	}
361}
362
363
364#pragma mark Tree Parsing
365- (void) consumeNode:(unsigned)nodeHash ofType:(NSInteger)type text:(NSString *)text
366{
367	[self sendToDebugger:[NSString stringWithFormat:@"consumeNode %u %d %@",
368		nodeHash,
369		type,
370		[self escapeNewlines:text]
371		]];
372}
373
374- (void) LT:(NSInteger)i foundNode:(unsigned)nodeHash ofType:(NSInteger)type text:(NSString *)text
375{
376	[self sendToDebugger:[NSString stringWithFormat:@"LN %d %u %d %@",
377		i,
378		nodeHash,
379		type,
380		[self escapeNewlines:text]
381		]];
382}
383
384
385#pragma mark AST Events
386
387- (void) createNilNode:(unsigned)hash
388{
389	[self sendToDebugger:[NSString stringWithFormat:@"nilNode %u", hash]];
390}
391
392- (void) createNode:(unsigned)hash text:(NSString *)text type:(NSInteger)type
393{
394	[self sendToDebugger:[NSString stringWithFormat:@"createNodeFromToken %u %d %@",
395		hash,
396		type,
397		[self escapeNewlines:text]
398		]];
399}
400
401- (void) createNode:(unsigned)hash fromTokenAtIndex:(NSInteger)tokenIndex
402{
403	[self sendToDebugger:[NSString stringWithFormat:@"createNode %u %d", hash, tokenIndex]];
404}
405
406- (void) becomeRoot:(unsigned)newRootHash old:(unsigned)oldRootHash
407{
408	[self sendToDebugger:[NSString stringWithFormat:@"becomeRoot %u %u", newRootHash, oldRootHash]];
409}
410
411- (void) addChild:(unsigned)childHash toTree:(unsigned)treeHash
412{
413	[self sendToDebugger:[NSString stringWithFormat:@"addChild %u %u", treeHash, childHash]];
414}
415
416- (void) setTokenBoundariesForTree:(unsigned)nodeHash From:(NSInteger)tokenStartIndex To:(NSInteger)tokenStopIndex
417{
418	[self sendToDebugger:[NSString stringWithFormat:@"setTokenBoundaries %u %d %d", nodeHash, tokenStartIndex, tokenStopIndex]];
419}
420
421
422
423@end
424