@@ -82,15 +82,18 @@ static ReadableString getName(AccessType access) {
8282
8383// Types used in machine instuctions
8484enum class ArgumentType {
85- Immediate, // Index to MemoryPlane::immediate
86- Reference, // Indirect index translated into both global and stack-relative locations within MemoryPlane::stack
87- MethodReference // Index to a method to call
85+ Immediate, // Index to MemoryPlane::immediate
86+ Reference, // Indirect index translated into both global and stack-relative locations within MemoryPlane::stack
87+ MethodReference, // Index to a method to call
88+ InstructionAddress // Index to a machine instruction, which instructions can jump to.
8889};
8990
9091// Types
91- // TODO: Make the use of FixedPoint optional in VirtualMachine
9292using DataType = int32_t ;
9393
94+ // Instruction addresses uses the list of instructions as their read-only memory.
95+ static const DataType DataType_InstructionAddress = -1 ;
96+
9497template <int32_t TYPE_COUNT>
9598struct Variable {
9699 String name;
@@ -129,9 +132,9 @@ struct ArgSig {
129132 : name(name), byValue(byValue), dataType(dataType) {}
130133 bool matches (ArgumentType argType, DataType dataType) const {
131134 if (this ->byValue ) {
132- return dataType == this ->dataType && (argType == ArgumentType::Immediate || argType == ArgumentType::Reference );
135+ return ( dataType == this ->dataType ) && (argType == ArgumentType::Reference || argType == ArgumentType:: Immediate || argType == ArgumentType::InstructionAddress );
133136 } else {
134- return dataType == this ->dataType && argType == ArgumentType::Reference;
137+ return ( dataType == this ->dataType ) && ( argType == ArgumentType::Reference) ;
135138 }
136139 }
137140};
@@ -276,6 +279,13 @@ struct VMTypeDef {
276279 : name(name), dataType(dataType), allowDefaultValue(allowDefaultValue), debugPrinter(debugPrinter) {}
277280};
278281
282+ struct InstructionLabel {
283+ int32_t address;
284+ String identifier;
285+ InstructionLabel (int32_t address, const ReadableString &identifier)
286+ : address(address), identifier(identifier) {}
287+ };
288+
279289template <int32_t TYPE_COUNT>
280290struct Method {
281291 String name;
@@ -293,6 +303,10 @@ struct Method {
293303 bool declaredLocals = false ; // Goes true when a local is declared
294304 List<Variable<TYPE_COUNT>> locals; // locals[0..inputCount-1] are the inputs, while locals[inputCount..inputCount+outputCount-1] are the outputs
295305
306+ bool ended = false ;
307+
308+ List<InstructionLabel> labels;
309+
296310 // Type-specific spaces
297311 FixedArray<int32_t , TYPE_COUNT> count;
298312 // Look-up table from a combination of type and type-local indices to unified-local indices
@@ -509,6 +523,15 @@ struct VirtualMachine {
509523 });
510524 }
511525
526+ bool isInsideOfMethod () const {
527+ return this ->methods .length () > 0 && !(this ->methods .last ().ended );
528+ }
529+
530+ void endCurrentMethod () {
531+ this ->addReturnInstruction ();
532+ this ->methods .last ().ended = true ;
533+ }
534+
512535 static ReadableString getArg (const List<String>& arguments, int32_t index) {
513536 if (index < 0 || index >= arguments.length ()) {
514537 return U" " ;
@@ -781,6 +804,19 @@ struct VirtualMachine {
781804 throwError (U" Using > without < for in-place temp allocation.\n " );
782805 return VMA (ArgumentType::Immediate, -1 , -1 );
783806 } else {
807+ // Look for a label.
808+ if (this ->isInsideOfMethod ()) {
809+ Method<TYPE_COUNT> &method = this ->methods .last ();
810+ String labelName = string_removeOuterWhiteSpace (content);
811+ debugText (U" Checking if " , labelName, U" is a label in the " , method.name , U" method.\n " );
812+ for (int32_t l = 0 ; l < method.labels .length (); l++) {
813+ debugText (U" * Label " , method.labels [l].identifier , U" @ " , method.labels [l].address , U" \n " );
814+ if (string_caseInsensitiveMatch (method.labels [l].identifier , labelName)) {
815+ return VMA (ArgumentType::InstructionAddress, DataType_InstructionAddress, method.labels [l].address );
816+ }
817+ }
818+ }
819+ // If it is not a label then look for variables.
784820 Variable<TYPE_COUNT>* resource = getResource (content, methodIndex);
785821 if (resource) {
786822 return VMA (ArgumentType::Reference, resource->typeDescription ->dataType , resource->getGlobalIndex ());
@@ -808,6 +844,7 @@ struct VirtualMachine {
808844 if (this ->methods .length () == 1 ) {
809845 // When more than one function exists, the init method must end with a return instruction
810846 // Otherwise it would start executing instructions in another method and crash
847+ // Finalizing the init method must wait until the next method begins, because it is extended each time a global variable is introduced at the top.
811848 this ->addReturnInstruction ();
812849 }
813850 this ->methods .pushConstruct (getArg (arguments, 0 ), this ->machineWords .length (), this ->machineTypeCount );
@@ -822,10 +859,31 @@ struct VirtualMachine {
822859 } else if (string_caseInsensitiveMatch (command, U" Output" )) {
823860 this ->declareVariable (methods.length () - 1 , AccessType::Output, getArg (arguments, 0 ), getArg (arguments, 1 ), true , getArg (arguments, 2 ));
824861 } else if (string_caseInsensitiveMatch (command, U" End" )) {
862+ if (!this ->isInsideOfMethod ()) throwError (U" Can not end a method outside of methods!\n " );
825863 this ->addReturnInstruction ();
826864 } else if (string_caseInsensitiveMatch (command, U" Call" )) {
865+ if (!this ->isInsideOfMethod ()) throwError (U" Can not make calls outside of methods!\n " );
827866 this ->addCallInstructions (arguments);
867+ } else if (string_caseInsensitiveMatch (command, U" Label" )) {
868+ // TODO: Move this into a method for creating a label.
869+ // TODO: Prevent overlapping names between methods, local variables and local labels.
870+ // TODO: Resolve label identifiers into ArgumentType::InstructionAddress and assign the argument's index directly to the program counter when jumping.
871+ if (!this ->isInsideOfMethod ()) {
872+ throwError (U" Can not place labels outside of methods!\n " );
873+ } else if (arguments.length () < 1 ) {
874+ throwError (U" Labels need to be named to allow identifying the following machine instruction!\n " );
875+ } else if (arguments.length () > 1 ) {
876+ throwError (U" Labels may not have more than one argument containing the identifier!\n " );
877+ } else {
878+ int32_t targetAddress = this ->machineWords .length ();
879+ String labelIdentifier = arguments[0 ];
880+ // TODO: Reject invalid label names.
881+ debugText (U" Declaring label " , labelIdentifier, U" @ " , targetAddress, U" \n " );
882+ this ->methods .last ().labels .pushConstruct (targetAddress, labelIdentifier);
883+ }
828884 } else {
885+ // TODO: Delay resolving so that functions can be declared after calls to them and jumps can go to later labels.
886+ // If the command did not match with any of the generic instructions, resolve the arguments and look for a custom machine instruction.
829887 int32_t methodIndex = this ->methods .length () - 1 ;
830888 List<VMA> resolvedArguments;
831889 for (int32_t a = 0 ; a < arguments.length (); a++) {
0 commit comments