// TLRead.m
// by David Phillip Oster
// September 2007
//
// Cocoa centric lisp
//
//  Copyright 2007 David Phillip Oster

//     Licensed under the Apache License, Version 2.0 (the "License");
//     you may not use this file except in compliance with the License.
//     You may obtain a copy of the License at

//         http://www.apache.org/licenses/LICENSE-2.0

//     Unless required by applicable law or agreed to in writing, software
//     distributed under the License is distributed on an "AS IS" BASIS,
//     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//     See the License for the specific language governing permissions and
//     limitations under the License.
#import "TLLisp.h"

// Floating point or int.
static TLID *TLReadNumberString(NSString *inp, int *ioStartIndexp) {
  int iCount = [inp length];
  int i = *ioStartIndexp;
  int e;
  BOOL hasSeenDecimal= NO;
  for (e = i;e < iCount; ++e) {
    unichar c = [inp characterAtIndex:e];
    if ( ! ('0' <= c && c <= '9')) {
      if ( ! hasSeenDecimal && '.' == c) {
        hasSeenDecimal = YES;
      } else {
        break;
      }
    }
  }
  NSString *s = [inp substringWithRange:NSMakeRange(i, e-i)];
  *ioStartIndexp = e;
  if (hasSeenDecimal) {
    float f = atof([s UTF8String]);
    return [NSNumber numberWithFloat:f];
  } else {
    int n = atoi([s UTF8String]);
    return [NSNumber numberWithInt:n];
  }
}

// to get backslash to work, we build up strings one char at a time.
static TLID *TLReadQuotedString(NSString *inp, int *ioStartIndexp) {
  NSMutableString *val = [NSMutableString string];
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  int iCount = [inp length];
  int i = *ioStartIndexp;
  int e;
  ++i;
  for (e = i;e < iCount; ++e) {
    unichar c = [inp characterAtIndex:e];
    if (c == '\\' && e < iCount-1) {
      ++e;
      c = [inp characterAtIndex:e];
    } else if (c == '"') {
      break;
    }
    [val appendString:[NSString stringWithCharacters:&c length:1]];
  }
  [pool release];
  *ioStartIndexp = e+1;
  return val;
}

// allow atoms like ->, <->, etc.
TLID *TLReadAngleAtomString(NSString *inp, int *ioStartIndexp) {
  int iCount = [inp length];
  int i = *ioStartIndexp;
  int e;
  for (e = i;e < iCount; ++e) {
    unichar c = [inp characterAtIndex:e];
    if ( ! (c == '-' || c == '=' || c == '<' || c == '>')) {
      break;
    }
  }
  NSString *s = [inp substringWithRange:NSMakeRange(i, e-i)];
  *ioStartIndexp = e;
  return TLIntern(s);
}

TLID *TLReadAtomString(NSString *inp, int *ioStartIndexp) {
  int iCount = [inp length];
  int i = *ioStartIndexp;
  int e;
  for (e = i;e < iCount; ++e) {
    unichar c = [inp characterAtIndex:e];
    if ((c == '(' || c == ')' || c == '"' || c == '\'' || c == ',') || ! ('!' <= c && c <= 'z')) {
      break;
    }
  }
  NSString *s = [inp substringWithRange:NSMakeRange(i, e-i)];
  *ioStartIndexp = e;
  return TLIntern(s);
}

// dispatch to token build base on first character
TLID *TLReadTokenString(NSString *inp, int *ioStartIndexp) {
  TLID *retVal = nil;
  int i = *ioStartIndexp;
  int iCount = [inp length];
  unichar c, c1;
  for (;i < iCount; ++i) {
    c = [inp characterAtIndex:i];
    if (c <= ' ') {
      /* skip */
    } else if ('0' <= c && c <= '9') {
      retVal = TLReadNumberString(inp, &i);
      break;
    } else if ('.' == c && i < iCount-1 && 
        '0' <=  (c1 = [inp characterAtIndex:i+1]) && c1 <= '9') {
      retVal = TLReadNumberString(inp, &i);
      break;
    } else if (c == '"') {
      retVal = TLReadQuotedString(inp, &i);
      break;
    } else if (c == '-' || c == '=' || c == '<' || c == '>') {
      retVal = TLReadAngleAtomString(inp, &i);
      break;
    } else if (('!' <= c && c <= '/') || ('[' <= c && c <= '`') || ('{' <= c && c <= '~') || c == 0x3BB) {
      // allow lambda
      retVal = TLIntern([inp substringWithRange:NSMakeRange(i, 1)]);
      ++i;
      break;
    } else if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) {
      retVal = TLReadAtomString(inp, &i);
      break;
    }
  }
  *ioStartIndexp = i;
  return retVal;
}

// call self recursively to build up lists == NSArray
TLID *TLReadStringInner(NSString *inp, int *ioStartIndexp) {
  TLID *val = TLReadTokenString(inp, ioStartIndexp);
  if (gTLOpenParen == val) {
    NSMutableArray *retVal = [NSMutableArray array];
    for (;;) {
      val = TLReadStringInner(inp, ioStartIndexp);
      if (nil == val || gTLCloseParen == val) {
        break;
      }
      [retVal addObject:val];
    }
    val = retVal;
  } else if (gTLSingleQuote == val) {
    NSMutableArray *retVal = [NSMutableArray array];
    val = TLReadStringInner(inp, ioStartIndexp);
    if (nil != val) {
      [retVal addObject:gTLSingleQuote];
      [retVal addObject:val];
      val = retVal;
    }
  }
  return val;
}


TLID *TLReadString(NSString *inp) {
  int startIndex = 0;
  return TLReadStringInner(inp, &startIndex);
}


TLID *TLReadFILE(FILE *inp) {
  char s[512];
  NSString *str = [NSString stringWithUTF8String:fgets(s, sizeof s, inp)];
  return TLReadString(str);
}

TLID *TLRead0(void) {
  return TLReadFILE(stdin);
}

