* Variables and functions can now be constants gscript

Sun, 08 Mar 2015 14:07:41 +0200

author
cemkalyoncu
date
Sun, 08 Mar 2015 14:07:41 +0200
branch
gscript
changeset 641
3b84f287340f
parent 640
f2a3c75324fa
child 642
1c840a774090

* Variables and functions can now be constants
* Mutating functions cannot be called for constants

Source/Scripting/Builtin.cpp file | annotate | diff | comparison | revisions
Source/Scripting/Data.cpp file | annotate | diff | comparison | revisions
Source/Scripting/Data.h file | annotate | diff | comparison | revisions
Source/Scripting/Exceptions.h file | annotate | diff | comparison | revisions
Source/Scripting/Reflection.h file | annotate | diff | comparison | revisions
Source/Scripting/VirtualMachine.cpp file | annotate | diff | comparison | revisions
Source/Scripting/VirtualMachine.h file | annotate | diff | comparison | revisions
--- a/Source/Scripting/Builtin.cpp	Sat Mar 07 19:45:49 2015 +0200
+++ b/Source/Scripting/Builtin.cpp	Sun Mar 08 14:07:41 2015 +0200
@@ -414,6 +414,21 @@
 						},
 						MappedFunctions(Return1, Return0), MappedMethods(),
 						KeywordTag
+					),
+					new MappedFunction("const",
+						"Makes a given variable a constant",
+						nullptr, nullptr,
+						ParameterList {
+							new Parameter(
+								"Variable",
+								"This is the variable to become constant",
+								String, ReferenceTag
+							)
+						},
+						MappedFunctions([](std::string varname) {
+							VirtualMachine::Get().GetVariable(varname).MakeConstant();
+						}), MappedMethods(),
+						KeywordTag
 					)
 				},
 			};
--- a/Source/Scripting/Data.cpp	Sat Mar 07 19:45:49 2015 +0200
+++ b/Source/Scripting/Data.cpp	Sun Mar 08 14:07:41 2015 +0200
@@ -11,6 +11,9 @@
 		type=other.type;
 		isreference=other.isreference;
 		
+		if(isreference)
+			isconstant=other.isconstant;
+		
 		
 		if(type && type->IsReferenceType() && data.Pointer() && VirtualMachine::Exists()) {
 			VirtualMachine::Get().References.Increase(*this);
@@ -20,7 +23,9 @@
 	Data::Data(Data &&other) {
 		data=other.data;
 		type=other.type;
+		
 		isreference=other.isreference;
+		isconstant=other.isconstant;
 		
 		other.data=Any();
 		other.type=nullptr;
@@ -34,7 +39,10 @@
 		}
 	}
 	
