2013-04-11 11:04:16 +00:00
using System.IO ;
2012-07-10 10:20:45 +00:00
using System.Collections.Generic ;
using System.Globalization ;
using CodeImp.DoomBuilder.ZDoom ;
using CodeImp.DoomBuilder.GZBuilder.Data ;
2012-07-12 22:34:12 +00:00
//mxd. ACS parser used to create ScriptItems for use in script editor's navigator
namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
2012-07-10 10:20:45 +00:00
{
2013-09-11 09:47:53 +00:00
internal sealed class AcsParserSE : ZDTextParser
{
internal delegate void IncludeDelegate ( AcsParserSE parser , string includefile ) ;
internal IncludeDelegate OnInclude ;
2015-04-28 08:31:06 +00:00
private readonly List < string > parsedlumps ;
2014-07-16 09:47:23 +00:00
private readonly List < string > includes ;
2015-04-28 08:31:06 +00:00
private readonly List < ScriptItem > namedscripts ;
private readonly List < ScriptItem > numberedscripts ;
2014-07-16 09:47:23 +00:00
private readonly List < ScriptItem > functions ;
2013-09-11 09:47:53 +00:00
2015-04-28 08:31:06 +00:00
internal List < ScriptItem > NamedScripts { get { return namedscripts ; } }
internal List < ScriptItem > NumberedScripts { get { return numberedscripts ; } }
2014-07-16 09:47:23 +00:00
internal List < ScriptItem > Functions { get { return functions ; } }
2015-04-28 08:31:06 +00:00
internal IEnumerable < string > Includes { get { return includes ; } }
2013-09-11 09:47:53 +00:00
2014-12-03 23:15:26 +00:00
internal AcsParserSE ( )
{
2015-04-28 08:31:06 +00:00
namedscripts = new List < ScriptItem > ( ) ;
numberedscripts = new List < ScriptItem > ( ) ;
2014-07-16 09:47:23 +00:00
functions = new List < ScriptItem > ( ) ;
2015-04-28 08:31:06 +00:00
parsedlumps = new List < string > ( ) ;
2013-09-11 09:47:53 +00:00
includes = new List < string > ( ) ;
2015-07-27 09:02:28 +00:00
specialtokens + = "(,)" ;
2013-09-11 09:47:53 +00:00
}
2014-12-03 23:15:26 +00:00
public override bool Parse ( Stream stream , string sourcefilename )
{
2013-09-11 09:47:53 +00:00
return Parse ( stream , sourcefilename , false , false ) ;
}
2015-04-28 08:31:06 +00:00
public bool Parse ( Stream stream , string sourcefilename , bool processincludes , bool isinclude )
2014-12-03 23:15:26 +00:00
{
2013-09-11 09:47:53 +00:00
base . Parse ( stream , sourcefilename ) ;
//already parsed this?
2015-04-28 08:31:06 +00:00
if ( parsedlumps . Contains ( sourcefilename ) ) return false ;
parsedlumps . Add ( sourcefilename ) ;
2013-08-05 13:03:08 +00:00
if ( isinclude ) includes . Add ( sourcefilename ) ;
2015-04-28 08:31:06 +00:00
int bracelevel = 0 ;
2012-07-23 21:28:23 +00:00
2013-09-11 09:47:53 +00:00
// Keep local data
Stream localstream = datastream ;
string localsourcename = sourcename ;
BinaryReader localreader = datareader ;
2012-07-23 21:28:23 +00:00
2013-09-11 09:47:53 +00:00
// Continue until at the end of the stream
2014-12-03 23:15:26 +00:00
while ( SkipWhitespace ( true ) )
{
2013-09-11 09:47:53 +00:00
string token = ReadToken ( ) ;
2015-04-28 08:31:06 +00:00
if ( string . IsNullOrEmpty ( token ) ) continue ;
2012-07-10 10:20:45 +00:00
2015-04-28 08:31:06 +00:00
// Ignore inner scope stuff
if ( token = = "{" ) { bracelevel + + ; continue ; }
if ( token = = "}" ) { bracelevel - - ; continue ; }
if ( bracelevel > 0 ) continue ;
2012-07-10 10:20:45 +00:00
2015-04-28 08:31:06 +00:00
switch ( token . ToLowerInvariant ( ) )
{
case "script" :
2014-12-03 23:15:26 +00:00
{
2013-09-11 09:47:53 +00:00
SkipWhitespace ( true ) ;
2015-04-28 08:31:06 +00:00
int startpos = ( int ) stream . Position ;
2013-09-11 09:47:53 +00:00
token = ReadToken ( ) ;
2012-07-10 10:20:45 +00:00
2013-09-11 09:47:53 +00:00
//is it named script?
2014-12-03 23:15:26 +00:00
if ( token . IndexOf ( '"' ) ! = - 1 )
{
2015-04-28 08:31:06 +00:00
startpos + = 1 ;
2015-07-27 09:02:28 +00:00
string scriptname = StripTokenQuotes ( token ) ;
2013-08-07 09:25:37 +00:00
2015-07-27 09:02:28 +00:00
// Try to parse argument names
List < KeyValuePair < string , string > > args = ParseArgs ( ) ;
List < string > argnames = new List < string > ( ) ;
foreach ( KeyValuePair < string , string > group in args ) argnames . Add ( group . Value ) ;
// Add to collection
namedscripts . Add ( new ScriptItem ( scriptname , argnames , startpos , isinclude ) ) ;
2014-12-03 23:15:26 +00:00
}
else //should be numbered script
{
2014-02-21 14:42:12 +00:00
int n ;
2014-12-03 23:15:26 +00:00
if ( int . TryParse ( token , NumberStyles . Integer , CultureInfo . InvariantCulture , out n ) )
{
2015-07-27 09:02:28 +00:00
// Try to parse argument names
List < KeyValuePair < string , string > > args = ParseArgs ( ) ;
// Now find the opening brace
2014-12-03 23:15:26 +00:00
do
{
2015-03-21 19:41:54 +00:00
if ( ! SkipWhitespace ( true ) ) break ;
2013-09-11 09:47:53 +00:00
token = ReadToken ( ) ;
2015-03-21 19:41:54 +00:00
} while ( ! string . IsNullOrEmpty ( token ) & & token ! = "{" ) ;
2013-09-11 09:47:53 +00:00
token = ReadLine ( ) ;
string name = "" ;
2015-04-28 08:31:06 +00:00
bracelevel = 1 ;
2013-09-11 09:47:53 +00:00
2015-03-21 19:41:54 +00:00
if ( ! string . IsNullOrEmpty ( token ) )
2014-12-03 23:15:26 +00:00
{
2015-04-28 08:31:06 +00:00
int commentstart = token . IndexOf ( "//" ) ;
if ( commentstart ! = - 1 ) //found comment
2014-12-03 23:15:26 +00:00
{
2015-04-28 08:31:06 +00:00
commentstart + = 2 ;
name = token . Substring ( commentstart , token . Length - commentstart ) . Trim ( ) ;
2013-09-11 09:47:53 +00:00
}
}
name = ( name . Length > 0 ? name + " [" + n + "]" : "Script " + n ) ;
2015-07-27 09:02:28 +00:00
List < string > argnames = new List < string > ( ) ;
foreach ( KeyValuePair < string , string > group in args ) argnames . Add ( group . Value ) ;
// Add to collection
numberedscripts . Add ( new ScriptItem ( n , name , argnames , startpos , isinclude ) ) ;
2013-09-11 09:47:53 +00:00
}
}
2015-04-28 08:31:06 +00:00
}
break ;
case "function" :
2014-12-03 23:15:26 +00:00
{
2014-07-16 09:47:23 +00:00
SkipWhitespace ( true ) ;
2015-04-28 08:31:06 +00:00
int startpos = ( int ) stream . Position ;
2014-07-16 09:47:23 +00:00
string funcname = ReadToken ( ) ; //read return type
SkipWhitespace ( true ) ;
funcname + = " " + ReadToken ( ) ; //read function name
2015-07-27 09:02:28 +00:00
// Try to parse argument names
List < KeyValuePair < string , string > > args = ParseArgs ( ) ;
List < string > argnames = new List < string > ( ) ;
foreach ( KeyValuePair < string , string > group in args ) argnames . Add ( group . Value ) ;
2014-07-16 09:47:23 +00:00
2015-07-27 09:02:28 +00:00
// Add to collection
functions . Add ( new ScriptItem ( funcname , argnames , startpos , isinclude ) ) ;
2015-04-28 08:31:06 +00:00
}
break ;
2013-09-11 09:47:53 +00:00
2015-04-28 08:31:06 +00:00
default :
if ( processincludes & & ( token = = "#include" | | token = = "#import" ) )
2014-12-03 23:15:26 +00:00
{
2015-04-28 08:31:06 +00:00
SkipWhitespace ( true ) ;
string includelump = StripTokenQuotes ( ReadToken ( ) ) . ToLowerInvariant ( ) ;
2013-09-11 09:47:53 +00:00
2015-04-28 08:31:06 +00:00
if ( ! string . IsNullOrEmpty ( includelump ) )
{
string includename = Path . GetFileName ( includelump ) ;
2013-09-11 09:47:53 +00:00
2015-04-28 08:31:06 +00:00
if ( includename = = "zcommon.acs" | | includename = = "common.acs" | | includes . Contains ( includename ) )
continue ;
// Callback to parse this file
if ( OnInclude ! = null ) OnInclude ( this , includelump . Replace ( Path . AltDirectorySeparatorChar , Path . DirectorySeparatorChar ) ) ;
// Set our buffers back to continue parsing
datastream = localstream ;
datareader = localreader ;
sourcename = localsourcename ;
}
else
{
General . ErrorLogger . Add ( ErrorType . Error , "Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber ( ) + ": got #include directive without include path!" ) ;
}
2013-09-11 09:47:53 +00:00
}
2015-04-28 08:31:06 +00:00
break ;
2013-09-11 09:47:53 +00:00
}
}
return true ;
}
2015-07-27 09:02:28 +00:00
private List < KeyValuePair < string , string > > ParseArgs ( ) //type, name
{
List < KeyValuePair < string , string > > argnames = new List < KeyValuePair < string , string > > ( ) ;
SkipWhitespace ( true ) ;
string token = ReadToken ( ) ;
// Should be ENTER/OPEN etc. script type
if ( token ! = "(" )
{
argnames . Add ( new KeyValuePair < string , string > ( token . ToUpperInvariant ( ) , string . Empty ) ) ;
return argnames ;
}
while ( SkipWhitespace ( true ) )
{
string argtype = ReadToken ( ) ; // should be type
if ( IsSpecialToken ( argtype ) ) break ;
if ( argtype . ToUpperInvariant ( ) = = "VOID" )
{
argnames . Add ( new KeyValuePair < string , string > ( "(void)" , string . Empty ) ) ;
break ;
}
SkipWhitespace ( true ) ;
token = ReadToken ( ) ; // should be arg name
argnames . Add ( new KeyValuePair < string , string > ( argtype , token ) ) ;
SkipWhitespace ( true ) ;
token = ReadToken ( ) ; // should be comma or ")"
if ( token ! = "," ) break ;
}
return argnames ;
}
2013-09-11 09:47:53 +00:00
}
2012-07-10 14:14:53 +00:00
}