unit mystringlistunit; {$mode objfpc}{$H+} interface uses Classes, SysUtils, RegExpr, Process, protokollunit, matheunit; type tMyStringList = class (tStringList) private line: longint; prot: tProtokollant; srNoGo,srBuff: tMyStringList; // Liste der in SubRoutinen verbotenen Zeilenanfänge, sowie der aufgeschobenen Zeilen procedure gibAus(s: string; ausgaben: byte); public constructor create; overload; constructor create(protokollant: tProtokollant; name: string); overload; destructor destroy; override; procedure loadFromFile(const s: ansiString); override; procedure loadFromGz(const s: ansiString); procedure loadFromResource(const s: ansiString); procedure saveToGz(const s: ansiString); function readln(out s: string): boolean; inline; function metaReadln(out s: string; subRoutine: boolean): boolean; inline; procedure grep(expr: string); function grepFirst(expr: string): string; function hatZeile(zeile: string): boolean; // invers zu "grep -c" function eof: boolean; procedure rewind; function stillNeed(bez: string): boolean; function needInLine(bez: string; lin: longint): boolean; procedure insert(index: longint; const s: ansistring); override; function unfoldMacros: boolean; overload; inline; function unfoldMacros(kvs: tKnownValues; cbgv: tCallBackGetValue): boolean; overload; procedure subst(regex,ersatz: string); procedure dump(pro: tProtokollant; prefix: string); procedure nichtInSubRoutine(s: string); end; procedure _del(var s: string; p,c: longint); inline; // identisch zu delete(s,p,c) -- lediglich um delete innerhalb von tMyStringlist verfügbar zu haben procedure _mov(const source;var dest;count:SizeInt); // identisch zu move(source,dest,count) -- lediglich um move innerhalb von tMyStringlist verfügbar zu haben function numerischerVergleich(list: tStringList; index1,index2: integer): integer; implementation uses Math, lowlevelunit, systemunit, fileUnit; // tMyStringlist *************************************************************** constructor tMyStringlist.create; begin create(nil,''); end; constructor tMyStringlist.create(protokollant: tProtokollant; name: string); begin inherited create; if assigned(protokollant) then prot:=tProtokollant.create(protokollant,name) else prot:=nil; srNoGo:=nil; srBuff:=nil; line:=0; end; destructor tMyStringlist.destroy; begin srNoGo.free; srBuff.free; inherited destroy; end; procedure tMyStringlist.gibAus(s: string; ausgaben: byte); begin if assigned(prot) then prot.schreibe(s,odd(ausgaben shr 1)) else lowlevelunit.gibAus(s,ausgaben); end; procedure tMyStringlist.loadFromFile(const s: ansiString); var i: longint; begin inherited loadFromFile(s); for i:=0 to count-1 do self[i]:=trim(self[i]); line:=0; gibAus(inttostr(count)+' Zeilen eingelesen',1); end; procedure tMyStringlist.loadFromGz(const s: ansiString); var len: longint; pt: pointer; buf: ansistring; begin fileunit.loadFromGz(s,pt,len); setlength(buf,len); _mov(pt^,buf[1],len); text:=buf; setlength(buf,0); for len:=0 to count-1 do self[len]:=trim(self[len]); line:=0; gibAus(inttostr(count)+' Zeilen eingelesen',1); end; procedure tMyStringlist.loadFromResource(const s: ansiString); var st: tResourceStream; buf: ansistring; begin st:=tResourceStream.create(hInstance,s,RT_RCDATA); setlength(buf,st.size); st.readBuffer(buf[1],length(buf)); st.free; text:=buf; end; procedure tMyStringlist.saveToGz(const s: ansiString); begin fileunit.saveToGz(s,@(text[1]),length(text)); end; function tMyStringlist.readln(out s: string): boolean; begin result:=not eof; if not result then begin s:=''; exit; end; s:=self[line]; inc(line); end; function tMyStringlist.metaReadln(out s: string; subRoutine: boolean): boolean; begin if assigned(srNoGo) then begin if subRoutine then begin repeat result:=readln(s); if not srNoGo.hatZeile(s) then exit; srBuff.add(s); until false; end else if srBuff.count>0 then begin result:=true; s:=srBuff[0]; srBuff.delete(0); exit; end; end; result:=readln(s); end; procedure tMyStringlist.grep(expr: string); var re: tRegExpr; i: longint; begin re:=tRegExpr.create; re.expression:=expr; for i:=count-1 downto 0 do if not re.exec(self[i]) then delete(i); re.free; end; function tMyStringlist.grepFirst(expr: string): string; var re: tRegExpr; i: longint; begin re:=tRegExpr.create; re.expression:=expr; for i:=count-1 downto 0 do if re.exec(self[i]) then begin result:=self[i]; re.free; exit; end; re.free; result:=''; end; function tMyStringlist.hatZeile(zeile: string): boolean; var re: tRegExpr; i: longint; begin re:=tRegExpr.create; result:=true; for i:=0 to count-1 do begin re.expression:=self[i]; if re.exec(zeile) then begin re.free; exit; end; end; re.free; result:=false; end; function tMyStringlist.eof: boolean; begin result:=line>=count; end; procedure tMyStringlist.rewind; begin line:=0; end; function tMyStringlist.stillNeed(bez: string): boolean; var i: longint; begin result:=true; for i:=max(0,line-1) to count-1 do if needInLine(bez,i) then exit; result:=false; end; function tMyStringlist.needInLine(bez: string; lin: longint): boolean; var s,t: string; begin result:=true; s:=' '+self[lin]+' '; while pos(bez,s)>0 do begin t:=copy(s,1,pos(bez,s)+length(bez)); _del(s,1,pos(bez,s)+length(bez)-1); _del(t,1,length(t)-length(bez)-2); if (t[1] in [' ',#9,':','[']) and (t[length(t)] in [' ',#9,':',']']) and (copy(t,2,length(bez))=bez) then exit; end; result:=false; end; procedure tMyStringlist.insert(index: longint; const s: ansistring); begin inherited insert(index,s); if index<=line then inc(line); end; function tMyStringlist.unfoldMacros: boolean; begin result:=unfoldMacros(nil,nil); end; function tMyStringlist.unfoldMacros(kvs: tKnownValues; cbgv: tCallBackGetValue): boolean; var i,j,k,l,Ebene: longint; s,t,u,v: string; SchleifenInhalt: tMyStringlist; istWahr,gefunden,wasGefunden: boolean; const binops: array[0..12] of string = ('<=','>=','<>','≤','≥','=','≠','<','>','in','∈','notIn','∉'); begin result:=false; i:=0; while iself[i] then begin wasGefunden:=true; self[i]:=s; end; end; if wasGefunden then continue; i:=0; while i=0 do begin // Substitutionen auswerten s:=self[i]; if startetMit('!setze',s) then begin wasGefunden:=true; t:=erstesArgument(s,':'); if pos('$',t)<>1 then begin gibAus('ungültiger Variablenname '''+t+''' für Ersetzung!',3); exit; end; delete(i); for j:=i to count-1 do begin u:=self[j]; k:=length(u); while (pos(t,u)>0) and (k>0) do begin // ... nach Ersetzung ... u:=copy(u,1,pos(t,u)-1)+s+copy(u,pos(t,u)+length(t),length(u)); dec(k); end; self[j]:=u; end; end else dec(i); end; if wasGefunden then continue; for i:=0 to count-1 do begin // Shellvariablen auswerten s:=shellSubst(self[i]); if s<>self[i] then begin wasGefunden:=true; self[i]:=s; end; end; i:=0; while i'$') then begin gibAus('Schleifenvariable muss mit ''$'' beginnen!',3); exit; end; SchleifenInhalt:=tMyStringlist.create(nil,''); // Schleifenkörper merken Ebene:=0; while (i0) or (self[i]<>'!Schleifenende')) do begin SchleifenInhalt.Add(self[i]); if self[i]='!Schleifenende' then dec(Ebene); if pos('!Schleife:',self[i])=1 then inc(Ebene); delete(i); end; if i>=count then begin gibAus('Ich kann kein zugehöriges Schleifenende zu Schleife über Variable '''+t+''' finden!',3); gibAus(' ich habe hier noch:',3); for i:=0 to SchleifenInhalt.count-1 do gibAus(SchleifenInhalt[i],3); exit; end; delete(i); u:=s; s:=''; while length(u)>0 do begin v:=erstesArgument(u); if pos('..',v)<>0 then v:=intervallAusrollen(v); s:=s+' '+v; end; s:=trim(s); while length(s)>0 do begin // Schleifenzähler laufen lassen u:=erstesArgument(s); for j:=0 to SchleifenInhalt.Count-1 do begin // Schleifenkörper ... v:=SchleifenInhalt[j]; k:=length(v); while (pos(t,v)>0) and (k>0) do begin // ... nach Ersetzung ... v:=copy(v,1,pos(t,v)-1)+u+copy(v,pos(t,v)+length(t),length(v)); dec(k); end; insert(i,v); // ... kopieren inc(i); end; end; SchleifenInhalt.free; i:=l; continue; end; self[i]:=s; inc(i); end; if wasGefunden then continue; i:=0; while i0 then begin gefunden:=true; u:=trim(copy(t,1,pos(binops[j],t)-1)); _del(t,1,pos(binops[j],t)+length(binops[j])-1); t:=trim(t); case binops[j] of '≤','<=': istWahr:=strtofloat(u)<=strtofloat(t); '≥','>=': istWahr:=strtofloat(u)>=strtofloat(t); '=': try istWahr:=strtofloat(u)=strtofloat(t); except istWahr:=u=t; end; '≠','<>': try istWahr:=strtofloat(u)<>strtofloat(t); except istWahr:=u<>t; end; '<': istWahr:=strtofloat(u)': istWahr:=strtofloat(u)>strtofloat(t); 'in','∈': begin istWahr:=false; t:=t+' '; while (t<>'') and not istWahr do istWahr:=erstesArgument(t)=u; end; 'notIn','∉': begin istWahr:=true; t:=t+' '; while (t<>'') and istWahr do istWahr:=erstesArgument(t)<>u; end; else begin gibAus('Operator '''+binops[j]+''' ist nicht implementiert!',3); exit; end; end{of case}; if not istWahr then delete(i); break; end; if not gefunden then begin gibAus('Ich kann keinen gültigen Operator in Bedingung '''+t+''' finden!',3); exit; end; if not istWahr then continue; self[i]:=s; end; inc(i); end; until not wasGefunden; result:=true; end; procedure tMyStringlist.subst(regex,ersatz: string); var i,cnt: longint; re: tRegExpr; begin re:=tRegExpr.create; re.expression:='^(.*)('+regex+')(.*)$'; cnt:=3; for i:=1 to length(regex) do if (regex[i]='(') and ((i=1) or (regex[i-1]<>'\')) then inc(cnt); for i:=0 to count-1 do begin re.exec(self[i]); self[i]:=re.substitute('$1'+ersatz+'$'+inttostr(cnt)); end; re.free; end; procedure tMyStringlist.dump(pro: tProtokollant; prefix: string); var i: longint; begin for i:=0 to count-1 do pro.schreibe(prefix+self[i]); end; procedure tMyStringList.nichtInSubRoutine(s: string); begin if not assigned(srNoGo) then begin srNoGo:=tMyStringList.create; srBuff:=tMyStringList.create; end; srNoGo.add(s); end; // allgemeine Funktionen ******************************************************* procedure _del(var s: string; p,c: longint); begin delete(s,p,c); end; procedure _mov(const source;var dest;count:SizeInt); begin move(source,dest,count); end; function numerischerVergleich(list: tStringList; index1,index2: integer): integer; var neg: boolean; i: longint; begin if (leftStr(list[index1],1)='-') xor (leftStr(list[index2],1)='-') then begin result:=2*byte(leftStr(list[index2],1)='-')-1; exit; end; neg:=leftStr(list[index1],1)='-'; result:=length(list[index1])-length(list[index2]); if result=0 then begin for i:=1 to length(list[index1]) do begin if list[index1][i]list[index2][i] then begin result:=1; break; end; end; end; result:=result*(1-2*byte(neg)); end; end.