Sun, 08 Mar 2015 14:07:41 +0200
* Variables and functions can now be constants
* Mutating functions cannot be called for constants
--- 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> ¶ms); - 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"; }