4using System.Collections;
5using System.Collections.Generic;
6using System.Collections.ObjectModel;
20 #region Private Classes
35 #region Private Variables
42 private Dictionary<object, Instruction>
locks;
43 private List<Interpreter>
jobs;
55 #region Private Methods
61 if (value ==
null)
throw new ExecutionException($
"Cannot apply {opName} operation to null.");
62 if (value is NullReference)
throw new ExecutionException($
"Cannot apply {opName} operation to 'null'.");
64 Type t = value.GetType();
68 switch (Type.GetTypeCode(t))
70 case TypeCode.Int32:
return (
int)value;
71 case TypeCode.Boolean:
return ((
bool)value) ? 1 : 0;
72 case TypeCode.Char:
return (
char)value;
73 case TypeCode.SByte:
return (sbyte)value;
74 case TypeCode.Byte:
return (
byte)value;
75 case TypeCode.Int16:
return (
short)value;
76 case TypeCode.UInt16:
return (ushort)value;
77 case TypeCode.UInt32: { uint u = (uint)value;
if (u >
int.MaxValue)
throw new OverflowException();
return (
int)u; }
78 case TypeCode.Int64: {
long l = (long)value;
if (l < int.MinValue || l >
int.MaxValue)
throw new OverflowException();
return (
int)l; }
79 case TypeCode.UInt64: { ulong ul = (ulong)value;
if (ul > (ulong)
int.MaxValue)
throw new OverflowException();
return (
int)ul; }
80 case TypeCode.Single: {
float f = (float)value;
if (
float.IsNaN(f) ||
float.IsInfinity(f) || f < int.MinValue || f >
int.MaxValue)
throw new OverflowException();
return (
int)f; }
81 case TypeCode.Double: {
double d = (double)value;
if (
double.IsNaN(d) ||
double.IsInfinity(d) || d < int.MinValue || d >
int.MaxValue)
throw new OverflowException();
return (
int)d; }
82 case TypeCode.Decimal:
return decimal.ToInt32((decimal)value);
83 default:
throw new InvalidCastException();
88 throw new ExecutionException($
"Values of type '{t.Name}' cannot be used in {opName} operations.");
102 return operand.
Value;
127 if ((
string)operand.
Member ==
"toString")
132 StringBuilder sb =
new StringBuilder();
134 foreach (KeyValuePair<object, object> element
in array)
136 sb.Append(element.Value.ToString());
145 object objectValue = associativeArray[operand.
Member];
150 else if (src.GetType() == typeof(
string))
153 string strSource = (string)src;
155 object objectIndex = operand.
Member;
157 if (objectIndex.GetType() == typeof(
string))
158 if (((
string)objectIndex) ==
"length")
159 return strSource.Length;
161 if (objectIndex.GetType() != typeof(
int))
164 return strSource[(int)objectIndex] +
"";
171 var memberName = operand.
Member?.ToString() ??
"";
172 return _clr.GetMember(src, memberName);
199 else if (src.GetType() == typeof(
string))
202 string strSource = (string)src;
206 if (objectIndex.GetType() != typeof(
int))
209 return strSource[(int)objectIndex] +
"";
217 if (
_clr.TryGetIndex(src, objectIndex, out var v))
219 throw new ExecutionException(
"Nur Arrays, Dictionaries und Strings sind indexierbar.");
224 throw new ExecutionException(
"Der Typ '"+ operand.
Type +
"' kann an dieser Stelle nicht verarbeitet werden.");
238 string identifier = (string)dst.
Value;
258 _clr.SetMember(target, dst.
Member.ToString()!, val);
274 if (container is
string)
278 if (
_clr.TrySetIndex(container, key, val))
281 throw new ExecutionException($
"Das Ziel '{dst}' vom Typ '{container?.GetType().FullName}' ist nicht indexierbar.");
306 Type typeDest = objectValueDest.GetType();
307 Type typeSource = objectValueSource.GetType();
312 if (typeDest == typeof(
String))
320 ((
ArrayList)objectValueDest).Add(objectValueSource);
328 if (typeDest == typeof(
String))
331 objectValueDest.ToString().Replace(objectValueSource.ToString(),
""));
336 ((
ArrayList)objectValueDest).Subtract(objectValueSource);
422 switch (Type.GetTypeCode(typeDest))
424 case TypeCode.Int32: valueDest = (int)objectValueDest;
break;
425 case TypeCode.Single: valueDest = (decimal)(
float)objectValueDest;
break;
426 case TypeCode.Double: valueDest = (decimal)(
double)objectValueDest;
break;
427 case TypeCode.Decimal: valueDest = (decimal)objectValueDest;
break;
429 throw new ScriptStackException(
"Values of type '" + typeDest.Name +
"' cannot be used as destination in arithmetic instructions.");
433 switch (Type.GetTypeCode(typeSource))
435 case TypeCode.Int32: valueSource = (int)objectValueSource;
break;
436 case TypeCode.Single: valueSource = (decimal)(
float)objectValueSource;
break;
437 case TypeCode.Double: valueSource = (decimal)(
double)objectValueSource;
break;
438 case TypeCode.Decimal: valueSource = (decimal)objectValueSource;
break;
440 throw new ScriptStackException(
"Values of type '" + typeSource.Name +
"' cannot be used as source in arithmetic instructions.");
446 case OpCode.ADD: result = valueDest + valueSource;
break;
447 case OpCode.SUB: result = valueDest - valueSource;
break;
448 case OpCode.MUL: result = valueDest * valueSource;
break;
449 case OpCode.DIV: result = valueDest / valueSource;
break;
450 case OpCode.MOD: result = valueDest % valueSource;
break;
456 switch (Type.GetTypeCode(typeDest))
464 throw new ScriptStackException(
"Values of type '" + typeDest.Name +
"' cannot be used in arithmetic instructions.");
483 string identifier = (string)
instruction.First.Value;
489 Type typeDest = dst.GetType();
491 Type typeSource = src.GetType();
499 if (typeDest == typeof(NullReference) || typeSource == typeof(NullReference))
516 if (typeDest == typeof(NullReference) && typeSource == typeof(NullReference))
517 message =
"Die Operation '" + instruction.OpCode +
"' kann nicht auf den Typ 'null' angewendet werden.";
519 else if (typeDest == typeof(NullReference))
520 message =
"Die Operation '" + instruction.OpCode +
"' kann nicht auf den Typ 'null' als Ziel (links des Operators) angewendet werden.";
523 message =
"Die Operation '" + instruction.OpCode +
"' kann nicht auf den Typ 'null' als Quelle (rechts des Operators) angewendet werden.";
541 if (typeDest == typeof(
string) || typeSource == typeof(
string))
544 string strDst =
"" + dst;
546 string strSrc =
"" + src;
552 result = strDst == strSrc;
556 result = strDst != strSrc;
560 result = strDst.CompareTo(strSrc) > 0;
564 result = strDst.CompareTo(strSrc) >= 0;
568 result = strDst.CompareTo(strSrc) < 0;
572 result = strDst.CompareTo(strSrc) <= 0;
588 typeDest == typeof(
int) ||
589 typeDest == typeof(
float) ||
590 typeDest == typeof(
double) ||
591 typeDest == typeof(
char);
594 typeSource == typeof(
int) ||
595 typeSource == typeof(
float) ||
596 typeSource == typeof(
double) ||
597 typeSource == typeof(
char);
604 result = (instruction.OpCode ==
OpCode.CEQ) ? eq : !eq;
611 if (!(numericDest && numericSource) &&
613 dst is IComparable cmpDst)
618 cmp = cmpDst.CompareTo(
ClrBridge.ScriptNullToClr(src));
623 cmp =
string.Compare(dst.ToString(), src.ToString(), StringComparison.Ordinal);
628 case OpCode.CG: result = cmp > 0;
break;
629 case OpCode.CGE: result = cmp >= 0;
break;
630 case OpCode.CL: result = cmp < 0;
break;
631 case OpCode.CLE: result = cmp <= 0;
break;
642 if (typeDest == typeof(
int))
643 dstVal =
double.Parse(
"" + (
int)dst);
645 else if (typeDest == typeof(
float))
646 dstVal =
double.Parse(
"" + (
float)dst);
648 else if (typeDest == typeof(
char))
649 dstVal =
double.Parse(
"" + (
char)dst);
651 else if (typeDest == typeof(
double))
652 dstVal = (double)dst;
655 throw new ExecutionException(
"Der Typ '" + typeDest.Name +
"' kann in relationalen Operationen als Ziel (links des Operators) nicht verarbeitet werden.");
657 if (typeSource == typeof(
int))
658 srcVal =
double.Parse(
"" + (
int)src);
660 else if (typeSource == typeof(
float))
661 srcVal =
double.Parse(
"" + (
float)src);
663 else if (typeSource == typeof(
char))
664 srcVal =
double.Parse(
"" + (
char)src);
666 else if (typeSource == typeof(
double))
667 srcVal = (double)src;
670 throw new ExecutionException(
"Der Typ '" + typeSource.Name +
"' kann in relationalen Operationen als Quelle (rechts des Operators) nicht verarbeitet werden.");
676 result = dstVal == srcVal;
680 result = dstVal != srcVal;
684 result = dstVal > srcVal;
688 result = dstVal >= srcVal;
692 result = dstVal < srcVal;
696 result = dstVal <= srcVal;
716 string identifier = (string)
instruction.First.Value;
722 Type typeDest = dst.GetType();
724 Type typeSource = src.GetType();
732 if (typeSource == typeof(
bool))
735 else if (typeSource == typeof(NullReference))
739 srcVal = ((double)src != 0.0) ? true :
false;
742 if (typeDest == typeof(
bool))
745 else if (typeDest == typeof(NullReference))
749 dstVal = ((double)dst != 0.0) ? true :
false;
755 result = dstVal && srcVal;
759 result = dstVal || srcVal;
774 if (array.Count == 0)
783 foreach (
object tmp
in array.Keys)
792 if (Equals(tmp, iterator))
800 Dictionary<object, object>.KeyCollection.Enumerator keys = array.Keys.GetEnumerator();
809 next = NullReference.Instance;
827 foreach (
object tmp
in dict.Keys)
836 if (Equals(tmp, iterator))
843 IDictionaryEnumerator en = dict.GetEnumerator();
849 next = NullReference.Instance;
864 if (iterator.GetType() != typeof(
int))
873 int elements = (int)iterator;
875 if (elements < str.Length - 1)
996 string identifier = (string)
instruction.First.Value;
1000 Type typeDest = val.GetType();
1002 if (typeDest == typeof(
char))
1007 else if (typeDest == typeof(
int))
1010 else if (typeDest == typeof(
float))
1013 else if (typeDest == typeof(
double))
1016 else if (typeDest == typeof(decimal))
1020 throw new ExecutionException(
"Der Typ '" + typeDest.Name +
"' kann nicht inkrementiert werden.");
1030 string identifier = (string)
instruction.First.Value;
1034 Type typeDest = val.GetType();
1036 if (typeDest == typeof(
char))
1041 else if (typeDest == typeof(
int))
1044 else if (typeDest == typeof(
float))
1047 else if (typeDest == typeof(
double))
1050 else if (typeDest == typeof(decimal))
1054 throw new ExecutionException(
"Der Typ '" + typeDest.Name +
"' kann nicht dekrementiert werden.");
1064 string identifier = (string)
instruction.First.Value;
1068 Type typeDest = val.GetType();
1070 if (typeDest == typeof(
int))
1073 else if (typeDest == typeof(
float))
1076 else if (typeDest == typeof(
double))
1079 else if (typeDest == typeof(
char))
1083 throw new ExecutionException(
"Der Typ '" + typeDest.Name +
"' kann nicht negiert werden.");
1093 string identifier =
null;
1099 switch (operand.
Type)
1105 identifier = operand.
Value.ToString();
1112 throw new ExecutionException(
"Der Typ '" + operand.
Type +
"' kann in Bitoperationen nicht verarbeitet werden.");
1124 string identifier =
null;
1130 switch (operand.
Type)
1136 identifier = operand.
Value.ToString();
1143 throw new ExecutionException(
"Der Typ '" + operand.
Type +
"' kann in Bitoperationen nicht verarbeitet werden.");
1154 string identifier = (string)
instruction.First.Value;
1158 localMemory[identifier] = val == NullReference.Instance;
1230 string identifier = (string)
instruction.First.Value;
1234 Type typeDest = val.GetType();
1236 if (typeDest != typeof(
bool) && typeDest != typeof(
int))
1237 throw new ExecutionException(
"Der Typ '" + typeDest.Name +
"' kann nicht negiert werden.");
1239 if (typeDest == typeof(
bool))
1242 else if (typeDest == typeof(
int))
1252 string identifier =
null;
1258 switch (operand.
Type)
1265 identifier = operand.
Value.ToString();
1272 throw new ExecutionException(
"Der Typ '" + operand.
Type +
"' kann an dieser Stelle nicht verarbeitet werden.");
1281 string identifier =
null;
1287 switch (operand.
Type)
1294 identifier = operand.
Value.ToString();
1301 throw new ExecutionException(
"Operand type '" + operand.
Type +
"' not supported by logical AND instruction.");
1318 int src = (int)srcObj;
1327 string identifier =
null;
1333 switch (operand.
Type)
1340 identifier = operand.
Value.ToString();
1347 throw new ExecutionException(
"Der Typ '" + operand.
Type +
"' kann an dieser Stelle nicht verarbeitet werden.");
1361 frame.nextInstruction = (int)
instruction.First.InstructionPointer.Address;
1378 frame.nextInstruction = (int)target.
Address;
1393 frame.nextInstruction = (int)
instruction.Second.InstructionPointer.Address;
1402 throw new ExecutionException(
"DCG opcodes cannot be executed within a function frame.");
1428 string strIdentifier =
null;
1434 switch (operand.
Type)
1440 strIdentifier = operand.
Value.ToString();
1447 throw new ExecutionException(
"Operand type '" + operand.
Type +
"' not supported by logical LNEG instruction.");
1466 string enumerableVar =
instruction.Second.Value.ToString();
1475 if (enumerable is
string s)
1481 if (enumerable is IList list)
1488 if (enumerable is IDictionary dict)
1495 if (enumerable is IEnumerable en)
1498 foreach (var item
in en)
1499 materialized.Add(item ?? NullReference.Instance);
1524 frame.function =
function;
1528 frame.nextInstruction = (int)
function.EntryPoint.Address;
1545 Host stackHandler =
null;
1550 stackHandler =
host;
1554 stackHandler = routine.
Handler;
1556 List<object> parameters =
new List<object>();
1561 routine.
Verify(parameters);
1563 object objectResult =
null;
1565 if (stackHandler !=
null)
1569 objectResult = stackHandler.
Invoke(routine.
Name, parameters);
1578 routine.
Verify(objectResult);
1582 if (objectResult ==
null)
1583 objectResult = NullReference.Instance;
1604 string targetIdentifier =
instruction.First.Value.ToString();
1605 string methodName =
instruction.First.Member?.ToString() ??
"";
1611 if (raw is
int i) argc = i;
1612 else if (raw is
string s &&
int.TryParse(s, out var j)) argc = j;
1617 if (target ==
null || target is NullReference)
1624 List<object> args =
new List<object>(argc);
1625 for (
int i = 0; i < argc; i++)
1628 object result =
_clr.Invoke(target, methodName, args);
1630 result = NullReference.Instance;
1720 List<object> parameters =
new List<object>();
1722 for (
int i = 0; i <
function.ParameterCount; i++)
1738 if (first.GetType() == typeof(NullReference))
1741 if (
script.Manager.Locks.ContainsKey(first))
1758 script.Manager.Locks[first] =
this;
1771 if (first.GetType() == typeof(NullReference))
1774 if (!
script.Manager.Locks.ContainsKey(first))
1777 locks.Remove(first);
1779 script.Manager.Locks.Remove(first);
1793 for (
int i =
jobs.Count - 1; i >= 0; i--)
1872 #region Public Methods
1877 if (
function.ParameterCount != parameters.Count)
1878 throw new ExecutionException(
"Die Funktion '" +
function.Name +
"' wurde mit " + parameters.Count +
" statt erwartet " +
function.ParameterCount +
" Parametern aufgerufen.");
1883 this.function =
function;
1885 script =
function.Executable.Script;
1893 locks =
new Dictionary<object, Instruction>();
1895 jobs =
new List<Interpreter>();
1903 foreach (
object parameter
in parameters)
1906 if (parameter ==
null)
1912 Type parameterType = parameter.GetType();
1914 if (parameterType == typeof(NullReference))
1917 else if (parameterType == typeof(
int)
1918 || parameterType == typeof(
float)
1919 || parameterType == typeof(
double)
1920 || parameterType == typeof(
bool)
1921 || parameterType == typeof(
string)
1922 || parameterType == typeof(
char)
1927 throw new ExecutionException(
"Der Typ '" + parameterType.Name +
"' ist kein generischer Typ.");
1957 functionFrame.function =
function;
1961 functionFrame.nextInstruction = (int)
function.EntryPoint.Address;
1971 foreach (
object currentLock
in locks.Keys)
1972 script.Manager.Locks.Remove(currentLock);
2011 DateTime end = DateTime.Now + interval;
2028 if (DateTime.Now >= end)
2067 #region Public Properties
2080 public ReadOnlyCollection<Interpreter>
Jobs
2082 get {
return jobs.AsReadOnly(); }
2108 List<Function> listFunctions =
new List<Function>();
2110 listFunctions.Add(functionFrame.
function);
2111 return new List<Function>(listFunctions).AsReadOnly();
2127 get {
return host; }
2128 set {
host = value; }
2136 if (list.Count == 0)
2141 if (iterator.GetType() != typeof(
int))
2147 int i = (int)iterator;
2149 if (i < list.Count - 1)
Centralized CLR interop bridge for ScriptStack. Handles reflection-based member access,...
A function, forward declared in a script.
An instruction in a virtual intermediate language.
For every forward declared function, a new function frame is created including a memory object holdin...
void CALL()
Call a Function.
void Iterator(IList list)
InterpreterOptions _options
Interpreter(Script script, InterpreterOptions? options=null)
void JZ()
Jump to the instruction the second operand is pointing at if the first operand is true.
void Assignment(Operand dst, object val)
void ExecuteInstruction()
Dictionary< object, Instruction > locks
void POP()
Pop a value from the stack into an atom.
ReadOnlyCollection< object > ParameterStack
void NEG()
Negate a literal (* -1).
void Iterator(IDictionary dict)
void JMP()
Jump to the address the first operator points at.
void Iterator(ArrayList array)
int ToInt32Bitwise(object value, string opName="bitwise")
void PTR()
A pointer in foreach loops.
void RUN()
Run a Function in Background.
uint Interpret(TimeSpan interval)
void MOV()
Basic assignment.
void NOT()
Negate a boolean or int.
void Iterator(string str)
uint ExecuteBackgroundJobs()
ReadOnlyCollection< Interpreter > Jobs
Stack< FunctionFrame > functionStack
Interpreter(Function function)
ReadOnlyCollection< Function > FunctionStack
void JNZ()
Jump to the instruction the second operand is pointing at if the first operand is false.
uint Interpret(uint instructions)
void RET()
Return from current function frame to the last one on the stack, copying local memory to the new one.
void MIV()
Invoke a CLR instance method via reflection.
Interpreter(Script script, List< object > parameters, InterpreterOptions? options=null)
Stack< object > parameterStack
void Arithmetic()
Ausführung arithmetischer Operationen.
void Logic()
Usually its a boolean operation but it allows numerics too.
object Evaluate(Operand operand)
void INV()
Invoke a Routine, if no result is specified a null is pushed onto the stack.
void Relation()
Ausführung einer Vergleichsoperation.
Interpreter(Function function, List< object > parameters, InterpreterOptions? options=null)
Options for the ScriptStack interpreter.
static Memory AllocateLocalMemory(Memory scriptMemory)
A Routine is an abstract representation of a method.
void Verify(List< object > parameters)
Verify the parameter types of a Routine. If null or void was specified values arent verified.
List< Type > ParameterTypes
The main interface to create a Host. A Host can implement Routine's to extend its functionality.
object Invoke(string routine, List< object > parameters)
Called when a Routine is invoked.