UltimateZoneBuilder/Source/Core/GZBuilder/GZDoom/AcsParserSE.cs

216 lines
6.8 KiB
C#
Raw Normal View History

using System.IO;
using System.Collections.Generic;
using System.Globalization;
using CodeImp.DoomBuilder.ZDoom;
using CodeImp.DoomBuilder.GZBuilder.Data;
//mxd. ACS parser used to create ScriptItems for use in script editor's navigator
namespace CodeImp.DoomBuilder.GZBuilder.GZDoom
{
internal sealed class AcsParserSE : ZDTextParser
{
internal delegate void IncludeDelegate(AcsParserSE parser, string includefile);
internal IncludeDelegate OnInclude;
private readonly List<string> parsedlumps;
private readonly List<string> includes;
private readonly List<ScriptItem> namedscripts;
private readonly List<ScriptItem> numberedscripts;
private readonly List<ScriptItem> functions;
internal List<ScriptItem> NamedScripts { get { return namedscripts; } }
internal List<ScriptItem> NumberedScripts { get { return numberedscripts; } }
internal List<ScriptItem> Functions { get { return functions; } }
internal IEnumerable<string> Includes { get { return includes; } }
internal AcsParserSE()
{
namedscripts = new List<ScriptItem>();
numberedscripts = new List<ScriptItem>();
functions = new List<ScriptItem>();
parsedlumps = new List<string>();
includes = new List<string>();
specialtokens += "(,)";
}
public override bool Parse(Stream stream, string sourcefilename)
{
return Parse(stream, sourcefilename, false, false);
}
public bool Parse(Stream stream, string sourcefilename, bool processincludes, bool isinclude)
{
base.Parse(stream, sourcefilename);
//already parsed this?
if (parsedlumps.Contains(sourcefilename)) return false;
parsedlumps.Add(sourcefilename);
if (isinclude) includes.Add(sourcefilename);
int bracelevel = 0;
// Keep local data
Stream localstream = datastream;
string localsourcename = sourcename;
BinaryReader localreader = datareader;
// Continue until at the end of the stream
while (SkipWhitespace(true))
{
string token = ReadToken();
if(string.IsNullOrEmpty(token)) continue;
// Ignore inner scope stuff
if(token == "{") { bracelevel++; continue; }
if(token == "}") { bracelevel--; continue; }
if(bracelevel > 0) continue;
switch (token.ToLowerInvariant())
{
case "script":
{
SkipWhitespace(true);
int startpos = (int)stream.Position;
token = ReadToken();
//is it named script?
if (token.IndexOf('"') != -1)
{
startpos += 1;
string scriptname = StripTokenQuotes(token);
// 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));
}
else //should be numbered script
{
int n;
if (int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out n))
{
// Try to parse argument names
List<KeyValuePair<string, string>> args = ParseArgs();
// Now find the opening brace
do
{
if(!SkipWhitespace(true)) break;
token = ReadToken();
} while (!string.IsNullOrEmpty(token) && token != "{");
token = ReadLine();
string name = "";
bracelevel = 1;
if (!string.IsNullOrEmpty(token))
{
int commentstart = token.IndexOf("//");
if (commentstart != -1) //found comment
{
commentstart += 2;
name = token.Substring(commentstart, token.Length - commentstart).Trim();
}
}
name = (name.Length > 0 ? name + " [" + n + "]" : "Script " + n);
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));
}
}
}
break;
case "function":
{
SkipWhitespace(true);
int startpos = (int)stream.Position;
string funcname = ReadToken(); //read return type
SkipWhitespace(true);
funcname += " " + ReadToken(); //read function name
// 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
functions.Add(new ScriptItem(funcname, argnames, startpos, isinclude));
}
break;
default:
if (processincludes && (token == "#include" || token == "#import"))
{
SkipWhitespace(true);
string includelump = StripTokenQuotes(ReadToken()).ToLowerInvariant();
if (!string.IsNullOrEmpty(includelump))
{
string includename = Path.GetFileName(includelump);
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!");
}
}
break;
}
}
return true;
}
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;
}
}
}