diff --git a/include/CrossLang.hpp b/include/CrossLang.hpp index b060042..9cb522f 100644 --- a/include/CrossLang.hpp +++ b/include/CrossLang.hpp @@ -666,7 +666,8 @@ typedef enum { PUSHCONTINUE, JMPIFBREAK, JMPIFCONTINUE, - JMPIFDEFINED + JMPIFDEFINED, + DECLARECONSTVARIABLE } Instruction; /** * @brief Base type for bytecode instruction @@ -991,6 +992,12 @@ constexpr std::string_view DictionaryExpression = "dictionaryExpression"; * */ constexpr std::string_view DeclareExpression = "declareExpression"; +/** + * @brief const v = 59; + * + */ + +constexpr std::string_view ConstExpression = "constExpression"; /** * @brief Closure expression (a,b)=> a * b * @@ -1666,10 +1673,10 @@ class GC { virtual void Mark(); }; - + void ThrowConstError(std::string key); class TEnvironment : public THeapObject { - + std::vector consts; public: std::vector defers; TObject LoadFile(GC* gc, TFile* f); @@ -1687,6 +1694,9 @@ class GC { virtual void SetVariable(std::string key, TObject value)=0; TDictionary* EnsureDictionary(GC* gc, std::string key); virtual void DeclareVariable(std::string key, TObject value)=0; + void DeclareConstVariable(std::string key, TObject value); + bool HasConstForDeclare(std::string key); + virtual bool HasConstForSet(std::string key); void DeclareVariable(GC* gc,std::vector key, TObject value); virtual TRootEnvironment* GetRootEnvironment()=0; virtual TEnvironment* GetParentEnvironment()=0; @@ -1744,6 +1754,8 @@ class GC { TObject SetVariable(GCList& ls, std::string key, TObject v); void DeclareVariable(std::string key, TObject value); + bool HasConstForSet(std::string key); + TRootEnvironment* GetRootEnvironment(); TEnvironment* GetParentEnvironment(); @@ -1846,7 +1858,8 @@ class GC { bool HasVariable(std::string key); bool HasVariableRecurse(std::string key); bool HasVariableOrFieldRecurse(std::string key, bool setting=false); - + bool HasConstForSet(std::string key); + TObject GetVariable(std::string key); void SetVariable(std::string key, TObject value); TObject GetVariable(GCList& ls, std::string key); @@ -2226,6 +2239,7 @@ class GC { bool GetArray(GC* gc); bool SetArray(GC* gc); bool DeclareVariable(GC* gc); + bool DeclareConstVariable(GC* gc); bool PushLong(GC* gc); bool PushDouble(GC* gc); bool PushChar(GC* gc); diff --git a/src/assembler/asm.cpp b/src/assembler/asm.cpp index ab17e88..e5df266 100644 --- a/src/assembler/asm.cpp +++ b/src/assembler/asm.cpp @@ -451,6 +451,10 @@ namespace Tesses::CrossLang { { instrs.push_back(std::make_shared(DECLAREVARIABLE)); } + else if(name == "declareconstvariable") + { + instrs.push_back(std::make_shared(DECLARECONSTVARIABLE)); + } else if(name == "setfield") { instrs.push_back(std::make_shared(SETFIELD)); diff --git a/src/assembler/disasm.cpp b/src/assembler/disasm.cpp index 02280c6..a61582b 100644 --- a/src/assembler/disasm.cpp +++ b/src/assembler/disasm.cpp @@ -459,6 +459,9 @@ namespace Tesses::CrossLang { case DECLAREVARIABLE: buffer.append("declarevariable"); break; + case DECLARECONSTVARIABLE: + buffer.append("declareconstvariable"); + break; case SETFIELD: buffer.append("setfield"); break; diff --git a/src/compiler/codegen.cpp b/src/compiler/codegen.cpp index b8d008b..f1858d0 100644 --- a/src/compiler/codegen.cpp +++ b/src/compiler/codegen.cpp @@ -1257,6 +1257,29 @@ namespace Tesses::CrossLang } } + else if(varNode.nodeName == ConstExpression && varNode.nodes.size() == 1 && std::holds_alternative(varNode.nodes[0])) + { + GenNode(instructions,varNode.nodes[0],scope,contscope,brkscope,contI,brkI); + GenNode(instructions,adv.nodes[1],scope,contscope,brkscope,contI,brkI); + instructions.push_back(new SimpleInstruction(DECLARECONSTVARIABLE)); + } + else if(varNode.nodeName == ConstExpression && varNode.nodes.size() == 1 && std::holds_alternative(varNode.nodes[0])) + { + auto adv2 = std::get(varNode.nodes[0]); + + if(adv2.nodeName == ArrayExpression && adv2.nodes.size() == 1) + { + + auto vars= StringifyListOfVars(adv2.nodes[0]); + + auto nArray = AdvancedSyntaxNode::Create(ArrayExpression,true,{vars}); + + this->GenNode(instructions,nArray ,scope ,contscope ,brkscope ,contI , brkI); + GenNode(instructions,adv.nodes[1],scope,contscope,brkscope,contI,brkI); + instructions.push_back(new SimpleInstruction(DECLARECONSTVARIABLE)); + + } + } else if(varNode.nodeName == GetFieldExpression && varNode.nodes.size() == 2) { GenNode(instructions,varNode.nodes[0],scope,contscope,brkscope,contI,brkI); diff --git a/src/compiler/parser.cpp b/src/compiler/parser.cpp index dc4bf48..c7509d1 100644 --- a/src/compiler/parser.cpp +++ b/src/compiler/parser.cpp @@ -730,6 +730,33 @@ namespace Tesses::CrossLang node = AdvancedSyntaxNode::Create(DeclareExpression,true,{variable.text}); } } + else if(IsIdentifier("const")) + { + if(i >= tokens.size()) throw std::out_of_range("End of file"); + auto variable = tokens[i]; + + i++; + if(variable.type == LexTokenType::Symbol && variable.text == ".") + { + EnsureSymbol("["); + node = AdvancedSyntaxNode::Create(ConstExpression,true,{ + AdvancedSyntaxNode::Create(GetVariableExpression ,true,{ParseExpression()}) + }); + EnsureSymbol("]"); + } + else if(variable.type == LexTokenType::Symbol && variable.text == "[") + { + node = AdvancedSyntaxNode::Create(ConstExpression,true,{ + AdvancedSyntaxNode::Create(ArrayExpression ,true,{ParseExpression()}) + }); + EnsureSymbol("]"); + } + else if(variable.type != LexTokenType::Identifier) throw SyntaxException(variable.lineInfo, "Expected an identifier got a " + LexTokenType_ToString(variable.type) + " \"" + variable.text + "\""); + else + { + node = AdvancedSyntaxNode::Create(ConstExpression,true,{variable.text}); + } + } else if(IsIdentifier("comptime")) { SyntaxNode n = nullptr; diff --git a/src/types/classenvironment.cpp b/src/types/classenvironment.cpp index dac73da..2a2f666 100644 --- a/src/types/classenvironment.cpp +++ b/src/types/classenvironment.cpp @@ -1,6 +1,13 @@ #include "CrossLang.hpp" namespace Tesses::CrossLang { - + bool TClassEnvironment::HasConstForSet(std::string key) + { + if(this->env->HasVariableRecurse(key)) + { + return this->env->HasConstForSet(key); + } + return false; + } TClassEnvironment* TClassEnvironment::Create(GCList* gc,TEnvironment* env,TClassObject* obj) { @@ -95,6 +102,7 @@ namespace Tesses::CrossLang { if(GetObjectHeap(res,call)) return call->Call(ls,{v}); } if(this->clsObj->HasValue(clsName,key)) { this->clsObj->SetValue(clsName,key,v); return v;} + return this->env->SetVariable(ls,key,v); } diff --git a/src/types/rootenvironment.cpp b/src/types/rootenvironment.cpp index 19eccd8..9b2dfea 100644 --- a/src/types/rootenvironment.cpp +++ b/src/types/rootenvironment.cpp @@ -2,6 +2,27 @@ #include #include namespace Tesses::CrossLang { + void ThrowConstError(std::string key) + { + throw std::runtime_error("Cannot set \"" + key + "\" because it is a const"); + } + + void TEnvironment::DeclareConstVariable(std::string key, TObject value) + { + this->DeclareVariable(key,value); + this->consts.push_back(key); + } + bool TEnvironment::HasConstForDeclare(std::string key) + { + for(auto item : this->consts) + if(item == key) + return true; + return false; + } + bool TEnvironment::HasConstForSet(std::string key) + { + return HasConstForDeclare(key); + } bool TRootEnvironment::TryFindClass(std::vector& name, size_t& index) { for(size_t i = 0; i < this->classes.size(); i++) diff --git a/src/types/subenvironment.cpp b/src/types/subenvironment.cpp index 166657d..37d8c53 100644 --- a/src/types/subenvironment.cpp +++ b/src/types/subenvironment.cpp @@ -50,6 +50,18 @@ namespace Tesses::CrossLang { return Undefined(); } + bool TSubEnvironment::HasConstForSet(std::string key) + { + if(this->dict->HasValue(key)) + { + return this->HasConstForDeclare(key); + } + if(this->env->HasVariableRecurse(key)) + { + return this->env->HasConstForSet(key); + } + return false; + } TObject TSubEnvironment::SetVariable(GCList& ls, std::string key, TObject value) { ls.GetGC()->BarrierBegin(); diff --git a/src/vm/vm.cpp b/src/vm/vm.cpp index d696f7c..330033d 100644 --- a/src/vm/vm.cpp +++ b/src/vm/vm.cpp @@ -4390,19 +4390,53 @@ namespace Tesses::CrossLang { gc->BarrierBegin(); if(args.size() > 1 && GetArgument(args,0,key)) - env->SetVariable(key,args[1]); + { + if(env->HasConstForSet(key)) + { + gc->BarrierEnd(); + ThrowConstError(key); + } + + env->SetVariable(key,args[1]); + + } gc->BarrierEnd(); cse.back()->Push(gc,nullptr); return false; } - if(key == "DeclareVariable") { std::string key; gc->BarrierBegin(); if(args.size() > 1 && GetArgument(args,0,key)) - env->DeclareVariable(key,args[1]); + { + if(env->HasConstForDeclare(key)) + { + gc->BarrierEnd(); + ThrowConstError(key); + } + env->DeclareVariable(key,args[1]); + + } + gc->BarrierEnd(); + cse.back()->Push(gc,nullptr); + return false; + } + if(key == "DeclareConstVariable") + { + std::string key; + + gc->BarrierBegin(); + if(args.size() > 1 && GetArgument(args,0,key)) + { + if(env->HasConstForDeclare(key)) + { + gc->BarrierEnd(); + ThrowConstError(key); + } + env->DeclareConstVariable(key,args[1]); + } gc->BarrierEnd(); cse.back()->Push(gc,nullptr); return false; @@ -6048,6 +6082,12 @@ namespace Tesses::CrossLang { if(std::holds_alternative(key)) { gc->BarrierBegin(); + + if(stk->env->HasConstForSet(std::get(key))) + { + gc->BarrierEnd(); + ThrowConstError(std::get(key)); + } stk->Push(gc,stk->env->SetVariable(ls,std::get(key),value)); gc->BarrierEnd(); @@ -6072,6 +6112,12 @@ namespace Tesses::CrossLang { { auto val = valueLs->Get(i); result->SetValue(mkey, val); + + if(stk->env->HasConstForSet(mkey)) + { + gc->BarrierEnd(); + ThrowConstError(mkey); + } stk->env->SetVariable(ls,mkey,val); } } @@ -6093,6 +6139,12 @@ namespace Tesses::CrossLang { auto val = valueDynList->GetAt(ls,i); gc->BarrierBegin(); result->SetValue(mkey, val); + + if(stk->env->HasConstForSet(mkey)) + { + gc->BarrierEnd(); + ThrowConstError(mkey); + } stk->env->SetVariable(ls,mkey,val); } } @@ -6111,6 +6163,12 @@ namespace Tesses::CrossLang { { auto val = valueDict->GetValue(mkey); result->SetValue(mkey, val); + + if(stk->env->HasConstForSet(mkey)) + { + gc->BarrierEnd(); + ThrowConstError(mkey); + } stk->env->SetVariable(ls,mkey,val); } } @@ -6131,6 +6189,12 @@ namespace Tesses::CrossLang { gc->BarrierBegin(); result->SetValue(mkey, val); + + if(stk->env->HasConstForSet(mkey)) + { + gc->BarrierEnd(); + ThrowConstError(mkey); + } stk->env->SetVariable(ls,mkey,val); } } @@ -6144,6 +6208,12 @@ namespace Tesses::CrossLang { auto item = mls->Get(i); if(GetObject(item,mkey)) { + if(stk->env->HasConstForSet(mkey)) + { + gc->BarrierEnd(); + ThrowConstError(mkey); + } + stk->env->SetVariable(ls,mkey, value); } } @@ -6174,6 +6244,11 @@ namespace Tesses::CrossLang { if(std::holds_alternative(key)) { gc->BarrierBegin(); + if(stk->env->HasConstForDeclare(std::get(key))) + { + gc->BarrierEnd(); + ThrowConstError(std::get(key)); + } stk->env->DeclareVariable(std::get(key),value); stk->Push(gc, value); gc->BarrierEnd(); @@ -6198,6 +6273,11 @@ namespace Tesses::CrossLang { { auto val = valueLs->Get(i); result->SetValue(mkey, val); + if(stk->env->HasConstForDeclare(mkey)) + { + gc->BarrierEnd(); + ThrowConstError(mkey); + } stk->env->DeclareVariable(mkey,val); } } @@ -6219,6 +6299,11 @@ namespace Tesses::CrossLang { auto val = valueDynList->GetAt(ls,i); gc->BarrierBegin(); result->SetValue(mkey, val); + if(stk->env->HasConstForDeclare(mkey)) + { + gc->BarrierEnd(); + ThrowConstError(mkey); + } stk->env->DeclareVariable(mkey,val); } } @@ -6237,6 +6322,11 @@ namespace Tesses::CrossLang { { auto val = valueDict->GetValue(mkey); result->SetValue(mkey, val); + if(stk->env->HasConstForDeclare(mkey)) + { + gc->BarrierEnd(); + ThrowConstError(mkey); + } stk->env->DeclareVariable(mkey,val); } } @@ -6257,6 +6347,11 @@ namespace Tesses::CrossLang { gc->BarrierBegin(); result->SetValue(mkey, val); + if(stk->env->HasConstForDeclare(mkey)) + { + gc->BarrierEnd(); + ThrowConstError(mkey); + } stk->env->DeclareVariable(mkey,val); } } @@ -6270,6 +6365,11 @@ namespace Tesses::CrossLang { auto item = mls->Get(i); if(GetObject(item,mkey)) { + if(stk->env->HasConstForDeclare(mkey)) + { + gc->BarrierEnd(); + ThrowConstError(mkey); + } stk->env->DeclareVariable(mkey, value); } } @@ -6287,6 +6387,165 @@ namespace Tesses::CrossLang { } + bool InterperterThread::DeclareConstVariable(GC* gc) + { + std::vector& cse=this->call_stack_entries; + if(!cse.empty()) + { + auto stk = cse.back(); + GCList ls(gc); + + auto value = stk->Pop(ls); + auto key = stk->Pop(ls); + + TList* mls; + + if(std::holds_alternative(key)) + { + gc->BarrierBegin(); + if(stk->env->HasConstForDeclare(std::get(key))) + { + gc->BarrierEnd(); + ThrowConstError(std::get(key)); + } + stk->env->DeclareConstVariable(std::get(key),value); + stk->Push(gc, value); + gc->BarrierEnd(); + } + else if(GetObjectHeap(key,mls)) + { + gc->BarrierBegin(); + + TList* valueLs; + TDynamicList* valueDynList; + TDictionary* valueDict; + TDynamicDictionary* valueDynDict; + if(GetObjectHeap(value, valueLs)) + { + TDictionary* result = TDictionary::Create(ls); + int64_t len = std::min(valueLs->Count(), mls->Count()); + for(int64_t i = 0; i < len; i++) + { + std::string mkey; + auto item = mls->Get(i); + if(GetObject(item,mkey)) + { + auto val = valueLs->Get(i); + result->SetValue(mkey, val); + if(stk->env->HasConstForDeclare(mkey)) + { + gc->BarrierEnd(); + ThrowConstError(mkey); + } + stk->env->DeclareConstVariable(mkey,val); + } + } + stk->Push(gc,result); + } + else if(GetObjectHeap(value, valueDynList)) + { + TDictionary* result = TDictionary::Create(ls); + gc->BarrierEnd(); + int64_t len = std::min(valueDynList->Count(ls), mls->Count()); + gc->BarrierBegin(); + for(int64_t i = 0; i < len; i++) + { + std::string mkey; + auto item = mls->Get(i); + if(GetObject(item,mkey)) + { + gc->BarrierEnd(); + auto val = valueDynList->GetAt(ls,i); + gc->BarrierBegin(); + result->SetValue(mkey, val); + if(stk->env->HasConstForDeclare(mkey)) + { + gc->BarrierEnd(); + ThrowConstError(mkey); + } + stk->env->DeclareConstVariable(mkey,val); + } + } + stk->Push(gc,result); + } + else if(GetObjectHeap(value, valueDict)) + { + + TDictionary* result = TDictionary::Create(ls); + int64_t len = mls->Count(); + for(int64_t i = 0; i < len; i++) + { + std::string mkey; + auto item = mls->Get(i); + if(GetObject(item,mkey)) + { + auto val = valueDict->GetValue(mkey); + result->SetValue(mkey, val); + if(stk->env->HasConstForDeclare(mkey)) + { + gc->BarrierEnd(); + ThrowConstError(mkey); + } + stk->env->DeclareConstVariable(mkey,val); + } + } + stk->Push(gc,result); + } + else if(GetObjectHeap(value, valueDynDict)) + { + TDictionary* result = TDictionary::Create(ls); + int64_t len =mls->Count(); + for(int64_t i = 0; i < len; i++) + { + std::string mkey; + auto item = mls->Get(i); + if(GetObject(item,mkey)) + { + gc->BarrierEnd(); + auto val = valueDynDict->GetField(ls,mkey); + gc->BarrierBegin(); + + result->SetValue(mkey, val); + if(stk->env->HasConstForDeclare(mkey)) + { + gc->BarrierEnd(); + ThrowConstError(mkey); + } + stk->env->DeclareConstVariable(mkey,val); + } + } + stk->Push(gc,result); + } + else { + int64_t len =mls->Count(); + for(int64_t i = 0; i < len; i++) + { + std::string mkey; + auto item = mls->Get(i); + if(GetObject(item,mkey)) + { + if(stk->env->HasConstForDeclare(mkey)) + { + gc->BarrierEnd(); + ThrowConstError(mkey); + } + stk->env->DeclareConstVariable(mkey, value); + } + } + stk->Push(gc, value); + } + gc->BarrierEnd(); + } + else + { + + throw VMException("[DECLARECONSTVARIABLE] Can't pop string, got type " + GetObjectTypeString(key) + " = " + ToString(gc,key) + "."); + } + } + return false; + } + + bool InterperterThread::PushResource(GC* gc) { std::vector& cse=this->call_stack_entries; diff --git a/src/vm/vm_opcode_table.h b/src/vm/vm_opcode_table.h index 5beaa6a..9df75cb 100644 --- a/src/vm/vm_opcode_table.h +++ b/src/vm/vm_opcode_table.h @@ -63,7 +63,7 @@ static opcode opcodes[256]={ &InterperterThread::JumpIfBreak, &InterperterThread::JumpIfContinue, &InterperterThread::JumpIfDefined, - &InterperterThread::Illegal, + &InterperterThread::DeclareConstVariable, &InterperterThread::Illegal, &InterperterThread::Illegal, &InterperterThread::Illegal,