unit _Open_Format;

{
  ver 0.02

  Mefistofel
    (Костромин Кирилл)
  22.05.2010

  json-like file format loader
  (and save)!todo
}

interface

type
 //node
 TNode = record
   name:string;
   parent:cardinal;
   Value:string;
 end;

 //json hierarhi
 TOpen_format = class
 private
 public
  Node:Array of TNode;
  Max_Nodes:cardinal;
  Cur_Node:cardinal;
  constructor create;
  procedure Load(fn:string);
  function find_node_by_name(name:string):integer;Overload;
  function find_node_by_name(parent:integer;name:string):integer;Overload;
  function find_next_node(last:integer;name:string):integer;Overload;
  function find_next_node(last,parent:integer;name:string):integer;Overload;
  function Count_Child(parent:integer;name:string):integer;
  function Add(name,value:string;parent:cardinal):cardinal;
 end;

var
  Parser:TOpen_Format;

implementation
{$REGION 'Create'}
constructor TOpen_Format.create;
begin
 Max_Nodes:=500;
 Cur_Node:=0;
 SetLength(Node,Max_Nodes+1);
end;
{$ENDREGION}

{$REGION 'Add'}
function TOpen_Format.Add(name,value:string;parent:cardinal):cardinal;
begin
 Cur_Node:=Cur_Node+1;
 if (Cur_Node>Max_Nodes) then
  begin
    Max_Nodes:=Max_Nodes+5;
    SetLength(Node,Max_Nodes+1);
  end;
 Node[Cur_Node].name:=name;
 Node[Cur_Node].value:=value;
 Node[Cur_Node].parent:=parent;
 Add:=Cur_Node;
end;
{$ENDREGION}

{$REGION 'load'}
procedure TOpen_Format.Load(fn:string);
var
 a:char;
 name,value:string;
 f:file of char;
 string_state:boolean;{false - read all true - read except ""}
 fase:byte;{0 - name 1 - value 2 - none/next}
 parent:integer;
begin
 name:='';
 value:='';
 fase:=0;
 string_state:=false;
 parent:=0;
 Assign(f,fn);
 reset(f);
 repeat
  read(f,a);
  if string_state then
  begin
    if (a='"') then string_state:=false
    else
    case fase of
      0:name:=name+a;
      1:value:=value+a;
    end;
  end
  else
  case a of
   '(','{':
     begin
      parent:=add(name,value,parent);
      fase:=0;
      name:='';
     end;//level up
   ')','}':
     begin
      add(name,value,parent);
      parent:=Node[parent].parent;//level down
      if (name<>'') then
       add(name,value,parent);
      fase:=0;
      name:='';
     end;
   '=',':':
     begin
      fase:=1;
      value:='';
     end;
   ',',';':
     begin
      if (name<>'') then      
       add(name,value,parent);
      fase:=0;
      name:='';
     end;
   '"':string_state:=true;
   'a'..'z','A'..'Z','1'..'9','0','_':
   begin
     case fase of
      0:name:=name+a;
      1:value:=value+a;
     end;
   end;
  end;
 until eof(f);
 if (name<>'') and (value<>'') then  
  add(name,value,parent);
end;
{$ENDREGION}

{$REGION 'Find'}
function TOpen_format.find_node_by_name(name:string):integer;
Var i:integer;
begin
 find_node_by_name:=-1;
 for i:=0 to Max_Nodes do
   if (name=Node[i].name) then
    begin
     find_node_by_name:=i;
     break;
    end;
end;

function TOpen_format.find_node_by_name(parent:integer;name:string):integer;
Var i:integer;
begin
 find_node_by_name:=-1;
 for i:=0 to Max_Nodes do
   if (name=Node[i].name) and (parent=Node[i].parent) then
    begin
     find_node_by_name:=i;
     break;
    end;
end;

function TOpen_format.find_next_node(last:integer;name:string):integer;
Var i:integer;
begin
 find_next_node:=-1;
 for i:=last+1 to Max_Nodes do
   if (name=Node[i].name) then
    begin
     find_next_node:=i;
     break;
    end;
end;

function TOpen_format.find_next_node(last,parent:integer;name:string):integer;
Var i:integer;
begin
 find_next_node:=-1;
 for i:=last+1 to Max_Nodes do
   if (name=Node[i].name) and (parent=Node[i].parent) then
    begin
     find_next_node:=i;
     break;
    end;
end;
{$ENDREGION}

{$REGION 'Count_child'}
function TOpen_format.Count_Child(parent:integer;name:string):integer;
Var i,count:integer;
begin
 count:=0;
 for i:=0 to Max_Nodes do
  if (parent=node[i].parent) then
   if (name=Node[i].name) then
    begin
     count:=count+1;
    end;
 Count_Child:=count;
end;
{$ENDREGION}
end.
