// Path: java/monkey/token/Token.java
// public class Token {
//   private Type type;
//   private String literal;
// 
//   public Token(Type type, String literal) {
//     this.type = type;
//     this.literal = literal;
//   }
// 
//   public Token() {
//     this(Type.ILLEGAL, "");
//   }
// 
//   public Type getType() {
//     return type;
//   }
// 
//   public String getLiteral() {
//     return literal;
//   }
// 
//   @Override
//   public String toString() {
//     return MoreObjects.toStringHelper(this)
//       .add("type", type)
//       .add("literal", literal)
//       .toString();
//   }
// 
//   public enum Type {
//     ILLEGAL,
//     EOF,
// 
//     // Identifiers + literals
//     IDENT,
//     INT,
// 
//     // Operators
//     ASSIGN,
//     PLUS,
//     MINUS,
//     BANG,
//     ASTERISK,
//     SLASH,
// 
//     // Relations
//     LT,
//     GT,
//     EQ,
//     NOT_EQ,
// 
//     // Delimiters
//     COMMA,
//     SEMICOLON,
// 
//     LPAREN,
//     RPAREN,
//     LBRACE,
//     RBRACE,
// 
//     // Keywords
//     FUNCTION,
//     LET,
//     TRUE,
//     FALSE,
//     IF,
//     ELSE,
//     RETURN;
// 
//     private static final Map<String, Type> keywords = ImmutableMap.<String, Type>builder()
//         .put("fn", FUNCTION)
//         .put("let", LET)
//         .put("true", TRUE)
//         .put("false", FALSE)
//         .put("if", IF)
//         .put("else", ELSE)
//         .put("return", RETURN)
//         .build();
// 
//     public static Type lookupIdent(String ident) {
//       if (keywords.containsKey(ident)) {
//         return keywords.get(ident);
//       }
//       return IDENT;
//     }
//   }
// }
// 
// Path: java/monkey/lexer/Lexer.java
// public class Lexer {
//   private String input;
//   private int position;
//   private int readPosition;
//   private char ch;
// 
//   public Lexer(String input) {
//     this.input = input;
//     this.readPosition = 0;
//     readChar();
//   }
// 
//   public Token nextToken() {
//     skipWhitespace();
// 
//     Token t;
//     switch (ch) {
//       case '=':
//         if (peekChar() == '=') {
//           readChar();
//           t = new Token(Token.Type.EQ, "==");
//         } else {
//           t = new Token(Token.Type.ASSIGN, "=");
//         }
//         break;
//       case '+':
//         t = new Token(Token.Type.PLUS, "+");
//         break;
//       case '-':
//         t = new Token(Token.Type.MINUS, "-");
//         break;
//       case '!':
//         if (peekChar() == '=') {
//           readChar();
//           t = new Token(Token.Type.NOT_EQ, "!=");
//         } else {
//           t = new Token(Token.Type.BANG, "!");
//         }
//         break;
//       case '*':
//         t = new Token(Token.Type.ASTERISK, "*");
//         break;
//       case '/':
//         t = new Token(Token.Type.SLASH, "/");
//         break;
//       case '<':
//         t = new Token(Token.Type.LT, "<");
//         break;
//       case '>':
//         t = new Token(Token.Type.GT, ">");
//         break;
//       case ';':
//         t = new Token(Token.Type.SEMICOLON, ";");
//         break;
//       case ',':
//         t = new Token(Token.Type.COMMA, ",");
//         break;
//       case '(':
//         t = new Token(Token.Type.LPAREN, "(");
//         break;
//       case ')':
//         t = new Token(Token.Type.RPAREN, ")");
//         break;
//       case '{':
//         t = new Token(Token.Type.LBRACE, "{");
//         break;
//       case '}':
//         t = new Token(Token.Type.RBRACE, "}");
//         break;
//       case 0:
//         t = new Token(Token.Type.EOF, "");
//         break;
//       default:
//         if (isLetter(ch)) {
//           String ident = readIdentifier();
//           Token.Type type = Token.Type.lookupIdent(ident);
//           return new Token(type, ident);
//         } else if (Character.isDigit(ch)) {
//           String number = readNumber();
//           return new Token(Token.Type.INT, number);
//         } else {
//           t = new Token(Token.Type.ILLEGAL, Character.toString(ch));
//         }
//         break;
//     }
//     readChar();
//     return t;
//   }
// 
//   private void readChar() {
//     if (readPosition >= input.length()) {
//       ch = 0;
//     } else {
//       ch = input.charAt(readPosition);
//     }
//     position = readPosition;
//     readPosition += 1;
//   }
// 
//   private char peekChar() {
//     if (readPosition >= input.length()) {
//       return 0;
//     } else {
//       return input.charAt(readPosition);
//     }
//   }
// 
//   private String readIdentifier() {
//     int start = position;
//     while (isLetter(ch)) {
//       readChar();
//     }
//     return input.substring(start, position);
//   }
// 
//   private boolean isLetter(char ch) {
//     return Character.isLetter(ch) || ch == '_';
//   }
// 
//   private String readNumber() {
//     int start = position;
//     while (Character.isDigit(ch)) {
//       readChar();
//     }
//     return input.substring(start, position);
//   }
// 
//   private void skipWhitespace() {
//     while (Character.isWhitespace(ch)) {
//       readChar();
//     }
//   }
// }


import static org.junit.Assert.assertEquals;
import org.junit.Test;
import monkey.token.Token;
import monkey.lexer.Lexer;
package monkey.lexer;