-	Data::Data(const Type *type, const Any &data, bool isreference) : type(type), data(data), isreference(isreference) {
+	Data::Data(const Type *type, const Any &data, bool isreference, bool isconstant) : 
+		type(type), data(data), 
+		isreference(isreference), isconstant(isconstant)
+	{
 		check();
 		ASSERT((type!=nullptr), "Data type cannot be nullptr", 1, 2);
 		
@@ -74,6 +82,9 @@
 		data=other.data;
 		isreference=other.isreference;
 		
+		if(isreference)
+			isconstant=other.isconstant;
+		
 		other.data=Any();
 		other.type=nullptr;
 		
@@ -98,8 +109,8 @@
 	
 	Data GetVariableValue(const std::string &varname) { throw 0; }
 	
-	Any Data::GetReference() {
-		ASSERT(!isreference, "Reference of a reference");
+	Data Data::GetReference() {
+		if(isreference) return *this;
 		
 		void *r=data.GetRaw();
 		void **p = new void*(r);
@@ -107,7 +118,7 @@
 		Any a={type->PtrTypeInterface, p};
 		delete p;
 		
-		return a;
+		return {type, a, true, isconstant};
 	}
 	
 } }
--- a/Source/Scripting/Data.h	Sat Mar 07 19:45:49 2015 +0200
+++ b/Source/Scripting/Data.h	Sun Mar 08 14:07:41 2015 +0200
@@ -32,10 +32,10 @@
 			Data(Data &&other);
 
 			/// Any constructor. Allows both data and type to be specified
-			Data(const Type *type, const Any &data, bool isreference=false);
+			Data(const Type *type, const Any &data, bool isreference=false, bool isconstant=false);
 
 			/// Any constructor. Allows both data and type to be specified
-			Data(const Type &type, const Any &data, bool isreference=false) : Data(&type, data, isreference) {
+			Data(const Type &type, const Any &data, bool isreference=false, bool isconstant=false) : Data(&type, data, isreference, isconstant) {
 			}
 			
 			/// Default value constructor. Value of the data is determined from the type
@@ -79,17 +79,28 @@
 				return data;
 			}
 			
-			Any GetReference();
+			Data GetReference();
 			
 			/// Returns if the data is in a valid state
 			bool IsValid() const {
 				return type != nullptr;
 			}
 			
+			/// Returns if this data contains a reference
 			bool IsReference() const {
 				return isreference;
 			}
 			
+			/// Returns if this data is constant
+			bool IsConstant() const {
+				return isconstant;
+			}
+			
+			/// Makes this data a constant
+			void MakeConstant() {
+				isconstant=true;
+			}
+			
 			/// Returns the type of the data
 			const Type &GetType() const {
 				ASSERT(type, "Type is not set", 1, 2);
@@ -108,8 +119,12 @@
 			///Type of the data
 			const Type *type = nullptr;
 			
+			/// Is a reference, data is a ptr to the original type
 			bool isreference = false;
 			
+			/// This data is a constant and should not be changed
+			bool isconstant = false;
+			
 			
 		private:
 			/// Constructs an invalid data. Performing any operation on this data might cause
--- a/Source/Scripting/Exceptions.h	Sat Mar 07 19:45:49 2015 +0200
+++ b/Source/Scripting/Exceptions.h	Sun Mar 08 14:07:41 2015 +0200
@@ -23,6 +23,7 @@
 			InstructionError,
 			MismatchedParenthesis,
 			UnexpectedToken,
+			Constant,
 		};
 		
 		DefineEnumStrings(ExceptionType,
@@ -38,7 +39,8 @@
 			{ExceptionType::CastError, "Cast error"},
 			{ExceptionType::InstructionError, "Instruction error"},
 			{ExceptionType::MismatchedParenthesis, "Mismatched paranthesis"},
-			{ExceptionType::UnexpectedToken, "Unexpected token"}
+			{ExceptionType::UnexpectedToken, "Unexpected token"},
+			{ExceptionType::Constant, "Identifier is constant"}
 		);
 		
 		enum class SymbolType {
@@ -221,7 +223,14 @@
 				this->details=details;
 			}
 		};
-
+		
+		class ConstantException : public Exception {
+		public:
+			explicit ConstantException(const std::string& identifier, const std::string &details="", long int linenumber = 0) :
+			Exception(ExceptionType::Constant, identifier+" is a constant", linenumber) {
+				this->details=details;
+			}
+		};
 		
 		/// This class contains information about a parse error. It is not intended to be
 		/// used as an exception.
--- a/Source/Scripting/Reflection.h	Sat Mar 07 19:45:49 2015 +0200
+++ b/Source/Scripting/Reflection.h	Sun Mar 08 14:07:41 2015 +0200
@@ -72,7 +72,10 @@
 			OperatorTag,
 			
 			/// Redirects all the code inside a scoped keyword to the keyword
-			RedirectTag
+			RedirectTag,
+			
+			/// Marks a parameter or a function constant
+			ConstTag,
 		};
 		
 		typedef std::vector<Any> OptionList;
