Skip to content
Browse files

Initial check-in.

git-svn-id: svn://witness.is-a-geek.org/svn@1 378c4bed-2673-4746-83ae-d22ddc8c5b7c
  • Loading branch information...
0 parents commit f9268999e6cf65cb16b4e7795fcabaf4be43b1b4 uli committed Jul 4, 2009
8 .bzrignore
@@ -0,0 +1,8 @@
+.DS_Store
+build
+*.pbxuser
+*.perspective
+*.perspectivev3
+*~.nib
+*.mode1
+*.mode1v3
319 AngelDiff.xcodeproj/project.pbxproj
@@ -0,0 +1,319 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 45;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD58140DA1D0A300B32029 /* MainMenu.xib */; };
+ 553744150E4430ED00B1011D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 553744140E4430ED00B1011D /* AppDelegate.m */; };
+ 5537441B0E4431B600B1011D /* UKDiffParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 5537441A0E4431B600B1011D /* UKDiffParser.m */; };
+ 553744DF0E44637F00B1011D /* UKDiffView.m in Sources */ = {isa = PBXBuildFile; fileRef = 553744DE0E44637F00B1011D /* UKDiffView.m */; };
+ 553748F80E48E38200B1011D /* UKObjectLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 553748F70E48E38200B1011D /* UKObjectLogger.m */; };
+ 55EA2B970E4E4E010057AFAF /* UKDiffViewChooseLeft.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 55EA2B950E4E4E010057AFAF /* UKDiffViewChooseLeft.pdf */; };
+ 55EA2B980E4E4E010057AFAF /* UKDiffViewChooseRight.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 55EA2B960E4E4E010057AFAF /* UKDiffViewChooseRight.pdf */; };
+ 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
+ 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
+ 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
+ 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
+ 1DDD58150DA1D0A300B32029 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = "<group>"; };
+ 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
+ 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
+ 32CA4F630368D1EE00C91783 /* AngelDiff_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AngelDiff_Prefix.pch; sourceTree = "<group>"; };
+ 5508EC040E4E5E1A00740CF8 /* Testfile1.udiff */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Testfile1.udiff; sourceTree = "<group>"; };
+ 553744130E4430ED00B1011D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+ 553744140E4430ED00B1011D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+ 553744190E4431B600B1011D /* UKDiffParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UKDiffParser.h; sourceTree = "<group>"; };
+ 5537441A0E4431B600B1011D /* UKDiffParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UKDiffParser.m; sourceTree = "<group>"; };
+ 5537441F0E443B8900B1011D /* UKHelperMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UKHelperMacros.h; path = ../UliKit/UKHelperMacros.h; sourceTree = SOURCE_ROOT; };
+ 553744DD0E44637F00B1011D /* UKDiffView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UKDiffView.h; sourceTree = "<group>"; };
+ 553744DE0E44637F00B1011D /* UKDiffView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UKDiffView.m; sourceTree = "<group>"; };
+ 5537457C0E4471E600B1011D /* Testfile1.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Testfile1.txt; sourceTree = "<group>"; };
+ 5537457D0E4471EE00B1011D /* Testfile2.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Testfile2.txt; sourceTree = "<group>"; };
+ 5537457E0E44720000B1011D /* Testfile1.diff */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Testfile1.diff; sourceTree = "<group>"; };
+ 553748F60E48E38200B1011D /* UKObjectLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UKObjectLogger.h; sourceTree = "<group>"; };
+ 553748F70E48E38200B1011D /* UKObjectLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UKObjectLogger.m; sourceTree = "<group>"; };
+ 55EA2B950E4E4E010057AFAF /* UKDiffViewChooseLeft.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = UKDiffViewChooseLeft.pdf; sourceTree = "<group>"; };
+ 55EA2B960E4E4E010057AFAF /* UKDiffViewChooseRight.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = UKDiffViewChooseRight.pdf; sourceTree = "<group>"; };
+ 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ 8D1107320486CEB800E47090 /* AngelDiff.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AngelDiff.app; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 8D11072E0486CEB800E47090 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 080E96DDFE201D6D7F000001 /* Classes */ = {
+ isa = PBXGroup;
+ children = (
+ 553744130E4430ED00B1011D /* AppDelegate.h */,
+ 553744140E4430ED00B1011D /* AppDelegate.m */,
+ 553744DD0E44637F00B1011D /* UKDiffView.h */,
+ 553744DE0E44637F00B1011D /* UKDiffView.m */,
+ 553744190E4431B600B1011D /* UKDiffParser.h */,
+ 5537441A0E4431B600B1011D /* UKDiffParser.m */,
+ );
+ name = Classes;
+ sourceTree = "<group>";
+ };
+ 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
+ );
+ name = "Linked Frameworks";
+ sourceTree = "<group>";
+ };
+ 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 29B97324FDCFA39411CA2CEA /* AppKit.framework */,
+ 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */,
+ 29B97325FDCFA39411CA2CEA /* Foundation.framework */,
+ );
+ name = "Other Frameworks";
+ sourceTree = "<group>";
+ };
+ 19C28FACFE9D520D11CA2CBB /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 8D1107320486CEB800E47090 /* AngelDiff.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 29B97314FDCFA39411CA2CEA /* AngelDiff */ = {
+ isa = PBXGroup;
+ children = (
+ 5537457C0E4471E600B1011D /* Testfile1.txt */,
+ 5537457E0E44720000B1011D /* Testfile1.diff */,
+ 5508EC040E4E5E1A00740CF8 /* Testfile1.udiff */,
+ 5537457D0E4471EE00B1011D /* Testfile2.txt */,
+ 080E96DDFE201D6D7F000001 /* Classes */,
+ 29B97315FDCFA39411CA2CEA /* Other Sources */,
+ 29B97317FDCFA39411CA2CEA /* Resources */,
+ 29B97323FDCFA39411CA2CEA /* Frameworks */,
+ 19C28FACFE9D520D11CA2CBB /* Products */,
+ );
+ name = AngelDiff;
+ sourceTree = "<group>";
+ };
+ 29B97315FDCFA39411CA2CEA /* Other Sources */ = {
+ isa = PBXGroup;
+ children = (
+ 553748F60E48E38200B1011D /* UKObjectLogger.h */,
+ 553748F70E48E38200B1011D /* UKObjectLogger.m */,
+ 32CA4F630368D1EE00C91783 /* AngelDiff_Prefix.pch */,
+ 29B97316FDCFA39411CA2CEA /* main.m */,
+ 5537441F0E443B8900B1011D /* UKHelperMacros.h */,
+ );
+ name = "Other Sources";
+ sourceTree = "<group>";
+ };
+ 29B97317FDCFA39411CA2CEA /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 8D1107310486CEB800E47090 /* Info.plist */,
+ 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */,
+ 1DDD58140DA1D0A300B32029 /* MainMenu.xib */,
+ 55EA2B950E4E4E010057AFAF /* UKDiffViewChooseLeft.pdf */,
+ 55EA2B960E4E4E010057AFAF /* UKDiffViewChooseRight.pdf */,
+ );
+ name = Resources;
+ sourceTree = "<group>";
+ };
+ 29B97323FDCFA39411CA2CEA /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */,
+ 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 8D1107260486CEB800E47090 /* AngelDiff */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "AngelDiff" */;
+ buildPhases = (
+ 8D1107290486CEB800E47090 /* Resources */,
+ 8D11072C0486CEB800E47090 /* Sources */,
+ 8D11072E0486CEB800E47090 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = AngelDiff;
+ productInstallPath = "$(HOME)/Applications";
+ productName = AngelDiff;
+ productReference = 8D1107320486CEB800E47090 /* AngelDiff.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 29B97313FDCFA39411CA2CEA /* Project object */ = {
+ isa = PBXProject;
+ buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "AngelDiff" */;
+ compatibilityVersion = "Xcode 3.1";
+ hasScannedForEncodings = 1;
+ mainGroup = 29B97314FDCFA39411CA2CEA /* AngelDiff */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 8D1107260486CEB800E47090 /* AngelDiff */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 8D1107290486CEB800E47090 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */,
+ 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */,
+ 55EA2B970E4E4E010057AFAF /* UKDiffViewChooseLeft.pdf in Resources */,
+ 55EA2B980E4E4E010057AFAF /* UKDiffViewChooseRight.pdf in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 8D11072C0486CEB800E47090 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8D11072D0486CEB800E47090 /* main.m in Sources */,
+ 553744150E4430ED00B1011D /* AppDelegate.m in Sources */,
+ 5537441B0E4431B600B1011D /* UKDiffParser.m in Sources */,
+ 553744DF0E44637F00B1011D /* UKDiffView.m in Sources */,
+ 553748F80E48E38200B1011D /* UKObjectLogger.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 089C165DFE840E0CC02AAC07 /* English */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+ 1DDD58140DA1D0A300B32029 /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 1DDD58150DA1D0A300B32029 /* English */,
+ );
+ name = MainMenu.xib;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ C01FCF4B08A954540054247B /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = AngelDiff_Prefix.pch;
+ GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1";
+ INFOPLIST_FILE = Info.plist;
+ INSTALL_PATH = "$(HOME)/Applications";
+ PRODUCT_NAME = AngelDiff;
+ };
+ name = Debug;
+ };
+ C01FCF4C08A954540054247B /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_MODEL_TUNING = G5;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = AngelDiff_Prefix.pch;
+ INFOPLIST_FILE = Info.plist;
+ INSTALL_PATH = "$(HOME)/Applications";
+ PRODUCT_NAME = AngelDiff;
+ };
+ name = Release;
+ };
+ C01FCF4F08A954540054247B /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ PREBINDING = NO;
+ SDKROOT = macosx10.5;
+ };
+ name = Debug;
+ };
+ C01FCF5008A954540054247B /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ PREBINDING = NO;
+ SDKROOT = macosx10.5;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "AngelDiff" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ C01FCF4B08A954540054247B /* Debug */,
+ C01FCF4C08A954540054247B /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ C01FCF4E08A954540054247B /* Build configuration list for PBXProject "AngelDiff" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ C01FCF4F08A954540054247B /* Debug */,
+ C01FCF5008A954540054247B /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
+}
7 AngelDiff_Prefix.pch
@@ -0,0 +1,7 @@
+//
+// Prefix header for all source files of the 'AngelDiff' target in the 'AngelDiff' project
+//
+
+#ifdef __OBJC__
+ #import <Cocoa/Cocoa.h>
+#endif
25 AppDelegate.h
@@ -0,0 +1,25 @@
+//
+// AppDelegate.h
+// AngelDiff
+//
+// Created by Uli Kusterer on 02.08.08.
+// Copyright 2008 The Void Software. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+
+@class UKDiffView;
+
+
+@interface AppDelegate : NSObject
+{
+ IBOutlet UKDiffView* diffView;
+ NSString* currPath;
+}
+
+@property (retain) NSString* currPath;
+
+-(void) exportMerged: (id)sender;
+
+@end
62 AppDelegate.m
@@ -0,0 +1,62 @@
+//
+// AppDelegate.m
+// AngelDiff
+//
+// Created by Uli Kusterer on 02.08.08.
+// Copyright 2008 The Void Software. All rights reserved.
+//
+
+#import "AppDelegate.h"
+#import "UKDiffParser.h"
+#import "UKDiffView.h"
+#import "UKHelperMacros.h"
+
+
+@implementation AppDelegate
+
+@synthesize currPath;
+
+-(void) dealloc
+{
+ DESTROY(currPath);
+
+ [super dealloc];
+}
+
+- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename
+{
+ self.currPath = [[filename stringByDeletingPathExtension] stringByAppendingPathExtension: @"txt"];
+ NSString* stringOne = [NSString stringWithContentsOfFile: self.currPath];
+ NSString* diffPath = [[filename stringByDeletingPathExtension] stringByAppendingPathExtension: @"udiff"];
+ BOOL uniDiff = YES;
+ if( ![[NSFileManager defaultManager] fileExistsAtPath: diffPath] )
+ {
+ diffPath = [[filename stringByDeletingPathExtension] stringByAppendingPathExtension: @"diff"];
+ uniDiff = NO;
+ }
+ NSString* differences = [NSString stringWithContentsOfFile: diffPath];
+
+ UKDiffParser* dp = nil;
+ if( uniDiff )
+ dp = [[[UKDiffParser alloc] initWithUnifiedDiffText: differences] autorelease];
+ else
+ dp = [[[UKDiffParser alloc] initWithDiffText: differences] autorelease];
+ [dp applyOriginalText: stringOne];
+
+ [diffView setDiffParser: dp];
+ NSRect box = [diffView frame];
+ box.size = [diffView bestSize];
+ [diffView setFrame: box];
+ [diffView setNeedsDisplay: YES];
+
+ return YES;
+}
+
+
+-(void) exportMerged: (id)sender
+{
+ NSString* merged = [[diffView diffParser] mergedString];
+ [merged writeToFile: [[currPath stringByDeletingPathExtension] stringByAppendingPathExtension: @"merged.txt"] atomically: NO encoding: NSUTF8StringEncoding error: nil];
+}
+
+@end
BIN English.lproj/InfoPlist.strings
Binary file not shown.
3,194 English.lproj/MainMenu.xib
3,194 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
45 Info.plist
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleDocumentTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleTypeName</key>
+ <string>MyDocumentType</string>
+ <key>CFBundleTypeOSTypes</key>
+ <array>
+ <string>****</string>
+ </array>
+ <key>CFBundleTypeRole</key>
+ <string>Editor</string>
+ <key>LSTypeIsPackage</key>
+ <false/>
+ <key>NSPersistentStoreTypeKey</key>
+ <string>XML</string>
+ </dict>
+ </array>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.thevoidsoftware.${PRODUCT_NAME:identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
40 Testfile1.diff
@@ -0,0 +1,40 @@
+6c6
+< * Copyright (c) 2003 M. Uli Kusterer. All rights reserved.
+---
+> * Copyright (c) 2009 Gerda the Great. All rights reserved.
+294,309d293
+< protected:
+< BCTokenList mTokens; // List of tokens (after Tokenize() has been called).
+< BCInstructionList mCode; // Code (after Parse() has been called).
+< char* mText; // Text being tokenized/parsed (only until parsing is complete)
+< bool mCompileForDebug; // Generate instructions that aid in source-level debugging?
+< std::string mCurrentFilename; // Name of current source file.
+< BCConstantStringsMap mConstantStrings; // String constant -> String instruction index mappings used by AddStringInstruction to allow re-use.
+< static BCIdentifierIDMap sIdentifiers; // List of identifiers (after construction).
+< static BCOperatorInstrMap sOperatorInstrs; // Mappings from operator token ID to instruction type.
+< static BCOpPrecedenceMap sOpPrecedences; // Mappings from operator instruction type to operator precedence (higher means gets the left argument).
+< static BCSystemFunctionMap sSystemFunctions; // Mappings from system function name to signature.
+< static BCClassMap sClasses; // Information about the different classes we've seen so far.
+< static BCUInt32 sObjectIDSeed; // Number for a new object's ID. If you use it, add one to it so the next user gets a fresh one.
+< static BCIntegerConstMap sIntegralConstants; // Mappings from token type for a constant to its integer value.
+< static BCStringConstMap sStringConstants; // Mappings from token type for a constant to its integral value.
+<
+348a333,334
+> void TokenizeOneStringChar( size_t x, std::string& vCurrTokenStr, unsigned *ioTokenState,
+> BCToken* vCurrToken );
+359c345
+< void TokenizeOneNumberChar( size_t x, std::string& vCurrTokenStr,
+---
+> void TokenizeOneNumberChar( size_t x, std::deque& vCurrTokenStr,
+362c348
+< void TokenizeOneCommentChar( size_t x, std::string& vCurrTokenStr,
+---
+> void TokenizeOneCommentChar( size_t x, std::deque& vCurrTokenStr,
+365c351,352
+< void TokenizeOneBlockCommentChar( size_t x, std::string& vCurrTokenStr,
+---
+> //additional line for testing.
+> void TokenizeOneBlockCommentChar( size_t x, std::deque& vCurrTokenStr,
+369,370d355
+< void TokenizeOneStringChar( size_t x, std::string& vCurrTokenStr, unsigned *ioTokenState,
+< BCToken* vCurrToken );
372 Testfile1.merged.txt
@@ -0,0 +1,372 @@
+/*
+ * BCParser.h
+ * 1FACH
+ *
+ * Created by Uli Kusterer on Sun Jul 20 2003.
+ * Copyright (c) 2009 Gerda the Great. All rights reserved.
+ *
+ */
+
+/* -----------------------------------------------------------------------------
+ Headers:
+ -------------------------------------------------------------------------- */
+
+#include <deque>
+#include <map>
+#include <string>
+#include <stdexcept>
+#include "BCInstruction.h"
+
+
+#ifndef BCPARSER_DEBUG
+#define BCPARSER_DEBUG 0
+#endif
+
+#ifndef BCDEBUG_TOKENIZER
+#define BCDEBUG_TOKENIZER BCPARSER_DEBUG
+#endif
+
+#if BCPARSER_DEBUG
+#define DEBUG_OUTPUT(n) printf("%s",n)
+#define DEBUG_OUTPUT2(n,o) printf(n,o)
+#define DEBUG_OUTPUT3(n,o,p) printf(n,o,p)
+#define DEBUG_OUTPUT4(n,o,p,q) printf(n,o,p,q)
+#else
+#define DEBUG_OUTPUT(n) // (n)
+#define DEBUG_OUTPUT2(n,o) // (n,o)
+#define DEBUG_OUTPUT3(n,o,p) // (n,o,p)
+#define DEBUG_OUTPUT4(n,o,p,q) // (n,o,p,q)
+#endif
+
+
+/* -----------------------------------------------------------------------------
+ BCToken:
+ Constants and struct definition for the data structure used for keeping
+ a list of tokens after tokenizing.
+ -------------------------------------------------------------------------- */
+
+// Possible values for mTokenType/during tokenizing:
+enum BCTokenTypeEnum
+{
+ BCT_WHITESPACE = 0, // We're currently scanning whitespace. There should never be a token that has this type in BCParser::mTokens. This may be used as an indicator for end-of-list.
+ BCT_NEWLINE, // A line break of any kind.
+ BCT_IDENTIFIER, // Any kind of identifier, operator etc.
+ BCT_STRING, // A double-quoted string constant.
+ BCT_INTEGER, // An identifier consisting only of numerical digits.
+ BCT_DOUBLE, // An identifier consisting only of numerical digits and a period.
+};
+
+
+// More readable variant of BCT_WHITESPACE when used as end-of-list indicator:
+#define BCT_INVALID BCT_WHITESPACE
+
+
+// Possible values for mTokenID:
+// If you add something here, you must also add it to mIdentifiers in BCParser's constructor.
+enum BCTokenIDEnum
+{
+ BCTI_ARBITRARY = 0, // Not a specially-recognized token.
+ BCTI_ON, // "on" for message handlers.
+ BCTI_END, // "end" for indicating end if, end handler etc.
+ BCTI_IF, // "if" ... then ...
+ BCTI_THEN, // if ... "then" ...
+ BCTI_ELSE, // if ... then ... "else"
+ BCTI_PUT, // "put" ... into ...
+ BCTI_INTO, // put ... "into" ...
+ BCTI_PASS, // "pass" through ...
+ BCTI_THROUGH, // pass "through" ...
+ BCTI_OPERATOR_PLUS, // "+"
+ BCTI_OPERATOR_MINUS, // "-"
+ BCTI_OPERATOR_MULTIPLY, // "*"
+ BCTI_OPERATOR_DIVIDE, // "/"
+ BCTI_OPERATOR_LIST_START, // "["
+ BCTI_OPERATOR_LIST_END, // "]"
+ BCTI_OPERATOR_IS, // "is" or "="
+ BCTI_OPERATOR_COMMA, // ","
+ BCTI_OPERATOR_AMPERSAND, // "&"
+ BCTI_WITH, // "with"
+ BCTI_OPERATOR_APOSTROPHE, // "'" for "object's property"
+ BCTI_OPERATOR_OPEN_BRACKET, // "("
+ BCTI_OPERATOR_CLOSE_BRACKET, // ")"
+ BCTI_PROPERTY, // "property" or "properties"
+ BCTI_OPERATOR_MOD, // "mod" for remainder of division operator.
+ BCTI_SYSTEM, // "system" identifier for system-native API calls.
+ BCTI_S, // "s" for "object's property"
+ BCTI_THE, // "the" for "the result" and such stuff
+ BCTI_RESULT, // "result" for "the result"
+ BCTI_NEW, // "new" as in "new function xy" [FFI]
+ BCTI_FUNCTION, // "function" as in "new function xy" [FFI]
+ BCTI_IN, // "in" as in "new function xy in <library>" [FFI]
+ BCTI_RETURNS, // "in" as in "new function xy returns type" [FFI]
+ BCTI_NOTHING, // "nothing" as in "new function xy returns nothing" for void functions [FFI]
+ BCTI_INTEGER, // "integer" as in "new function xy( integer )" for short integers [FFI]
+ BCTI_CHARACTER, // "character" data type [FFI]
+ BCTI_POINTER, // "pointer" data type [FFI]
+ BCTI_PASCAL, // "pascal" data type qualifier [FFI]
+ BCTI_STRING, // "string" data type [FFI]
+ BCTI_FOUNDATION, // "foundation" data type qualifier for Macs [FFI]
+ BCTI_UNSIGNED, // "unsigned" data type qualifier [FFI]
+ BCTI_DECIMAL, // "decimal" data type qualifier [FFI]
+ BCTI_NUMBER, // "number" data type [FFI]
+ BCTI_BIG, // "big" data type qualifier [FFI]
+ BCTI_OPERATOR_NOT, // binary "is not" operator or unary "not".
+ BCTI_OPERATOR_IS_NOT, // binary single-character "is not" operator ("=/="), and token type returned for "is not" by ParseBinaryOperator.
+ BCTI_CONTAINER, // "container" qualifier for pass-by-reference of parameters.
+ BCTI_AS, // "as" qualifier indicating name for associative array item.
+ BCTI_REPEAT, // "repeat" identifier used in loops.
+ BCTI_WHILE, // "while" qualifier used in repeat loops.
+ BCTI_TRUE, // "true" constant (1).
+ BCTI_FALSE, // "false" constant (0).
+ BCTI_RETURN, // "return" constant (ASCII character 13) or "return" command.
+ BCTI_LINEFEED, // "linefeed" constant (ASCII character 10).
+ BCTI_NULL, // "null" constant (ASCII character 0).
+ BCTI_TAB, // "tab" constant (ASCII character 9).
+ BCTI_OPERATOR_GREATER_THAN, // ">" operator
+ BCTI_OPERATOR_LESS_THAN, // "<" operator
+};
+
+
+// Character constants (may be platform-specific):
+
+// Unicode 0x0226:
+#define BCC_IS_NOT_SIGN_STR "≠"
+#define BCC_IS_NOT_SIGN_CHAR '≠'
+
+// Possible return values for ParseValue:
+enum
+{
+ BCV_NONE, // Nothing eligible for a value found.
+ BCV_STRING, // String constant parsed.
+ BCV_INTEGER, // Integer constant parsed.
+ BCV_DOUBLE, // Decimal number constant parsed.
+ BCV_LIST, // Constant list of values parsed.
+ BCV_OBJECT, // Object descriptor parsed.
+ BCV_DYNAMIC, // Any non-constant value, like a function result or list entry.
+};
+
+
+/* This is a data structure which we use as a shorthand notation for a token
+ (a "word"). It may seem silly to keep info about a word of four characters
+ in a 16-byte data structure, but since this one is fixed-size, scanning
+ forward and backward is a lot faster, and comparisons usually only involve
+ comparing four bytes instead of an entire string. */
+
+typedef unsigned int BCTokenType;
+typedef unsigned int BCTokenID;
+typedef unsigned int BCOffset;
+
+struct BCToken
+{
+ BCTokenType mTokenType; // String, number, identifier?
+ BCTokenID mTokenID; // Number to identify some tokens we recognize right away.
+ BCOffset mStartOffs; // Offset to first char of token in string.
+ BCOffset mEndOffs; // Offset to last char of token in string.
+};
+
+
+// Masks for our tokenizer's state machine:
+enum BCTokenState
+{
+ BC_TOKEN_STATE_WHITESPACE = 0,
+ BC_TOKEN_STATE_IDENTIFIER = (1 << 0),
+ BC_TOKEN_STATE_STRING = (1 << 1),
+ BC_TOKEN_STATE_ESCAPE_SEQ = (1 << 2), // Only when BC_TOKEN_STATE_STRING is set.
+ BC_TOKEN_STATE_INTEGER = (1 << 3), // Check this *and* BC_TOKEN_STATE_NUMBER. If NUMBER is set and this isn't, it means we already found a decimal point.
+ BC_TOKEN_STATE_NUMBER = (1 << 4),
+ BC_TOKEN_STATE_COMMENT = (1 << 5),
+ BC_TOKEN_STATE_BLOCK_COMMENT = (1 << 6),
+};
+
+
+/* -----------------------------------------------------------------------------
+ Data Types:
+ -------------------------------------------------------------------------- */
+
+class BCSysFcnEntry
+{
+public:
+ std::string mFunctionSignature; // Function signature (param and return value types).
+ std::string mFunctionNameLib; // Name of function and library.
+
+public:
+ BCSysFcnEntry() {};
+ BCSysFcnEntry( const BCSysFcnEntry* sfe ) { mFunctionSignature.assign( sfe->mFunctionSignature ); mFunctionNameLib.assign( sfe->mFunctionNameLib ); };
+ BCSysFcnEntry( const BCSysFcnEntry& sfe ) { mFunctionSignature.assign( sfe.mFunctionSignature ); mFunctionNameLib.assign( sfe.mFunctionNameLib ); };
+ BCSysFcnEntry( const std::string& sig, const std::string& nm ) : mFunctionSignature(sig), mFunctionNameLib(nm) {};
+ BCSysFcnEntry( const char* sig, const char* nm ) : mFunctionSignature(sig), mFunctionNameLib(nm) {};
+
+ BCSysFcnEntry& operator =( const BCSysFcnEntry& e ) { mFunctionSignature.assign( e.mFunctionSignature ); mFunctionNameLib.assign( e.mFunctionNameLib ); return *this; };
+ //const BCSysFcnEntry& operator =( const BCSysFcnEntry& e ) { mFunctionSignature.assign( e.mFunctionSignature ); mFunctionNameLib.assign( e.mFunctionNameLib ); return *this; };
+};
+
+class BCClassEntry
+{
+public:
+ std::string mClassName; // Name of this class.
+ BCClassEntry* mSuperclass; // Superclass of this class, NULL if none.
+ unsigned int mSuperclassCount; // Number of classes we inherit stuff from.
+ std::map<std::string,unsigned int> mInstanceVars; // Variable name --> slot offset mappings.
+
+public:
+ BCClassEntry() {};
+ BCClassEntry( std::string nm, BCClassEntry* su ) : mClassName(nm), mSuperclass(su) { CalcSuperclassCount(); };
+ BCClassEntry( const BCClassEntry* tm ) { mClassName.assign( tm->mClassName ); mSuperclass = tm->mSuperclass; mSuperclassCount = tm->mSuperclassCount; mInstanceVars = tm->mInstanceVars; };
+ BCClassEntry( const BCClassEntry& tm ) { mClassName.assign( tm.mClassName ); mSuperclass = tm.mSuperclass; mSuperclassCount = tm.mSuperclassCount; mInstanceVars = tm.mInstanceVars; };
+
+protected:
+ void CalcSuperclassCount() { if( !mSuperclass ) { mSuperclassCount = 0; return; } mSuperclassCount = mSuperclass->mSuperclassCount +1; };
+};
+
+typedef std::deque<BCToken> BCTokenList; // A list of BCToken objects.
+typedef std::deque<BCInstruction> BCInstructionList; // A list of BCInstruction structs.
+typedef std::map<std::string,BCTokenID> BCIdentifierIDMap; // Mapping from identifier names to their token IDs. Used by the tokenizer.
+typedef std::map<BCTokenID,BCUInt32> BCOperatorInstrMap; // Mapping from a certain operator's token ID to the associated instruction ID. Note that some operators may actually consist of several tokens, in which case the token ID is a "virtual" ID that represents *both* tokens or an equivalent token's ID.
+typedef std::map<BCUInt32,int> BCOpPrecedenceMap; // Mapping from a particular operator's instruction ID to an operator precedence value, to decide which operation in an expression will be executed first.
+typedef std::map<std::string,BCSysFcnEntry> BCSystemFunctionMap; // Mapping from system function name to its signature.
+typedef std::map<std::string,BCClassEntry> BCClassMap; // Mapping from class names to type information.
+typedef std::map<std::string,int> BCVarNameToIndexMap; // Mapping from local var to its stack index.
+typedef std::map<std::string,BCUInt32> BCConstantStringsMap; // Look-up table that allows re-using string constant instructions. AddStringInstruction() adds all strings it creates to this, and whenever a string is requested it returns an existing entry from this table, where possible.
+typedef std::map<BCTokenID,BCUInt32> BCIntegerConstMap; // Register all integral constants with this.
+typedef std::map<BCTokenID,std::string> BCStringConstMap; // Register all built-in string or character constants with this.
+
+
+/* -----------------------------------------------------------------------------
+ Exceptions:
+ These are a couple of exception objects that are thrown when an error
+ occurs.
+ -------------------------------------------------------------------------- */
+
+// Couldn't create a file:
+class BCCreateFileError : public std::runtime_error
+{
+public:
+ BCCreateFileError( const std::string name ) : std::runtime_error(name) {};
+
+ virtual const char* what() const throw()
+ {
+ static char vMsg[512];
+
+ snprintf( vMsg, sizeof(vMsg), "Can't create file \"%s\".", std::runtime_error::what() );
+
+ return vMsg;
+ };
+};
+
+
+// Couldn't write to a file:
+class BCWriteFileError : public std::runtime_error
+{
+public:
+ BCWriteFileError( const std::string name ) : std::runtime_error(name) {};
+};
+
+
+// Syntax error or something like that in the parser:
+class BCParserError : public std::runtime_error
+{
+ unsigned int mOffset; // Offset into the text where the error occurred.
+
+public:
+ BCParserError( const std::string name, unsigned int offs = 0 ) : std::runtime_error(name) { mOffset = offs; };
+
+ virtual const char* what() const throw()
+ {
+ static char vMsg[512];
+
+ snprintf( vMsg, sizeof(vMsg), "%s (offset %u).", std::runtime_error::what(), mOffset );
+
+ return vMsg;
+ };
+};
+
+
+/* -----------------------------------------------------------------------------
+ BCParser:
+ The parser class. This is an object which can be used to turn text into
+ instructions by tokenizing and parsing the text. There are also some
+ methods that allow adding certain instructions to the code (e.g. if you
+ just want to evaluate an expression and print its result to the
+ console), and some methods that allow saving code to disk.
+ -------------------------------------------------------------------------- */
+
+class BCParser
+{
+protected:
+ BCTokenList mTokens; // List of tokens (after Tokenize() has been called).
+ BCInstructionList mCode; // Code (after Parse() has been called).
+ char* mText; // Text being tokenized/parsed (only until parsing is complete)
+ bool mCompileForDebug; // Generate instructions that aid in source-level debugging?
+ std::string mCurrentFilename; // Name of current source file.
+ BCConstantStringsMap mConstantStrings; // String constant -> String instruction index mappings used by AddStringInstruction to allow re-use.
+ static BCIdentifierIDMap sIdentifiers; // List of identifiers (after construction).
+ static BCOperatorInstrMap sOperatorInstrs; // Mappings from operator token ID to instruction type.
+ static BCOpPrecedenceMap sOpPrecedences; // Mappings from operator instruction type to operator precedence (higher means gets the left argument).
+ static BCSystemFunctionMap sSystemFunctions; // Mappings from system function name to signature.
+ static BCClassMap sClasses; // Information about the different classes we've seen so far.
+ static BCUInt32 sObjectIDSeed; // Number for a new object's ID. If you use it, add one to it so the next user gets a fresh one.
+ static BCIntegerConstMap sIntegralConstants; // Mappings from token type for a constant to its integer value.
+ static BCStringConstMap sStringConstants; // Mappings from token type for a constant to its integral value.
+
+public:
+ BCParser();
+
+ void SetCurrentFilename( const std::string& s) { mCurrentFilename.assign( s ); }; // File path where the debugger should look for this source file.
+
+ void Tokenize( const char* inText, size_t len ); // Call this first, and hand it your text to be parsed. The text needn't be zero-terminated, and length shouldn't include any terminating zero bytes if you add them. You mustn't dispose of the text until it's been parsed.
+ void ClearTokens() { mTokens.clear(); };
+
+ void Parse(); // Call this or ParseMethodBody second.
+ void ParseMethodBody( BCTokenList::iterator& itty, BCUInt32* numParams, BCVarNameToIndexMap& vNameToIndex, BCToken* stopTokens = NULL );
+ void ParseMethodBody( BCUInt32* numParams, BCVarNameToIndexMap& vNameToIndex );
+ void ParseHeaderFile();
+
+ void AddEndInstruction(); // Mark end of code block.
+ void AddPrintResultInstructions(); // Adds a "print" instruction that outputs the variable "the result". Useful if you want to generate code using ParseMethodBody() that outputs the result instead of just leaving it on the stack to rot.
+ void AddPrintInstruction();
+
+ void PrintAllTokens();
+ void PrintCode( std::ostream& outs ); // Prints the code in human-readable form to the specified stream.
+ void SaveToFile( const char* fpath ); // Saves raw code (without instances).
+
+ void SetCompileForDebug( bool n ) { mCompileForDebug = n; };
+
+ static void InitLookupTables(); // Called by the first BCParser's constructor automatically.
+
+protected:
+ void EndToken( std::string& ioCurrTokenStr, BCToken* ioCurrToken, unsigned int inNewStart );
+ void ParseObject( BCTokenList::iterator& itty );
+ void ParseExpression( BCTokenList::iterator& itty, BCUInt32* numParams, BCVarNameToIndexMap& vNameToIndex );
+ unsigned int ParseBinaryOperator( BCTokenList::iterator& itty );
+ unsigned int ParseValue( BCTokenList::iterator& itty, BCUInt32* numParams, BCVarNameToIndexMap& vNameToIndex, bool needContainer = false );
+ bool ParseMethod( BCTokenList::iterator& itty );
+ BCUInt32* AddPushLocalVarsInstruction( int numVars );
+ BCUInt32 AddStringInstruction( std::string& str, bool addPushInstr = true );
+ void AddDebugInstructions( BCTokenList::iterator& itty );
+ void AppendInstruction( BCInstruction& instr );
+ char TokensToTypeChar( BCTokenList::iterator& itty );
+ void TokenizeOneWhitespaceChar( size_t x, std::string& vCurrTokenStr, unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ void TokenizeOneStringChar( size_t x, std::string& vCurrTokenStr, unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ bool IsOperatorChar( char c );
+ void TokenizeOneNewlineChar( size_t x, std::string& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ void TokenizeOneOperatorChar( size_t x, std::string& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ void TokenizeOneIdentifierChar( size_t x, std::string& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ void TokenizeOneNumberChar( size_t x, std::deque& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ void TokenizeOneCommentChar( size_t x, std::deque& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ //additional line for testing.
+ void TokenizeOneBlockCommentChar( size_t x, std::deque& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ void AppendEscapedCharsFor( char escapeSequence, std::string& vCurrTokenStr );
+};
371 Testfile1.txt
@@ -0,0 +1,371 @@
+/*
+ * BCParser.h
+ * 1FACH
+ *
+ * Created by Uli Kusterer on Sun Jul 20 2003.
+ * Copyright (c) 2003 M. Uli Kusterer. All rights reserved.
+ *
+ */
+
+/* -----------------------------------------------------------------------------
+ Headers:
+ -------------------------------------------------------------------------- */
+
+#include <deque>
+#include <map>
+#include <string>
+#include <stdexcept>
+#include "BCInstruction.h"
+
+
+#ifndef BCPARSER_DEBUG
+#define BCPARSER_DEBUG 0
+#endif
+
+#ifndef BCDEBUG_TOKENIZER
+#define BCDEBUG_TOKENIZER BCPARSER_DEBUG
+#endif
+
+#if BCPARSER_DEBUG
+#define DEBUG_OUTPUT(n) printf("%s",n)
+#define DEBUG_OUTPUT2(n,o) printf(n,o)
+#define DEBUG_OUTPUT3(n,o,p) printf(n,o,p)
+#define DEBUG_OUTPUT4(n,o,p,q) printf(n,o,p,q)
+#else
+#define DEBUG_OUTPUT(n) // (n)
+#define DEBUG_OUTPUT2(n,o) // (n,o)
+#define DEBUG_OUTPUT3(n,o,p) // (n,o,p)
+#define DEBUG_OUTPUT4(n,o,p,q) // (n,o,p,q)
+#endif
+
+
+/* -----------------------------------------------------------------------------
+ BCToken:
+ Constants and struct definition for the data structure used for keeping
+ a list of tokens after tokenizing.
+ -------------------------------------------------------------------------- */
+
+// Possible values for mTokenType/during tokenizing:
+enum BCTokenTypeEnum
+{
+ BCT_WHITESPACE = 0, // We're currently scanning whitespace. There should never be a token that has this type in BCParser::mTokens. This may be used as an indicator for end-of-list.
+ BCT_NEWLINE, // A line break of any kind.
+ BCT_IDENTIFIER, // Any kind of identifier, operator etc.
+ BCT_STRING, // A double-quoted string constant.
+ BCT_INTEGER, // An identifier consisting only of numerical digits.
+ BCT_DOUBLE, // An identifier consisting only of numerical digits and a period.
+};
+
+
+// More readable variant of BCT_WHITESPACE when used as end-of-list indicator:
+#define BCT_INVALID BCT_WHITESPACE
+
+
+// Possible values for mTokenID:
+// If you add something here, you must also add it to mIdentifiers in BCParser's constructor.
+enum BCTokenIDEnum
+{
+ BCTI_ARBITRARY = 0, // Not a specially-recognized token.
+ BCTI_ON, // "on" for message handlers.
+ BCTI_END, // "end" for indicating end if, end handler etc.
+ BCTI_IF, // "if" ... then ...
+ BCTI_THEN, // if ... "then" ...
+ BCTI_ELSE, // if ... then ... "else"
+ BCTI_PUT, // "put" ... into ...
+ BCTI_INTO, // put ... "into" ...
+ BCTI_PASS, // "pass" through ...
+ BCTI_THROUGH, // pass "through" ...
+ BCTI_OPERATOR_PLUS, // "+"
+ BCTI_OPERATOR_MINUS, // "-"
+ BCTI_OPERATOR_MULTIPLY, // "*"
+ BCTI_OPERATOR_DIVIDE, // "/"
+ BCTI_OPERATOR_LIST_START, // "["
+ BCTI_OPERATOR_LIST_END, // "]"
+ BCTI_OPERATOR_IS, // "is" or "="
+ BCTI_OPERATOR_COMMA, // ","
+ BCTI_OPERATOR_AMPERSAND, // "&"
+ BCTI_WITH, // "with"
+ BCTI_OPERATOR_APOSTROPHE, // "'" for "object's property"
+ BCTI_OPERATOR_OPEN_BRACKET, // "("
+ BCTI_OPERATOR_CLOSE_BRACKET, // ")"
+ BCTI_PROPERTY, // "property" or "properties"
+ BCTI_OPERATOR_MOD, // "mod" for remainder of division operator.
+ BCTI_SYSTEM, // "system" identifier for system-native API calls.
+ BCTI_S, // "s" for "object's property"
+ BCTI_THE, // "the" for "the result" and such stuff
+ BCTI_RESULT, // "result" for "the result"
+ BCTI_NEW, // "new" as in "new function xy" [FFI]
+ BCTI_FUNCTION, // "function" as in "new function xy" [FFI]
+ BCTI_IN, // "in" as in "new function xy in <library>" [FFI]
+ BCTI_RETURNS, // "in" as in "new function xy returns type" [FFI]
+ BCTI_NOTHING, // "nothing" as in "new function xy returns nothing" for void functions [FFI]
+ BCTI_INTEGER, // "integer" as in "new function xy( integer )" for short integers [FFI]
+ BCTI_CHARACTER, // "character" data type [FFI]
+ BCTI_POINTER, // "pointer" data type [FFI]
+ BCTI_PASCAL, // "pascal" data type qualifier [FFI]
+ BCTI_STRING, // "string" data type [FFI]
+ BCTI_FOUNDATION, // "foundation" data type qualifier for Macs [FFI]
+ BCTI_UNSIGNED, // "unsigned" data type qualifier [FFI]
+ BCTI_DECIMAL, // "decimal" data type qualifier [FFI]
+ BCTI_NUMBER, // "number" data type [FFI]
+ BCTI_BIG, // "big" data type qualifier [FFI]
+ BCTI_OPERATOR_NOT, // binary "is not" operator or unary "not".
+ BCTI_OPERATOR_IS_NOT, // binary single-character "is not" operator ("=/="), and token type returned for "is not" by ParseBinaryOperator.
+ BCTI_CONTAINER, // "container" qualifier for pass-by-reference of parameters.
+ BCTI_AS, // "as" qualifier indicating name for associative array item.
+ BCTI_REPEAT, // "repeat" identifier used in loops.
+ BCTI_WHILE, // "while" qualifier used in repeat loops.
+ BCTI_TRUE, // "true" constant (1).
+ BCTI_FALSE, // "false" constant (0).
+ BCTI_RETURN, // "return" constant (ASCII character 13) or "return" command.
+ BCTI_LINEFEED, // "linefeed" constant (ASCII character 10).
+ BCTI_NULL, // "null" constant (ASCII character 0).
+ BCTI_TAB, // "tab" constant (ASCII character 9).
+ BCTI_OPERATOR_GREATER_THAN, // ">" operator
+ BCTI_OPERATOR_LESS_THAN, // "<" operator
+};
+
+
+// Character constants (may be platform-specific):
+
+// Unicode 0x0226:
+#define BCC_IS_NOT_SIGN_STR "�"
+#define BCC_IS_NOT_SIGN_CHAR '�'
+
+// Possible return values for ParseValue:
+enum
+{
+ BCV_NONE, // Nothing eligible for a value found.
+ BCV_STRING, // String constant parsed.
+ BCV_INTEGER, // Integer constant parsed.
+ BCV_DOUBLE, // Decimal number constant parsed.
+ BCV_LIST, // Constant list of values parsed.
+ BCV_OBJECT, // Object descriptor parsed.
+ BCV_DYNAMIC, // Any non-constant value, like a function result or list entry.
+};
+
+
+/* This is a data structure which we use as a shorthand notation for a token
+ (a "word"). It may seem silly to keep info about a word of four characters
+ in a 16-byte data structure, but since this one is fixed-size, scanning
+ forward and backward is a lot faster, and comparisons usually only involve
+ comparing four bytes instead of an entire string. */
+
+typedef unsigned int BCTokenType;
+typedef unsigned int BCTokenID;
+typedef unsigned int BCOffset;
+
+struct BCToken
+{
+ BCTokenType mTokenType; // String, number, identifier?
+ BCTokenID mTokenID; // Number to identify some tokens we recognize right away.
+ BCOffset mStartOffs; // Offset to first char of token in string.
+ BCOffset mEndOffs; // Offset to last char of token in string.
+};
+
+
+// Masks for our tokenizer's state machine:
+enum BCTokenState
+{
+ BC_TOKEN_STATE_WHITESPACE = 0,
+ BC_TOKEN_STATE_IDENTIFIER = (1 << 0),
+ BC_TOKEN_STATE_STRING = (1 << 1),
+ BC_TOKEN_STATE_ESCAPE_SEQ = (1 << 2), // Only when BC_TOKEN_STATE_STRING is set.
+ BC_TOKEN_STATE_INTEGER = (1 << 3), // Check this *and* BC_TOKEN_STATE_NUMBER. If NUMBER is set and this isn't, it means we already found a decimal point.
+ BC_TOKEN_STATE_NUMBER = (1 << 4),
+ BC_TOKEN_STATE_COMMENT = (1 << 5),
+ BC_TOKEN_STATE_BLOCK_COMMENT = (1 << 6),
+};
+
+
+/* -----------------------------------------------------------------------------
+ Data Types:
+ -------------------------------------------------------------------------- */
+
+class BCSysFcnEntry
+{
+public:
+ std::string mFunctionSignature; // Function signature (param and return value types).
+ std::string mFunctionNameLib; // Name of function and library.
+
+public:
+ BCSysFcnEntry() {};
+ BCSysFcnEntry( const BCSysFcnEntry* sfe ) { mFunctionSignature.assign( sfe->mFunctionSignature ); mFunctionNameLib.assign( sfe->mFunctionNameLib ); };
+ BCSysFcnEntry( const BCSysFcnEntry& sfe ) { mFunctionSignature.assign( sfe.mFunctionSignature ); mFunctionNameLib.assign( sfe.mFunctionNameLib ); };
+ BCSysFcnEntry( const std::string& sig, const std::string& nm ) : mFunctionSignature(sig), mFunctionNameLib(nm) {};
+ BCSysFcnEntry( const char* sig, const char* nm ) : mFunctionSignature(sig), mFunctionNameLib(nm) {};
+
+ BCSysFcnEntry& operator =( const BCSysFcnEntry& e ) { mFunctionSignature.assign( e.mFunctionSignature ); mFunctionNameLib.assign( e.mFunctionNameLib ); return *this; };
+ //const BCSysFcnEntry& operator =( const BCSysFcnEntry& e ) { mFunctionSignature.assign( e.mFunctionSignature ); mFunctionNameLib.assign( e.mFunctionNameLib ); return *this; };
+};
+
+class BCClassEntry
+{
+public:
+ std::string mClassName; // Name of this class.
+ BCClassEntry* mSuperclass; // Superclass of this class, NULL if none.
+ unsigned int mSuperclassCount; // Number of classes we inherit stuff from.
+ std::map<std::string,unsigned int> mInstanceVars; // Variable name --> slot offset mappings.
+
+public:
+ BCClassEntry() {};
+ BCClassEntry( std::string nm, BCClassEntry* su ) : mClassName(nm), mSuperclass(su) { CalcSuperclassCount(); };
+ BCClassEntry( const BCClassEntry* tm ) { mClassName.assign( tm->mClassName ); mSuperclass = tm->mSuperclass; mSuperclassCount = tm->mSuperclassCount; mInstanceVars = tm->mInstanceVars; };
+ BCClassEntry( const BCClassEntry& tm ) { mClassName.assign( tm.mClassName ); mSuperclass = tm.mSuperclass; mSuperclassCount = tm.mSuperclassCount; mInstanceVars = tm.mInstanceVars; };
+
+protected:
+ void CalcSuperclassCount() { if( !mSuperclass ) { mSuperclassCount = 0; return; } mSuperclassCount = mSuperclass->mSuperclassCount +1; };
+};
+
+typedef std::deque<BCToken> BCTokenList; // A list of BCToken objects.
+typedef std::deque<BCInstruction> BCInstructionList; // A list of BCInstruction structs.
+typedef std::map<std::string,BCTokenID> BCIdentifierIDMap; // Mapping from identifier names to their token IDs. Used by the tokenizer.
+typedef std::map<BCTokenID,BCUInt32> BCOperatorInstrMap; // Mapping from a certain operator's token ID to the associated instruction ID. Note that some operators may actually consist of several tokens, in which case the token ID is a "virtual" ID that represents *both* tokens or an equivalent token's ID.
+typedef std::map<BCUInt32,int> BCOpPrecedenceMap; // Mapping from a particular operator's instruction ID to an operator precedence value, to decide which operation in an expression will be executed first.
+typedef std::map<std::string,BCSysFcnEntry> BCSystemFunctionMap; // Mapping from system function name to its signature.
+typedef std::map<std::string,BCClassEntry> BCClassMap; // Mapping from class names to type information.
+typedef std::map<std::string,int> BCVarNameToIndexMap; // Mapping from local var to its stack index.
+typedef std::map<std::string,BCUInt32> BCConstantStringsMap; // Look-up table that allows re-using string constant instructions. AddStringInstruction() adds all strings it creates to this, and whenever a string is requested it returns an existing entry from this table, where possible.
+typedef std::map<BCTokenID,BCUInt32> BCIntegerConstMap; // Register all integral constants with this.
+typedef std::map<BCTokenID,std::string> BCStringConstMap; // Register all built-in string or character constants with this.
+
+
+/* -----------------------------------------------------------------------------
+ Exceptions:
+ These are a couple of exception objects that are thrown when an error
+ occurs.
+ -------------------------------------------------------------------------- */
+
+// Couldn't create a file:
+class BCCreateFileError : public std::runtime_error
+{
+public:
+ BCCreateFileError( const std::string name ) : std::runtime_error(name) {};
+
+ virtual const char* what() const throw()
+ {
+ static char vMsg[512];
+
+ snprintf( vMsg, sizeof(vMsg), "Can't create file \"%s\".", std::runtime_error::what() );
+
+ return vMsg;
+ };
+};
+
+
+// Couldn't write to a file:
+class BCWriteFileError : public std::runtime_error
+{
+public:
+ BCWriteFileError( const std::string name ) : std::runtime_error(name) {};
+};
+
+
+// Syntax error or something like that in the parser:
+class BCParserError : public std::runtime_error
+{
+ unsigned int mOffset; // Offset into the text where the error occurred.
+
+public:
+ BCParserError( const std::string name, unsigned int offs = 0 ) : std::runtime_error(name) { mOffset = offs; };
+
+ virtual const char* what() const throw()
+ {
+ static char vMsg[512];
+
+ snprintf( vMsg, sizeof(vMsg), "%s (offset %u).", std::runtime_error::what(), mOffset );
+
+ return vMsg;
+ };
+};
+
+
+/* -----------------------------------------------------------------------------
+ BCParser:
+ The parser class. This is an object which can be used to turn text into
+ instructions by tokenizing and parsing the text. There are also some
+ methods that allow adding certain instructions to the code (e.g. if you
+ just want to evaluate an expression and print its result to the
+ console), and some methods that allow saving code to disk.
+ -------------------------------------------------------------------------- */
+
+class BCParser
+{
+protected:
+ BCTokenList mTokens; // List of tokens (after Tokenize() has been called).
+ BCInstructionList mCode; // Code (after Parse() has been called).
+ char* mText; // Text being tokenized/parsed (only until parsing is complete)
+ bool mCompileForDebug; // Generate instructions that aid in source-level debugging?
+ std::string mCurrentFilename; // Name of current source file.
+ BCConstantStringsMap mConstantStrings; // String constant -> String instruction index mappings used by AddStringInstruction to allow re-use.
+ static BCIdentifierIDMap sIdentifiers; // List of identifiers (after construction).
+ static BCOperatorInstrMap sOperatorInstrs; // Mappings from operator token ID to instruction type.
+ static BCOpPrecedenceMap sOpPrecedences; // Mappings from operator instruction type to operator precedence (higher means gets the left argument).
+ static BCSystemFunctionMap sSystemFunctions; // Mappings from system function name to signature.
+ static BCClassMap sClasses; // Information about the different classes we've seen so far.
+ static BCUInt32 sObjectIDSeed; // Number for a new object's ID. If you use it, add one to it so the next user gets a fresh one.
+ static BCIntegerConstMap sIntegralConstants; // Mappings from token type for a constant to its integer value.
+ static BCStringConstMap sStringConstants; // Mappings from token type for a constant to its integral value.
+
+public:
+ BCParser();
+
+ void SetCurrentFilename( const std::string& s) { mCurrentFilename.assign( s ); }; // File path where the debugger should look for this source file.
+
+ void Tokenize( const char* inText, size_t len ); // Call this first, and hand it your text to be parsed. The text needn't be zero-terminated, and length shouldn't include any terminating zero bytes if you add them. You mustn't dispose of the text until it's been parsed.
+ void ClearTokens() { mTokens.clear(); };
+
+ void Parse(); // Call this or ParseMethodBody second.
+ void ParseMethodBody( BCTokenList::iterator& itty, BCUInt32* numParams, BCVarNameToIndexMap& vNameToIndex, BCToken* stopTokens = NULL );
+ void ParseMethodBody( BCUInt32* numParams, BCVarNameToIndexMap& vNameToIndex );
+ void ParseHeaderFile();
+
+ void AddEndInstruction(); // Mark end of code block.
+ void AddPrintResultInstructions(); // Adds a "print" instruction that outputs the variable "the result". Useful if you want to generate code using ParseMethodBody() that outputs the result instead of just leaving it on the stack to rot.
+ void AddPrintInstruction();
+
+ void PrintAllTokens();
+ void PrintCode( std::ostream& outs ); // Prints the code in human-readable form to the specified stream.
+ void SaveToFile( const char* fpath ); // Saves raw code (without instances).
+
+ void SetCompileForDebug( bool n ) { mCompileForDebug = n; };
+
+ static void InitLookupTables(); // Called by the first BCParser's constructor automatically.
+
+protected:
+ void EndToken( std::string& ioCurrTokenStr, BCToken* ioCurrToken, unsigned int inNewStart );
+ void ParseObject( BCTokenList::iterator& itty );
+ void ParseExpression( BCTokenList::iterator& itty, BCUInt32* numParams, BCVarNameToIndexMap& vNameToIndex );
+ unsigned int ParseBinaryOperator( BCTokenList::iterator& itty );
+ unsigned int ParseValue( BCTokenList::iterator& itty, BCUInt32* numParams, BCVarNameToIndexMap& vNameToIndex, bool needContainer = false );
+ bool ParseMethod( BCTokenList::iterator& itty );
+ BCUInt32* AddPushLocalVarsInstruction( int numVars );
+ BCUInt32 AddStringInstruction( std::string& str, bool addPushInstr = true );
+ void AddDebugInstructions( BCTokenList::iterator& itty );
+ void AppendInstruction( BCInstruction& instr );
+ char TokensToTypeChar( BCTokenList::iterator& itty );
+ void TokenizeOneWhitespaceChar( size_t x, std::string& vCurrTokenStr, unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ bool IsOperatorChar( char c );
+ void TokenizeOneNewlineChar( size_t x, std::string& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ void TokenizeOneOperatorChar( size_t x, std::string& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ void TokenizeOneIdentifierChar( size_t x, std::string& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ void TokenizeOneNumberChar( size_t x, std::string& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ void TokenizeOneCommentChar( size_t x, std::string& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ void TokenizeOneBlockCommentChar( size_t x, std::string& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ void AppendEscapedCharsFor( char escapeSequence, std::string& vCurrTokenStr );
+ void TokenizeOneStringChar( size_t x, std::string& vCurrTokenStr, unsigned *ioTokenState,
+ BCToken* vCurrToken );
+};
65 Testfile1.udiff
@@ -0,0 +1,65 @@
+--- /Users/uli/Programming/AngelDiff/Testfile1.txt 2004-01-03 00:19:05.000000000 +0100
++++ /Users/uli/Programming/AngelDiff/Testfile2.txt 2008-08-02 12:34:16.000000000 +0200
+@@ -3,7 +3,7 @@
+ * 1FACH
+ *
+ * Created by Uli Kusterer on Sun Jul 20 2003.
+- * Copyright (c) 2003 M. Uli Kusterer. All rights reserved.
++ * Copyright (c) 2009 Gerda the Great. All rights reserved.
+ *
+ */
+
+@@ -291,22 +291,6 @@
+
+ class BCParser
+ {
+-protected:
+- BCTokenList mTokens; // List of tokens (after Tokenize() has been called).
+- BCInstructionList mCode; // Code (after Parse() has been called).
+- char* mText; // Text being tokenized/parsed (only until parsing is complete)
+- bool mCompileForDebug; // Generate instructions that aid in source-level debugging?
+- std::string mCurrentFilename; // Name of current source file.
+- BCConstantStringsMap mConstantStrings; // String constant -> String instruction index mappings used by AddStringInstruction to allow re-use.
+- static BCIdentifierIDMap sIdentifiers; // List of identifiers (after construction).
+- static BCOperatorInstrMap sOperatorInstrs; // Mappings from operator token ID to instruction type.
+- static BCOpPrecedenceMap sOpPrecedences; // Mappings from operator instruction type to operator precedence (higher means gets the left argument).
+- static BCSystemFunctionMap sSystemFunctions; // Mappings from system function name to signature.
+- static BCClassMap sClasses; // Information about the different classes we've seen so far.
+- static BCUInt32 sObjectIDSeed; // Number for a new object's ID. If you use it, add one to it so the next user gets a fresh one.
+- static BCIntegerConstMap sIntegralConstants; // Mappings from token type for a constant to its integer value.
+- static BCStringConstMap sStringConstants; // Mappings from token type for a constant to its integral value.
+-
+ public:
+ BCParser();
+
+@@ -346,6 +330,8 @@
+ char TokensToTypeChar( BCTokenList::iterator& itty );
+ void TokenizeOneWhitespaceChar( size_t x, std::string& vCurrTokenStr, unsigned *ioTokenState,
+ BCToken* vCurrToken );
++ void TokenizeOneStringChar( size_t x, std::string& vCurrTokenStr, unsigned *ioTokenState,
++ BCToken* vCurrToken );
+ bool IsOperatorChar( char c );
+ void TokenizeOneNewlineChar( size_t x, std::string& vCurrTokenStr,
+ unsigned *ioTokenState,
+@@ -356,16 +342,15 @@
+ void TokenizeOneIdentifierChar( size_t x, std::string& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+- void TokenizeOneNumberChar( size_t x, std::string& vCurrTokenStr,
++ void TokenizeOneNumberChar( size_t x, std::deque& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+- void TokenizeOneCommentChar( size_t x, std::string& vCurrTokenStr,
++ void TokenizeOneCommentChar( size_t x, std::deque& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+- void TokenizeOneBlockCommentChar( size_t x, std::string& vCurrTokenStr,
++ //additional line for testing.
++ void TokenizeOneBlockCommentChar( size_t x, std::deque& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ void AppendEscapedCharsFor( char escapeSequence, std::string& vCurrTokenStr );
+- void TokenizeOneStringChar( size_t x, std::string& vCurrTokenStr, unsigned *ioTokenState,
+- BCToken* vCurrToken );
+ };
+\ No newline at end of file
356 Testfile2.txt
@@ -0,0 +1,356 @@
+/*
+ * BCParser.h
+ * 1FACH
+ *
+ * Created by Uli Kusterer on Sun Jul 20 2003.
+ * Copyright (c) 2009 Gerda the Great. All rights reserved.
+ *
+ */
+
+/* -----------------------------------------------------------------------------
+ Headers:
+ -------------------------------------------------------------------------- */
+
+#include <deque>
+#include <map>
+#include <string>
+#include <stdexcept>
+#include "BCInstruction.h"
+
+
+#ifndef BCPARSER_DEBUG
+#define BCPARSER_DEBUG 0
+#endif
+
+#ifndef BCDEBUG_TOKENIZER
+#define BCDEBUG_TOKENIZER BCPARSER_DEBUG
+#endif
+
+#if BCPARSER_DEBUG
+#define DEBUG_OUTPUT(n) printf("%s",n)
+#define DEBUG_OUTPUT2(n,o) printf(n,o)
+#define DEBUG_OUTPUT3(n,o,p) printf(n,o,p)
+#define DEBUG_OUTPUT4(n,o,p,q) printf(n,o,p,q)
+#else
+#define DEBUG_OUTPUT(n) // (n)
+#define DEBUG_OUTPUT2(n,o) // (n,o)
+#define DEBUG_OUTPUT3(n,o,p) // (n,o,p)
+#define DEBUG_OUTPUT4(n,o,p,q) // (n,o,p,q)
+#endif
+
+
+/* -----------------------------------------------------------------------------
+ BCToken:
+ Constants and struct definition for the data structure used for keeping
+ a list of tokens after tokenizing.
+ -------------------------------------------------------------------------- */
+
+// Possible values for mTokenType/during tokenizing:
+enum BCTokenTypeEnum
+{
+ BCT_WHITESPACE = 0, // We're currently scanning whitespace. There should never be a token that has this type in BCParser::mTokens. This may be used as an indicator for end-of-list.
+ BCT_NEWLINE, // A line break of any kind.
+ BCT_IDENTIFIER, // Any kind of identifier, operator etc.
+ BCT_STRING, // A double-quoted string constant.
+ BCT_INTEGER, // An identifier consisting only of numerical digits.
+ BCT_DOUBLE, // An identifier consisting only of numerical digits and a period.
+};
+
+
+// More readable variant of BCT_WHITESPACE when used as end-of-list indicator:
+#define BCT_INVALID BCT_WHITESPACE
+
+
+// Possible values for mTokenID:
+// If you add something here, you must also add it to mIdentifiers in BCParser's constructor.
+enum BCTokenIDEnum
+{
+ BCTI_ARBITRARY = 0, // Not a specially-recognized token.
+ BCTI_ON, // "on" for message handlers.
+ BCTI_END, // "end" for indicating end if, end handler etc.
+ BCTI_IF, // "if" ... then ...
+ BCTI_THEN, // if ... "then" ...
+ BCTI_ELSE, // if ... then ... "else"
+ BCTI_PUT, // "put" ... into ...
+ BCTI_INTO, // put ... "into" ...
+ BCTI_PASS, // "pass" through ...
+ BCTI_THROUGH, // pass "through" ...
+ BCTI_OPERATOR_PLUS, // "+"
+ BCTI_OPERATOR_MINUS, // "-"
+ BCTI_OPERATOR_MULTIPLY, // "*"
+ BCTI_OPERATOR_DIVIDE, // "/"
+ BCTI_OPERATOR_LIST_START, // "["
+ BCTI_OPERATOR_LIST_END, // "]"
+ BCTI_OPERATOR_IS, // "is" or "="
+ BCTI_OPERATOR_COMMA, // ","
+ BCTI_OPERATOR_AMPERSAND, // "&"
+ BCTI_WITH, // "with"
+ BCTI_OPERATOR_APOSTROPHE, // "'" for "object's property"
+ BCTI_OPERATOR_OPEN_BRACKET, // "("
+ BCTI_OPERATOR_CLOSE_BRACKET, // ")"
+ BCTI_PROPERTY, // "property" or "properties"
+ BCTI_OPERATOR_MOD, // "mod" for remainder of division operator.
+ BCTI_SYSTEM, // "system" identifier for system-native API calls.
+ BCTI_S, // "s" for "object's property"
+ BCTI_THE, // "the" for "the result" and such stuff
+ BCTI_RESULT, // "result" for "the result"
+ BCTI_NEW, // "new" as in "new function xy" [FFI]
+ BCTI_FUNCTION, // "function" as in "new function xy" [FFI]
+ BCTI_IN, // "in" as in "new function xy in <library>" [FFI]
+ BCTI_RETURNS, // "in" as in "new function xy returns type" [FFI]
+ BCTI_NOTHING, // "nothing" as in "new function xy returns nothing" for void functions [FFI]
+ BCTI_INTEGER, // "integer" as in "new function xy( integer )" for short integers [FFI]
+ BCTI_CHARACTER, // "character" data type [FFI]
+ BCTI_POINTER, // "pointer" data type [FFI]
+ BCTI_PASCAL, // "pascal" data type qualifier [FFI]
+ BCTI_STRING, // "string" data type [FFI]
+ BCTI_FOUNDATION, // "foundation" data type qualifier for Macs [FFI]
+ BCTI_UNSIGNED, // "unsigned" data type qualifier [FFI]
+ BCTI_DECIMAL, // "decimal" data type qualifier [FFI]
+ BCTI_NUMBER, // "number" data type [FFI]
+ BCTI_BIG, // "big" data type qualifier [FFI]
+ BCTI_OPERATOR_NOT, // binary "is not" operator or unary "not".
+ BCTI_OPERATOR_IS_NOT, // binary single-character "is not" operator ("=/="), and token type returned for "is not" by ParseBinaryOperator.
+ BCTI_CONTAINER, // "container" qualifier for pass-by-reference of parameters.
+ BCTI_AS, // "as" qualifier indicating name for associative array item.
+ BCTI_REPEAT, // "repeat" identifier used in loops.
+ BCTI_WHILE, // "while" qualifier used in repeat loops.
+ BCTI_TRUE, // "true" constant (1).
+ BCTI_FALSE, // "false" constant (0).
+ BCTI_RETURN, // "return" constant (ASCII character 13) or "return" command.
+ BCTI_LINEFEED, // "linefeed" constant (ASCII character 10).
+ BCTI_NULL, // "null" constant (ASCII character 0).
+ BCTI_TAB, // "tab" constant (ASCII character 9).
+ BCTI_OPERATOR_GREATER_THAN, // ">" operator
+ BCTI_OPERATOR_LESS_THAN, // "<" operator
+};
+
+
+// Character constants (may be platform-specific):
+
+// Unicode 0x0226:
+#define BCC_IS_NOT_SIGN_STR "�"
+#define BCC_IS_NOT_SIGN_CHAR '�'
+
+// Possible return values for ParseValue:
+enum
+{
+ BCV_NONE, // Nothing eligible for a value found.
+ BCV_STRING, // String constant parsed.
+ BCV_INTEGER, // Integer constant parsed.
+ BCV_DOUBLE, // Decimal number constant parsed.
+ BCV_LIST, // Constant list of values parsed.
+ BCV_OBJECT, // Object descriptor parsed.
+ BCV_DYNAMIC, // Any non-constant value, like a function result or list entry.
+};
+
+
+/* This is a data structure which we use as a shorthand notation for a token
+ (a "word"). It may seem silly to keep info about a word of four characters
+ in a 16-byte data structure, but since this one is fixed-size, scanning
+ forward and backward is a lot faster, and comparisons usually only involve
+ comparing four bytes instead of an entire string. */
+
+typedef unsigned int BCTokenType;
+typedef unsigned int BCTokenID;
+typedef unsigned int BCOffset;
+
+struct BCToken
+{
+ BCTokenType mTokenType; // String, number, identifier?
+ BCTokenID mTokenID; // Number to identify some tokens we recognize right away.
+ BCOffset mStartOffs; // Offset to first char of token in string.
+ BCOffset mEndOffs; // Offset to last char of token in string.
+};
+
+
+// Masks for our tokenizer's state machine:
+enum BCTokenState
+{
+ BC_TOKEN_STATE_WHITESPACE = 0,
+ BC_TOKEN_STATE_IDENTIFIER = (1 << 0),
+ BC_TOKEN_STATE_STRING = (1 << 1),
+ BC_TOKEN_STATE_ESCAPE_SEQ = (1 << 2), // Only when BC_TOKEN_STATE_STRING is set.
+ BC_TOKEN_STATE_INTEGER = (1 << 3), // Check this *and* BC_TOKEN_STATE_NUMBER. If NUMBER is set and this isn't, it means we already found a decimal point.
+ BC_TOKEN_STATE_NUMBER = (1 << 4),
+ BC_TOKEN_STATE_COMMENT = (1 << 5),
+ BC_TOKEN_STATE_BLOCK_COMMENT = (1 << 6),
+};
+
+
+/* -----------------------------------------------------------------------------
+ Data Types:
+ -------------------------------------------------------------------------- */
+
+class BCSysFcnEntry
+{
+public:
+ std::string mFunctionSignature; // Function signature (param and return value types).
+ std::string mFunctionNameLib; // Name of function and library.
+
+public:
+ BCSysFcnEntry() {};
+ BCSysFcnEntry( const BCSysFcnEntry* sfe ) { mFunctionSignature.assign( sfe->mFunctionSignature ); mFunctionNameLib.assign( sfe->mFunctionNameLib ); };
+ BCSysFcnEntry( const BCSysFcnEntry& sfe ) { mFunctionSignature.assign( sfe.mFunctionSignature ); mFunctionNameLib.assign( sfe.mFunctionNameLib ); };
+ BCSysFcnEntry( const std::string& sig, const std::string& nm ) : mFunctionSignature(sig), mFunctionNameLib(nm) {};
+ BCSysFcnEntry( const char* sig, const char* nm ) : mFunctionSignature(sig), mFunctionNameLib(nm) {};
+
+ BCSysFcnEntry& operator =( const BCSysFcnEntry& e ) { mFunctionSignature.assign( e.mFunctionSignature ); mFunctionNameLib.assign( e.mFunctionNameLib ); return *this; };
+ //const BCSysFcnEntry& operator =( const BCSysFcnEntry& e ) { mFunctionSignature.assign( e.mFunctionSignature ); mFunctionNameLib.assign( e.mFunctionNameLib ); return *this; };
+};
+
+class BCClassEntry
+{
+public:
+ std::string mClassName; // Name of this class.
+ BCClassEntry* mSuperclass; // Superclass of this class, NULL if none.
+ unsigned int mSuperclassCount; // Number of classes we inherit stuff from.
+ std::map<std::string,unsigned int> mInstanceVars; // Variable name --> slot offset mappings.
+
+public:
+ BCClassEntry() {};
+ BCClassEntry( std::string nm, BCClassEntry* su ) : mClassName(nm), mSuperclass(su) { CalcSuperclassCount(); };
+ BCClassEntry( const BCClassEntry* tm ) { mClassName.assign( tm->mClassName ); mSuperclass = tm->mSuperclass; mSuperclassCount = tm->mSuperclassCount; mInstanceVars = tm->mInstanceVars; };
+ BCClassEntry( const BCClassEntry& tm ) { mClassName.assign( tm.mClassName ); mSuperclass = tm.mSuperclass; mSuperclassCount = tm.mSuperclassCount; mInstanceVars = tm.mInstanceVars; };
+
+protected:
+ void CalcSuperclassCount() { if( !mSuperclass ) { mSuperclassCount = 0; return; } mSuperclassCount = mSuperclass->mSuperclassCount +1; };
+};
+
+typedef std::deque<BCToken> BCTokenList; // A list of BCToken objects.
+typedef std::deque<BCInstruction> BCInstructionList; // A list of BCInstruction structs.
+typedef std::map<std::string,BCTokenID> BCIdentifierIDMap; // Mapping from identifier names to their token IDs. Used by the tokenizer.
+typedef std::map<BCTokenID,BCUInt32> BCOperatorInstrMap; // Mapping from a certain operator's token ID to the associated instruction ID. Note that some operators may actually consist of several tokens, in which case the token ID is a "virtual" ID that represents *both* tokens or an equivalent token's ID.
+typedef std::map<BCUInt32,int> BCOpPrecedenceMap; // Mapping from a particular operator's instruction ID to an operator precedence value, to decide which operation in an expression will be executed first.
+typedef std::map<std::string,BCSysFcnEntry> BCSystemFunctionMap; // Mapping from system function name to its signature.
+typedef std::map<std::string,BCClassEntry> BCClassMap; // Mapping from class names to type information.
+typedef std::map<std::string,int> BCVarNameToIndexMap; // Mapping from local var to its stack index.
+typedef std::map<std::string,BCUInt32> BCConstantStringsMap; // Look-up table that allows re-using string constant instructions. AddStringInstruction() adds all strings it creates to this, and whenever a string is requested it returns an existing entry from this table, where possible.
+typedef std::map<BCTokenID,BCUInt32> BCIntegerConstMap; // Register all integral constants with this.
+typedef std::map<BCTokenID,std::string> BCStringConstMap; // Register all built-in string or character constants with this.
+
+
+/* -----------------------------------------------------------------------------
+ Exceptions:
+ These are a couple of exception objects that are thrown when an error
+ occurs.
+ -------------------------------------------------------------------------- */
+
+// Couldn't create a file:
+class BCCreateFileError : public std::runtime_error
+{
+public:
+ BCCreateFileError( const std::string name ) : std::runtime_error(name) {};
+
+ virtual const char* what() const throw()
+ {
+ static char vMsg[512];
+
+ snprintf( vMsg, sizeof(vMsg), "Can't create file \"%s\".", std::runtime_error::what() );
+
+ return vMsg;
+ };
+};
+
+
+// Couldn't write to a file:
+class BCWriteFileError : public std::runtime_error
+{
+public:
+ BCWriteFileError( const std::string name ) : std::runtime_error(name) {};
+};
+
+
+// Syntax error or something like that in the parser:
+class BCParserError : public std::runtime_error
+{
+ unsigned int mOffset; // Offset into the text where the error occurred.
+
+public:
+ BCParserError( const std::string name, unsigned int offs = 0 ) : std::runtime_error(name) { mOffset = offs; };
+
+ virtual const char* what() const throw()
+ {
+ static char vMsg[512];
+
+ snprintf( vMsg, sizeof(vMsg), "%s (offset %u).", std::runtime_error::what(), mOffset );
+
+ return vMsg;
+ };
+};
+
+
+/* -----------------------------------------------------------------------------
+ BCParser:
+ The parser class. This is an object which can be used to turn text into
+ instructions by tokenizing and parsing the text. There are also some
+ methods that allow adding certain instructions to the code (e.g. if you
+ just want to evaluate an expression and print its result to the
+ console), and some methods that allow saving code to disk.
+ -------------------------------------------------------------------------- */
+
+class BCParser
+{
+public:
+ BCParser();
+
+ void SetCurrentFilename( const std::string& s) { mCurrentFilename.assign( s ); }; // File path where the debugger should look for this source file.
+
+ void Tokenize( const char* inText, size_t len ); // Call this first, and hand it your text to be parsed. The text needn't be zero-terminated, and length shouldn't include any terminating zero bytes if you add them. You mustn't dispose of the text until it's been parsed.
+ void ClearTokens() { mTokens.clear(); };
+
+ void Parse(); // Call this or ParseMethodBody second.
+ void ParseMethodBody( BCTokenList::iterator& itty, BCUInt32* numParams, BCVarNameToIndexMap& vNameToIndex, BCToken* stopTokens = NULL );
+ void ParseMethodBody( BCUInt32* numParams, BCVarNameToIndexMap& vNameToIndex );
+ void ParseHeaderFile();
+
+ void AddEndInstruction(); // Mark end of code block.
+ void AddPrintResultInstructions(); // Adds a "print" instruction that outputs the variable "the result". Useful if you want to generate code using ParseMethodBody() that outputs the result instead of just leaving it on the stack to rot.
+ void AddPrintInstruction();
+
+ void PrintAllTokens();
+ void PrintCode( std::ostream& outs ); // Prints the code in human-readable form to the specified stream.
+ void SaveToFile( const char* fpath ); // Saves raw code (without instances).
+
+ void SetCompileForDebug( bool n ) { mCompileForDebug = n; };
+
+ static void InitLookupTables(); // Called by the first BCParser's constructor automatically.
+
+protected:
+ void EndToken( std::string& ioCurrTokenStr, BCToken* ioCurrToken, unsigned int inNewStart );
+ void ParseObject( BCTokenList::iterator& itty );
+ void ParseExpression( BCTokenList::iterator& itty, BCUInt32* numParams, BCVarNameToIndexMap& vNameToIndex );
+ unsigned int ParseBinaryOperator( BCTokenList::iterator& itty );
+ unsigned int ParseValue( BCTokenList::iterator& itty, BCUInt32* numParams, BCVarNameToIndexMap& vNameToIndex, bool needContainer = false );
+ bool ParseMethod( BCTokenList::iterator& itty );
+ BCUInt32* AddPushLocalVarsInstruction( int numVars );
+ BCUInt32 AddStringInstruction( std::string& str, bool addPushInstr = true );
+ void AddDebugInstructions( BCTokenList::iterator& itty );
+ void AppendInstruction( BCInstruction& instr );
+ char TokensToTypeChar( BCTokenList::iterator& itty );
+ void TokenizeOneWhitespaceChar( size_t x, std::string& vCurrTokenStr, unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ void TokenizeOneStringChar( size_t x, std::string& vCurrTokenStr, unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ bool IsOperatorChar( char c );
+ void TokenizeOneNewlineChar( size_t x, std::string& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ void TokenizeOneOperatorChar( size_t x, std::string& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ void TokenizeOneIdentifierChar( size_t x, std::string& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ void TokenizeOneNumberChar( size_t x, std::deque& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ void TokenizeOneCommentChar( size_t x, std::deque& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ //additional line for testing.
+ void TokenizeOneBlockCommentChar( size_t x, std::deque& vCurrTokenStr,
+ unsigned *ioTokenState,
+ BCToken* vCurrToken );
+ void AppendEscapedCharsFor( char escapeSequence, std::string& vCurrTokenStr );
+};
88 UKDiffParser.h
@@ -0,0 +1,88 @@
+//
+// UKDiffParser.h
+// AngelDiff
+//
+// Created by Uli Kusterer on 02.08.08.
+// Copyright 2008 The Void Software. All rights reserved.
+//
+
+// -----------------------------------------------------------------------------
+// Headers:
+// -----------------------------------------------------------------------------
+
+#import <Cocoa/Cocoa.h>
+
+
+@class UKDiffEntry;
+
+// -----------------------------------------------------------------------------
+// Constants:
+// -----------------------------------------------------------------------------
+
+enum
+{
+ UKDiffOperationChange = 'c', // newText replaces oldText.
+ UKDiffOperationDelete = 'd', // oldText or nothing.
+ UKDiffOperationAdd = 'a', // nothing or newText.
+ UKDiffOperationUnchanged = 0 // oldText and newText are the same. destinationRange is unset.
+};
+typedef unichar UKDiffOperation;
+
+
+// -----------------------------------------------------------------------------
+// UKDiffParser:
+// Parses a diff in string form and can then turn a source text into the
+// destination text by applying this diff.
+// -----------------------------------------------------------------------------
+
+@interface UKDiffParser : NSObject
+{
+ NSMutableArray* differences;
+}
+
+@property (retain) NSMutableArray* differences;
+
+-(id) initWithDiffText: (NSString*)diffText; // The diff to apply.
+-(id) initWithUnifiedDiffText: (NSString*)diffText; // The diff to apply.
+
+-(void) applyOriginalText: (NSString*)origText; // The text to apply the diff to.
+
+-(NSString*) originalString; // Must have called applyOriginalText: once before using this.
+-(NSString*) destinationString; // Must have called applyOriginalText: once before using this.
+-(NSString*) mergedString; // Must have called applyOriginalText: once before using this.
+
+// Use these e.g for a view that displays a diff:
+-(NSUInteger) count;
+-(UKDiffEntry*) entryAtIndex: (NSUInteger)idx;
+
+// Going away:
+-(NSString*) stringByApplyingChangesToOriginalText: (NSString*)origText;
+
+@end
+
+
+// -----------------------------------------------------------------------------
+// UKDiffEntry:
+// Class used for storing a parsed version of the source, destination and
+// their differences.
+// -----------------------------------------------------------------------------
+
+@interface UKDiffEntry : NSObject
+{
+ UKDiffOperation operation;
+ NSRange originalRange;
+ NSRange destinationRange;
+ NSString* newText;
+ NSString* oldText;
+ BOOL apply; // YES if we should apply this change, NO if we should leave the original text in the output.
+}
+
+@property (assign) UKDiffOperation operation;
+@property (assign) NSRange originalRange;
+@property (assign) NSRange destinationRange;
+@property (retain) NSString* newText;
+@property (retain) NSString* oldText;
+@property (assign) BOOL apply;
+
+@end
+
636 UKDiffParser.m
@@ -0,0 +1,636 @@
+//
+// UKDiffParser.m
+// AngelDiff
+//
+// Created by Uli Kusterer on 02.08.08.
+// Copyright 2008 The Void Software. All rights reserved.
+//
+
+#import "UKDiffParser.h"
+#import "UKHelperMacros.h"
+
+
+@implementation UKDiffParser
+
+@synthesize differences;
+
+-(id) initWithDiffText: (NSString*)diffText
+{
+ if(( self = [super init] ))
+ {
+ self.differences = [NSMutableArray array];
+ NSUInteger currPos = 0, endPos = [diffText length];
+ while( currPos < endPos )
+ {
+ unichar currCh = [diffText characterAtIndex: currPos];
+ if( currCh == '\n' || currCh == '\r' ) // Empty line? Skip.
+ {
+ currPos++;
+ continue;
+ }
+
+ // Get info about operation:
+ NSUInteger num[4] = { 0, 0, 0, 0 },
+ numIdx = 0;
+ unichar separators[4] = { 0, 0, 0, 0 };
+ NSMutableString* currStr = [NSMutableString string];
+ while( currPos < endPos )
+ {
+ currCh = [diffText characterAtIndex: currPos++];
+ if( isnumber( currCh ) )
+ [currStr appendFormat: @"%C", currCh];
+ else
+ {
+ num[numIdx] = [currStr integerValue];
+ separators[numIdx] = currCh;
+ numIdx++;
+ [currStr deleteCharactersInRange: NSMakeRange( 0, [currStr length] )];
+
+ if( currCh == '\n' || currCh == '\r' )
+ break;
+ }
+ }
+
+ NSRange originalRange, destRange;
+ originalRange.location = num[0] -1;
+ NSUInteger separatorIdx = 0;
+
+ if( separators[0] == ',' )
+ {
+ originalRange.length = num[1] -originalRange.location;
+ separatorIdx++;
+ }
+ else
+ originalRange.length = 1;
+
+ destRange.location = num[separatorIdx+1] -1;
+ if( separators[separatorIdx+1] == ',' )
+ destRange.length = num[separatorIdx+2] -destRange.location;
+ else
+ destRange.length = 1;
+
+ // If it's a source line, ignore it for now:
+ currCh = [diffText characterAtIndex: currPos];
+ while( currCh == '<' && currPos < endPos )
+ {
+ do
+ {
+ currPos++;
+ currCh = [diffText characterAtIndex: currPos];
+ }
+ while( currCh != '\n' && currCh != '\r' && currPos < endPos );
+ if( currPos < (endPos-1) )
+ {
+ currPos++;
+ currCh = [diffText characterAtIndex: currPos];
+ }
+ }
+ if( currCh == '-' && currPos < endPos ) // Skip separator line.
+ {
+ do
+ {
+ currPos++;
+ currCh = [diffText characterAtIndex: currPos];
+ }
+ while( currCh != '\n' && currCh != '\r' && currPos < endPos );
+ if( currPos < (endPos-1) )
+ {
+ currPos++;
+ currCh = [diffText characterAtIndex: currPos];
+ }
+ }
+
+ NSMutableString* newString = [NSMutableString string];
+ while( currCh == '>' && currPos < endPos )
+ {
+ currCh = [diffText characterAtIndex: ++currPos];
+ if( currCh != ' ' ) // There's a greater-than and a space, usually.
+ currPos--;
+
+ do
+ {
+ currPos++;
+ currCh = [diffText characterAtIndex: currPos];
+ [newString appendFormat: @"%C", currCh];
+ }
+ while( currCh != '\n' && currCh != '\r' && currPos < endPos );
+ if( currPos < (endPos-1) )
+ {
+ currPos++;
+ currCh = [diffText characterAtIndex: currPos];
+ }
+ }
+
+ UKDiffEntry* currDiff = [[[UKDiffEntry alloc] init] autorelease];
+
+ [currDiff setOperation: separators[separatorIdx]];
+ [currDiff setOriginalRange: originalRange];
+ [currDiff setDestinationRange: destRange];
+ [currDiff setNewText: newString];
+ [differences addObject: currDiff];
+ }
+ }
+
+ return self;
+}
+
+
+-(id) initWithUnifiedDiffText: (NSString*)diffText
+{
+ if(( self = [super init] ))
+ {
+ self.differences = [NSMutableArray array];
+ NSUInteger currPos = 0, endPos = [diffText length];
+ while( currPos < endPos )
+ {
+ UKLog(@"NEW ITERATION");
+ unichar currCh = [diffText characterAtIndex: currPos];
+ if( currCh == '-' || currCh == '+' )
+ {
+ while( (currCh != '\n' && currCh != '\r') && currPos < endPos )
+ {
+ currCh = [diffText characterAtIndex: ++currPos];
+ }
+ UKLog(@"\tSkipping file marker line");
+ }
+
+ if( currCh == '\n' || currCh == '\r' ) // Empty line? Skip.
+ {
+ UKLog(@"\tSkipping empty line");
+ ++currPos;
+ continue;
+ }
+
+ // @@
+ if( currCh != '@' )
+ {
+ [self autorelease];
+ UKLog(@"NO @@ Marker found");
+ return nil;
+ }
+
+ currCh = [diffText characterAtIndex: ++currPos];
+ if( currCh != '@' )
+ {
+ [self autorelease];
+ UKLog(@"NO @@ Marker found");
+ return nil;
+ }
+
+ UKLog(@"\t@@ Markers found");
+
+ currCh = [diffText characterAtIndex: ++currPos];
+ while( currCh == ' ' || currCh == '\t' )
+ currCh = [diffText characterAtIndex: ++currPos];
+
+ // -nnn,nn
+ if( currCh != '-' )
+ {
+ [self autorelease];
+ UKLog(@"NO - found");
+ return nil;
+ }
+
+ NSRange leftRange;
+ NSMutableString* numStr = [NSMutableString string];
+ while( currPos < endPos )
+ {
+ currCh = [diffText characterAtIndex: ++currPos];
+ if( !isnumber( currCh ) )
+ break;
+ [numStr appendFormat: @"%C", currCh];
+ }
+
+ leftRange.location = [numStr integerValue] -1;
+ UKLog(@"\tleftRange.location = %ld",leftRange.location);
+
+ if( currCh != ',' )
+ {
+ leftRange.length = 1;
+ currPos++;
+ }
+ else
+ {
+ [numStr deleteCharactersInRange: NSMakeRange( 0, [numStr length] )];
+ while( currPos < endPos )
+ {
+ currCh = [diffText characterAtIndex: ++currPos];
+ if( !isnumber( currCh ) )
+ break;
+ [numStr appendFormat: @"%C", currCh];
+ }
+
+ leftRange.length = [numStr integerValue];
+ }
+ UKLog(@"\tleftRange.length = %ld",leftRange.length);
+
+ while( currCh == ' ' || currCh == '\t' )
+ currCh = [diffText characterAtIndex: ++currPos];
+
+ // +nnn,nn
+ if( currCh != '+' )
+ {
+ [self autorelease];
+ UKLog(@"\tNO + found");
+ return nil;
+ }
+
+ NSRange rightRange;
+ numStr = [NSMutableString string];
+ while( currPos < endPos )
+ {
+ currCh = [diffText characterAtIndex: ++currPos];
+ if( !isnumber( currCh ) )
+ break;
+ [numStr appendFormat: @"%C", currCh];
+ }
+
+ rightRange.location = [numStr integerValue] -1;
+ UKLog(@"\trightRange.location = %ld",rightRange.location);
+
+ if( currCh != ',' )
+ {
+ rightRange.length = 1;
+ currPos++;
+ }
+ else
+ {
+ [numStr deleteCharactersInRange: NSMakeRange( 0, [numStr length] )];
+ while( currPos < endPos )
+ {
+ currCh = [diffText characterAtIndex: ++currPos];
+ if( !isnumber( currCh ) )
+ break;
+ [numStr appendFormat: @"%C", currCh];
+ }
+
+ rightRange.length = [numStr integerValue];
+ }
+ UKLog(@"\trightRange.length = %ld",rightRange.length);
+
+ while( currCh == ' ' || currCh == '\t' )
+ currCh = [diffText characterAtIndex: ++currPos];
+
+ if( currCh != '@' )
+ {
+ [self autorelease];
+ UKLog(@"NO ending @@");
+ return nil;
+ }
+ currCh = [diffText characterAtIndex: ++currPos];
+ if( currCh != '@' )
+ {
+ [self autorelease];
+ UKLog(@"NO ending @@");
+ return nil;
+ }
+
+ currCh = [diffText characterAtIndex: ++currPos];
+ while( (currCh == '\n' || currCh == '\r') && currPos < (endPos-1) )
+ currCh = [diffText characterAtIndex: ++currPos];
+
+ if( leftRange.location == 355 )
+ NSLog(@"355");
+
+ while( (currCh == ' ' || currCh == '-' || currCh == '+') && currPos < (endPos-1) )
+ {
+ leftRange.length = rightRange.length = 0;
+
+ // Now skip those useless context lines:
+ while( currCh == ' ' )
+ {
+ leftRange.location ++;
+ rightRange.location ++;
+ while( (currCh != '\n' && currCh != '\r') && currPos < (endPos-1) )
+ currCh = [diffText characterAtIndex: ++currPos];
+ while( (currCh == '\n' || currCh == '\r') && currPos < (endPos-1) )
+ currCh = [diffText characterAtIndex: ++currPos];
+ UKLog(@"Skipping context line");
+ }
+
+ // Obtain original lines:
+ NSMutableString* leftString = [NSMutableString string];
+ while( currCh == '-' )
+ {
+ currCh = [diffText characterAtIndex: ++currPos];
+ while( (currCh != '\n' && currCh != '\r') && currPos < (endPos-1) )
+ {
+ [leftString appendFormat: @"%C",currCh];
+ currCh = [diffText characterAtIndex: ++currPos];
+ }
+ while( (currCh == '\n' || currCh == '\r') && currPos < (endPos-1) )
+ {
+ leftRange.length++;
+ [leftString appendFormat: @"%C",currCh];
+ currCh = [diffText characterAtIndex: ++currPos];
+ }
+ UKLog(@"Found - line");
+ }
+
+ // Obtain new lines:
+ NSMutableString* rightString = [NSMutableString string];
+ while( currCh == '+' )
+ {
+ currCh = [diffText characterAtIndex: ++currPos];
+ while( (currCh != '\n' && currCh != '\r') && currPos < (endPos-1) )
+ {
+ [rightString appendFormat: @"%C",currCh];
+ currCh = [diffText characterAtIndex: ++currPos];
+ }
+ while( (currCh == '\n' || currCh == '\r') && currPos < (endPos-1) )
+ {
+ rightRange.length++;
+ [rightString appendFormat: @"%C",currCh];
+ currCh = [diffText characterAtIndex: ++currPos];
+ }
+ UKLog(@"Found + line");
+ }
+
+ UKDiffEntry* currDiff = [[[UKDiffEntry alloc] init] autorelease];
+
+ UKDiffOperation op = UKDiffOperationChange;
+ if( [leftString length] == 0 )
+ op = UKDiffOperationAdd;
+ else if( [rightString length] == 0 )
+ op = UKDiffOperationDelete;
+ [currDiff setOperation: op];
+ [currDiff setOriginalRange: leftRange];
+ [currDiff setDestinationRange: rightRange];
+ [currDiff setNewText: rightString];
+ [currDiff setOldText: leftString];
+ [differences addObject: currDiff];
+
+ UKLog( @"%@", currDiff );
+
+ leftRange.location += leftRange.length;
+ rightRange.location += rightRange.length;
+
+ // Skip remaining context lines:
+ while( currCh == ' ' || currCh == '\\' )
+ {
+ leftRange.location ++;
+ rightRange.location ++;
+ while( (currCh != '\n' && currCh != '\r') && currPos < (endPos-1) )
+ currCh = [diffText characterAtIndex: ++currPos];
+ while( (currCh == '\n' || currCh == '\r') && currPos < (endPos-1) )
+ currCh = [diffText characterAtIndex: ++currPos];
+ UKLog(@"Skipped trailing context line");
+ }
+ }
+ }
+ }
+
+ UKLog(@"parsed");
+
+ return self;
+}
+
+
+-(void) dealloc
+{
+ DESTROY(differences);
+
+ [super dealloc];
+}
+
+
+-(NSArray*) linesArrayForText: (NSString*)str
+{
+ NSMutableArray* lines = [NSMutableArray array];
+ NSMutableString* currStr = [NSMutableString string];
+ NSUInteger x = 0, len = [str length];
+ for( x = 0; x < len; x++ )
+ {
+ unichar currCh = [str characterAtIndex: x];
+ [currStr appendFormat: @"%C",currCh];
+ if( currCh == '\n' || currCh == '\r' )
+ {
+ [lines addObject: currStr];
+ currStr = [NSMutableString string];
+ }
+ }
+
+ if( [currStr length] > 0 )
+ [lines addObject: currStr];
+
+ return lines;
+}
+
+
+-(NSString*) stringByApplyingChangesToOriginalText: (NSString*)origText
+{
+ NSArray* originalLines = [self linesArrayForText: origText];
+ NSUInteger currOriginalLine = 0, maxOriginalLines = [originalLines count];
+ NSMutableString* outStr = [NSMutableString string];
+
+ for( UKDiffEntry* currDiff in differences )
+ {
+ UKDiffOperation operation = [currDiff operation];
+ NSRange originalRange = [currDiff originalRange];
+ NSString* newString = [currDiff newText];
+
+ if( operation == UKDiffOperationUnchanged ) // Ignore those, we do that ourselves.
+ continue;
+
+ while( originalRange.location > currOriginalLine )
+ {
+ NSString* theLine = [originalLines objectAtIndex: currOriginalLine++];
+ [outStr appendString: theLine];
+ }
+
+ if( operation == UKDiffOperationChange )
+ {
+ [outStr appendString: newString];
+ currOriginalLine += originalRange.length;
+ }
+ else if( operation == UKDiffOperationDelete )
+ {
+ currOriginalLine += originalRange.length;
+ }
+ else if( operation == UKDiffOperationAdd )
+ {
+ while( (originalRange.location +originalRange.length) > currOriginalLine )
+ {
+ NSString* theLine = [originalLines objectAtIndex: currOriginalLine++];
+ [outStr appendString: theLine];
+ }
+ [outStr appendString: newString];
+ }
+ }
+
+ while( maxOriginalLines > currOriginalLine )
+ [outStr appendString: [originalLines objectAtIndex: currOriginalLine++]];
+
+ return outStr;
+}
+
+
+-(void) applyOriginalText: (NSString*)origText
+{
+ NSArray* originalLines = [self linesArrayForText: origText];
+ NSUInteger currOriginalLine = 0, maxOriginalLines = [originalLines count];
+ NSUInteger prevOriginalLine = 0;
+ NSMutableArray* newDifferences = [NSMutableArray array];
+
+ if( [originalLines count] == 0 )
+ return;
+
+ for( UKDiffEntry* currDiff in differences )
+ {
+ NSRange originalRange = [currDiff originalRange];
+ UKDiffOperation operation = [currDiff operation];
+
+ if( operation == UKDiffOperationUnchanged ) // Drop any leftover unchanged entries from a previous run.
+ continue;
+
+ // Generate an entry for any unchanged text between this difference and the previous one:
+ NSUInteger lastLineNeeded = originalRange.location;
+ if( operation == UKDiffOperationAdd )
+ lastLineNeeded += originalRange.length;
+
+ prevOriginalLine = currOriginalLine;
+ NSMutableString* currStr = [NSMutableString string];
+ while( lastLineNeeded > currOriginalLine )
+ {
+ NSString* theLine = [originalLines objectAtIndex: currOriginalLine++];
+ [currStr appendString: theLine];
+ }
+
+ if( [currStr length] > 0 )
+ {
+ UKDiffEntry* newDiff = [[[UKDiffEntry alloc] init] autorelease];
+
+ [newDiff setOperation: UKDiffOperationUnchanged];
+ [newDiff setOriginalRange: NSMakeRange(prevOriginalLine,currOriginalLine -prevOriginalLine)];
+ [newDiff setOldText: currStr];
+ [newDiff setNewText: currStr];
+ [newDifferences addObject: newDiff];
+ }
+
+ // Now create an entry for the current change:
+ if( operation == UKDiffOperationAdd )
+ [newDifferences addObject: currDiff]; // Add object is already done, doesn't need an 'oldText'.
+ else // Change or delete:
+ {
+ // Capture old text and add it to this diff item:
+ NSMutableString* currStr = [NSMutableString string];
+ while( (originalRange.location +originalRange.length) > currOriginalLine )
+ {
+ NSString* theLine = [originalLines objectAtIndex: currOriginalLine++];
+ [currStr appendString: theLine];
+ }
+
+ [currDiff setOldText: currStr];
+ [newDifferences addObject: currDiff];
+ }
+ }
+
+ // Append any remaining unchanged text at end of diffs:
+ NSMutableString* finalStr = [NSMutableString string];
+ prevOriginalLine = currOriginalLine;
+ while( maxOriginalLines > currOriginalLine )
+ [finalStr appendString: [originalLines objectAtIndex: currOriginalLine++]];
+ if( [finalStr length] > 0 )
+ {
+ UKDiffEntry* newDiff = [[[UKDiffEntry alloc] init] autorelease];
+
+ [newDiff setOperation: UKDiffOperationUnchanged];
+ [newDiff setOriginalRange: NSMakeRange(prevOriginalLine,currOriginalLine -prevOriginalLine)];
+ [newDiff setOldText: finalStr];
+ [newDiff setNewText: finalStr];
+ [newDifferences addObject: newDiff];
+ }
+
+ // Replace old differences array with new one containing both texts:
+ self.differences = newDifferences;
+}
+
+
+-(NSString*) originalString // Must have called applyOriginalText: once before using this.
+{
+ NSMutableString* outStr = [NSMutableString string];
+
+ for( UKDiffEntry* currDiff in differences )
+ {
+ NSString* currStr = [currDiff oldText];
+ if( currStr )
+ [outStr appendString: currStr];
+ }
+
+ return outStr;
+}
+
+
+-(NSString*) destinationString // Must have called applyOriginalText: once before using this.
+{
+ NSMutableString* outStr = [NSMutableString string];
+
+ for( UKDiffEntry* currDiff in differences )
+ {
+ NSString* currStr = [currDiff newText];
+ if( currStr )
+ [outStr appendString: currStr];
+ }
+
+ return outStr;
+}
+
+
+-(NSString*) mergedString // Must have called applyOriginalText: once before using this.
+{
+ NSMutableString* outStr = [NSMutableString string];
+
+ for( UKDiffEntry* currDiff in differences )
+ {
+ NSString* currStr = [currDiff apply] ? [currDiff newText] : [currDiff oldText];
+ if( currStr )
+ [outStr appendString: currStr];
+ }
+
+ return outStr;
+}
+
+
+
+-(NSUInteger) count
+{
+ return [differences count];
+}
+
+
+-(UKDiffEntry*) entryAtIndex: (NSUInteger)idx
+{
+ return [differences objectAtIndex: idx];
+}
+
+
+@end
+
+
+
+@implementation UKDiffEntry
+
+@synthesize operation;
+@synthesize originalRange;
+@synthesize destinationRange;
+@synthesize newText;
+@synthesize oldText;
+@synthesize apply;
+
+-(id) init
+{
+ if(( self = [super init] ))
+ {
+ apply = YES;
+ }
+ return self;
+}
+
+-(NSString*) description
+{
+ return [NSString stringWithFormat: @"%@ { operation = %c originalRange = { %ld, %ld }, destinationRange = { %ld, %ld }, apply = %s }",
+ NSStringFromClass([self class]), operation, originalRange.location, originalRange.length,
+ destinationRange.location, destinationRange.length, (apply? "YES":"NO")];
+}
+
+@end
58 UKDiffView.h
@@ -0,0 +1,58 @@
+//
+// UKDiffView.h
+// AngelDiff
+//
+// Created by Uli Kusterer on 02.08.08.
+// Copyright 2008 The Void Software. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "UKDiffParser.h"
+
+
+@interface UKCachedDiffEntry : NSObject
+{
+ NSRect leftDrawBox;
+ NSRect rightDrawBox;
+ NSTextStorage* leftTextStorage;
+ NSTextStorage* rightTextStorage;
+ UKDiffOperation currOp;
+ NSDictionary* attributes;
+ BOOL apply;
+}
+
+@property (assign) NSRect leftDrawBox;
+@property (assign) NSRect rightDrawBox;
+@property (retain) NSTextStorage* leftTextStorage;
+@property (retain) NSTextStorage* rightTextStorage;
+@property (assign) UKDiffOperation currOp;
+@property (retain) NSDictionary* attributes;
+@property (assign) BOOL apply;
+
++(id) cachedEntryWithLeftString: (NSString*)leftStr rightString: (NSString*)rightStr attributes: (NSDictionary*)attrs applyFlag: (BOOL)apl;
+-(id) initWithLeftString: (NSString*)leftStr rightString: (NSString*)rightStr attributes: (NSDictionary*)attrs applyFlag: (BOOL)apl;
+
+-(void) drawSelected: (BOOL)selState;
+
+-(NSBezierPath*) pathWithConnectedBox: (NSRect)leftBox toBox: (NSRect)rightBox;
+
+@end
+
+
+
+@interface UKDiffView : NSView
+{
+ UKDiffParser* diffParser;
+ NSMutableArray* cachedDrawings;
+ NSInteger selectedRow;
+}
+
+@property (retain) UKDiffParser* diffParser;
+@property (retain) NSMutableArray* cachedDrawings;
+@property (assign) NSInteger selectedRow;
+
+-(NSSize) bestSize;
+
+-(void) updateDrawingCacheCompletely: (BOOL)recreate;
+
+@end
532 UKDiffView.m
@@ -0,0 +1,532 @@
+//
+// UKDiffView.m
+// AngelDiff
+//
+// Created by Uli Kusterer on 02.08.08.
+// Copyright 2008 The Void Software. All rights reserved.
+//
+
+// -----------------------------------------------------------------------------
+// Headers:
+// -----------------------------------------------------------------------------
+
+#import "UKDiffView.h"
+#import "UKDiffParser.h"
+#import "UKHelperMacros.h"
+
+
+// -----------------------------------------------------------------------------
+// Constants:
+// -----------------------------------------------------------------------------
+
+#define ROUNDING_SIZE 2.0
+#define SIDE_MARGIN 3.0
+#define HORZ_MARGIN 8.0
+#define VERT_MARGIN 4.0
+#define DIVIDER_WIDTH 16.0
+
+
+// -----------------------------------------------------------------------------
+// UKCachedDiffEntry:
+// We use this class to cache the text engine elements and positions and
+// sizes of our display elements for faster scrolling and hit testing.
+// -----------------------------------------------------------------------------
+
+@implementation UKCachedDiffEntry
+
+@synthesize leftDrawBox;
+@synthesize rightDrawBox;
+@synthesize leftTextStorage;
+@synthesize rightTextStorage;
+@synthesize currOp;
+@synthesize attributes;
+@synthesize apply;
+
++(id) cachedEntryWithLeftString: (NSString*)leftStr rightString: (NSString*)rightStr attributes: (NSDictionary*)attrs applyFlag: (BOOL)apl
+{
+ return [[[[self class] alloc] initWithLeftString: leftStr rightString: rightStr attributes: attrs applyFlag: apl] autorelease];
+}
+
+
+-(id) initWithLeftString: (NSString*)leftStr rightString: (NSString*)rightStr attributes: (NSDictionary*)attrs applyFlag: (BOOL)apl
+{
+ if(( self = [super init] ))
+ {
+ NSAttributedString* leftAttrStr = [[[NSAttributedString alloc] initWithString: leftStr ? leftStr : @"" attributes: attrs] autorelease];
+ leftTextStorage = [[NSTextStorage alloc] initWithAttributedString: leftAttrStr];
+ NSLayoutManager* layoutManager = [[NSLayoutManager alloc] init];
+ NSTextContainer* textContainer = [[NSTextContainer alloc] init];
+ [layoutManager addTextContainer:textContainer];
+ [textContainer release];
+ [leftTextStorage addLayoutManager:layoutManager];
+ [layoutManager release];
+
+ NSAttributedString* rightAttrStr = [[[NSAttributedString alloc] initWithString: rightStr ? rightStr : @"" attributes: attrs] autorelease];
+ rightTextStorage = [[NSTextStorage alloc] initWithAttributedString: rightAttrStr];
+ layoutManager = [[NSLayoutManager alloc] init];
+ textContainer = [[NSTextContainer alloc] init];
+ [layoutManager addTextContainer:textContainer];
+ [textContainer release];
+ [rightTextStorage addLayoutManager:layoutManager];
+ [layoutManager release];
+
+ apply = apl;
+
+ self.attributes = attrs;
+ }
+
+ return self;
+}
+
+-(void) dealloc
+{
+ self.leftTextStorage =