#############################################################################
##
#W  lex.g               OpenMath Package         Andrew Solomon
##
#H  @(#)$Id: lex.g,v 1.12 2006/01/02 01:01:26 gap Exp $
##
#Y  Copyright (C)  1997,  Lehrstuhl D fuer Mathematik,  RWTH Aachen,  Germany
#Y  (C) 1998 School Math and Comp. Sci., University of St.  Andrews, Scotland
##
##  This file contains the GAP level interface to the output of the 
##  lexical analyser gpipe.
##  Comprises the "instream" functions - deals with stream at byte level
##  and the "get" functions which get tokens and values from streams.
##


######################################################################
##
#C  BTWOBJ
##
##  Indicates that the pointer into the byte stream has just read an
##  end object and not read the next token (in case it isn't there).
##

BindGlobal("BTWOBJ", "IN_BETWEEN_OBJECTS");


######################################################################
##
##  OMinstream functions
##
##  functions for dealing with low level operations on an "instream"
##  which is a GAP level stream together with info about the next byte.
##

######################################################################
##
##  OMinstreamCreate(stream) 
##
##  Return an instream from GAP stream - assumes there is at least
##  one byte on stream.
##  
BindGlobal("OMinstreamCreate",
function(input)
	return rec(next := ReadByte(input), stream := input);
end);

######################################################################
##
##  OMinstreamNextByte(stream) 
##
##  The next byte on the stream which hasn't been "got".
##  
##  
BindGlobal("OMinstreamNextByte",
function(stream)
	# if we're at BTWOBJ then we've been explicitly asked
	# for the next thing, so we try.
	if stream.next = BTWOBJ then
		stream.next :=  ReadByte(stream.stream);
	fi;
  return stream.next;
end);

######################################################################
##
##  OMinstreamPopByte(stream) 
##
##  Pop a byte off the stream
##
BindGlobal("OMinstreamPopByte",
function(stream)
	local next;

	next := OMinstreamNextByte(stream);
	# don't try to read another byte off the stream after endobj unless 
	# explicitly asked to (do this in next byte)
	if next = OMtokenEndObject then
		stream.next := BTWOBJ;
	else
		stream.next :=  ReadByte(stream.stream);
	fi;
	return next;
end);

######################################################################
##
##  OMinstreamPopString(stream) 
##
##  Pop a string off the stream (all bytes until next OMtokenDelimiter).
##
BindGlobal("OMinstreamPopString",
function(stream)
	local s,i;

	s := "";
	i := 1;
	while OMinstreamNextByte(stream) <> OMtokenDelimiter  and
		OMinstreamNextByte(stream) <> fail do
		s[i] := OMinstreamPopByte(stream);
		i := i +1;
	od;
	OMinstreamPopByte(stream); # get rid of the delimiter
	return List(s,x->CHAR_INT(x));
end);
		
	
######################################################################
##
##  token level functions
##

######################################################################
##
##  OMnextToken
##
##  Just returns the next byte on the stream (without popping it).
##
BindGlobal("OMnextToken",
function(stream)
	return OMinstreamNextByte(stream);
end);

######################################################################
##
##  OMnextToken
##
##  return value of the next token and gets next byte off the stream.
##
BindGlobal("OMgetToken",
function(stream, token)
	local
		otok;

	otok := OMinstreamNextByte(stream);
  if otok <> token then
    Error("Invalid OpenMath object - expected ", token, " not ",otok);
  fi;

  # otherwise, just advance past the token
	OMinstreamPopByte(stream);
	return otok;
end);

######################################################################
##
##  OMgetInteger
## 
##  Get the next OMtokenInteger off the stream (complains if it isn't
##  an OMtokenInteger) and then the value. Returns the value as a 
##  GAP integer.
##

#convert a string of the form "xaf93bc" or "-xaf93bc"  into an integer
BindGlobal("Xint" , 
function(str)
	local digmap, i, neg, start, tot;

	digmap := function(c)
		if (c >= '0') and (c <= '9') then
			return  INT_CHAR(c) -  INT_CHAR('0');
		fi;

		if  (c >= 'a') and  (c <= 'f') then
			return  INT_CHAR(c) -  INT_CHAR('a') + 10;
		fi;

		Error("invalid input to digmap");
	end;


	if (str[1] <> 'x') and (str[2] <> 'x') then
		Error("Invalid format for hexadecimal in Xint");
	fi;

	if (str[1] = 'x') then
		start := 2;
		neg := false;
	else
		start := 3;
		neg := true;
	fi;

	tot := 0;
	for i in [start .. Length(str)] do
		tot := 16*tot + digmap(str[i]);
	od;

	if neg then
		return -tot;
	else
		return tot;
	fi;


end);

BindGlobal("OMgetInteger",
function(stream)
	local intstr;

  OMgetToken(stream,OMtokenInteger); # skip past token 
	# and get the value
	intstr := OMinstreamPopString(stream);
	if (intstr[1] = 'x') or 
           (Length(intstr) >= 2 and intstr[2]='x')
        then #need to convert hex
		return Xint(intstr);
	else
		return Int(intstr);
	fi;
end);

######################################################################
##
##  OMgetSymbol
## 
##  a symbol is returned as a pair [cd, name]
##
BindGlobal("OMgetSymbol",
function(stream)
  OMgetToken(stream,OMtokenSymbol); # skip past token

	# now return the [cd, name] pair
  return [OMinstreamPopString(stream), OMinstreamPopString(stream)];
end);

######################################################################
##
##  OMgetVar
##
##  a variable is returned as the string of its name.
##
BindGlobal("OMgetVar",
function(stream)
  OMgetToken(stream,OMtokenVar); # skip past token
  return OMinstreamPopString(stream);
end);


######################################################################
##
##  OMgetString
##
##  a string  is popped from stream and it is returned as a GAP string.
##
BindGlobal("OMgetString",
function(stream)
  local string;
  OMgetToken(stream,OMtokenString); # skip past token
  string := OMinstreamPopString(stream);

  # convert XML escaped chars
  string := ReplacedString( string, "&lt;", "<" );
  string := ReplacedString( string, "&gt;", ">" );
  string := ReplacedString( string, "&quot;", "\"" );
  string := ReplacedString( string, "&apos;", "\'" );
  string := ReplacedString( string, "&amp;", "&" );
  
  return string;
end);



######################################################################
#E
