Aladdin - Scala Bugtracking
[#171] project: compiler priority: low category: missing feature
submitter assigned to status date submitted
Erik Matthias won't fix 2003-10-01 15:15:00.0
subject Errormessages are not sorted on linenumbers
code
import scala.concurrent.Process;
import scala.concurrent.Process._;
import scala.concurrent.NameServer;
import java.net.Socket;

class Request {};
case class Header(buf:List[char]) extends Request {};
case class HeaderDone(buf:List[char],after:Binary) extends Request {};
case class Post(buf:List[char],len:int,info:Tuple5[Any,Any,Any,Any,Any]) extends Request {};


class HttpDriver {
    type handler = Process => unit;

    def start(port:int, fun:handler, max:int) =
	spawn({server(port, fun, max);()});

    def server(port:int, fun:handler, max:int) =
     new TcpServer(port, 
		   (socket:Process) => input_handler(socket, fun),
		   max,
		   0);

    private def input_handler(socket:Process, fun:handler) = {
	val s = self;
	val server = spawn_link({fun(s);});
	relay(socket, server, Header(Nil));
    }

    private def relay(Socket:Process, 
                      Server:Process, 
                      state:Request):unit = {
      receive {
	case Tuple3('tcp, Socket, bin:Binary) => {
	    parse_request(state, socket, server, bin);
	}
//	case Tuple3('tcp_closed, Socket) => {
//	    server ! Pair(self, 'closed);
//	}
	case Pair(Server, 'close) => {
	    Socket ! Pair(self,close);
        }
	case Pair(Server, Pair(headers:String, data:Binary)) => {
	    val headers1 = headers + "Content-Length " +
		data.length + "\r\n\r\n";
    	    Socket.getOutputStream().write(headers1);
            Socket.getOutputStream().write(data);
	    relay(Socket, server, state);
        }
	case Tuple3('EXIT, Server, _) => {
	    Socket!Pair(self,close);
	}
    }
  }


  def parse_request(request:Request,socket:Process, 
                      server:Process,data:Binary) = {
       request.match {
         case Header(buff) => {
           scan_header(data, buff).match {
	     case Header(buff1) =>
	       relay(socket, server, Header(buff1));
	     case HeaderDone(header, after) =>
	       got_header(socket, server, header, after)
           }
         }
         case Post(buff, len, x) => {
           collect_chunk(len, data, buff).match {
             case Tuple3('yes, postdata, after) => {
		 val args2 = parse_uri_args(postdata);
		 val Tuple5(op,vsn,uri,args1,env) = x;
		 val req = Tuple5(op,vsn,uri,args1+args2,env);
		 server ! Pair(self, req);
	         parse_request(Header(Nil), socket, server, after);
	     }
             case Tuple3('no,buff1, len1) => {
               val state = Post(buff1, len1, x);
	       relay(socket, server, state);
             }
           } 
         }
      }  
  }

  def got_header(socket:Process, server:Process, 
	header:List[char], after:Binary) = {
      val result = parse_header(header);
      result.match {
	case Tuple6(op, contentLen, vsn, uri, args, env) => {
	    if (ContentLen == 0) {
	       server ! Pair(self, Tuple5(op,vsn,uri,args,env));
	       parse_request(Header(Nil), socket, server, after);
            } else {
	    val state = Post(Nil, contentLen, Tuple5(op,vsn,uri,args,env));
	    parse_request(state, socket, server, after);
            }
        }
      }
  }

  def collect_chunk(n:int,newl:List[A],buf:List[A]) = {
    if (n == 0) Tuple3('yes, reverse(buf), newl);
	else newl.match {
		case h::t => {collect_chunk(n-1,t,h::buff);}
		case Nil => Tuple3('no, buff, n)
        }
  }

  def scan_header(In:Binary,Out:List[char]):Request = {
    if (In.length == 0) Header(Out) else {
      val t = In.drop(1); 	 
      Out.match {
        case List('\n','\r','\n','\r',l@(_*)) => HeaderDone(l.reverse,t);
        case _ => scan_header(t,In(0)::Out);
      }
    }
  }
				
//----------------------------------------------------------------------


  class classifier with java.net.FileNameMap {
    def classify(filename:String) =
      getContentTypeFor(filename);
  }

  def header(ctype:String) = {
    ctype.match { 
    case "text" =>  
      "HTTP/1.0 200 Ok\r\n" + powered_by + content_type("text/html");
    case "html" => 
      "HTTP/1.0 200 Ok\r\n" + powered_by + content_type("text/html");
    case "jpg" => 
      "HTTP/1.0 200 Ok\r\n" + powered_by + content_type("image/jpeg");
    case "gif"  => 
      "HTTP/1.0 200 Ok\r\n" + powered_by + content_type("image/gif");
//    case "redirect,To}) ->
//    ["HTTP/1.0 302 Come and get it!\r\n",
//     powered_by(), "Location: " ++ To ++ "\r\n"].
    }
  }


  def powered_by = "X-Powered-By: Scala \r\n";

  def content_type(x:String) = "Content-Type: " + x + "\r\n";

    //----------------------------------------------------------------------

    def parse_header(str:List[char]) = {
	val hd::tail = split(str);
	val prequest = parse_request(hd);
	val pargs = tail.map(x => isolate_arg(x));
	make_return_value(prequest, pargs);
    }
    
    def split(s:List[char]):List[List[char]] = {
	s.match {
	    case List(x@(_*),'\r','\n',y@(_*)) => x::split(y);
            case Nil => Nil;
	}
    }

    def make_return_value(rval:Tuple3[List[char],List[char],Pair[List[char],List[char]]],env:List[Tuple2[List[char],List[char]]]) = {
	val Tuple3(op,vsn,Pair(uri,args)) = rval;
	Tuple6(op, content_length(env), vsn, uri, args, env);
    }

    def content_length(valuePairs:List[Pair[List[char],List[char]]]):int = {
	valuePairs.match {
	    case Pair(str,value)::t => 
		str.match {
		    case List('c','o','n','t','e','n','t','-','l','e','n','g','t','h') => value.toInteger;
		    
		    case _ => content_length(t);
		}
	    case Nil => 0;
	}
    }

    def urlencoded2str(str:List[char]):List[char] = {
	str.match {
	   case List('%',hi,lo,t@(_*)) => 
	     decode_hex(hi, lo)::urlencoded2str(t);
	   case List('+',t@(_*)) => ' '::urlencoded2str(t);
	   case h::t => h::urlencoded2str(t);
	   case Nil => Nil;
	}
    }


    def isolate_arg(str:List[char]) = isolate_arg(str, Nil);

    def isolate_arg(str:List[char], L:List[char]) = {
	str.match {
	    case List(':',' ',t@(_*)) => Pair(to_lower(l.reverse),t);
	    case h::t => isolate_arg(t, h::l)
	}
    }
	    

    // decode_hex %%

    def decode_hex(hex1:char, hex2:char) = 
	hex2dec(hex1)*16 + hex2dec(hex2);
    
    def hex2dec(x:char):char = {
	if (x >= '0' & x <= '9') (x.asInstanceOf[int] - '0'.asInstanceOf[int]).asInstanceOf[char];
	else 
 	  x.match{
	    case 'A' => 10;
	    case 'B' => 11;
	    case 'C' => 12;
	    case 'D' => 13;
	    case 'E' => 14;
	    case 'F' => 15;
	    case 'a' => 10;
	    case 'b' => 11;
	    case 'c' => 12;
	    case 'd' => 13;
	    case 'e' => 14;
	    case 'f' => 15;
	}
    }

    def parse_request(str:List[char]) = {
	val Pair('ok, args) = split(Str);
        args.match {   
	  case "POST"::uri::vsn => Post(parse_vsn(vsn) ,parse_uri(uri));
	  case "GET"::uri::vsn => Get(parse_vsn(vsn), parse_uri(uri));
        }
    }

   def parse_vsn(x:List[char]) = {
     x.match{
      case List('H','T','T','P','/',v,'.',m) => Pair(v-'0',m-'0');
      case _ => Pair(0,0);
   }
 }

// A typical URI looks
// like
// URI = "/a/b/c?password=aaa&invisible=A+hidden+value"
   def tokens(string:List[char],Chars:List[char]):List[List[char]] = 
	tokens(string,Chars,Nil);

   def tokens(string:List[char],
               Chars:List[char],
               acc:List[char]):List[List[char]] = {
      prefix(Chars,string).match {
        case Some(tail) => acc.reverse :: tokens(tail,Chars,Nil);
	case None => tokens(string.tail,Chars,string.head::acc);
      }
   }

   def prefix(p:List[char],l:List[char]):Option[List[char]] = {
	if (p == Nil) Some(l);
	else 
          if(p.head == l.head) 
            prefix(p.tail,l.tail)
          else
            None
   }	

	   
       

   def parse_uri(uri:List[char]) = {
     tokens(uri, '?'::Nil).match {
	case root::Nil => Pair(root, Nil);
	case root::args::Nil =>
	    Pair(root, parse_uri_args(args))
     }
   }

  def parse_uri_args(args:List[char]) = {
     val args1 = tokens(args, List('&',';'));
     args1.map((keyval:List[char]) =>
	         tokens(keyval, '='::Nil).match {
		   case List(key,value) =>
		     Pair(urlencoded2str(key), urlencoded2str(value));
		   case List(key) =>
		     Pair(urlencoded2str(key), Nil);
                 }
               )
  }
}
what happened
For large files with many errrors some Error messages seems to be reported directly by the phase that finds them\
.

Please don't look at the troublesome code to closely... it is a direct translation from Erlang in progress...
what expected All messages should be collected and then sorted on line number and reported in line number order...
[back to overview]
Changes of this bug report
Martin  edited on  2003-10-06 12:27:46.0
Matthias  edited on  2003-10-07 02:20:54.0
Do we really want this? In Java compilers it is not common to sort error messages by line numbers. Personally, I also tend to favor a logical order of error messages, since sometimes you get an avalanche of error messages caused by a single error -- in this case, it's easier to find the reason if error messages are sorted chronologically.

Shall we make sorting error messages optional? Maybe by introducing a new command-line option?

Matthias  edited on  2004-04-26 12:44:52.0