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 "ANTLRDebugEventProxy.h" 28#import "ANTLRToken+DebuggerSupport.h" 29#include <string.h> 30 31static NSData *newlineData = nil; 32static unsigned lengthOfUTF8Ack = 0; 33 34@implementation ANTLRDebugEventProxy 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- (void) waitForDebuggerConnection 70{ 71 if (serverSocket == -1) { 72 serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 73 74 NSAssert1(serverSocket != -1, @"Failed to create debugger socket. %s", strerror(errno)); 75 76 int yes = 1; 77 setsockopt(serverSocket, SOL_SOCKET, SO_KEEPALIVE|SO_REUSEPORT|SO_REUSEADDR|TCP_NODELAY, (void *)&yes, sizeof(NSInteger)); 78 79 struct sockaddr_in server_addr; 80 bzero(&server_addr, sizeof(struct sockaddr_in)); 81 server_addr.sin_family = AF_INET; 82 server_addr.sin_port = htons([self debuggerPort]); 83 server_addr.sin_addr.s_addr = htonl(INADDR_ANY); 84 NSAssert1( bind(serverSocket, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) != -1, @"bind(2) failed. %s", strerror(errno)); 85 86 NSAssert1(listen(serverSocket,50) == 0, @"listen(2) failed. %s", strerror(errno)); 87 88 NSLog(@"ANTLR waiting for debugger attach (grammar %@)", [self grammarName]); 89 90 debuggerSocket = accept(serverSocket, &debugger_sockaddr, &debugger_socklen); 91 NSAssert1( debuggerSocket != -1, @"accept(2) failed. %s", strerror(errno)); 92 93 debuggerFH = [[NSFileHandle alloc] initWithFileDescriptor:debuggerSocket]; 94 [self sendToDebugger:[NSString stringWithFormat:@"ANTLR %d", ANTLRDebugProtocolVersion] waitForResponse:NO]; 95 [self sendToDebugger:[NSString stringWithFormat:@"grammar \"%@", [self grammarName]] waitForResponse:NO]; 96 } 97} 98 99- (void) waitForAck 100{ 101 NSString *response; 102 @try { 103 NSData *newLine = [debuggerFH readDataOfLength:lengthOfUTF8Ack]; 104 response = [[NSString alloc] initWithData:newLine encoding:NSUTF8StringEncoding]; 105 if (![response isEqualToString:@"ack\n"]) @throw [NSException exceptionWithName:@"ANTLRDebugEventProxy" reason:@"illegal response from debugger" userInfo:nil]; 106 } 107 @catch (NSException *e) { 108 NSLog(@"socket died or debugger misbehaved: %@ read <%@>", e, response); 109 } 110 @finally { 111 [response release]; 112 } 113} 114 115- (void) sendToDebugger:(NSString *)message 116{ 117 [self sendToDebugger:message waitForResponse:YES]; 118} 119 120- (void) sendToDebugger:(NSString *)message waitForResponse:(BOOL)wait 121{ 122 if (! debuggerFH ) return; 123 [debuggerFH writeData:[message dataUsingEncoding:NSUTF8StringEncoding]]; 124 [debuggerFH writeData:newlineData]; 125 if (wait) [self waitForAck]; 126} 127 128- (NSInteger) serverSocket 129{ 130 return serverSocket; 131} 132 133- (void) setServerSocket: (NSInteger) aServerSocket 134{ 135 serverSocket = aServerSocket; 136} 137 138- (NSInteger) debuggerSocket 139{ 140 return debuggerSocket; 141} 142 143- (void) setDebuggerSocket: (NSInteger) aDebuggerSocket 144{ 145 debuggerSocket = aDebuggerSocket; 146} 147 148- (NSString *) grammarName 149{ 150 return grammarName; 151} 152 153- (void) setGrammarName: (NSString *) aGrammarName 154{ 155 if (grammarName != aGrammarName) { 156 [aGrammarName retain]; 157 [grammarName release]; 158 grammarName = aGrammarName; 159 } 160} 161 162- (NSInteger) debuggerPort 163{ 164 return debuggerPort; 165} 166 167- (void) setDebuggerPort: (NSInteger) aDebuggerPort 168{ 169 debuggerPort = aDebuggerPort; 170} 171 172- (NSString *) escapeNewlines:(NSString *)aString 173{ 174 NSMutableString *escapedText; 175 if (aString) { 176 escapedText = [NSMutableString stringWithString:aString]; 177 NSRange wholeString = NSMakeRange(0,[escapedText length]); 178 [escapedText replaceOccurrencesOfString:@"%" withString:@"%25" options:0 range:wholeString]; 179 [escapedText replaceOccurrencesOfString:@"\n" withString:@"%0A" options:0 range:wholeString]; 180 [escapedText replaceOccurrencesOfString:@"\r" withString:@"%0D" options:0 range:wholeString]; 181 } else { 182 escapedText = [NSMutableString stringWithString:@""]; 183 } 184 return escapedText; 185} 186 187#pragma mark - 188 189#pragma mark DebugEventListener Protocol 190- (void) enterRule:(NSString *)ruleName 191{ 192 [self sendToDebugger:[NSString stringWithFormat:@"enterRule %@", ruleName]]; 193} 194 195- (void) enterAlt:(NSInteger)alt 196{ 197 [self sendToDebugger:[NSString stringWithFormat:@"enterAlt %d", alt]]; 198} 199 200- (void) exitRule:(NSString *)ruleName 201{ 202 [self sendToDebugger:[NSString stringWithFormat:@"exitRule %@", ruleName]]; 203} 204 205- (void) enterSubRule:(NSInteger)decisionNumber 206{ 207 [self sendToDebugger:[NSString stringWithFormat:@"enterSubRule %d", decisionNumber]]; 208} 209 210- (void) exitSubRule:(NSInteger)decisionNumber 211{ 212 [self sendToDebugger:[NSString stringWithFormat:@"exitSubRule %d", decisionNumber]]; 213} 214 215- (void) enterDecision:(NSInteger)decisionNumber 216{ 217 [self sendToDebugger:[NSString stringWithFormat:@"enterDecision %d", decisionNumber]]; 218} 219 220- (void) exitDecision:(NSInteger)decisionNumber 221{ 222 [self sendToDebugger:[NSString stringWithFormat:@"exitDecision %d", decisionNumber]]; 223} 224 225- (void) consumeToken:(id<ANTLRToken>)t 226{ 227 [self sendToDebugger:[NSString stringWithFormat:@"consumeToken %@", [self escapeNewlines:[t description]]]]; 228} 229 230- (void) consumeHiddenToken:(id<ANTLRToken>)t 231{ 232 [self sendToDebugger:[NSString stringWithFormat:@"consumeHiddenToken %@", [self escapeNewlines:[t description]]]]; 233} 234 235- (void) LT:(NSInteger)i foundToken:(id<ANTLRToken>)t 236{ 237 [self sendToDebugger:[NSString stringWithFormat:@"LT %d %@", i, [self escapeNewlines:[t description]]]]; 238} 239 240- (void) mark:(NSInteger)marker 241{ 242 [self sendToDebugger:[NSString stringWithFormat:@"mark %d", marker]]; 243} 244- (void) rewind:(NSInteger)marker 245{ 246 [self sendToDebugger:[NSString stringWithFormat:@"rewind %d", marker]]; 247} 248 249- (void) rewind 250{ 251 [self sendToDebugger:@"rewind"]; 252} 253 254- (void) beginBacktrack:(NSInteger)level 255{ 256 [self sendToDebugger:[NSString stringWithFormat:@"beginBacktrack %d", level]]; 257} 258 259- (void) endBacktrack:(NSInteger)level wasSuccessful:(BOOL)successful 260{ 261 [self sendToDebugger:[NSString stringWithFormat:@"endBacktrack %d %d", level, successful ? 1 : 0]]; 262} 263 264- (void) locationLine:(NSInteger)line column:(NSInteger)pos 265{ 266 [self sendToDebugger:[NSString stringWithFormat:@"location %d %d", line, pos]]; 267} 268 269- (void) recognitionException:(ANTLRRecognitionException *)e 270{ 271#warning TODO: recognition exceptions 272 // these must use the names of the corresponding Java exception classes, because ANTLRWorks recreates the exception 273 // objects on the Java side. 274 // Write categories for Objective-C exceptions to provide those names 275} 276 277- (void) beginResync 278{ 279 [self sendToDebugger:@"beginResync"]; 280} 281 282- (void) endResync 283{ 284 [self sendToDebugger:@"endResync"]; 285} 286 287- (void) semanticPredicate:(NSString *)predicate matched:(BOOL)result 288{ 289 [self sendToDebugger:[NSString stringWithFormat:@"semanticPredicate %d %@", result?1:0, [self escapeNewlines:predicate]]]; 290} 291 292- (void) commence 293{ 294 // no need to send event 295} 296 297- (void) terminate 298{ 299 [self sendToDebugger:@"terminate"]; 300 @try { 301 [debuggerFH closeFile]; 302 } 303 @finally { 304#warning TODO: make socket handling robust. too lazy now... 305 shutdown(serverSocket,SHUT_RDWR); 306 serverSocket = -1; 307 } 308} 309 310 311#pragma mark Tree Parsing 312- (void) consumeNode:(unsigned)nodeHash ofType:(NSInteger)type text:(NSString *)text 313{ 314 [self sendToDebugger:[NSString stringWithFormat:@"consumeNode %u %d %@", 315 nodeHash, 316 type, 317 [self escapeNewlines:text] 318 ]]; 319} 320 321- (void) LT:(NSInteger)i foundNode:(unsigned)nodeHash ofType:(NSInteger)type text:(NSString *)text 322{ 323 [self sendToDebugger:[NSString stringWithFormat:@"LN %d %u %d %@", 324 i, 325 nodeHash, 326 type, 327 [self escapeNewlines:text] 328 ]]; 329} 330 331 332#pragma mark AST Events 333 334- (void) createNilNode:(unsigned)hash 335{ 336 [self sendToDebugger:[NSString stringWithFormat:@"nilNode %u", hash]]; 337} 338 339- (void) createNode:(unsigned)hash text:(NSString *)text type:(NSInteger)type 340{ 341 [self sendToDebugger:[NSString stringWithFormat:@"createNodeFromToken %u %d %@", 342 hash, 343 type, 344 [self escapeNewlines:text] 345 ]]; 346} 347 348- (void) createNode:(unsigned)hash fromTokenAtIndex:(NSInteger)tokenIndex 349{ 350 [self sendToDebugger:[NSString stringWithFormat:@"createNode %u %d", hash, tokenIndex]]; 351} 352 353- (void) becomeRoot:(unsigned)newRootHash old:(unsigned)oldRootHash 354{ 355 [self sendToDebugger:[NSString stringWithFormat:@"becomeRoot %u %u", newRootHash, oldRootHash]]; 356} 357 358- (void) addChild:(unsigned)childHash toTree:(unsigned)treeHash 359{ 360 [self sendToDebugger:[NSString stringWithFormat:@"addChild %u %u", treeHash, childHash]]; 361} 362 363- (void) setTokenBoundariesForTree:(unsigned)nodeHash From:(NSInteger)tokenStartIndex To:(NSInteger)tokenStopIndex 364{ 365 [self sendToDebugger:[NSString stringWithFormat:@"setTokenBoundaries %u %d %d", nodeHash, tokenStartIndex, tokenStopIndex]]; 366} 367 368 369 370@end 371