private static void EmitExpressionValue(Token t, Queue<Token> smt, ExpressionType type) { EmitByte(0x20); bool hadRef = false; switch (t.type) { case TokenType.edid: if (smt.Count > 0 && smt.Peek().IsSymbol(".")) { EmitRefLabel(t, RefType.Expression); smt.Dequeue(); if (smt.Count == 0) { throw new ExpressionParseException("Unexpected end of line"); } if (farVars.ContainsKey(t.token)) { Dictionary<string, ushort> fars = farVars[t.token]; t = smt.Dequeue(); if (fars.ContainsKey(t.token)) { EmitByte(0x73); Emit(fars[t.token]); break; } } else { t = smt.Dequeue(); } hadRef = true; goto case TokenType.Function; } else { if (type == ExpressionType.Numeric) { AddError("Reference type not valid here"); } EmitRefLabel(t, RefType.Standalone); } break; case TokenType.Local: LocalVar lv = locals[t.token]; if (lv.type == VarType.Ref && smt.Count > 0 && smt.Peek().IsSymbol(".")) { goto case TokenType.edid; } if (lv.type == VarType.Ref && type == ExpressionType.Numeric) { AddError("Reference type not valid here"); } if (lv.type != VarType.Ref && type == ExpressionType.Ref) { AddError("A reference assignment must consist of a single edid or function"); } if (lv.type == VarType.Int) { EmitByte(0x73); } else { EmitByte(0x66); } Emit((ushort)lv.index); break; case TokenType.Global: if (type == ExpressionType.Ref) { AddError("A reference assignment must consist of a single edid or function"); } EmitRefLabel(t, RefType.Expression); break; case TokenType.Float: case TokenType.Integer: if (type == ExpressionType.Ref && t.token != "0") { AddError("A reference assignment must consist of a single edid or function"); } bw.Write(Encoding.Instance.GetBytes(t.token)); break; case TokenType.Function: // FunctionSig fs=functionList[t.token]; // if(fs.requiredArgs!=fs.args.Length) throw new ExpressionParseException("functions with variable argument count cannot be used in expressions"); // if(fs.ret==VarType.None) throw new ExpressionParseException("Functions with no return type cannot be used in expressions"); // if(smt.Count<fs.args.Length) throw new ExpressionParseException("Not enough parameters to function"); var args = new Token[smt.Count + 1]; args[0] = t; for (int i = 1; i < args.Length; i++) { args[i] = smt.Dequeue(); } EmitFunctionCall(ref args, true, hadRef, type == ExpressionType.Ref); for (int i = 0; i < args.Length; i++) { smt.Enqueue(args[i]); } break; default: AddError("Expected <local>|<global>|<number>|<function>"); break; } }
private static void EmitFunctionCall(ref Token[] smt, bool expression, bool hadref, bool requiresRef) { FunctionSig fs = functionList[smt[0].token]; if (hadref && !fs.allowref) { AddError("Object reference not valid on this function"); } if (expression) { EmitByte(0x58); // if(fs.ret==VarType.None) AddError("Functions with no return type cannot be used in expressions"); } // if(requiresRef&&fs.ret!=VarType.Ref) AddError("Function does not return a reference"); Emit(fs.opcode); if (fs.skipArgs) { if (expression) { AddError("SkipArgs is not valid on functions used in expressions"); } // for(int j=1;j<smt.Length;j++) smt[j-1]=smt[j]; // Array.Resize<Token>(ref smt, smt.Length-1); smt = new Token[0]; Emit(0); return; } if (smt.Length == 1) { if (fs.requiredArgs > 0) { AddError("Not enough arguments to function"); } if (fs.args.Length > 0) { Emit(2); } Emit(0); smt = new Token[0]; return; } if (fs.args.Length == 0) { Emit(0); for (int j = 1; j < smt.Length; j++) { smt[j - 1] = smt[j]; } Array.Resize(ref smt, smt.Length - 1); return; } long pos = bw.BaseStream.Length; ushort argcount = 0; Emit(0); Emit(0); int i = 0; bool lastwasref = false; while (true) { i++; if (i == smt.Length) { if (argcount < fs.requiredArgs) { AddError("Not enough arguments to function. Expected " + fs.requiredArgs); } smt = new Token[0]; break; } if (smt[i].type == TokenType.Symbol) { if (smt[i].IsSymbol(".") && lastwasref) { if (i < smt.Length - 1 && farVars.ContainsKey(smt[i - 1].token)) { i++; EmitByte(0x73); Dictionary<string, ushort> vars = farVars[smt[i - 2].token]; if (!vars.ContainsKey(smt[i].token)) { AddError("Reference '" + smt[i - 2].utoken + "' has no variable called '" + smt[i].utoken + "'"); } else { Emit(vars[smt[i].token]); } continue; } } else if (smt[i].IsSymbol("-") && (!expression || (argcount < fs.requiredArgs))) { if (i < smt.Length - 1 && (smt[i + 1].type == TokenType.Integer || smt[i + 1].type == TokenType.Float)) { smt[i + 1] = new Token(smt[i + 1].type, "-" + smt[i + 1].token); continue; } } if (expression) { if (argcount < fs.requiredArgs) { AddError("Not enough arguments to function. Expected " + fs.requiredArgs); } for (int j = i; j < smt.Length; j++) { smt[j - i] = smt[j]; } Array.Resize(ref smt, smt.Length - i); break; } else { AddError("Unexpected symbol '" + smt[i].token + "' in function arguments"); } } if (argcount == fs.args.Length) { AddError("Too many arguments given to function. Expected " + fs.args.Length); } argcount++; lastwasref = false; switch (fs.args[argcount - 1]) { case VarType.Axis: switch (smt[i].token) { case "x": EmitByte((byte)'X'); continue; case "y": EmitByte((byte)'Y'); continue; case "z": EmitByte((byte)'Z'); continue; default: AddError("Expected 'x', 'y' or 'z'"); continue; } case VarType.Enum: if (smt[i].type == TokenType.Integer) { Emit(ushort.Parse(smt[i].token)); } else { Dictionary<string, ushort> Enum = enumList[fs.reftypes[argcount - 1]]; if (!Enum.ContainsKey(smt[i].token)) { AddError("'" + smt[i].token + "' is not a valid entry of the enum '" + fs.reftypes[argcount - 1] + "'"); } else { Emit(Enum[smt[i].token]); } } continue; case VarType.Short: if (smt[i].type != TokenType.Integer) { AddError("Expected integer argument"); } else { Emit(ushort.Parse(smt[i].token)); } continue; case VarType.String: Emit((ushort)smt[i].token.Length); bw.Write(System.Text.Encoding.Default.GetBytes(smt[i].token)); continue; } switch (smt[i].type) { case TokenType.edid: if (i == smt.Length - 1 || !smt[i + 1].IsSymbol(".")) { if (fs.args[argcount - 1] != VarType.Ref) { AddError("Invalid argument " + i + " to function. Expected " + fs.args[argcount - 1].ToString()); } if (fs.reftypes[argcount - 1] != null && fs.reftypes[argcount - 1] != edidList[smt[i].token].Value) { AddError("Invalid record type at argument " + i + " of function. Expected " + fs.reftypes[argcount - 1]); } } EmitRefLabel(smt[i], RefType.Expression); lastwasref = true; break; case TokenType.Local: LocalVar vt = locals[smt[i].token]; switch (vt.type) { case VarType.Int: if (fs.args[argcount - 1] != VarType.Float && fs.args[argcount - 1] != VarType.Int) { AddError("Invalid argument " + i + " to function. Expected " + fs.args[argcount - 1].ToString()); } EmitByte(0x73); Emit((ushort)locals[smt[i].token].index); break; case VarType.Float: if (fs.args[argcount - 1] != VarType.Float && fs.args[argcount - 1] != VarType.Int) { AddError("Invalid argument " + i + " to function. Expected " + fs.args[argcount - 1].ToString()); } EmitByte(0x66); Emit((ushort)locals[smt[i].token].index); break; case VarType.Ref: if (fs.args[argcount - 1] != VarType.Ref) { AddError("Invalid argument " + i + " to function. Expected " + fs.args[argcount - 1].ToString()); } EmitRefLabel(smt[i], RefType.Expression); break; } break; case TokenType.Global: if (fs.args[argcount - 1] != VarType.Float && fs.args[argcount - 1] != VarType.Int) { AddError("Invalid argument " + i + " to function. Expected " + fs.args[argcount - 1].ToString()); } EmitRefLabel(smt[i], RefType.Expression); break; case TokenType.Integer: if (fs.args[argcount - 1] == VarType.Float) { goto case TokenType.Float; } if (fs.args[argcount - 1] != VarType.Int) { AddError("Invalid argument " + i + " to function. Expected " + fs.args[argcount - 1].ToString()); } EmitByte(0x6e); bw.Write(int.Parse(smt[i].token)); break; case TokenType.Float: if (fs.args[argcount - 1] != VarType.Float && fs.args[argcount - 1] != VarType.Int) { AddError("Invalid argument " + i + " to function. Expected " + fs.args[argcount - 1].ToString()); } EmitByte(0x7a); bw.Write(double.Parse(smt[i].token)); break; default: AddError("Expected <global>|<local>|<constant>"); return; } } for (int j = 0; j < fs.paddingbytes; j++) { EmitByte(0); } bw.BaseStream.Position = pos; Emit((ushort)(bw.BaseStream.Length - (pos + 2))); Emit(argcount); bw.BaseStream.Position = bw.BaseStream.Length; }
private static void EmitBegin(Token[] smt) { Emit(0x10); if (!blockList.ContainsKey(smt[1].token)) { AddError("Unknown block type"); EmitLong(0); EmitLong(0); return; } FunctionSig fs = blockList[smt[1].token]; long pos = bw.BaseStream.Length; Emit(0); Emit(fs.opcode); EmitLong(0); if (smt.Length > fs.args.Length + 2) { AddError("Too many arguments to 'begin' block"); } // for(int i=0;i<fs.paddingbytes;i++) EmitByte(0); if (fs.args.Length > 0) { Emit((ushort)(smt.Length - 2)); for (int i = 2; i < smt.Length; i++) { switch (fs.args[i - 2]) { case VarType.Short: if (smt[i].type != TokenType.Integer) { AddError("Block argument: Expected short"); } else { Emit(ushort.Parse(smt[i].token)); } break; case VarType.Int: if (smt[i].type != TokenType.Integer) { AddError("Block argument: Expected integer"); } else { EmitByte(0x73); EmitLong(uint.Parse(smt[i].token)); } break; case VarType.Ref: if (smt[i].type != TokenType.edid) { AddError("Block argument: Expected edid"); } else { EmitRefLabel(smt[i], RefType.Expression); } break; default: AddError("Sanity check failed. VarType of block argument was invalid"); break; } } } bw.BaseStream.Position = pos; Emit((ushort)(bw.BaseStream.Length - (pos + 2))); bw.BaseStream.Position = bw.BaseStream.Length; }
private static void EmitExpression(Token[] smt, ExpressionType type) { long pos = bw.BaseStream.Length; Emit(0); try { EmitExpression2(new Queue<Token>(smt), int.MaxValue, false, type); } catch (ExpressionParseException ex) { AddError(ex.Message); } bw.BaseStream.Position = pos; Emit((ushort)(bw.BaseStream.Length - (pos + 2))); bw.BaseStream.Position = bw.BaseStream.Length; }
public LocalVar(int index, Token t) { this.index = index; switch (t.keyword) { case Keywords.Int: this.type = VarType.Int; break; case Keywords.Float: this.type = VarType.Float; break; case Keywords.Ref: this.type = VarType.Ref; break; default: throw new Exception("Should never happen: Invalid type passed to local variable constructor"); } }
private static Token[] TrimStatement(Token[] smt, int size) { var smt2 = new Token[smt.Length - size]; for (int i = 0; i < smt2.Length; i++) { smt2[i] = smt[i + size]; } return smt2; }
private static void HandleStatement(Token[] smt) { if (smt[0].type == TokenType.Function) { EmitFunctionCall(ref smt, false, false, false); } else if (smt[0].IsKeyword(Keywords.ShowMessage)) { EmitShowMessage(smt); } else if (smt[0].IsKeyword(Keywords.Set)) { if (smt.Length < 4 || !(smt[2].IsKeyword(Keywords.To) || smt[2].IsSymbol("."))) { AddError("Expected 'set <var> to <expression>'"); return; } Emit(0x15); long pos = bw.BaseStream.Length; Emit(0); if (smt[1].type == TokenType.Local) { LocalVar lv = locals[smt[1].token]; if (lv.type == VarType.Int) { EmitByte(0x73); } else { EmitByte(0x66); } Emit((ushort)lv.index); EmitExpression(TrimStatement(smt, 3), (lv.type == VarType.Ref) ? ExpressionType.Ref : ExpressionType.Numeric); } else if (smt[1].type == TokenType.Global) { EmitRefLabel(smt[1], RefType.Expression); EmitExpression(TrimStatement(smt, 3), ExpressionType.Numeric); } else if (smt[1].type == TokenType.edid && farVars.ContainsKey(smt[1].token) && smt[2].IsSymbol(".")) { if (smt.Length < 6 || !smt[4].IsKeyword(Keywords.To)) { AddError("Expected 'set <var> to <expression>'"); return; } EmitRefLabel(smt[1], RefType.Expression); EmitByte(0x73); if (!farVars[smt[1].token].ContainsKey(smt[3].token)) { AddError("Local variable '" + smt[3].token + " does not exist in quest " + smt[1].token); } else { Emit(farVars[smt[1].token][smt[3].token]); } EmitExpression(TrimStatement(smt, 5), ExpressionType.If); } else { AddError("Expected set <local>|<global> to <expression>"); } bw.BaseStream.Position = pos; Emit((ushort)(bw.BaseStream.Length - (pos + 2))); bw.BaseStream.Position = bw.BaseStream.Length; } else if (smt[0].type == TokenType.edid) { if (smt.Length < 3 || !smt[1].IsSymbol(".") || smt[2].type != TokenType.Function) { AddError("Expected ref.function"); return; } EmitRefLabel(smt[0], RefType.Standard); smt = TrimStatement(smt, 2); EmitFunctionCall(ref smt, false, true, false); } else if (smt[0].type == TokenType.Local) { LocalVar lv = locals[smt[0].token]; if (lv.type != VarType.Ref) { AddError("Expected 'Set', <function> or <ref>.<function>"); return; } if (smt.Length < 3 || !smt[1].IsSymbol(".") || smt[2].type != TokenType.Function) { AddError("Expected ref.function"); return; } EmitRefLabel(smt[0], RefType.Standard); smt = TrimStatement(smt, 2); EmitFunctionCall(ref smt, false, true, false); } else { AddError("Expected 'Set', <function> or <ref>.<function>"); } }
private static void EmitShowMessage(Token[] smt) { // ShowMessage DoorOpenedScienceMsg passSkill // 59 10 0E 00 01 00 72 02 00 01 00 73 01 00 00 00 00 00 Emit(0x1059); if (smt.Length == 1) { AddError("Not enough arguments to ShowMessage"); return; } long pos = bw.BaseStream.Length; Emit(0); Emit(1); switch (smt[1].type) { case TokenType.edid: if (edidList[smt[1].token].Value != "MESG") { goto default; } else { EmitRefLabel(smt[1], RefType.Expression); } break; case TokenType.Local: LocalVar vt = locals[smt[1].token]; if (vt.type != VarType.Ref) { goto default; } EmitRefLabel(smt[1], RefType.Expression); break; default: AddError("First argument to ShowMessage must be an MESG record"); return; } if (smt.Length == 2) { Emit(0); } else { bool lastwasref = false; Emit((ushort)(smt.Length - 2)); for (int i = 2; i < smt.Length; i++) { if (smt[i].type == TokenType.Symbol) { if (smt[i].IsSymbol(".") && lastwasref) { if (i < smt.Length - 1 && farVars.ContainsKey(smt[i - 1].token)) { i++; EmitByte(0x73); Dictionary<string, ushort> vars = farVars[smt[i - 2].token]; if (!vars.ContainsKey(smt[i].token)) { AddError("Reference '" + smt[i - 2].utoken + "' has no variable called '" + smt[i].utoken + "'"); } else { Emit(vars[smt[i].token]); } continue; } } else if (smt[i].IsSymbol("-")) { if (i < smt.Length - 1 && (smt[i + 1].type == TokenType.Integer || smt[i + 1].type == TokenType.Float)) { smt[i + 1] = new Token(smt[i + 1].type, "-" + smt[i + 1].token); continue; } } AddError("Unexpected symbol '" + smt[i].token + "' in ShowMessage arguments"); } lastwasref = false; switch (smt[i].type) { case TokenType.edid: EmitRefLabel(smt[i], RefType.Expression); lastwasref = true; break; case TokenType.Local: LocalVar vt = locals[smt[i].token]; switch (vt.type) { case VarType.Int: EmitByte(0x73); Emit((ushort)locals[smt[i].token].index); break; case VarType.Float: EmitByte(0x66); Emit((ushort)locals[smt[i].token].index); break; case VarType.Ref: EmitRefLabel(smt[i], RefType.Expression); break; } break; case TokenType.Global: EmitRefLabel(smt[i], RefType.Expression); break; case TokenType.Integer: EmitByte(0x6e); bw.Write(int.Parse(smt[i].token)); break; case TokenType.Float: EmitByte(0x7a); bw.Write(double.Parse(smt[i].token)); break; default: AddError("Expected <global>|<local>|<constant>"); return; } } } Emit(0); Emit(0); bw.BaseStream.Position = pos; Emit((ushort)(bw.BaseStream.Length - (pos + 2))); bw.BaseStream.Position = bw.BaseStream.Length; }
private static void EmitRefLabel(Token t, RefType type) { if (t.type == TokenType.Global) { EmitByte(0x47); } else { switch (type) { case RefType.Standard: Emit(0x1c); break; case RefType.Expression: EmitByte(0x72); break; case RefType.Standalone: EmitByte(0x5a); break; } } if (t.type == TokenType.Local) { LocalVar var = locals[t.token]; if (var.refid == 0) { AddError("Variable was not of type ref"); } else { Emit((ushort)var.refid); } } else if (t.type == TokenType.edid || t.type == TokenType.Global) { if (!edidRefs.ContainsKey(t.token)) { var sr = new SubRecord(); sr.Name = "SCRO"; if (t.type == TokenType.edid) { sr.SetData(TypeConverter.i2h(edidList[t.token].Key)); } else { sr.SetData(TypeConverter.i2h(globals[t.token])); } r.AddRecord(sr); refcount++; edidRefs[t.token] = (ushort)refcount; } Emit(edidRefs[t.token]); } else { AddError("Expected ref variable or edid"); } }