{ This unit is part of the Web Service Toolkit Copyright (c) 2006 by Inoussa OUEDRAOGO This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. } unit ws_parser; {$mode objfpc}{$H+} interface uses Classes, SysUtils, parserdefs; Type EParserException = class(Exception) End; { TPascalParser } TPascalParser = class private FStream : TStream; FTokenizer : TParser; FErrorMessage : String; FSymbolTable : TSymbolTable; FCurrentSymbol : TAbstractSymbolDefinition; private Property Tokenizer : TParser Read FTokenizer; procedure SetErrorMessage(Const AValue : String); procedure CheckCurrentString(Const AString : String); private procedure BeginParsing(); procedure EndParsing(); function GetSourceLine: Integer; function ReadUntil(Const AText : String; Const ARaiseException : Boolean):Boolean; procedure SkipComment(); function NextToken(): Char; Private procedure ParseHeader(); procedure ParseInterfaceSection(); procedure ParseUses(); procedure ParseTypeDeclaration(); procedure ParseInterfaceType(Const AName : String); procedure ParseEnumType(Const AName : String); procedure ParseClassType(Const AName : String); public constructor Create(AStream : TStream); destructor Destroy();override; procedure Error(Const AMsg : String);overload; procedure Error(Const AMsg : String; Const AArgs : Array of const);overload; function Parse():Boolean; property SourceLine: Integer read GetSourceLine; property ErrorMessage : String read FErrorMessage; property SymbolTable : TSymbolTable Read FSymbolTable; End; implementation Type TPascalToken = ( ptNone, ptUnit, ptInterface, ptUses, ptType, ptImplementation, ptEnd, ptProcedure, ptFunction, ptSemicolon, ptComma, ptPeriod, ptEqual, ptColon, ptLeftParenthesis, ptRigthParenthesis, ptLeftSquareBracket, ptRigthSquareBracket, ptConst, ptVar, ptOut, ptClass ); Const PascalTokenStrMAP : Array[TPascalToken] Of String = ( '', 'UNIT', 'INTERFACE', 'USES', 'TYPE', 'IMPLEMENTATION', 'END', 'PROCEDURE', 'FUNCTION', ';', ',', '.', '=', ':', '(', ')', '[', ']', 'CONST', 'VAR', 'OUT', 'CLASS' ); function GetPascalTokenStr(Const AToken:TPascalToken):String; begin Result := PascalTokenStrMAP[AToken]; end; function GetPascalTokenFromStr(Const ATokenStr:String):TPascalToken; begin Result := Succ(ptNone); For Result := Result To High(TPascalToken) Do Begin If AnsiSameText(ATokenStr,PascalTokenStrMAP[Result]) Then Exit; End; Result := ptNone; end; { TPascalParser } procedure TPascalParser.SetErrorMessage(const AValue: String); begin FErrorMessage := AValue; end; procedure TPascalParser.Error(const AMsg: String); begin Raise EParserException.Create(AMsg); end; procedure TPascalParser.Error(const AMsg: String; const AArgs: array of const); begin Raise EParserException.CreateFmt(AMsg,AArgs); end; procedure TPascalParser.CheckCurrentString(const AString: String); begin If Not AnsiSameText(Tokenizer.TokenString,AString) Then Error('"%s" expected.',[AString]); end; procedure TPascalParser.BeginParsing(); begin end; procedure TPascalParser.EndParsing(); begin end; function TPascalParser.GetSourceLine: Integer; begin Result := Tokenizer.SourceLine; end; function TPascalParser.ReadUntil(Const AText : String; Const ARaiseException : Boolean):Boolean; begin While ( Tokenizer.Token <> toEOF ) And ( Not AnsiSameText(Tokenizer.TokenString,AText) ) Do Tokenizer.NextToken(); Result := AnsiSameText(Tokenizer.TokenString,AText); If ARaiseException And ( Not Result ) Then Error('"%s" not found.',[AText]); end; procedure TPascalParser.SkipComment(); const L_C = '{'; R_C = '}'; begin While ( FTokenizer.TokenString = L_C ) do begin ReadUntil(R_C,False); FTokenizer.NextToken(); end; end; function TPascalParser.NextToken(): Char; begin SkipComment(); Result := FTokenizer.NextToken(); SkipComment(); end; procedure TPascalParser.ParseHeader(); begin // Unit UnitName; SkipComment(); Tokenizer.CheckTokenSymbol(GetPascalTokenStr(ptUnit)); NextToken(); Tokenizer.CheckToken(toSymbol); FSymbolTable.Name := Tokenizer.TokenString; NextToken(); CheckCurrentString(GetPascalTokenStr(ptSemicolon)); NextToken(); end; procedure TPascalParser.ParseInterfaceSection(); begin ReadUntil(GetPascalTokenStr(ptInterface),True); Tokenizer.CheckTokenSymbol(GetPascalTokenStr(ptInterface)); NextToken(); Tokenizer.CheckToken(toSymbol); If Tokenizer.TokenSymbolIs(GetPascalTokenStr(ptUses)) Then ParseUses(); Tokenizer.CheckToken(toSymbol); If Tokenizer.TokenSymbolIs(GetPascalTokenStr(ptType)) Then Begin NextToken(); Repeat ParseTypeDeclaration(); Until ( Tokenizer.Token = toEOF ) Or Tokenizer.TokenSymbolIs(GetPascalTokenStr(ptImplementation)) Or Tokenizer.TokenSymbolIs(GetPascalTokenStr(ptProcedure)) Or Tokenizer.TokenSymbolIs(GetPascalTokenStr(ptFunction)); End; end; procedure TPascalParser.ParseUses(); begin Tokenizer.CheckTokenSymbol(GetPascalTokenStr(ptUses)); NextToken(); Repeat Tokenizer.CheckToken(toSymbol); //UnitName NextToken(); If AnsiSameText(Tokenizer.TokenString,GetPascalTokenStr(ptSemicolon)) Then Break; CheckCurrentString(GetPascalTokenStr(ptComma)); NextToken(); Until ( Tokenizer.Token = toEOF ) Or AnsiSameText(Tokenizer.TokenString,GetPascalTokenStr(ptSemicolon)); CheckCurrentString(GetPascalTokenStr(ptSemicolon)); NextToken(); end; procedure TPascalParser.ParseTypeDeclaration(); var sname : string; begin Tokenizer.CheckToken(toSymbol); sname := Tokenizer.TokenString; NextToken(); CheckCurrentString(GetPascalTokenStr(ptEqual)); NextToken(); if AnsiSameText(Tokenizer.TokenString,GetPascalTokenStr(ptLeftParenthesis)) then self.ParseEnumType(sname) else if AnsiSameText(Tokenizer.TokenString,GetPascalTokenStr(ptClass)) then self.ParseClassType(sname) else begin Tokenizer.CheckToken(toSymbol); if Tokenizer.TokenSymbolIs(GetPascalTokenStr(ptInterface)) then ParseInterfaceType(sname) else begin ReadUntil(GetPascalTokenStr(ptEnd),True); NextToken();// End NextToken();// ; end; end; end; procedure TPascalParser.ParseInterfaceType(const AName: String); Var sbl : TInterfaceDefinition; procedure ReadIntfHeader(); Var tmpStr : String; begin NextToken(); Repeat Tokenizer.CheckToken(toSymbol); tmpStr := Tokenizer.TokenString; NextToken(); If AnsiSameText(Tokenizer.TokenString,GetPascalTokenStr(ptRigthParenthesis)) Then Begin NextToken(); Break; End; NextToken(); CheckCurrentString(GetPascalTokenStr(ptComma)); NextToken(); Until ( Tokenizer.Token = toEOF ) ; end; procedure ReadProcedure(Const AProc : Boolean); Var tmpStr,prmName : String; tmpTkn : TPascalToken; pr : TMethodDefinition; prmM : TParameterModifier; dataTypeSbl : TTypeDefinition; foundSymbol : TAbstractSymbolDefinition; begin NextToken(); Tokenizer.CheckToken(toSymbol); tmpStr := Tokenizer.TokenString; If AProc Then pr := sbl.AddMethod(tmpStr,mtProcedure) Else pr := sbl.AddMethod(tmpStr,mtFunction); NextToken(); If AnsiSameText(Tokenizer.TokenString,GetPascalTokenStr(ptLeftParenthesis)) Then Begin NextToken(); If AnsiSameText(Tokenizer.TokenString,GetPascalTokenStr(ptRigthParenthesis)) Then NextToken() Else Begin Repeat Tokenizer.CheckToken(toSymbol); tmpStr := Tokenizer.TokenString; tmpTkn := GetPascalTokenFromStr(tmpStr); prmM := pmNone; If ( tmpTkn = ptConst ) Then Begin prmM := pmConst; End Else If ( tmpTkn = ptVar ) Then Begin prmM := pmVar; End Else If ( tmpTkn = ptOut ) Then Begin prmM := pmOut; End; If ( prmM > pmNone ) Then Begin NextToken(); tmpStr := Tokenizer.TokenString; End; prmName := tmpStr; NextToken(); CheckCurrentString(GetPascalTokenStr(ptColon)); NextToken(); Tokenizer.CheckToken(toSymbol); tmpStr := Tokenizer.TokenString; foundSymbol := FSymbolTable.Find(tmpStr); If Assigned(foundSymbol) And ( Not foundSymbol.InheritsFrom(TTypeDefinition) ) Then Error('Type symbol expected where "%s" was found.',[foundSymbol.Name]); If Assigned(foundSymbol) Then dataTypeSbl := foundSymbol As TTypeDefinition Else Begin dataTypeSbl := TTypeDefinition.Create(tmpStr); FSymbolTable.Add(dataTypeSbl); End; pr.AddParameter(prmName,prmM,dataTypeSbl); NextToken(); tmpStr := Tokenizer.TokenString; If AnsiSameText(Tokenizer.TokenString,GetPascalTokenStr(ptRigthParenthesis)) Then Begin NextToken(); Break; End; NextToken(); Until ( Tokenizer.Token = toEOF ) ; End; End; If Not AProc Then Begin CheckCurrentString(GetPascalTokenStr(ptColon)); NextToken(); Tokenizer.CheckToken(toSymbol); tmpStr := Tokenizer.TokenString; dataTypeSbl := FSymbolTable.Find(tmpStr) As TTypeDefinition; If Not Assigned(dataTypeSbl) Then Begin dataTypeSbl := TTypeDefinition.Create(tmpStr); FSymbolTable.Add(dataTypeSbl); End; pr.AddParameter('result',pmOut,dataTypeSbl); NextToken(); End; CheckCurrentString(GetPascalTokenStr(ptSemicolon)); NextToken(); end; procedure ReadFunction(); begin ReadProcedure(False); end; procedure ReadGUID(); begin //['{804A3825-ADA5-4499-87BF-CF5491BFD674}'] CheckCurrentString(GetPascalTokenStr(ptLeftSquareBracket)); NextToken(); FTokenizer.CheckToken(toString); sbl.InterfaceGUID := FTokenizer.TokenString; NextToken(); CheckCurrentString(GetPascalTokenStr(ptRigthSquareBracket)); NextToken(); end; begin Tokenizer.CheckTokenSymbol(GetPascalTokenStr(ptInterface)); sbl := TInterfaceDefinition.Create(AName); FSymbolTable.Add(sbl); FCurrentSymbol := sbl; NextToken(); if AnsiSameText(Tokenizer.TokenString,GetPascalTokenStr(ptLeftParenthesis)) then ReadIntfHeader(); if AnsiSameText(Tokenizer.TokenString,GetPascalTokenStr(ptLeftSquareBracket)) then ReadGUID(); repeat if Tokenizer.TokenSymbolIs(GetPascalTokenStr(ptProcedure)) then ReadProcedure(True) else if Tokenizer.TokenSymbolIs(GetPascalTokenStr(ptFunction)) then ReadFunction() else if Tokenizer.TokenSymbolIs(GetPascalTokenStr(ptEnd)) then begin NextToken(); NextToken(); Break; end else begin Error('"%s", "%s", "%s" expected.',[GetPascalTokenStr(ptProcedure),GetPascalTokenStr(ptFunction),GetPascalTokenStr(ptEnd)]); end; until ( Tokenizer.Token = toEOF ) ; end; procedure TPascalParser.ParseEnumType(const AName: String); Var sbl : TEnumTypeDefinition; tmpStr : String; sblItem : TEnumItemDefinition; tmpInt : Integer; begin sbl := TEnumTypeDefinition.Create(AName); FSymbolTable.Add(sbl); FCurrentSymbol := sbl; CheckCurrentString(GetPascalTokenStr(ptLeftParenthesis)); NextToken(); Tokenizer.CheckToken(toSymbol); tmpInt := 0; Repeat tmpStr := Tokenizer.TokenString; If ( FSymbolTable.IndexOf(tmpStr) > -1 ) Then Error('Duplicated symbol : "%s"',[tmpStr]); sblItem := TEnumItemDefinition.Create(tmpStr,tmpInt); FSymbolTable.Add(sblItem); sbl.AddItem(sblItem); NextToken(); If AnsiSameText(Tokenizer.TokenString,GetPascalTokenStr(ptRigthParenthesis)) Then Break; If AnsiSameText(Tokenizer.TokenString,GetPascalTokenStr(ptComma)) Then NextToken(); Tokenizer.CheckToken(toSymbol); Inc(tmpInt); Until ( Tokenizer.Token = toEOF ) ; CheckCurrentString(GetPascalTokenStr(ptRigthParenthesis)); NextToken(); CheckCurrentString(GetPascalTokenStr(ptSemicolon)); NextToken(); end; procedure TPascalParser.ParseClassType(const AName: String); Var sbl : TClassTypeDefinition; tmpStr : String; begin sbl := TClassTypeDefinition.Create(AName); FSymbolTable.Add(sbl); FCurrentSymbol := sbl; CheckCurrentString(GetPascalTokenStr(ptClass)); NextToken(); ReadUntil(GetPascalTokenStr(ptEnd),True); CheckCurrentString(GetPascalTokenStr(ptEnd)); NextToken(); CheckCurrentString(GetPascalTokenStr(ptSemicolon)); NextToken(); end; constructor TPascalParser.Create(AStream : TStream); begin Assert(Assigned(AStream)); FStream := AStream; FTokenizer := TParser.Create(FStream); FSymbolTable := TSymbolTable.Create(''); FCurrentSymbol := Nil; end; destructor TPascalParser.Destroy(); begin FTokenizer.Free(); FreeAndNil(FSymbolTable); inherited Destroy(); end; function TPascalParser.Parse(): Boolean; begin BeginParsing(); Try Try SkipComment(); ParseHeader(); ParseInterfaceSection(); Except On E : Exception Do Begin Result := False; SetErrorMessage(E.Message ); End; End; Finally EndParsing(); End; end; end.