You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
424 lines
12 KiB
424 lines
12 KiB
// [The "BSD licence"]
|
|
// Copyright (c) 2006-2007 Kay Roepke
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions
|
|
// are met:
|
|
// 1. Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// 2. Redistributions in binary form must reproduce the above copyright
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
// documentation and/or other materials provided with the distribution.
|
|
// 3. The name of the author may not be used to endorse or promote products
|
|
// derived from this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
#import "DebugEventSocketProxy.h"
|
|
#import "Token+DebuggerSupport.h"
|
|
#include <string.h>
|
|
|
|
static NSData *newlineData = nil;
|
|
static unsigned lengthOfUTF8Ack = 0;
|
|
|
|
@implementation DebugEventSocketProxy
|
|
|
|
+ (void) initialize
|
|
{
|
|
if (!newlineData) newlineData = [@"\n" dataUsingEncoding:NSUTF8StringEncoding];
|
|
if (!lengthOfUTF8Ack) lengthOfUTF8Ack = [[@"ack\n" dataUsingEncoding:NSUTF8StringEncoding] length];
|
|
}
|
|
|
|
- (id) init
|
|
{
|
|
return [self initWithGrammarName:nil debuggerPort:DEFAULT_DEBUGGER_PORT];
|
|
}
|
|
|
|
- (id) initWithGrammarName:(NSString *)aGrammarName debuggerPort:(NSInteger)aPort
|
|
{
|
|
self = [super init];
|
|
if (self) {
|
|
serverSocket = -1;
|
|
[self setGrammarName:aGrammarName];
|
|
if (aPort == -1) aPort = DEFAULT_DEBUGGER_PORT;
|
|
[self setDebuggerPort:aPort];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void) dealloc
|
|
{
|
|
if (serverSocket != -1)
|
|
shutdown(serverSocket,SHUT_RDWR);
|
|
serverSocket = -1;
|
|
[debuggerFH release];
|
|
[self setGrammarName:nil];
|
|
[super dealloc];
|
|
}
|
|
|
|
/* Java stuff
|
|
public void handshake() throws IOException {
|
|
if ( serverSocket==nil ) {
|
|
serverSocket = new ServerSocket(port);
|
|
socket = serverSocket.accept();
|
|
socket.setTcpNoDelay(true);
|
|
OutputStream os = socket.getOutputStream();
|
|
OutputStreamWriter osw = new OutputStreamWriter(os, "UTF8");
|
|
out = new PrintWriter(new BufferedWriter(osw));
|
|
InputStream is = socket.getInputStream();
|
|
InputStreamReader isr = new InputStreamReader(is, "UTF8");
|
|
in = new BufferedReader(isr);
|
|
out.println("ANTLR "+ DebugEventListener.PROTOCOL_VERSION);
|
|
out.println("grammar \""+ grammarFileName);
|
|
out.flush();
|
|
ack();
|
|
}
|
|
}
|
|
|
|
- (void) commence
|
|
{
|
|
// don't bother sending event; listener will trigger upon connection
|
|
}
|
|
|
|
- (void) terminate
|
|
{
|
|
[self transmit:@"terminate";
|
|
[out close];
|
|
try {
|
|
[socket close];
|
|
}
|
|
catch (IOException *ioe) {
|
|
ioe.printStackTrace(System.err);
|
|
}
|
|
}
|
|
|
|
- (void) ack
|
|
{
|
|
try {
|
|
in.readLine();
|
|
}
|
|
catch (IOException ioe) {
|
|
ioe.printStackTrace(System.err);
|
|
}
|
|
}
|
|
|
|
protected void transmit(String event) {
|
|
out.println(event);
|
|
out.flush();
|
|
ack();
|
|
}
|
|
*/
|
|
|
|
- (void) waitForDebuggerConnection
|
|
{
|
|
if (serverSocket == -1) {
|
|
serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
|
|
NSAssert1(serverSocket != -1, @"Failed to create debugger socket. %s", strerror(errno));
|
|
|
|
int yes = 1;
|
|
setsockopt(serverSocket, SOL_SOCKET, SO_KEEPALIVE|SO_REUSEPORT|SO_REUSEADDR|TCP_NODELAY, (void *)&yes, sizeof(NSInteger));
|
|
|
|
struct sockaddr_in server_addr;
|
|
bzero(&server_addr, sizeof(struct sockaddr_in));
|
|
server_addr.sin_family = AF_INET;
|
|
server_addr.sin_port = htons([self debuggerPort]);
|
|
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
NSAssert1( bind(serverSocket, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) != -1, @"bind(2) failed. %s", strerror(errno));
|
|
|
|
NSAssert1(listen(serverSocket,50) == 0, @"listen(2) failed. %s", strerror(errno));
|
|
|
|
NSLog(@"ANTLR waiting for debugger attach (grammar %@)", [self grammarName]);
|
|
|
|
debuggerSocket = accept(serverSocket, &debugger_sockaddr, &debugger_socklen);
|
|
NSAssert1( debuggerSocket != -1, @"accept(2) failed. %s", strerror(errno));
|
|
|
|
debuggerFH = [[NSFileHandle alloc] initWithFileDescriptor:debuggerSocket];
|
|
[self sendToDebugger:[NSString stringWithFormat:@"ANTLR %d", DebugProtocolVersion] waitForResponse:NO];
|
|
[self sendToDebugger:[NSString stringWithFormat:@"grammar \"%@", [self grammarName]] waitForResponse:NO];
|
|
}
|
|
}
|
|
|
|
- (void) waitForAck
|
|
{
|
|
NSString *response;
|
|
@try {
|
|
NSData *newLine = [debuggerFH readDataOfLength:lengthOfUTF8Ack];
|
|
response = [[NSString alloc] initWithData:newLine encoding:NSUTF8StringEncoding];
|
|
if (![response isEqualToString:@"ack\n"]) @throw [NSException exceptionWithName:@"DebugEventSocketProxy" reason:@"illegal response from debugger" userInfo:nil];
|
|
}
|
|
@catch (NSException *e) {
|
|
NSLog(@"socket died or debugger misbehaved: %@ read <%@>", e, response);
|
|
}
|
|
@finally {
|
|
[response release];
|
|
}
|
|
}
|
|
|
|
- (void) sendToDebugger:(NSString *)message
|
|
{
|
|
[self sendToDebugger:message waitForResponse:YES];
|
|
}
|
|
|
|
- (void) sendToDebugger:(NSString *)message waitForResponse:(BOOL)wait
|
|
{
|
|
if (! debuggerFH ) return;
|
|
[debuggerFH writeData:[message dataUsingEncoding:NSUTF8StringEncoding]];
|
|
[debuggerFH writeData:newlineData];
|
|
if (wait) [self waitForAck];
|
|
}
|
|
|
|
- (NSInteger) serverSocket
|
|
{
|
|
return serverSocket;
|
|
}
|
|
|
|
- (void) setServerSocket: (NSInteger) aServerSocket
|
|
{
|
|
serverSocket = aServerSocket;
|
|
}
|
|
|
|
- (NSInteger) debuggerSocket
|
|
{
|
|
return debuggerSocket;
|
|
}
|
|
|
|
- (void) setDebuggerSocket: (NSInteger) aDebuggerSocket
|
|
{
|
|
debuggerSocket = aDebuggerSocket;
|
|
}
|
|
|
|
- (NSString *) grammarName
|
|
{
|
|
return grammarName;
|
|
}
|
|
|
|
- (void) setGrammarName: (NSString *) aGrammarName
|
|
{
|
|
if (grammarName != aGrammarName) {
|
|
[aGrammarName retain];
|
|
[grammarName release];
|
|
grammarName = aGrammarName;
|
|
}
|
|
}
|
|
|
|
- (NSInteger) debuggerPort
|
|
{
|
|
return debuggerPort;
|
|
}
|
|
|
|
- (void) setDebuggerPort: (NSInteger) aDebuggerPort
|
|
{
|
|
debuggerPort = aDebuggerPort;
|
|
}
|
|
|
|
- (NSString *) escapeNewlines:(NSString *)aString
|
|
{
|
|
NSMutableString *escapedText;
|
|
if (aString) {
|
|
escapedText = [NSMutableString stringWithString:aString];
|
|
NSRange wholeString = NSMakeRange(0,[escapedText length]);
|
|
[escapedText replaceOccurrencesOfString:@"%" withString:@"%25" options:0 range:wholeString];
|
|
[escapedText replaceOccurrencesOfString:@"\n" withString:@"%0A" options:0 range:wholeString];
|
|
[escapedText replaceOccurrencesOfString:@"\r" withString:@"%0D" options:0 range:wholeString];
|
|
} else {
|
|
escapedText = [NSMutableString stringWithString:@""];
|
|
}
|
|
return escapedText;
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark DebugEventListener Protocol
|
|
- (void) enterRule:(NSString *)ruleName
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"enterRule %@", ruleName]];
|
|
}
|
|
|
|
- (void) enterAlt:(NSInteger)alt
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"enterAlt %d", alt]];
|
|
}
|
|
|
|
- (void) exitRule:(NSString *)ruleName
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"exitRule %@", ruleName]];
|
|
}
|
|
|
|
- (void) enterSubRule:(NSInteger)decisionNumber
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"enterSubRule %d", decisionNumber]];
|
|
}
|
|
|
|
- (void) exitSubRule:(NSInteger)decisionNumber
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"exitSubRule %d", decisionNumber]];
|
|
}
|
|
|
|
- (void) enterDecision:(NSInteger)decisionNumber
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"enterDecision %d", decisionNumber]];
|
|
}
|
|
|
|
- (void) exitDecision:(NSInteger)decisionNumber
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"exitDecision %d", decisionNumber]];
|
|
}
|
|
|
|
- (void) consumeToken:(id<Token>)t
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"consumeToken %@", [self escapeNewlines:[t description]]]];
|
|
}
|
|
|
|
- (void) consumeHiddenToken:(id<Token>)t
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"consumeHiddenToken %@", [self escapeNewlines:[t description]]]];
|
|
}
|
|
|
|
- (void) LT:(NSInteger)i foundToken:(id<Token>)t
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"LT %d %@", i, [self escapeNewlines:[t description]]]];
|
|
}
|
|
|
|
- (void) mark:(NSInteger)marker
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"mark %d", marker]];
|
|
}
|
|
- (void) rewind:(NSInteger)marker
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"rewind %d", marker]];
|
|
}
|
|
|
|
- (void) rewind
|
|
{
|
|
[self sendToDebugger:@"rewind"];
|
|
}
|
|
|
|
- (void) beginBacktrack:(NSInteger)level
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"beginBacktrack %d", level]];
|
|
}
|
|
|
|
- (void) endBacktrack:(NSInteger)level wasSuccessful:(BOOL)successful
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"endBacktrack %d %d", level, successful ? 1 : 0]];
|
|
}
|
|
|
|
- (void) locationLine:(NSInteger)line column:(NSInteger)pos
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"location %d %d", line, pos]];
|
|
}
|
|
|
|
- (void) recognitionException:(RecognitionException *)e
|
|
{
|
|
#warning TODO: recognition exceptions
|
|
// these must use the names of the corresponding Java exception classes, because ANTLRWorks recreates the exception
|
|
// objects on the Java side.
|
|
// Write categories for Objective-C exceptions to provide those names
|
|
}
|
|
|
|
- (void) beginResync
|
|
{
|
|
[self sendToDebugger:@"beginResync"];
|
|
}
|
|
|
|
- (void) endResync
|
|
{
|
|
[self sendToDebugger:@"endResync"];
|
|
}
|
|
|
|
- (void) semanticPredicate:(NSString *)predicate matched:(BOOL)result
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"semanticPredicate %d %@", result?1:0, [self escapeNewlines:predicate]]];
|
|
}
|
|
|
|
- (void) commence
|
|
{
|
|
// no need to send event
|
|
}
|
|
|
|
- (void) terminate
|
|
{
|
|
[self sendToDebugger:@"terminate"];
|
|
@try {
|
|
[debuggerFH closeFile];
|
|
}
|
|
@finally {
|
|
#warning TODO: make socket handling robust. too lazy now...
|
|
shutdown(serverSocket,SHUT_RDWR);
|
|
serverSocket = -1;
|
|
}
|
|
}
|
|
|
|
|
|
#pragma mark Tree Parsing
|
|
- (void) consumeNode:(unsigned)nodeHash ofType:(NSInteger)type text:(NSString *)text
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"consumeNode %u %d %@",
|
|
nodeHash,
|
|
type,
|
|
[self escapeNewlines:text]
|
|
]];
|
|
}
|
|
|
|
- (void) LT:(NSInteger)i foundNode:(unsigned)nodeHash ofType:(NSInteger)type text:(NSString *)text
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"LN %d %u %d %@",
|
|
i,
|
|
nodeHash,
|
|
type,
|
|
[self escapeNewlines:text]
|
|
]];
|
|
}
|
|
|
|
|
|
#pragma mark AST Events
|
|
|
|
- (void) createNilNode:(unsigned)hash
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"nilNode %u", hash]];
|
|
}
|
|
|
|
- (void) createNode:(unsigned)hash text:(NSString *)text type:(NSInteger)type
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"createNodeFromToken %u %d %@",
|
|
hash,
|
|
type,
|
|
[self escapeNewlines:text]
|
|
]];
|
|
}
|
|
|
|
- (void) createNode:(unsigned)hash fromTokenAtIndex:(NSInteger)tokenIndex
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"createNode %u %d", hash, tokenIndex]];
|
|
}
|
|
|
|
- (void) becomeRoot:(unsigned)newRootHash old:(unsigned)oldRootHash
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"becomeRoot %u %u", newRootHash, oldRootHash]];
|
|
}
|
|
|
|
- (void) addChild:(unsigned)childHash toTree:(unsigned)treeHash
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"addChild %u %u", treeHash, childHash]];
|
|
}
|
|
|
|
- (void) setTokenBoundariesForTree:(unsigned)nodeHash From:(NSInteger)tokenStartIndex To:(NSInteger)tokenStopIndex
|
|
{
|
|
[self sendToDebugger:[NSString stringWithFormat:@"setTokenBoundaries %u %d %d", nodeHash, tokenStartIndex, tokenStopIndex]];
|
|
}
|
|
|
|
|
|
|
|
@end
|