@@ -93,6 +96,8 @@
 		 * **OutputTag**: This parameter becomes output only reference. Output only references can be
 		 * read after it is set first time. The function is free not to set the value of an output
 		 * reference, however, this may cause confusion to the user.
+		 * 
+		 * **ConstTag**: This parameter is a constant and its value cannot be changed.
 		 */
 		class Parameter {
 		public:
@@ -192,6 +197,10 @@
 						output=true;
 						input=false;
 						break;
+					case ConstTag:
+						constant=true;
+						break;
+						
 					default:
 						Utils::ASSERT_FALSE("Unknown tag");
 				}
@@ -207,6 +216,7 @@
 			bool reference = false;
 			bool input     = true;
 			bool output    = false;
+			bool constant  = false;
 		};
 		
 		using ParameterList = Containers::Collection<const Parameter>;
@@ -266,6 +276,9 @@
 		 * functions can be accessed from the type using scope resolution: [type]function
 		 * 
 		 * **OperatorTag**: Makes this function an operator. Operators could be symbols or regular identifiers.
+		 * 
+		 * **ConstTag**: Works only on member functions. This function becomes a constant, unable to change
+		 * the contents of this object.
 		 */
 		class Function {
 			friend class Type;
@@ -366,6 +379,11 @@
 			bool IsStatic() const {
 				return staticmember;
 			}
+			
+			/// Returns whether this function is a constant
+			bool IsConstant() const {
+				return constant;
+			}
 
 			/// Returns if this function is a member function of a type.
 			bool IsMember() const {
@@ -486,6 +504,9 @@
 						isoperator=true;
 						assert(!staticmember && "Cannot be static operator");
 						break;
+					case ConstTag:
+						constant=true;
+						break;
 					default:
 						ASSERT(false, "Unknown tag", 2, 16);
 				}
@@ -549,6 +570,9 @@
 			/// Makes this function an operator. All operators should be member functions
 			bool isoperator = false;
 			
+			/// Makes this function constant. Only works on member functions.
+			bool constant = false;
+			
 		private:
 			void init();
 			
--- a/Source/Scripting/VirtualMachine.cpp	Sat Mar 07 19:45:49 2015 +0200
+++ b/Source/Scripting/VirtualMachine.cpp	Sun Mar 08 14:07:41 2015 +0200
@@ -563,31 +563,50 @@
 			}
 		}
 		
