//==============================================================================
// unit that parses Jass library files.
// As common.j may have functions and blizzard.j may have natives,
// there is actually no difference between them, it seems,
// so this single thing parses both.
//
// This unit keeps a collection of all function declarations encountered
// during parsing.
//==============================================================================
unit jasslib;

//==============================================================================
//  INTERFACE
//==============================================================================
interface
uses SysUtils, Classes;

type
   TJassFunc = class(TObject)
   public
       isnative : boolean;
       name : string;
       arguments: TStringList;
       returntype : string;
       isconstant : boolean;
   end;

procedure Reset();

// extract function and native definitions from a file
procedure parseFile( const filename: TFileName);

// get jass function or native definition from name
// can return nil
function GetJassFuncDefinition( const name:string): TJassFunc;

// Adds a native line, returns false if the native was already known...
function AddNativeLine( const s:string): boolean;


//==============================================================================
//  IMPLEMENTATION
//==============================================================================
implementation
uses StringHash, vxFilesys, Constants, TextOnly, TextCommon, Contnrs;

var
   FuncHash: TStringHash;
   FuncArray : TObjectList;
   currentFileName : string; // file currently being parsed

//==============================================================================
//  clear all collections
//==============================================================================
procedure Reset;
begin
    if FuncHash = nil
        then FuncHash := TStringHash.Create
        else FuncHash.Clear;

    if FuncArray = nil
        then FuncArray := TObjectList.Create
        else FuncArray.Clear;
end;

//==============================================================================
//  parses function definition (one line)
//==============================================================================
function ParseFuncLine( const constant:boolean ; const native:boolean; const s:string; var x:integer):boolean;
var
    y:integer;
    name, word:string;
    func: TJassFunc;
begin
    Result:=false;
    GetLineWord(s, name, x,x);
    if(FuncHash.Contains(name)) then begin
        // double definition, ignore
        Result := true;
        exit;
    end;
    if (not CompareLineWord('takes',s,x,x) ) then exit;

    func := TJassFunc.Create;
    func.isconstant := constant;
    func.isnative:=native;
    FuncArray.Add(func);
    FuncHash.add(name, FuncArray.Count-1);
    func.name:=name;
    func.arguments:= TStringList.Create();

    if not CompareLineWord('nothing',s,y,x) then begin
        //we got arguments!!111
        while(true) do begin
            GetLineToken(s,word,y,x);
            if(word='returns') then break;
            if(word='') then break;
            x:=y;
            func.arguments.Add(word); // save parameter type
            GetLineToken(s,word,x,x); // discard parameter name
        end;

    end else x:=y;
    if not CompareLineWord('returns',s,x,x) then Exit;
    GetLineWord(s,func.returntype,x,x);
    Result := true;
end;

//==============================================================================
function AddNativeLine( const s:string): boolean;
var x:integer;
    word:string;
    constant:boolean;
begin
    GetLineWord(s, word, x);
    constant := (word='constant');
    if constant then
        GetLineWord(s, word,x,x);
    Result:=ParseFuncLine(constant, true, s,x);
end;

//==============================================================================
//  parse only functions and natives for now (treat them the same)
//  constants, variables and types are ignored
//==============================================================================
procedure p_parseStuff(InputLines : TStringList);
var
    i,x:integer;
    word:string;
    isConstant : boolean;
begin
    i:=0;
    while(i<InputLines.Count) do begin
        GetLineWord(InputLines[i],word,x);
        if ( (word <> '') and (word[1]<>'/') ) then begin
            isConstant := word = 'constant';
            if(isConstant) then GetLineWord(InputLines[i],word,x,x);
            if(word='native') or (word='function') then begin
                if not ParseFuncLine(isConstant, (word='native'), InputLines[i], x) then begin
                    raise Exception.Create(
                          currentFileName
                        + ' : could not parse function definition.' + CRLF
                        + InputLines[i]);
                end;
            end;
        end;
        i:=i+1;
    end;
end;

//==============================================================================
function GetJassFuncDefinition(const name:string):TJassFunc;
var k:integer;
begin
    k:=FuncHash.ValueOf(name);
    if k = -1
        then Result := nil
        else Result := (FuncArray[k] as TJassFunc);
end;

//==============================================================================
//  MAIN
//==============================================================================
procedure parseFile(const filename:TfileName);
begin
    currentFileName := filename;  // save in case we need to throw exception
    p_ParseStuff(breakIntoLines(LoadFile(filename)));
end;

//==============================================================================
//  INITIALIZATION
//==============================================================================
begin
    Reset();
end.