public class TestLexer {
  @Test
  public void testNextToken() {
    String input = String.join("\n",
        "let ten = 10;",
        "let add = fn(x, y) { x + y; };",
        "let result = add(ten, ten);",
// Path: java/monkey/token/Token.java
// public class Token {
//   private Type type;
//   private String literal;
// 
//   public Token(Type type, String literal) {
//     this.type = type;
//     this.literal = literal;
//   }
// 
//   public Token() {
//     this(Type.ILLEGAL, "");
//   }
// 
//   public Type getType() {
//     return type;
//   }
// 
//   public String getLiteral() {
//     return literal;
//   }
// 
//   @Override
//   public String toString() {
//     return MoreObjects.toStringHelper(this)
//       .add("type", type)
//       .add("literal", literal)
//       .toString();
//   }
// 
//   public enum Type {
//     ILLEGAL,
//     EOF,
// 
//     // Identifiers + literals
//     IDENT,
//     INT,
// 
//     // Operators
//     ASSIGN,
//     PLUS,
//     MINUS,
//     BANG,
//     ASTERISK,
//     SLASH,
// 
//     // Relations
//     LT,
//     GT,
//     EQ,
//     NOT_EQ,
// 
//     // Delimiters
//     COMMA,
//     SEMICOLON,
// 
//     LPAREN,
//     RPAREN,
//     LBRACE,
//     RBRACE,
// 
//     // Keywords
//     FUNCTION,
//     LET,
//     TRUE,
//     FALSE,
//     IF,
//     ELSE,
//     RETURN;
// 
//     private static final Map<String, Type> keywords = ImmutableMap.<String, Type>builder()
//         .put("fn", FUNCTION)
//         .put("let", LET)
//         .put("true", TRUE)
//         .put("false", FALSE)
//         .put("if", IF)
//         .put("else", ELSE)
//         .put("return", RETURN)
//         .build();
// 
//     public static Type lookupIdent(String ident) {
//       if (keywords.containsKey(ident)) {
//         return keywords.get(ident);
//       }
//       return IDENT;
//     }
//   }
// }
// 
// Path: java/monkey/lexer/Lexer.java
// public class Lexer {
//   private String input;
//   private int position;
//   private int readPosition;
//   private char ch;
// 
//   public Lexer(String input) {
//     this.input = input;
//     this.readPosition = 0;
//     readChar();
//   }
// 
//   public Token nextToken() {
//     skipWhitespace();
// 
//     Token t;
//     switch (ch) {
//       case '=':
//         if (peekChar() == '=') {
//           readChar();
//           t = new Token(Token.Type.EQ, "==");
//         } else {
//           t = new Token(Token.Type.ASSIGN, "=");
//         }
//         break;
//       case '+':
//         t = new Token(Token.Type.PLUS, "+");
//         break;
//       case '-':
//         t = new Token(Token.Type.MINUS, "-");
//         break;
//       case '!':
//         if (peekChar() == '=') {
//           readChar();
//           t = new Token(Token.Type.NOT_EQ, "!=");
//         } else {
//           t = new Token(Token.Type.BANG, "!");
//         }
//         break;
//       case '*':
//         t = new Token(Token.Type.ASTERISK, "*");
//         break;
//       case '/':
//         t = new Token(Token.Type.SLASH, "/");
//         break;
//       case '<':
//         t = new Token(Token.Type.LT, "<");
//         break;
//       case '>':
//         t = new Token(Token.Type.GT, ">");
//         break;
//       case ';':
//         t = new Token(Token.Type.SEMICOLON, ";");
//         break;
//       case ',':
//         t = new Token(Token.Type.COMMA, ",");
//         break;
//       case '(':
//         t = new Token(Token.Type.LPAREN, "(");
//         break;
//       case ')':
//         t = new Token(Token.Type.RPAREN, ")");
//         break;
//       case '{':
//         t = new Token(Token.Type.LBRACE, "{");
//         break;
//       case '}':
//         t = new Token(Token.Type.RBRACE, "}");
//         break;
//       case 0:
//         t = new Token(Token.Type.EOF, "");
//         break;
//       default:
//         if (isLetter(ch)) {
//           String ident = readIdentifier();
//           Token.Type type = Token.Type.lookupIdent(ident);
//           return new Token(type, ident);
//         } else if (Character.isDigit(ch)) {
//           String number = readNumber();
//           return new Token(Token.Type.INT, number);
//         } else {
//           t = new Token(Token.Type.ILLEGAL, Character.toString(ch));
//         }
//         break;
//     }
//     readChar();
//     return t;
//   }
// 
//   private void readChar() {
//     if (readPosition >= input.length()) {
//       ch = 0;
//     } else {
//       ch = input.charAt(readPosition);
//     }
//     position = readPosition;
//     readPosition += 1;
//   }
// 
//   private char peekChar() {
//     if (readPosition >= input.length()) {
//       return 0;
//     } else {
//       return input.charAt(readPosition);
//     }
//   }
// 
//   private String readIdentifier() {
//     int start = position;
//     while (isLetter(ch)) {
//       readChar();
//     }
//     return input.substring(start, position);
//   }
// 
//   private boolean isLetter(char ch) {
//     return Character.isLetter(ch) || ch == '_';
//   }
// 
//   private String readNumber() {
//     int start = position;
//     while (Character.isDigit(ch)) {
//       readChar();
//     }
//     return input.substring(start, position);
//   }
// 
//   private void skipWhitespace() {
//     while (Character.isWhitespace(ch)) {
//       readChar();
//     }
//   }
// }


// Path: javatests/monkey/lexer/TestLexer.java
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import monkey.token.Token;
import monkey.lexer.Lexer;

package monkey.lexer;



public class TestLexer {
  @Test
  public void testNextToken() {
    String input = String.join("\n",
        "let ten = 10;",
        "let add = fn(x, y) { x + y; };",
        "let result = add(ten, ten);",
        "!-/*5;",