-- FILE: ParseImpl.mesa
-- Last edited by John Ousterhout, January 24, 1984 1:06 pm

DIRECTORY
    Globals,
    IO,
    Parse,
    Rope;

ParseImpl: CEDAR PROGRAM
IMPORTS
    IO,
    Rope
EXPORTS Parse =

BEGIN
OPEN Parse, Globals;


Args: PUBLIC PROC[line: Rope.ROPE] RETURNS [Arg] =

    BEGIN
    result: Arg ← NIL;
    prev: Arg ← NIL;
    cur: Arg;
    stream: IO.STREAM ← NIL;
    token1, token2: Rope.ROPE;
    length: INT;
    
    -- Turn the rope into a stream, then strip off the arguments
    -- one at a time.
    
    stream ← IO.RIS[line];
    WHILE TRUE DO        
        ENABLE  IO.EndOfStream => EXIT;
        token2 ← NIL;
        [token: token1] ← IO.GetTokenRope[stream, WhiteSpace];
        IF token1 = NIL THEN LOOP;
        
        -- Things are a bit tricky if the first letter of the token is
        -- "-".  In this case, the argument could be either a switch
        -- (like in Unix, remember?), or it could signify the beginning
        -- of a comment.  If it's not a comment, make sure that
        -- the rope doesn't contain more than one more character, and
        -- if it does then chop off the stuff after the first character into
        -- a separate token.
        
        length ← Rope.Length[token1];
        IF length >= 2 THEN
            IF (Rope.Fetch[token1, 0] = '-) AND (Rope.Fetch[token1, 1] = '-)
                THEN EXIT;
        IF (Rope.Fetch[token1, 0] = '-) AND (length > 2) THEN
            BEGIN
            token2 ← Rope.Substr[base: token1, start: 2, len: length-2];
            token1 ← Rope.Substr[base: token1, start: 0, len: 2];
            END;
        
        -- Generate either one or two Arg objects for the token(s).
        
        cur ← NEW[ArgRec];
        cur.rope ← token1;
        IF result = NIL THEN result ← cur;
        IF prev # NIL THEN prev.next ← cur;
        prev ← cur;
        IF token2 # NIL THEN
            BEGIN
            cur ← NEW[ArgRec];
            cur.rope ← token2;
            prev.next ← cur;
            prev ← cur;
            END;
        ENDLOOP;
    RETURN [result];
    END;


Real: PUBLIC PROC[arg: Arg] RETURNS [parseOK: BOOLEAN, val: REAL] =

    BEGIN
    ENABLE ANY => 
        BEGIN
        parseOK ← FALSE;
        val ← 0.0;
        CONTINUE;
        END;
    
    IF arg = NIL THEN RETURN [FALSE, 0.0];
    val ← IO.GetReal[IO.RIS[arg.rope]];
    RETURN [TRUE, val];
    END;

Int: PUBLIC PROC[arg: Arg] RETURNS [parseOK: BOOLEAN, val: INT] =

    BEGIN
    ENABLE ANY =>
        BEGIN
        parseOK ← FALSE;
        val ← 0;
        CONTINUE;
        END;
            
    IF arg = NIL THEN RETURN [FALSE, 0];
    val ← IO.GetInt[IO.RIS[arg.rope]];
    RETURN [TRUE, val];
    END;


Lookup: PUBLIC PROC[rope: Rope.ROPE,
    table: DESCRIPTOR FOR ARRAY OF Rope.ROPE]
    RETURNS [index: INT] = 
    
    TRUSTED BEGIN
    botIndex: INTEGER ← 0;
    topIndex: INTEGER ← LENGTH[table] - 1;
    char: CHAR;
    
    -- The search is carried out using two indices into the table.
    -- One index marches up from the start of the table, one marches
    -- down from the back of the table.  The two indices delimit
    -- the entries of the table whose first characters match the
    -- part of rope that we've examined (this only works if the
    -- table is ordered monotonically).  To do the match, we examine
    -- characters of rope one at a time, squeezing the indices together
    -- until we've got a match.
    
    FOR i: INTEGER IN [0..1000) DO
        IF i >= Rope.Length[rope] THEN
            BEGIN
            IF botIndex = topIndex THEN RETURN [botIndex];
            RETURN [-1];
            END;
        char ← Rope.Fetch[rope, i];
        
        -- Move botIndex up until we find a table entry that matches
        -- the current character of rope.
        
        WHILE TRUE DO
            IF Rope.Length[table[botIndex]] > i THEN
                IF Rope.Fetch[table[botIndex], i] = char THEN EXIT;
            IF botIndex = topIndex THEN RETURN [-2];
            botIndex ← botIndex + 1;
            ENDLOOP;
            
        -- Move topIndex down until we find a table entry that matches
        -- the current character of rope.
        
        WHILE TRUE DO
            IF Rope.Length[table[topIndex]] > i THEN
                IF Rope.Fetch[table[topIndex], i] = char THEN EXIT;
            IF botIndex = topIndex THEN RETURN [-2];
            topIndex ← topIndex - 1;
            ENDLOOP;
        ENDLOOP;
    RETURN [-2];
    END;


WhiteSpace: PUBLIC IO.BreakProc =
    BEGIN
    IF (char = IO.CR) OR (char = IO.TAB) OR (char = IO.LF)
        OR (char = IO.SP) THEN RETURN [sepr]
        ELSE RETURN[other];
    END;

END.