/*## (c) SECURITY EXPLORATIONS    2018 poland                                #*/
/*##     http://www.security-explorations.com                                #*/

/* RESEARCH MATERIAL:	SRP-2018-02                                           */
/* SlimCORE assembler                                                         */

/* THIS SOFTWARE IS PROTECTED BY DOMESTIC AND INTERNATIONAL COPYRIGHT LAWS    */
/* UNAUTHORISED COPYING OF THIS SOFTWARE IN EITHER SOURCE OR BINARY FORM IS   */
/* EXPRESSLY FORBIDDEN. ANY USE, INCLUDING THE REPRODUCTION, MODIFICATION,    */
/* DISTRIBUTION, TRANSMISSION, RE-PUBLICATION, STORAGE OR DISPLAY OF ANY      */
/* PART OF THE SOFTWARE, FOR COMMERCIAL OR ANY OTHER PURPOSES REQUIRES A      */
/* VALID LICENSE FROM THE COPYRIGHT HOLDER.                                   */

/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS    */
/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,*/
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    */
/* SECURITY EXPLORATIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF  */
/* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     */
/* SOFTWARE.                                                                  */

import java.lang.*;
import java.io.*;
import java.util.*;

public class Token {
 public static final int TOK_COMMA    = 1;
 public static final int TOK_HASH     = 2;
 public static final int TOK_LPARAN   = 3;
 public static final int TOK_RPARAN   = 4;
 public static final int TOK_LSHIFT   = 5;
 public static final int TOK_RSHIFT   = 6;
 public static final int TOK_PLUS     = 7;
 public static final int TOK_EQUAL    = 8;
 public static final int TOK_LBRACKET = 9;
 public static final int TOK_RBRACKET = 10;
 public static final int TOK_AMPER    = 11;
 public static final int TOK_LABEL    = 12;
 public static final int TOK_SYM      = 13;
 public static final int TOK_NUMBER   = 14;

 int type;
 String val;

 public Token(int type,String val) {
  this.type=type;
  this.val=val;
 }

 public Token(int type) {
  this.type=type;
  this.val="";
 }

 public static boolean is_separator(int ch) {
  String separators="\t ,#()&<>+=[]:";

  return separators.indexOf(ch)!=-1;
 }

 public static boolean is_hexdigit(int ch) {
  String hexdigits="0123456789abcdef";

  return hexdigits.indexOf(ch)!=-1;
 }

 public static boolean is_digit(int ch) {
  String digits="0123456789";

  return digits.indexOf(ch)!=-1;
 }

 public static boolean is_letter(int ch) {
  String letters="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

  return letters.indexOf(ch)!=-1;
 }

 public static Token parse_number(PushbackInputStream pbis) throws Throwable {
  ByteArrayOutputStream baos=new ByteArrayOutputStream();

  int ch=0;
  int pos=0;
  int last_char=0;
  boolean is_hex=false;

  while((ch=pbis.read())!=-1) {
   if (is_digit(ch)) {
    if ((pos==1)&&(last_char=='0')) {
     is_hex=true;
    }
    
    last_char=ch;
    baos.write(ch);    
   } else

   if (is_hex) {
    if (is_hexdigit(ch)) {
     baos.write(ch);    
    } else

    if (is_separator(ch)) {
     pbis.unread(ch);
     break;
    } else return null;
   } else

   if (pos==1) {
    if (ch=='x') {
     is_hex=true;
     baos.write(ch);
    } else

    if (is_separator(ch)) {
     pbis.unread(ch);
     break;
    } else return null;
   } else

   if (is_separator(ch)) {
    pbis.unread(ch);
    break;   
   } else return null;

   pos++;
  }

  byte data[]=baos.toByteArray();

  String sdata=new String(data);

  if (is_hex&&(sdata.length()<3)&&sdata.endsWith("x")) return null;

  return new Token(Token.TOK_NUMBER,sdata);
 }

 public static Token parse_symbol_or_label(PushbackInputStream pbis) throws Throwable {
  ByteArrayOutputStream baos=new ByteArrayOutputStream();

  int ch=0;
  int pos=0;
  int token_type=0;

  while((ch=pbis.read())!=-1) {
   if (is_letter(ch)||(ch=='_')||((pos==0)&&(ch=='.'))) {
    baos.write(ch);
   } else

   if ((pos>0)&&is_digit(ch)) {
    baos.write(ch);
   } else

   if (ch==':') {
    baos.write(ch);
    token_type=Token.TOK_LABEL;
    break;    
   } else

   if (is_separator(ch)) {
    pbis.unread(ch);
    token_type=Token.TOK_SYM;
    break;   
   } else return null;

   pos++;
  }

  byte data[]=baos.toByteArray();

  if ((ch==-1)&&(token_type==0)) {
   token_type=Token.TOK_SYM;
  }

  return new Token(token_type,new String(data));
 }

 public static Token[] tokenize(String line) {
  Token tokens[]=null;

  try {
   line=line.trim();

   ByteArrayInputStream bais=new ByteArrayInputStream(line.getBytes());
   PushbackInputStream pbis=new PushbackInputStream(bais);

   Vector v=new Vector();

   int ch=-1;

   while((ch=pbis.read())!=-1) {
    Token tok=null;

    if ((ch=='/')||(ch==';')) {
     break;
    } else

    if (is_digit(ch)) {
     pbis.unread(ch);
     tok=parse_number(pbis);
    } else
 
    if (is_letter(ch)||(ch=='.')) {
     pbis.unread(ch);
     tok=parse_symbol_or_label(pbis);
    } else 
 
    if (Character.isWhitespace(ch)) {
     continue;
    } else {
     switch(ch) {
      case ',':
       tok=new Token(Token.TOK_COMMA,",");
       break;
      case '#':
       tok=new Token(Token.TOK_HASH,"#");
       break;
      case '(':
       tok=new Token(Token.TOK_LPARAN,"(");
       break;
      case ')':
       tok=new Token(Token.TOK_RPARAN,")");
       break;
      case '<':
       ch=pbis.read();
       if (ch!=-1) {
        if (ch=='<') {
         tok=new Token(Token.TOK_LSHIFT,"<<");
        } else {
         pbis.unread(ch);
        }
       }
       break;
      case '>':
       ch=pbis.read();
       if (ch!=-1) {
        if (ch=='>') {
         tok=new Token(Token.TOK_RSHIFT,">>");
        } else {
         pbis.unread(ch);
        }
       }
       break;
      case '+':
       tok=new Token(Token.TOK_PLUS,"+");
       break;
      case '=':
       tok=new Token(Token.TOK_EQUAL,"=");
       break;
      case '[':
       tok=new Token(Token.TOK_LBRACKET,"[");
       break;
      case ']':
       tok=new Token(Token.TOK_RBRACKET,"]");
       break;
      case '&':
       tok=new Token(Token.TOK_AMPER,"&");
       break;
     }
    }
 
    if (tok!=null) {
     v.add(tok);
    } else return null;
   }
 
   tokens=new Token[v.size()];

   for(int i=0;i<tokens.length;i++) {
    tokens[i]=(Token)v.elementAt(i);
   }

  } catch(Throwable t) {} 

  return tokens;
 }
}