-		Data VirtualMachine::getvalue(const Value &val) {
+		Data VirtualMachine::getvalue(const Value &val, bool reference) {
 			switch(val.Type) {
 				case ValueType::Literal:
+					if(reference) {
+						throw CastException("literal", "reference");
+					}
 					return val.Literal;
 					
 				case ValueType::Constant:
-					return FindConstant(val.Name).GetData();
+					if(reference) {
+						auto data=FindConstant(val.Name).GetData().GetReference();
+						data.MakeConstant();
+						return data;
+					}
+					else {
+						return FindConstant(val.Name).GetData();
+					}
 					
 				case ValueType::Temp: {
 					auto &data=temporaries[val.Result];
 					if(!data.IsValid()) {
 						throw std::runtime_error("Invalid temporary.");
 					}
-					return Data(data.GetType(), data.GetReference(), true);
+					if(reference)
+						return data.GetReference();
+					else
+						return data;
 				}
 				
 				case ValueType::Variable: {
 					auto &var=GetVariable(val.Name);
-					return Data(var.GetType(), var.GetReference(), true);
+					if(reference)
+						return var.GetReference();
+					else
+						return var;
 				}
 				case ValueType::Identifier:
 					
 					if(IsVariableSet(val.Name)) {
 						auto &var=GetVariable(val.Name);
-						return Data(var.GetType(), var.GetReference(), true);
+						if(reference)
+							return var.GetReference();
+						else
+							return var;
 					}
 					else {
 						int namespcs=std::count(val.Name.begin(), val.Name.end(), ':');
@@ -599,7 +618,15 @@
 							
 							auto &type=Libraries[lib].Types[typ];
 							if(type.Constants.Exists(name)) {
-								return type.Constants[name].GetData();
+								if(reference) {
+									auto data=type.Constants[name].GetData().GetReference();
+									data.MakeConstant();
+									
+									return data;
+								}
+								else {
+									return type.Constants[name].GetData();
+								}
 							}
 							else if(type.Functions.Exists(name)) {
 								Utils::NotImplemented("Function objects");
@@ -614,7 +641,15 @@
 							
 							auto &lib=Libraries[libname];
 							if(lib.Constants.Exists(name)) {
-								return lib.Constants[name].GetData();
+								if(reference) {
+									auto data=lib.Constants[name].GetData().GetReference();
+									data.MakeConstant();
+									
+									return data;
+								}
+								else {
+									return lib.Constants[name].GetData();
+								}
 							}
 							else if(lib.Functions.Exists(name)) {
 								Utils::NotImplemented("Function objects");
@@ -629,7 +664,15 @@
 								}
 								else {
 									if(range.first->second->Constants.Exists(name)) {
-										return range.first->second->Constants[name].GetData();
+										if(reference) {
+											auto data=range.first->second->Constants[name].GetData().GetReference();
+											data.MakeConstant();
+											
+											return data;
+										}
+										else {
+											return range.first->second->Constants[name].GetData();
+										}
 									}
 									else if(range.first->second->Functions.Exists(name)) {
 										Utils::NotImplemented("Function objects");
@@ -650,7 +693,15 @@
 							}
 							else {
 								if(range.first->second.type==SymbolType::Constant) {
-									return range.first->second.object.Get<const Constant*>()->GetData();
+									if(reference) {
+										auto data=range.first->second.object.Get<const Constant*>()->GetData().GetReference();
+										data.MakeConstant();
+										
+										return data;
+									}
+									else {
+										return range.first->second.object.Get<const Constant*>()->GetData();
+									}
 								}
 								else if(range.first->second.type==SymbolType::Function) {
 									Utils::NotImplemented("Function objects");
@@ -704,10 +755,12 @@
 			
 			std::vector<Data> params;
 			
-			if(fn->HasParent()) {
+			if(fn->HasParent() && !fn->IsStatic()) {
 				if(pin!=incomingparams.end()) {
-					Data param=getvalue(*pin);
+					// guarantees that the param is a reference
+					Data param=getvalue(*pin, true);
 					
+					// check for nullness
 					if(fn->GetParent().IsReferenceType()) {
 						if(param.IsNull()) {
 							if(pin->Type==ValueType::Variable) {
@@ -718,6 +771,13 @@
 							}
 						}
 					}
+					//else ok
+					
+					// check constantness
+					if(!fn->IsConstant() && param.IsConstant()) {
+						throw ConstantException(pin->Name, "While calling member function: "+fn->GetName());
+					}
+					//else ok
 					
 					if(param.GetType()==fn->GetParent()) {
 						//no worries
@@ -738,7 +798,7 @@
 			int ind=1;
 			for(const auto &pdef : fn->Parameters) {
 				if(pdef.IsReference()) {
-					if(pin->Type==ValueType::Variable) {
+					if(pin->Type==ValueType::Variable || pin->Type==ValueType::Identifier) {
 						if(pdef.IsInput()) {
 							GetVariable(pin->Name);
 						}
@@ -766,12 +826,13 @@
 					
 					params.push_back(param);
 				}
-				else {
+				else { // no value is given for the parameter
 					if(!pdef.IsOptional()) {
 						throw MissingParameterException(pdef.GetName(), ind, pdef.GetType().GetName(),
 														"Parameter "+pdef.GetName()+" is not optional."
 						);
 					}
+					//else ok, it was optional
 					
 					break;
 				}
@@ -796,7 +857,7 @@
 												 "Too many parameters supplied for "+fn->GetName()
 				);
 			}
-			//else ok
+			//else no additional parameters at the end
 			
 			return fn->Call(method, params);
 		}
@@ -857,11 +918,11 @@
 			}
 			else {
 				if(inst->Parameters.size()==0) {
-					throw std::runtime_error("Invalid intermediate instruction, missing this parameter");
+					throw std::runtime_error("Invalid instruction, missing this parameter");
 				}
 				
 				//search in the type of the first parameter
-				Data data=getvalue(inst->Parameters[0]);
+				Data data=getvalue(inst->Parameters[0], true);
 
 				if(functionname=="{}") { //constructor
 					if(data.GetType()==Types::String()) {
@@ -870,6 +931,7 @@
 					else if(data.GetType()!=Reflection.Types["Type"]) {
 						throw std::runtime_error("Invalid intermediate instruction, type parameter is not a type");
 					}
+					//else Reflection:Type typed variable/temporary
 					
 					std::vector<Data> params;
 					int i=0;
@@ -877,12 +939,16 @@
 						if(i++!=0) {
 							params.push_back(getvalue(pin));
 						}
+						//else ignore the first
 					}
 					
+					//call the constructor
 					Data ret=data.GetValue<const Type*>()->Construct(params);
+					
 					if(inst->Store) {
 						temporaries[inst->Store]=ret;
 					}
+					//else do not store the created object, maybe a warning is necessary
 					
 					return;
 				}				
@@ -899,9 +965,16 @@
 								ret=ret.GetValue<Data>();
 							}
 							temporaries[inst->Store]=ret;
+							if(data.IsConstant())
+								temporaries[inst->Store].MakeConstant();;
 						}
 					}
 					else if(inst->Parameters.size()==2) {
+						if(data.IsConstant()) {
+							throw ConstantException(inst->Parameters[0].Name, "While setting member: "+functionname.substr(1));
+						}
+						//else ok
+						
 						if(inst->Store) {
 							throw NoReturnException("Data member: "+data.GetType().DataMembers[functionname.substr(1)].GetName());
 						}
@@ -923,6 +996,7 @@
 							ASSERT(false, "Cannot assign to literal or constants");
 						}						
 						else {
+							Utils::ASSERT_FALSE("Unknown value type");
 						}						
 					}
 					else {
@@ -948,6 +1022,7 @@
 			// call it
 			Data ret=callfunction(fn, method, inst->Parameters);
 
+			// if scoped keyword
 			if(fn->IsScoped() && ret.IsValid() && ret.GetValue<Data>().IsValid() ) {
 				auto scope=new KeywordScope{*fn, ret.GetValue<Data>(), executionscopes.Last()->GetSource().GetPhysicalLine()};
 				keywordscopes.Add(scope);
@@ -962,12 +1037,15 @@
 			if(inst->Store) {
 				if(!fn->IsScoped() && fn->HasReturnType()) {
 					//store the result
+					
+					//fix variants
 					if(ret.GetType()==Types::Variant()) {
 						ret=ret.GetValue<Data>();
 					}
 					temporaries[inst->Store]=ret;
 				} 
 				else {
+					//if requested but function does not return a value
 					throw NoReturnException(functionname);
 				}
 			}
--- a/Source/Scripting/VirtualMachine.h	Sat Mar 07 19:45:49 2015 +0200
+++ b/Source/Scripting/VirtualMachine.h	Sun Mar 08 14:07:41 2015 +0200
@@ -286,7 +286,7 @@
 		private:
 			void execute(const Instruction* inst);
 			Data callfunction(const Function *fn, bool method, const std::vector<Value> &params);
-			Data getvalue(const Value &val);
+			Data getvalue(const Value &val, bool reference=false);
 			void functioncall(const Instruction *inst, bool memberonly, bool method);
 			std::thread::id getthread() const { throw "this should never be called"; }
 

mercurial