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-11-13 21:42:41 +00:00
|
|
|
|
private readonly HashSet<string> parsedlumps;
|
|
|
|
|
private readonly HashSet<string> includes;
|
2015-11-30 14:18:42 +00:00
|
|
|
|
private List<string> includestoskip;
|
|
|
|
|
private string libraryname;
|
2014-07-16 09:47:23 +00:00
|
|
|
|
|
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-11-30 14:18:42 +00:00
|
|
|
|
internal HashSet<string> Includes { get { return includes; } }
|
|
|
|
|
internal bool IsLibrary { get { return !string.IsNullOrEmpty(libraryname); } }
|
|
|
|
|
internal string LibraryName { get { return libraryname; } }
|
|
|
|
|
|
|
|
|
|
internal bool AddArgumentsToScriptNames;
|
|
|
|
|
internal bool IsMapScriptsLump;
|
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-11-13 21:42:41 +00:00
|
|
|
|
parsedlumps = new HashSet<string>();
|
|
|
|
|
includes = new HashSet<string>();
|
|
|
|
|
includestoskip = 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)
|
|
|
|
|
{
|
2015-11-13 21:42:41 +00:00
|
|
|
|
return Parse(stream, sourcefilename, new List<string>(), false, false);
|
2013-09-11 09:47:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-11-13 21:42:41 +00:00
|
|
|
|
public bool Parse(Stream stream, string sourcefilename, bool processincludes, bool isinclude)
|
2014-12-03 23:15:26 +00:00
|
|
|
|
{
|
2015-11-13 21:42:41 +00:00
|
|
|
|
return Parse(stream, sourcefilename, includestoskip, processincludes, isinclude);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Parse(Stream stream, string sourcefilename, List<string> configincludes, bool processincludes, bool isinclude)
|
|
|
|
|
{
|
2015-11-28 20:59:32 +00:00
|
|
|
|
// Integrity check
|
2015-11-28 23:31:54 +00:00
|
|
|
|
if(stream == null || stream.Length == 0)
|
2015-11-13 21:42:41 +00:00
|
|
|
|
{
|
|
|
|
|
ReportError("Unable to load " + (isinclude ? "include" : "") + " file '" + sourcefilename + "'!");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2013-09-11 09:47:53 +00:00
|
|
|
|
|
2015-11-28 23:31:54 +00:00
|
|
|
|
base.Parse(stream, sourcefilename);
|
|
|
|
|
|
2015-11-13 21:42:41 +00:00
|
|
|
|
// Already parsed this?
|
|
|
|
|
if(parsedlumps.Contains(sourcefilename)) return false;
|
2015-04-28 08:31:06 +00:00
|
|
|
|
parsedlumps.Add(sourcefilename);
|
2015-11-13 21:42:41 +00:00
|
|
|
|
if(isinclude && !includes.Contains(sourcefilename)) includes.Add(sourcefilename);
|
|
|
|
|
includestoskip = configincludes;
|
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
|
2015-11-13 21:42:41 +00:00
|
|
|
|
while(SkipWhitespace(true))
|
2014-12-03 23:15:26 +00:00
|
|
|
|
{
|
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-11-13 21:42:41 +00:00
|
|
|
|
switch(token.ToLowerInvariant())
|
2015-04-28 08:31:06 +00:00
|
|
|
|
{
|
|
|
|
|
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?
|
2015-11-13 21:42:41 +00:00
|
|
|
|
if(token.IndexOf('"') != -1)
|
2014-12-03 23:15:26 +00:00
|
|
|
|
{
|
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);
|
|
|
|
|
|
2015-11-30 14:18:42 +00:00
|
|
|
|
// Make full name
|
|
|
|
|
if(AddArgumentsToScriptNames) scriptname += " " + GetArgumentNames(args);
|
|
|
|
|
|
2015-07-27 09:02:28 +00:00
|
|
|
|
// 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;
|
2015-11-13 21:42:41 +00:00
|
|
|
|
if(int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out n))
|
2014-12-03 23:15:26 +00:00
|
|
|
|
{
|
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-11-13 21:42:41 +00:00
|
|
|
|
if(!string.IsNullOrEmpty(token))
|
2014-12-03 23:15:26 +00:00
|
|
|
|
{
|
2015-11-30 14:18:42 +00:00
|
|
|
|
int commentstart = token.IndexOf("//", System.StringComparison.Ordinal);
|
2015-11-13 21:42:41 +00:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-28 15:04:21 +00:00
|
|
|
|
bool customname = (name.Length > 0);
|
2015-11-30 14:18:42 +00:00
|
|
|
|
name = (customname ? name + " [Script " + 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);
|
|
|
|
|
|
2015-11-30 14:18:42 +00:00
|
|
|
|
// Make full name
|
|
|
|
|
if(AddArgumentsToScriptNames) name += " " + GetArgumentNames(args);
|
|
|
|
|
|
2015-07-27 09:02:28 +00:00
|
|
|
|
// Add to collection
|
2015-07-28 15:04:21 +00:00
|
|
|
|
numberedscripts.Add(new ScriptItem(n, name, argnames, startpos, isinclude, customname));
|
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-11-30 14:18:42 +00:00
|
|
|
|
// Make full name
|
|
|
|
|
if(AddArgumentsToScriptNames) funcname += GetArgumentNames(args);
|
|
|
|
|
|
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-11-30 14:18:42 +00:00
|
|
|
|
case "#library":
|
|
|
|
|
if(IsMapScriptsLump)
|
|
|
|
|
{
|
|
|
|
|
ReportError("Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": SCRIPTS lump can not be compiled as library!");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SkipWhitespace(true);
|
|
|
|
|
libraryname = ReadToken();
|
|
|
|
|
|
|
|
|
|
if(string.IsNullOrEmpty(libraryname) || !libraryname.StartsWith("\"") || !libraryname.EndsWith("\""))
|
|
|
|
|
{
|
|
|
|
|
ReportError("Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": invalid #library directive!");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
libraryname = StripTokenQuotes(libraryname);
|
|
|
|
|
break;
|
|
|
|
|
|
2015-04-28 08:31:06 +00:00
|
|
|
|
default:
|
2015-11-13 21:42:41 +00:00
|
|
|
|
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-11-13 21:42:41 +00:00
|
|
|
|
if(!string.IsNullOrEmpty(includelump))
|
2015-04-28 08:31:06 +00:00
|
|
|
|
{
|
|
|
|
|
string includename = Path.GetFileName(includelump);
|
2015-11-13 21:42:41 +00:00
|
|
|
|
if(includestoskip.Contains(includename) || includes.Contains(includename)) continue;
|
2015-04-28 08:31:06 +00:00
|
|
|
|
|
|
|
|
|
// Callback to parse this file
|
2015-11-13 21:42:41 +00:00
|
|
|
|
if(OnInclude != null) OnInclude(this, includelump.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar));
|
2015-04-28 08:31:06 +00:00
|
|
|
|
|
|
|
|
|
// Set our buffers back to continue parsing
|
|
|
|
|
datastream = localstream;
|
|
|
|
|
datareader = localreader;
|
|
|
|
|
sourcename = localsourcename;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2015-11-13 21:42:41 +00:00
|
|
|
|
ReportError("Error in '" + sourcefilename + "' at line " + GetCurrentLineNumber() + ": got #include directive without include path!");
|
|
|
|
|
return false;
|
2015-04-28 08:31:06 +00:00
|
|
|
|
}
|
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")
|
|
|
|
|
{
|
2015-11-30 14:18:42 +00:00
|
|
|
|
argnames.Add(new KeyValuePair<string, string>("void", string.Empty));
|
2015-07-27 09:02:28 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
2015-11-30 14:18:42 +00:00
|
|
|
|
|
|
|
|
|
private static string GetArgumentNames(List<KeyValuePair<string, string>> args)
|
|
|
|
|
{
|
|
|
|
|
// Make full name
|
|
|
|
|
if(args.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
List<string> argdescs = new List<string>(args.Count);
|
|
|
|
|
foreach(KeyValuePair<string, string> group in args)
|
|
|
|
|
argdescs.Add((group.Key + " " + group.Value).TrimEnd());
|
|
|
|
|
|
|
|
|
|
return "(" + string.Join(", ", argdescs.ToArray()) + ")";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "(void)";
|
|
|
|
|
}
|
2013-09-11 09:47:53 +00:00
|
|
|
|
}
|
2012-07-10 14:14:53 +00:00
|
|
|
|
}
|