internal ZScriptActorStructure(ZDTextParser zdparser, DecorateCategoryInfo catinfo, string _classname, string _replacesname, string _parentname) { this.catinfo = catinfo; //mxd parser = (ZScriptParser)zdparser; stream = parser.DataStream; tokenizer = new ZScriptTokenizer(parser.DataReader); parser.tokenizer = tokenizer; classname = _classname; replaceclass = _replacesname; //baseclass = parser.GetArchivedActorByName(_parentname); // this is not guaranteed to work here ZScriptToken cls_open = tokenizer.ExpectToken(ZScriptTokenType.OpenCurly); if (cls_open == null || !cls_open.IsValid) { parser.ReportError("Expected {, got " + ((Object)cls_open ?? "<null>").ToString()); return; } // this dict holds temporary user settings per field (function, etc) Dictionary <string, List <string> > var_props = new Dictionary <string, List <string> >(); // in the class definition, we can have the following: // - Defaults block // - States block // - method signature: [native] [action] <type [, type [...]]> <name> (<arguments>); // - method: <method signature (except native)> <block> // - field declaration: [native] <type> <name>; // - enum definition: enum <name> <block>; // we are skipping everything, except Defaults and States. while (true) { var_props.Clear(); while (true) { ZScriptToken tt = tokenizer.ExpectToken(ZScriptTokenType.Whitespace, ZScriptTokenType.BlockComment, ZScriptTokenType.LineComment, ZScriptTokenType.Newline); if (tt == null || !tt.IsValid) { break; } if (tt.Type == ZScriptTokenType.LineComment) { ParseGZDBComment(var_props, tt.Value); } } //tokenizer.SkipWhitespace(); long ocpos = stream.Position; ZScriptToken token = tokenizer.ExpectToken(ZScriptTokenType.Identifier, ZScriptTokenType.CloseCurly); if (token == null || !token.IsValid) { parser.ReportError("Expected identifier, got " + ((Object)cls_open ?? "<null>").ToString()); return; } if (token.Type == ZScriptTokenType.CloseCurly) // end of class { break; } string b_lower = token.Value.ToLowerInvariant(); switch (b_lower) { case "default": if (!ParseDefaultBlock()) { return; } continue; case "states": if (!ParseStatesBlock()) { return; } continue; case "enum": if (!parser.ParseEnum()) { return; } continue; case "const": if (!parser.ParseConst()) { return; } continue; // apparently we can have a struct inside a class, but not another class. case "struct": if (!parser.ParseClassOrStruct(true, false, null)) { return; } continue; // new properties syntax case "property": if (!ParseProperty()) { return; } continue; default: stream.Position = ocpos; break; } // try to read in a variable/method. bool bmethod = false; string[] availablemodifiers = new string[] { "static", "native", "action", "readonly", "protected", "private", "virtual", "override", "meta", "transient", "deprecated", "final", "play", "ui", "clearscope", "virtualscope", "version", "const" }; string[] versionedmodifiers = new string[] { "version", "deprecated" }; string[] methodmodifiers = new string[] { "action", "virtual", "override", "final" }; HashSet <string> modifiers = new HashSet <string>(); List <string> types = new List <string>(); List <List <int> > typearraylens = new List <List <int> >(); List <string> names = new List <string>(); List <List <int> > arraylens = new List <List <int> >(); List <ZScriptToken> args = null; // this is for the future //List<ZScriptToken> body = null; while (true) { tokenizer.SkipWhitespace(); long cpos = stream.Position; token = tokenizer.ExpectToken(ZScriptTokenType.Identifier); if (token == null || !token.IsValid) { parser.ReportError("Expected modifier or name, got " + ((Object)cls_open ?? "<null>").ToString()); return; } b_lower = token.Value.ToLowerInvariant(); if (availablemodifiers.Contains(b_lower)) { if (modifiers.Contains(b_lower)) { parser.ReportError("Field/method modifier '" + b_lower + "' was specified twice"); return; } if (methodmodifiers.Contains(b_lower)) { bmethod = true; } if (versionedmodifiers.Contains(b_lower)) { string version = ParseVersion(b_lower == "version"); // deprecated doesn't require version string for historical reasons. (compatibility with old gzdoom.pk3) if (version == null && b_lower == "version") { return; } } modifiers.Add(b_lower); } else { stream.Position = cpos; break; } } // read in the type name(s) // type name can be: // - identifier // - identifier<identifier> while (true) { tokenizer.SkipWhitespace(); string typename = ParseTypeName(); if (typename == null) { return; } types.Add(typename.ToLowerInvariant()); typearraylens.Add(null); long cpos = stream.Position; tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.Comma, ZScriptTokenType.Identifier, ZScriptTokenType.OpenSquare); if (token != null && !token.IsValid) { parser.ReportError("Expected comma, identifier or array dimensions, got " + ((Object)token ?? "<null>").ToString()); return; } if (token == null || token.Type != ZScriptTokenType.Comma) { stream.Position = cpos; if (token.Type == ZScriptTokenType.OpenSquare) { List <int> typelens = ParseArrayDimensions(); if (typelens == null) // error { return; } typearraylens[typearraylens.Count - 1] = typelens; } break; } } while (true) { string name = null; List <int> lens = null; // read in the method/field name tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.Identifier); if (token == null || !token.IsValid) { parser.ReportError("Expected field/method name, got " + ((Object)token ?? "<null>").ToString()); return; } name = token.Value.ToLowerInvariant(); // check the token. if it's a (, then it's a method. if it's a ;, then it's a field, if it's a [ it's an array field. // if it's a field and bmethod=true, report error. tokenizer.SkipWhitespace(); long cpos = stream.Position; token = tokenizer.ExpectToken(ZScriptTokenType.Comma, ZScriptTokenType.OpenParen, ZScriptTokenType.OpenSquare, ZScriptTokenType.Semicolon); if (token == null || !token.IsValid) { parser.ReportError("Expected comma, ;, [, or argument list, got " + ((Object)token ?? "<null>").ToString()); return; } if (token.Type == ZScriptTokenType.OpenParen) { // if we have multiple names if (names.Count > 0) { parser.ReportError("Cannot have multiple names in a method"); return; } bmethod = true; // now, I could parse this properly, but it won't be used anyway, so I'll do it as a fake expression. args = parser.ParseExpression(true); token = tokenizer.ExpectToken(ZScriptTokenType.CloseParen); if (token == null || !token.IsValid) { parser.ReportError("Expected ), got " + ((Object)token ?? "<null>").ToString()); return; } // also get the body block, if any. tokenizer.SkipWhitespace(); cpos = stream.Position; token = tokenizer.ExpectToken(ZScriptTokenType.Semicolon, ZScriptTokenType.OpenCurly, ZScriptTokenType.Identifier); if (token == null || !token.IsValid) { parser.ReportError("Expected 'const', ; or {, got " + ((Object)token ?? "<null>").ToString()); return; } // if (token.Type == ZScriptTokenType.Identifier) { if (token.Value.ToLowerInvariant() != "const") { parser.ReportError("Expected 'const', got " + ((Object)token ?? "<null>").ToString()); return; } tokenizer.SkipWhitespace(); cpos = stream.Position; token = tokenizer.ExpectToken(ZScriptTokenType.Semicolon, ZScriptTokenType.OpenCurly); if (token == null || !token.IsValid) { parser.ReportError("Expected ; or {, got " + ((Object)token ?? "<null>").ToString()); return; } } if (token.Type == ZScriptTokenType.OpenCurly) { stream.Position = cpos; parser.SkipBlock(); //body = parser.ParseBlock(false); } break; // end method parsing } else { if (bmethod) { parser.ReportError("Cannot have virtual, override or action fields"); return; } // array if (token.Type == ZScriptTokenType.OpenSquare) { stream.Position = cpos; lens = ParseArrayDimensions(); if (lens == null) // error { return; } tokenizer.SkipWhitespace(); ZScriptTokenType[] expectTokens; if (modifiers.Contains("static")) { expectTokens = new ZScriptTokenType[] { ZScriptTokenType.Semicolon, ZScriptTokenType.Comma, ZScriptTokenType.OpAssign } } ; else { expectTokens = new ZScriptTokenType[] { ZScriptTokenType.Semicolon, ZScriptTokenType.Comma } }; token = tokenizer.ExpectToken(expectTokens); if (token == null || !token.IsValid) { parser.ReportError("Expected ;, =, or comma, got " + ((Object)token ?? "<null>").ToString()); return; } // "static int A[] = {1, 2, 3};" if (token.Type == ZScriptTokenType.OpAssign) { // read in array data tokenizer.SkipWhitespace(); parser.SkipBlock(false); tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.Semicolon, ZScriptTokenType.Comma); if (token == null || !token.IsValid) { parser.ReportError("Expected ; or comma, got " + ((Object)token ?? "<null>").ToString()); return; } } } } names.Add(name); arraylens.Add(lens); if (token.Type != ZScriptTokenType.Comma) // next name { break; } } // validate modifiers here. // protected and private cannot be combined. if (bmethod) { if (modifiers.Contains("protected") && modifiers.Contains("private")) { parser.ReportError("Cannot have protected and private on the same method"); return; } // virtual and override cannot be combined. int cvirtual = modifiers.Contains("virtual") ? 1 : 0; cvirtual += modifiers.Contains("override") ? 1 : 0; cvirtual += modifiers.Contains("final") ? 1 : 0; if (cvirtual > 1) { parser.ReportError("Cannot have virtual, override and final on the same method"); return; } // meta (what the f**k is that?) probably cant be on a method if (modifiers.Contains("meta")) { parser.ReportError("Cannot have meta on a method"); return; } } // finished method or field parsing. /*for (int i = 0; i < names.Count; i++) * { * string name = names[i]; * int arraylen = arraylens[i]; * * string _args = ""; * if (args != null) _args = " (" + ZScriptTokenizer.TokensToString(args) + ")"; * else if (arraylen != -1) _args = " [" + arraylen.ToString() + "]"; * parser.LogWarning(string.Format("{0} {1} {2}{3}", string.Join(" ", modifiers.ToArray()), string.Join(", ", types.ToArray()), name, _args)); * }*/ // update 08.02.17: add user variables from ZScript actors. if (args == null && types.Count == 1) // it's a field { // we support: // - float // - int // - double // - bool string type = types[0]; UniversalType utype; object udefault = null; switch (type) { case "int": utype = UniversalType.Integer; break; case "float": case "double": utype = UniversalType.Float; break; case "bool": utype = UniversalType.Integer; break; case "string": utype = UniversalType.String; break; // todo test if class names and colors will work default: continue; // go read next field } UniversalType utype_reinterpret = utype; if (var_props.ContainsKey("$userreinterpret")) { string sp = var_props["$userreinterpret"][0].Trim().ToLowerInvariant(); switch (sp) { case "color": if (utype != UniversalType.Integer) { parser.LogWarning("Cannot use $UserReinterpret Color with non-integers"); break; } utype_reinterpret = UniversalType.Color; break; } } if (var_props.ContainsKey("$userdefaultvalue")) { string sp = var_props["$userdefaultvalue"][0]; switch (utype) { case UniversalType.String: if (sp[0] == '"' && sp[sp.Length - 1] == '"') { sp = sp.Substring(1, sp.Length - 2); } udefault = sp; break; case UniversalType.Float: float d; if (!float.TryParse(sp, out d)) { parser.LogWarning("Incorrect float default from string \"" + sp + "\""); break; } udefault = d; break; case UniversalType.Integer: int i; if (!int.TryParse(sp, out i)) { if (utype_reinterpret == UniversalType.Color) { sp = sp.ToLowerInvariant(); Rendering.PixelColor pc; if (!ZDTextParser.GetColorFromString(sp, out pc)) { parser.LogWarning("Incorrect color default from string \"" + sp + "\""); break; } udefault = pc.ToInt() & 0xFFFFFF; break; } if (type == "bool") { sp = sp.ToLowerInvariant(); if (sp == "true") { udefault = true; } else if (sp == "false") { udefault = false; } else { parser.LogWarning("Incorrect boolean default from string \"" + sp + "\""); } break; } parser.LogWarning("Incorrect integer default from string \"" + sp + "\""); break; } udefault = i; break; } } for (int i = 0; i < names.Count; i++) { string name = names[i]; if (arraylens[i] != null || typearraylens[0] != null) { continue; // we don't process arrays } if (!name.StartsWith("user_")) { continue; // we don't process non-user_ fields (because ZScript won't pick them up anyway) } // parent class is not guaranteed to be loaded already, so handle collisions later uservars.Add(name, utype_reinterpret); if (udefault != null) { uservar_defaults.Add(name, udefault); } } } } // parsing done, process thing arguments ParseCustomArguments(); } }
// This parses the given decorate stream // Returns false on errors public override bool Parse(TextResourceData data, bool clearerrors) { //mxd. Already parsed? if (!base.AddTextResource(data)) { if (clearerrors) { ClearError(); } return(true); } // Cannot process? if (!base.Parse(data, clearerrors)) { return(false); } // [ZZ] For whatever reason, the parser is closely tied to the tokenizer, and to the general scripting lumps framework (see scripttype). // For this reason I have to still inherit the old tokenizer while only using the new one. //ReportError("found zscript? :)"); prevstreamposition = -1; tokenizer = new ZScriptTokenizer(datareader); // region-as-category, ZScript ver List <DecorateCategoryInfo> regions = new List <DecorateCategoryInfo>(); while (true) { ZScriptToken token = tokenizer.ExpectToken(ZScriptTokenType.Identifier, // const, enum, class, etc ZScriptTokenType.Whitespace, ZScriptTokenType.Newline, ZScriptTokenType.BlockComment, ZScriptTokenType.LineComment, ZScriptTokenType.Preprocessor); if (token == null) // EOF reached, whatever. { break; } if (!token.IsValid) { ReportError("Expected preprocessor statement, const, enum or class declaraction, got " + token); return(false); } // toplevel tokens allowed are only Preprocessor and Identifier. switch (token.Type) { case ZScriptTokenType.Whitespace: case ZScriptTokenType.Newline: case ZScriptTokenType.BlockComment: break; case ZScriptTokenType.LineComment: { string cmtval = token.Value.TrimStart(); if (cmtval.Length <= 0 || cmtval[0] != '$') { break; } // check for $GZDB_SKIP if (cmtval.Trim().ToLowerInvariant() == "$gzdb_skip") { return(true); } // if we are in a region, read property using function from ZScriptActorStructure if (regions.Count > 0) { ZScriptActorStructure.ParseGZDBComment(regions.Last().Properties, cmtval); } } break; case ZScriptTokenType.Preprocessor: { tokenizer.SkipWhitespace(); ZScriptToken directive = tokenizer.ExpectToken(ZScriptTokenType.Identifier); if (directive == null || !directive.IsValid) { ReportError("Expected preprocessor directive, got " + ((Object)directive ?? "<null>").ToString()); return(false); } string d_value = directive.Value.ToLowerInvariant(); if (d_value == "include") { tokenizer.SkipWhitespace(); ZScriptToken include_name = tokenizer.ExpectToken(ZScriptTokenType.Identifier, ZScriptTokenType.String, ZScriptTokenType.Name); if (include_name == null || !include_name.IsValid) { ReportError("Cannot include: expected a string value, got " + ((Object)include_name ?? "<null>").ToString()); return(false); } if (!ParseInclude(include_name.Value)) { return(false); } } else if (d_value == "region") { // just read everything until newline. string region_name = ""; while (true) { token = tokenizer.ReadToken(); if (token == null || token.Type == ZScriptTokenType.Newline) { break; } region_name += token.Value; } DecorateCategoryInfo region = new DecorateCategoryInfo(); string[] cattitle = region_name.Split(DataManager.CATEGORY_SPLITTER, StringSplitOptions.RemoveEmptyEntries); if (regions.Count > 0) { region.Category.AddRange(regions.Last().Category); region.Properties = new Dictionary <string, List <string> >(regions.Last().Properties, StringComparer.OrdinalIgnoreCase); } region.Category.AddRange(cattitle); regions.Add(region); } else if (d_value == "endregion") { // read everything until newline too? // - only if it causes problems if (regions.Count > 0) { regions.RemoveAt(regions.Count - 1); // remove last region from the list } else { LogWarning("Superfluous #endregion found without corresponding #region"); } } else { ReportError("Unknown preprocessor directive: " + directive.Value); return(false); } break; } case ZScriptTokenType.Identifier: { // identifier can be one of: class, enum, const, struct // the only type that we really care about is class, as it's the one that has all actors. switch (token.Value.ToLowerInvariant()) { case "extend": tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.Identifier); if (token == null || !token.IsValid || ((token.Value.ToLowerInvariant() != "class") && (token.Value.ToLowerInvariant() != "struct"))) { ReportError("Expected class or struct, got " + ((Object)token ?? "<null>").ToString()); return(false); } if (!ParseClassOrStruct((token.Value.ToLowerInvariant() == "struct"), true, (regions.Count > 0 ? regions.Last() : null))) { return(false); } break; case "class": // todo parse class if (!ParseClassOrStruct(false, false, (regions.Count > 0 ? regions.Last() : null))) { return(false); } break; case "struct": // todo parse struct if (!ParseClassOrStruct(true, false, null)) { return(false); } break; case "const": if (!ParseConst()) { return(false); } break; case "enum": if (!ParseEnum()) { return(false); } break; case "version": // expect a string. do nothing about it. tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.String); if (token == null || !token.IsValid) { ReportError("Expected version string, got " + ((Object)token ?? "<null>").ToString()); return(false); } break; default: ReportError("Expected preprocessor statement, const, enum or class declaraction, got " + token); return(false); } break; } } } return(true); }
private bool ParseInclude(string filename) { Stream localstream = datastream; string localsourcename = sourcename; BinaryReader localreader = datareader; DataLocation locallocation = datalocation; //mxd string localtextresourcepath = textresourcepath; //mxd ZScriptTokenizer localtokenizer = tokenizer; // [ZZ] //INFO: ZDoom DECORATE include paths can't be relative ("../actor.txt") //or absolute ("d:/project/actor.txt") //or have backward slashes ("info\actor.txt") //include paths are relative to the first parsed entry, not the current one //also include paths may or may not be quoted //mxd. Sanity checks if (string.IsNullOrEmpty(filename)) { ReportError("Expected file name to include"); return(false); } //mxd. Check invalid path chars if (!CheckInvalidPathChars(filename)) { return(false); } //mxd. Absolute paths are not supported... if (Path.IsPathRooted(filename)) { ReportError("Absolute include paths are not supported by ZDoom"); return(false); } //mxd. Relative paths are not supported if (filename.StartsWith(RELATIVE_PATH_MARKER) || filename.StartsWith(CURRENT_FOLDER_PATH_MARKER) || filename.StartsWith(ALT_RELATIVE_PATH_MARKER) || filename.StartsWith(ALT_CURRENT_FOLDER_PATH_MARKER)) { ReportError("Relative include paths are not supported by ZDoom"); return(false); } //mxd. Backward slashes are not supported if (filename.Contains(Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture))) { ReportError("Only forward slashes are supported by ZDoom"); return(false); } //mxd. Already parsed? if (parsedlumps.Contains(filename)) { ReportError("Already parsed \"" + filename + "\". Check your include directives"); return(false); } //mxd. Add to collection parsedlumps.Add(filename); // Callback to parse this file now if (OnInclude != null) { OnInclude(this, filename); } //mxd. Bail out on error if (this.HasError) { return(false); } // Set our buffers back to continue parsing datastream = localstream; datareader = localreader; sourcename = localsourcename; datalocation = locallocation; //mxd textresourcepath = localtextresourcepath; //mxd tokenizer = localtokenizer; return(true); }
internal ZScriptStateStructure(ActorStructure actor, ZDTextParser zdparser) { ZScriptParser parser = (ZScriptParser)zdparser; Stream stream = parser.DataStream; ZScriptTokenizer tokenizer = new ZScriptTokenizer(parser.DataReader); parser.tokenizer = tokenizer; // todo: parse stuff // string[] control_keywords = new string[] { "goto", "loop", "wait", "fail", "stop" }; while (true) { // expect identifier or string. // if it's an identifier, it can be goto/loop/wait/fail/stop. // if it's a string, it's always a sprite name. tokenizer.SkipWhitespace(); long cpos = stream.Position; ZScriptToken token = tokenizer.ExpectToken(ZScriptTokenType.Identifier, ZScriptTokenType.String, ZScriptTokenType.CloseCurly); if (token == null || !token.IsValid) { ZScriptToken _token = TryReadSprite(parser, stream, tokenizer); if (_token == null) { parser.ReportError("Expected identifier or string, got " + ((Object)token ?? "<null>").ToString()); return; } token = _token; } if (token.Type == ZScriptTokenType.CloseCurly) { stream.Position--; break; // done } else if (token.Type == ZScriptTokenType.Identifier) { string s_keyword = token.Value.ToLowerInvariant(); if (control_keywords.Contains(s_keyword)) { if (s_keyword == "goto") // just use decorate goto here. should work. but check for semicolon! { gotostate = new ZScriptStateGoto(actor, parser); parser.tokenizer = tokenizer; } //parser.LogWarning(string.Format("keyword {0}", s_keyword)); tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.Semicolon); if (token == null || !token.IsValid) { parser.ReportError("Expected ;, got " + ((Object)token ?? "<null>").ToString()); return; } continue; } } // make sure it's not a label of the next state. if it is, break out. long cpos2 = stream.Position; // local rewind point ZScriptToken token2 = tokenizer.ExpectToken(ZScriptTokenType.Colon, ZScriptTokenType.Dot); bool nextstate = (token2 != null && token2.IsValid); stream.Position = cpos2; // rewind to before state label read if (nextstate) { stream.Position = cpos; break; } // it's a frame definition. read it. string spritename = token.Value.ToLowerInvariant(); if (spritename.Length != 4) { parser.ReportError("Sprite name should be exactly 4 characters long (got " + spritename + ")"); return; } tokenizer.SkipWhitespace(); token = TryReadSprite(parser, stream, tokenizer); if (token == null) { parser.ReportError("Expected sprite frame(s)"); return; } string spriteframes = token.Value; //parser.LogWarning(string.Format("sprite {0} {1}", spritename, spriteframes)); // duration int duration; tokenizer.SkipWhitespace(); // this can be a function call, or a constant. token = tokenizer.ExpectToken(ZScriptTokenType.Identifier); if (token != null && token.IsValid) { duration = -1; tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.OpenParen); if (token != null && token.IsValid) { List <ZScriptToken> tokens = parser.ParseExpression(true); tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.CloseParen); if (token == null || !token.IsValid) { parser.ReportError("Expected ), got " + ((Object)token ?? "<null>").ToString()); return; } } } else { if (!parser.ParseInteger(out duration)) { return; } } // now, it can also contain BRIGHT, LIGHT(), OFFSET() string[] allspecials = new string[] { "bright", "light", "offset", "fast", "slow", "nodelay", "canraise" }; HashSet <string> specials = new HashSet <string>(); // maybe something else. I don't know. FrameInfo info = new FrameInfo(); // Make the sprite name string realspritename = (spritename + spriteframes[0]).ToUpperInvariant(); // Ignore some odd ZDoom things if (/*!realspritename.StartsWith("TNT1") && */ !realspritename.StartsWith("----") && !realspritename.Contains("#")) // [ZZ] some actors have only TNT1 state and receive a random image because of this { info.Sprite = realspritename; //mxd info.Duration = duration; sprites.Add(info); } while (true) { tokenizer.SkipWhitespace(); cpos2 = stream.Position; token = tokenizer.ExpectToken(ZScriptTokenType.Identifier, ZScriptTokenType.Semicolon, ZScriptTokenType.OpenCurly); if (token == null || !token.IsValid) { parser.ReportError("Expected identifier, ;, or {, got " + ((Object)token ?? "<null>").ToString()); return; } // if it's opencurly, it means that everything else is an anonymous block. // skip/parse that. // if it's semicolon, it means end of the frame. // if it's BRIGHT, LIGHT() or OFFSET(), it should be processed. // if it's something else (but identifier), then it's an action function call, process it. if (token.Type == ZScriptTokenType.OpenCurly) { stream.Position--; if (!parser.SkipBlock()) { return; } break; // this block is done } else if (token.Type == ZScriptTokenType.Semicolon) { break; // done } else // identifier { string special = token.Value.ToLowerInvariant(); if (allspecials.Contains(special)) { if (specials.Contains(special)) { parser.ReportError("'" + special + "' cannot be used twice"); return; } specials.Add(special); if (special == "bright") { info.Bright = true; } else if (special == "light" || special == "offset") { tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.OpenParen); if (token == null || !token.IsValid) { parser.ReportError("Expected (, got " + ((Object)token ?? "<null>").ToString()); return; } List <ZScriptToken> tokens = parser.ParseExpression(true); tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.CloseParen); if (token == null || !token.IsValid) { parser.ReportError("Expected ), got " + ((Object)token ?? "<null>").ToString()); return; } // parse the light expression. if (special == "light") { if (tokens.Count != 1 || (tokens[0].Type != ZScriptTokenType.String && tokens[0].Type != ZScriptTokenType.Identifier)) { parser.ReportError("Light() special takes one string argument"); return; } info.LightName = tokens[0].Value; } } } else { // stream.Position = cpos2; string actionfunction = parser.ParseDottedIdentifier(); //parser.LogWarning("actionfunction = " + actionfunction); if (actionfunction == null) { return; } // tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.OpenParen); if (token != null && token.IsValid) { List <ZScriptToken> tokens = parser.ParseExpression(true); tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.CloseParen); if (token == null || !token.IsValid) { parser.ReportError("Expected ), got " + ((Object)token ?? "<null>").ToString()); return; } // possibly do something with the arguments? not now though. } // expect semicolon and break. tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.Semicolon); if (token == null || !token.IsValid) { parser.ReportError("Expected ;, got " + ((Object)token ?? "<null>").ToString()); return; } break; } // if not special } // if identifier } // frame parsing loop (inner) } // state parsing loop (outer) TrimLeft(); }
private bool ParseDefaultBlock() { tokenizer.SkipWhitespace(); ZScriptToken token = tokenizer.ExpectToken(ZScriptTokenType.OpenCurly); if (token == null || !token.IsValid) { parser.ReportError("Expected {, got " + ((Object)token ?? "<null>").ToString()); return(false); } ZScriptTokenType[] whitespacetypes = new ZScriptTokenType[] { ZScriptTokenType.Newline, ZScriptTokenType.Whitespace, ZScriptTokenType.BlockComment, ZScriptTokenType.LineComment }; // todo parse defaults block while (true) { long cpos = stream.Position; token = tokenizer.ExpectToken(ZScriptTokenType.Whitespace, ZScriptTokenType.BlockComment, ZScriptTokenType.Newline, ZScriptTokenType.LineComment, ZScriptTokenType.OpAdd, ZScriptTokenType.OpSubtract, ZScriptTokenType.Identifier, ZScriptTokenType.CloseCurly, ZScriptTokenType.Semicolon); if (token == null || !token.IsValid) { parser.ReportError("Expected comment, flag, property, or }, got " + ((Object)token ?? "<null>").ToString()); return(false); } //if (ClassName == "Enforcer") // parser.LogWarning(token.ToString()); if (token.Type == ZScriptTokenType.CloseCurly) { break; } switch (token.Type) { case ZScriptTokenType.Whitespace: case ZScriptTokenType.BlockComment: case ZScriptTokenType.Newline: break; case ZScriptTokenType.LineComment: ParseGZDBComment(props, token.Value); break; // flag definition (+/-) case ZScriptTokenType.OpAdd: case ZScriptTokenType.OpSubtract: { bool flagset = (token.Type == ZScriptTokenType.OpAdd); string flagname = parser.ParseDottedIdentifier(); if (flagname == null) { return(false); } //parser.LogWarning(string.Format("{0}{1}", (flagset ? '+' : '-'), flagname)); // set flag flags[flagname] = flagset; break; } // property or combo definition case ZScriptTokenType.Identifier: { stream.Position = cpos; string propertyname = parser.ParseDottedIdentifier(); if (propertyname == null) { return(false); } List <string> propertyvalues = new List <string>(); // read in property values, until semicolon reached while (true) { tokenizer.SkipWhitespace(); List <ZScriptToken> expr = parser.ParseExpression(); string exprstring = ZScriptTokenizer.TokensToString(expr); token = tokenizer.ExpectToken(ZScriptTokenType.Comma, ZScriptTokenType.Semicolon); if (token == null || !token.IsValid) { parser.ReportError("Expected comma or ;, got " + ((Object)token ?? "<null>").ToString()); return(false); } propertyvalues.Add(exprstring); if (token.Type == ZScriptTokenType.Semicolon) { break; } } //parser.LogWarning(string.Format("{0} = [{1}]", propertyname, string.Join(", ", propertyvalues.ToArray()))); // set property // translate "scale" to x and y scale if (propertyname == "scale") { props["xscale"] = props["yscale"] = propertyvalues; } else { props[propertyname] = propertyvalues; } break; } } } return(true); }
internal ZScriptStateGoto(ActorStructure actor, ZDTextParser zdparser) { // goto syntax that is accepted by GZDB is [classname::]statename[+offset] ZScriptParser parser = (ZScriptParser)zdparser; Stream stream = parser.DataStream; ZScriptTokenizer tokenizer = new ZScriptTokenizer(parser.DataReader); parser.tokenizer = tokenizer; tokenizer.SkipWhitespace(); string firsttarget = parser.ParseDottedIdentifier(); if (firsttarget == null) { return; } ZScriptToken token; string secondtarget = null; int offset = 0; tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.DoubleColon); if (token != null && token.IsValid) { secondtarget = parser.ParseDottedIdentifier(); if (secondtarget == null) { return; } } tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.OpAdd); if (token != null && token.IsValid) { tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.Integer); if (token == null || !token.IsValid) { parser.ReportError("Expected state offset, got " + ((Object)token ?? "<null>").ToString()); return; } offset = token.ValueInt; } // Check if we don't have the class specified if (string.IsNullOrEmpty(secondtarget)) { // First target is the state to go to classname = actor.ClassName; statename = firsttarget.ToLowerInvariant().Trim(); } else { // First target is the base class to use // Second target is the state to go to classname = firsttarget.ToLowerInvariant().Trim(); statename = secondtarget.ToLowerInvariant().Trim(); } spriteoffset = offset; if ((classname == "super") && (actor.BaseClass != null)) { classname = actor.BaseClass.ClassName; } }