diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d1cd9d..56c0d74 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ option(CROSSLANG_ENABLE_SQLITE "Enable sqlite (Embedded database, supports Wii)" option(CROSSLANG_ENABLE_PROCESS "Enable process" ON) option(CROSSLANG_ENABLE_TERMIOS "Enable termios (For changing terminal options)" ON) option(CROSSLANG_ENABLE_PLATFORM_FOLDERS "Enable platform folders" ON) +option(CROSSLANG_ENABLE_TIME "Enable Time" ON) option(CROSSLANG_SHARED_EXECUTABLES "Link with libcrosslang_shared" ON) option(CROSSLANG_FETCHCONTENT "Use fetchcontent" ON) option(CROSSLANG_ENABLE_CONFIG_ENVVAR "Allow setting config directory via the environment variable CROSSLANG_CONFIG" ON) @@ -51,6 +52,9 @@ else() find_package(TessesFramework REQUIRED) endif() function(CROSSLANG_LINK_DEPS CROSSLANG_TARGET_NAME) +if(CROSSLANG_ENABLE_TIME) +target_compile_definitions(${CROSSLANG_TARGET_NAME} PUBLIC CROSSLANG_ENABLE_TIME) +endif() if(CROSSLANG_ENABLE_CONFIG_ENVVAR) target_compile_definitions(${CROSSLANG_TARGET_NAME} PUBLIC CROSSLANG_ENABLE_CONFIG_ENVVAR) endif() diff --git a/include/CrossLang.hpp b/include/CrossLang.hpp index bf78afc..01ea972 100644 --- a/include/CrossLang.hpp +++ b/include/CrossLang.hpp @@ -116,6 +116,14 @@ namespace Tesses::CrossLang { { this->SetVersion(versionData[0],versionData[1],versionData[2],versionData[3],versionData[4]); } + void SetFromLong(uint64_t v) + { + + this->major = (uint8_t)((v >> 32) & 0xFF); + this->minor = (uint8_t)(v >> 24) & 0xFF; + this->patch = (uint8_t)(v >> 16) & 0xFF; + this->build = (uint16_t)(v & 0xFFFF); + } void ToArray(uint8_t* versionData) { versionData[0] = major; @@ -139,6 +147,10 @@ namespace Tesses::CrossLang { this->patch=0; this->SetBuild(0, TVMVersionStage::DevVersion); } + TVMVersion(uint64_t v) + { + this->SetFromLong(v); + } int CompareTo(TVMVersion& version) { if(this->major > version.major) return 1; @@ -315,6 +327,7 @@ typedef enum { LexTokenLineInfo lineInfo; LexTokenType type; std::string text; + std::string whiteSpaceCharsBefore; }; int Lex(std::string filename, std::istream& strm, std::vector& tokens); @@ -538,7 +551,7 @@ class CodeGen { ~CodeGen(); }; - +constexpr std::string_view HtmlRootExpression = "htmlRootExpression"; constexpr std::string_view EmbedExpression = "embedExpression"; constexpr std::string_view NegativeExpression = "negativeExpression"; constexpr std::string_view NotExpression = "notExpression"; @@ -628,6 +641,8 @@ SyntaxNode Deserialize(std::string astData); std::string Serialize(SyntaxNode node); class Parser { + uint32_t id; + uint32_t NewId(); uint64_t i; std::vector tokens; @@ -654,6 +669,7 @@ class Parser { SyntaxNode ParseFactor(); SyntaxNode ParseValue(); SyntaxNode ParseUnary(); + void ParseHtml(std::vector& nodes,std::string var); public: Parser(std::vector tokens); SyntaxNode ParseRoot() diff --git a/src/compiler/codegen.cpp b/src/compiler/codegen.cpp index 28e0b4d..638ba48 100644 --- a/src/compiler/codegen.cpp +++ b/src/compiler/codegen.cpp @@ -1075,14 +1075,36 @@ namespace Tesses::CrossLang instructions.push_back(new EmbedInstruction(GetResource(filename))); } + else if(adv.nodeName == HtmlRootExpression) + { + scope++; + instructions.push_back(new SimpleInstruction(SCOPEBEGIN)); + + instructions.push_back(new StringInstruction(GetString(std::get(adv.nodes[0])))); + instructions.push_back(new StringInstruction(GetString(""))); + instructions.push_back(new SimpleInstruction(DECLAREVARIABLE)); + instructions.push_back(new SimpleInstruction(POP)); + + for(size_t i = 1; i < adv.nodes.size(); i++) + { + GenNode(instructions,adv.nodes[i],scope,contscope,brkscope,contI,brkI); + GenPop(instructions,adv.nodes[i]); + } + + instructions.push_back(new StringInstruction(GetString(std::get(adv.nodes[0])))); + instructions.push_back(new SimpleInstruction(GETVARIABLE)); + instructions.push_back(new SimpleInstruction(SCOPEEND)); + scope--; + } else if(adv.nodeName == ScopeNode) { scope++; instructions.push_back(new SimpleInstruction(SCOPEBEGIN)); - for(auto item : adv.nodes) + for(size_t i = 0; i < adv.nodes.size(); i++) { - GenNode(instructions,item,scope,contscope,brkscope,contI,brkI); - GenPop(instructions,item); + GenNode(instructions,adv.nodes[i],scope,contscope,brkscope,contI,brkI); + if(!adv.isExpression || i < adv.nodes.size()-1) + GenPop(instructions,adv.nodes[i]); } instructions.push_back(new SimpleInstruction(SCOPEEND)); scope--; @@ -1419,8 +1441,9 @@ namespace Tesses::CrossLang void CodeGen::GenPop(std::vector& instrs,SyntaxNode n) { - if(std::holds_alternative(n) && std::get(n).isExpression) + if(std::holds_alternative(n)) { + if(std::get(n).isExpression) instrs.push_back(new SimpleInstruction(POP)); } else diff --git a/src/compiler/lexer.cpp b/src/compiler/lexer.cpp index 377b1f6..fb63cd9 100644 --- a/src/compiler/lexer.cpp +++ b/src/compiler/lexer.cpp @@ -140,33 +140,40 @@ namespace Tesses::CrossLang lineInfo.line = 1; lineInfo.offset = 0; + std::string whiteSpaceCharsBefore=""; - - auto Flush = [&buffer,&tokens,&lineInfo]() -> void { + auto Flush = [&buffer,&tokens,&lineInfo,&whiteSpaceCharsBefore]() -> void { if(!buffer.empty()) { LexToken token; token.text = buffer; + token.whiteSpaceCharsBefore = whiteSpaceCharsBefore; token.type = LexTokenType::Identifier; token.lineInfo = lineInfo; token.lineInfo.Subtract(buffer.size()); tokens.push_back(token); buffer.clear(); + + whiteSpaceCharsBefore=""; } }; - auto Symbol = [&tokens,&lineInfo](std::initializer_list chrs)-> void { + auto Symbol = [&tokens,&lineInfo,&whiteSpaceCharsBefore](std::initializer_list chrs)-> void { LexToken token; token.type = LexTokenType::Symbol; token.lineInfo = lineInfo; + token.whiteSpaceCharsBefore=whiteSpaceCharsBefore; + token.text.reserve(chrs.size()); for(auto i : chrs) token.text.push_back((char)i); tokens.push_back(token); + + whiteSpaceCharsBefore=""; }; auto ReadChr = [&lineInfo, &strm, Read]() -> std::pair { @@ -436,8 +443,9 @@ namespace Tesses::CrossLang while((read = Read()) != -1) { - + peek = Peek(); + switch(read) { @@ -671,6 +679,7 @@ namespace Tesses::CrossLang case '\r': case ' ': Flush(); + whiteSpaceCharsBefore += read; break; default: diff --git a/src/compiler/parser.cpp b/src/compiler/parser.cpp index 16b7395..f740ed7 100644 --- a/src/compiler/parser.cpp +++ b/src/compiler/parser.cpp @@ -108,6 +108,422 @@ namespace Tesses::CrossLang this->i = 0; this->tokens = tokens; } + void Parser::ParseHtml(std::vector& nodes,std::string var) + { + if(this->IsSymbol("!",true)) + { + if(this->i < this->tokens.size() && this->tokens[this->i].type == LexTokenType::Identifier) + { + std::string identifier = this->tokens[this->i++].text; + + if(identifier == "DOCTYPE") + { + if(this->i < this->tokens.size() && this->tokens[this->i].type == LexTokenType::Identifier) + { + std::string doctype_secArg = this->tokens[this->i++].text; + std::string r = ""; + + nodes.push_back(AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(AddExpression,true,{AdvancedSyntaxNode::Create(GetVariableExpression,true,{var}),r})})); + this->EnsureSymbol(">"); + if(this->IsSymbol("<")) + { + ParseHtml(nodes,var); + } + } + } + + } + } + else + { + auto parseFn = [this,var](std::vector& nodes,std::string tagName)->void{ + while(this->i < this->tokens.size()) + { + if(this->IsSymbol("<",false)) + { + nodes.push_back(AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(AddExpression,true,{AdvancedSyntaxNode::Create(GetVariableExpression,true,{var}),Tesses::Framework::Http::HttpUtils::HtmlEncode(this->tokens[i].whiteSpaceCharsBefore)})})); + + this->i++; + if(this->IsIdentifier("else",false) || this->IsIdentifier("elif",false)) + { + this->i--; + return; + } + + if(this->IsSymbol("/")) + { + if(!this->IsIdentifier(tagName)) + { + //error + + } + this->EnsureSymbol(">"); + if(tagName != "if" && tagName != "for" && tagName != "while" && tagName != "do" && tagName != "each") + { + std::string myVal = ""; + nodes.push_back(AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(AddExpression,true,{AdvancedSyntaxNode::Create(GetVariableExpression,true,{var}),myVal})})); + } + break; + } + else + { + ParseHtml(nodes,var); + } + } + else if(this->IsSymbol("{",false)) + { + nodes.push_back(AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(AddExpression,true,{AdvancedSyntaxNode::Create(GetVariableExpression,true,{var}),Tesses::Framework::Http::HttpUtils::HtmlEncode(this->tokens[i].whiteSpaceCharsBefore)})})); + this->i++; + + auto expr = ParseExpression(); + + this->EnsureSymbol("}"); + + //Net.Http.HtmlEncode(expr.ToString()) + + nodes.push_back(AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{ + AdvancedSyntaxNode::Create(AddExpression,true,{ + AdvancedSyntaxNode::Create(GetVariableExpression,true,{var}) + , + AdvancedSyntaxNode::Create(FunctionCallExpression,true,{ + AdvancedSyntaxNode::Create(GetFieldExpression,true,{ + AdvancedSyntaxNode::Create(GetFieldExpression,true, { + AdvancedSyntaxNode::Create(GetVariableExpression,true, { + "Net" + }), + "Http" + }), + "HtmlEncode" + }), + AdvancedSyntaxNode::Create(FunctionCallExpression,true,{ + AdvancedSyntaxNode::Create(GetFieldExpression,true,{ + AdvancedSyntaxNode::Create(ParenthesesExpression,true,{expr}) + , + "ToString" + }) + }) + }) + + + }) + })); + + } + else + { + std::string str = ""; + while(this->i < this->tokens.size() && !this->IsSymbol("{",false) && !this->IsSymbol("<",false)) + { + str+=this->tokens[this->i].whiteSpaceCharsBefore + this->tokens[this->i].text; + this->i++; + } + + nodes.push_back(AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(AddExpression,true,{AdvancedSyntaxNode::Create(GetVariableExpression,true,{var}),Tesses::Framework::Http::HttpUtils::HtmlEncode(str)})})); + + } + } + + }; + + if(this->i < this->tokens.size() && this->tokens[this->i].type == LexTokenType::Identifier) + { + std::string tagName = this->tokens[this->i++].text; + + + if(tagName == "if") + { + std::function readIf; + readIf = [this,&parseFn,var,&readIf]()->SyntaxNode { + EnsureSymbol("("); + auto expr = ParseExpression(); + EnsureSymbol(")"); + EnsureSymbol(">"); + + std::vector trueNodes; + + SyntaxNode falseNode=nullptr; + + parseFn(trueNodes,"if"); + + if(this->IsSymbol("<",false) && this->i + 1 < this->tokens.size()) + { + auto tkn = this->tokens[i+1]; + if(tkn.type == LexTokenType::Identifier && tkn.text == "else") + { + i += 2; + EnsureSymbol(">"); + std::vector falseNodes; + parseFn(falseNodes,"if"); + falseNode = AdvancedSyntaxNode::Create(ScopeNode,false,falseNodes); + } + else if(tkn.type == LexTokenType::Identifier && tkn.text == "elif") + { + + i += 2; + falseNode = readIf(); + + } + } + + + return AdvancedSyntaxNode::Create(IfStatement,false,{ + expr, + AdvancedSyntaxNode::Create(ScopeNode,false,trueNodes), + falseNode + }); + } ; + nodes.push_back(readIf()); + } + else if(tagName == "raw") + { + EnsureSymbol("("); + SyntaxNode expr = ParseExpression(); + + EnsureSymbol(")"); + EnsureSymbol(">"); + + nodes.push_back(AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{ + AdvancedSyntaxNode::Create(AddExpression,true,{ + AdvancedSyntaxNode::Create(GetVariableExpression,true,{var}), + AdvancedSyntaxNode::Create(FunctionCallExpression,true,{ + AdvancedSyntaxNode::Create(GetFieldExpression,true,{ + expr + , + "ToString" + }) + }) + }) + })); + } + else if(tagName == "while") + { + + EnsureSymbol("("); + SyntaxNode expr = ParseExpression(); + SyntaxNode body = nullptr; + + EnsureSymbol(")"); + EnsureSymbol(">"); + + + std::vector _nodes; + parseFn(_nodes,"while"); + body = AdvancedSyntaxNode::Create(ScopeNode,false,_nodes); + + nodes.push_back(AdvancedSyntaxNode::Create(WhileStatement,false,{expr,body})); + + } + else if(tagName == "do") + { + EnsureSymbol("("); + SyntaxNode expr = ParseExpression(); + SyntaxNode body = nullptr; + + EnsureSymbol(")"); + EnsureSymbol(">"); + + std::vector _nodes; + parseFn(_nodes,"do"); + body = AdvancedSyntaxNode::Create(ScopeNode,false,_nodes); + + nodes.push_back(AdvancedSyntaxNode::Create(DoStatement,false,{expr,body})); + } + else if(tagName == "for") + { + SyntaxNode init = nullptr; + SyntaxNode cond = true; + SyntaxNode inc = nullptr; + SyntaxNode body = nullptr; + EnsureSymbol("("); + if(!IsSymbol(";",false)) + { + init = ParseExpression(); + } + EnsureSymbol(";"); + if(!IsSymbol(";",false)) + { + cond = ParseExpression(); + } + EnsureSymbol(";"); + if(!IsSymbol(")",false)) + { + inc = ParseExpression(); + } + + EnsureSymbol(")"); + EnsureSymbol(">"); + std::vector _nodes; + parseFn(_nodes,"for"); + body = AdvancedSyntaxNode::Create(ScopeNode,false,_nodes); + + nodes.push_back(AdvancedSyntaxNode::Create(ForStatement,false,{init,cond,inc,body})); + + + } + else if(tagName == "each") + { + SyntaxNode item = nullptr; + EnsureSymbol("("); + SyntaxNode list = ParseExpression(); + SyntaxNode body = nullptr; + if(IsSymbol(":")) + { + item = list; + list = ParseExpression(); + } + EnsureSymbol(")"); + EnsureSymbol(">"); + + std::vector _nodes; + parseFn(_nodes,"each"); + body = AdvancedSyntaxNode::Create(ScopeNode,false,_nodes); + + nodes.push_back(AdvancedSyntaxNode::Create(EachStatement,false,{item,list,body})); + } + else + { + std::string s = "<"; + s.append(tagName); + + nodes.push_back(AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(AddExpression,true,{AdvancedSyntaxNode::Create(GetVariableExpression,true,{var}),s})})); + + while(!this->IsSymbol(">")) + { + //we need to get a name=value + if(this->i < this->tokens.size() && this->tokens[this->i].type == LexTokenType::Identifier) + { + std::string key = ""; + + while(this->i < this->tokens.size()) + { + if(this->tokens[i].type == Identifier) + { + key += this->tokens[i].text; + i++; + } + else if(this->tokens[i].type == Symbol) + { + if(this->tokens[i].text == "-" || this->tokens[i].text == ":" || this->tokens[i].text == "--" || tokens[i].text == ".") + { + key += this->tokens[i].text; + i++; + } + else break; + } + else break; + } + + if(this->IsSymbol(">")) + { + std::string myVal = " " + key; + + + nodes.push_back(AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(AddExpression,true,{AdvancedSyntaxNode::Create(GetVariableExpression,true,{var}),myVal})})); + + break; + } + else if(this->IsSymbol("=")) + { + + std::string myVal = " " + key + "=\""; + + + + EnsureSymbol("{"); + auto expr = ParseExpression(); + EnsureSymbol("}"); + if(std::holds_alternative(expr)) + { + nodes.push_back(AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{ + AdvancedSyntaxNode::Create(AddExpression,true,{ + AdvancedSyntaxNode::Create(GetVariableExpression,true,{var}), + AdvancedSyntaxNode::Create(AddExpression,true,{ + myVal, + AdvancedSyntaxNode::Create(AddExpression,true,{ + AdvancedSyntaxNode::Create(FunctionCallExpression,true,{ + AdvancedSyntaxNode::Create(GetFieldExpression,true,{ + AdvancedSyntaxNode::Create(GetFieldExpression,true, { + AdvancedSyntaxNode::Create(GetVariableExpression,true, { + "Net" + }), + "Http" + }), + "HtmlEncode" + }), + AdvancedSyntaxNode::Create(FunctionCallExpression,true,{ + AdvancedSyntaxNode::Create(GetFieldExpression,true,{ + expr + , + "ToString" + }) + }) + }), + "\"", + }) + + }) + }) + })); + + + + } + else if(std::holds_alternative(expr)) + { + myVal += std::to_string(std::get(expr)) + "\""; + + nodes.push_back(AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(AddExpression,true,{AdvancedSyntaxNode::Create(GetVariableExpression,true,{var}),myVal})})); + } + else if(std::holds_alternative(expr)) + { + myVal += std::to_string(std::get(expr)) + "\""; + + nodes.push_back(AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(AddExpression,true,{AdvancedSyntaxNode::Create(GetVariableExpression,true,{var}),myVal})})); + } + else if(std::holds_alternative(expr)) + { + myVal += std::holds_alternative(expr) ? "true\"" : "false\""; + + + nodes.push_back(AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(AddExpression,true,{AdvancedSyntaxNode::Create(GetVariableExpression,true,{var}),myVal})})); + } + else if(std::holds_alternative(expr)) + { + myVal += Tesses::Framework::Http::HttpUtils::HtmlEncode(std::get(expr)) + "\""; + nodes.push_back(AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(AddExpression,true,{AdvancedSyntaxNode::Create(GetVariableExpression,true,{var}),myVal})})); + } + //key = value + + + + } + else { + + std::string myVal = " " + key; + + + nodes.push_back(AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(AddExpression,true,{AdvancedSyntaxNode::Create(GetVariableExpression,true,{var}),myVal})})); + + } + } + } + + nodes.push_back(AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{AdvancedSyntaxNode::Create(AddExpression,true,{AdvancedSyntaxNode::Create(GetVariableExpression,true,{var}),">"})})); + + if(tagName != "img" && tagName != "input" && tagName != "br" && tagName != "hr" && tagName != "link" && tagName != "meta") + { + parseFn(nodes,tagName); + } + } + } + } + } + uint32_t Parser::NewId() + { + return id++; + } SyntaxNode Parser::ParseValue() { if(i >= tokens.size()) throw std::out_of_range("End of file"); @@ -125,6 +541,18 @@ namespace Tesses::CrossLang i++; } + else if(IsSymbol("<")) + { + uint32_t htmlId = NewId(); + std::string compHtml = "__compGenHtml"; + compHtml.append(std::to_string(htmlId)); + std::vector syntaxNode = { + compHtml + }; + ParseHtml(syntaxNode,compHtml); + + return AdvancedSyntaxNode::Create(HtmlRootExpression,true,syntaxNode); + } else if(IsSymbol("[")) { if(IsSymbol("]",false)) diff --git a/src/crosslang.cpp b/src/crosslang.cpp index 1d1a308..7dcdc39 100644 --- a/src/crosslang.cpp +++ b/src/crosslang.cpp @@ -1,7 +1,6 @@ #include "CrossLang.hpp" #include -#include "sago/platform_folders.h" -#include + #include using namespace Tesses::Framework; using namespace Tesses::CrossLang; diff --git a/src/runtime_methods/console.cpp b/src/runtime_methods/console.cpp index 0ca69a8..7d902a9 100644 --- a/src/runtime_methods/console.cpp +++ b/src/runtime_methods/console.cpp @@ -161,6 +161,38 @@ namespace Tesses::CrossLang { std::cout << ToString(ls.GetGC(),args[0]) << "\n"; return Undefined(); } + TObject Console_Error(GCList& ls, std::vector args) + { + if(args.size() < 1) + { + return Undefined(); + } + std::cerr << ToString(ls.GetGC(),args[0]); + return Undefined(); + } + TObject Console_ErrorLine(GCList& ls, std::vector args) + { + if(args.size() < 1) + { + std::cout << "\n"; + return Undefined(); + } + std::cerr << ToString(ls.GetGC(),args[0]) << "\n"; + return Undefined(); + } + TObject Console_getIn(GCList& ls, std::vector args) + { + return TStreamHeapObject::Create(ls, new Tesses::Framework::Streams::FileStream(stdin,false,"r",false)); + } + + TObject Console_getOut(GCList& ls, std::vector args) + { + return TStreamHeapObject::Create(ls, new Tesses::Framework::Streams::FileStream(stdout,false,"w",false)); + } + TObject Console_getError(GCList& ls, std::vector args) + { + return TStreamHeapObject::Create(ls, new Tesses::Framework::Streams::FileStream(stderr,false,"w",false)); + } void TStd::RegisterConsole(GC* gc,TRootEnvironment* env) { env->permissions.canRegisterConsole=true; @@ -181,8 +213,12 @@ namespace Tesses::CrossLang { dict->DeclareFunction(gc,"ReadLine","Reads line from stdin",{},Console_ReadLine); dict->DeclareFunction(gc,"Write","Write text \"text\" to stdout",{"text"},Console_Write); dict->DeclareFunction(gc,"WriteLine","Write text \"text\" to stdout with new line",{"text"},Console_WriteLine); + dict->DeclareFunction(gc,"Error", "Write text \"error\" to stderr",{"error"},Console_Error); + dict->DeclareFunction(gc,"ErrorLine","Write text \"error\" to stderr",{"error"},Console_ErrorLine); dict->DeclareFunction(gc,"Fatal","Stop the program with an optional error message",{"$text"},Console_Fatal); - + dict->DeclareFunction(gc,"getIn","Get stdin Stream",{},Console_getIn); + dict->DeclareFunction(gc,"getOut","Get stdout Stream",{},Console_getOut); + dict->DeclareFunction(gc,"getError", "Get stderr Stream",{},Console_getError); gc->BarrierBegin(); env->DeclareVariable("Console", dict); gc->BarrierEnd(); diff --git a/src/runtime_methods/crypto.cpp b/src/runtime_methods/crypto.cpp index 4ff1af6..1025fdc 100644 --- a/src/runtime_methods/crypto.cpp +++ b/src/runtime_methods/crypto.cpp @@ -1,59 +1,27 @@ #include "CrossLang.hpp" -#include -#if defined(TESSESFRAMEWORK_ENABLE_MBED) -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif +using namespace Tesses::Framework::Crypto; namespace Tesses::CrossLang { - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - static TObject Crypto_RandomBytes(GCList& ls, std::vector args) + static TObject Crypto_RandomBytes(GCList& ls, std::vector args) { int64_t size; std::string personalStr; if(GetArgument(args,0,size) && GetArgument(args,1,personalStr)) { - mbedtls_entropy_context entropy; - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_entropy_init(&entropy); - mbedtls_ctr_drbg_init(&ctr_drbg); - - int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *) personalStr.c_str(), personalStr.size()); - if(ret != 0) - { - mbedtls_ctr_drbg_free(&ctr_drbg); - mbedtls_entropy_free(&entropy); - return nullptr; - } std::vector bytes; bytes.resize((size_t)size); - ret = mbedtls_ctr_drbg_random(&ctr_drbg, bytes.data(),bytes.size()); - if (ret != 0) + if(RandomBytes(bytes,personalStr)) { - mbedtls_ctr_drbg_free(&ctr_drbg); - mbedtls_entropy_free(&entropy); - return nullptr; - } - mbedtls_ctr_drbg_free(&ctr_drbg); - mbedtls_entropy_free(&entropy); - TByteArray* ba = TByteArray::Create(ls); - ba->data = bytes; - return ba; + auto ba = TByteArray::Create(ls); + ba->data = bytes; + return ba; + } + return nullptr; + + } return nullptr; } @@ -66,228 +34,45 @@ namespace Tesses::CrossLang int64_t shanum; if(GetArgument(args,0,pass) && GetArgumentHeap(args,1, bArraySalt) && GetArgument(args,2, itterations) && GetArgument(args,3,keylength) && GetArgument(args,4,shanum)) { - mbedtls_md_context_t ctx; - mbedtls_md_init(&ctx); - const mbedtls_md_info_t* info = NULL; + ShaVersion version = VERSION_SHA384; + switch(shanum) { case 1: - info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1); + version = VERSION_SHA1; break; case 224: - info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA224); + version = VERSION_SHA224; break; case 256: - info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); + version = VERSION_SHA256; break; default: case 384: - info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384); + version = VERSION_SHA384; break; case 512: - info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512); + version = VERSION_SHA512; break; } - mbedtls_md_setup(&ctx, info, 1); std::vector key; key.resize((size_t)keylength); - - if(mbedtls_pkcs5_pbkdf2_hmac(&ctx, (const unsigned char*)pass.c_str(), pass.size(), bArraySalt->data.data(), bArraySalt->data.size(), (int)itterations,(uint32_t)key.size(),key.data()) == 0) + + if(PBKDF2(key,pass,bArraySalt->data,(long)itterations,version)) { - auto ba = TByteArray::Create(ls); + TByteArray* ba = TByteArray::Create(ls); ba->data = key; - mbedtls_md_free(&ctx); return ba; } - - mbedtls_md_free(&ctx); + } return nullptr; } - static TObject Crypto_Sha1(GCList& ls, std::vector args) - { - - TDictionary* dict = TDictionary::Create(ls); - mbedtls_sha1_context* ctx = new mbedtls_sha1_context(); - mbedtls_sha1_init(ctx); - mbedtls_sha1_starts(ctx); - - TNative* native = TNative::Create(ls,ctx,[](void* _ctx)->void{ - if(_ctx == nullptr) return; - mbedtls_sha1_context* ctx = (mbedtls_sha1_context*)_ctx; - mbedtls_sha1_free(ctx); - delete ctx; - }); - ls.GetGC()->BarrierBegin(); - dict->SetValue("_native",native); - ls.GetGC()->BarrierEnd(); - - dict->DeclareFunction(ls.GetGC(),"Update","Write more bytes",{"buffer","offset","size"},[native](GCList& ls2,std::vector args2)->TObject{ - if(!native->GetDestroyed() && args2.size() == 3 && std::holds_alternative(args2[0]) && std::holds_alternative(args2[1]) && std::holds_alternative(args2[2])) - { - auto buffer = dynamic_cast(std::get(args2[0]).obj); - auto offset = (size_t)std::get(args2[1]); - auto len = (size_t)std::get(args2[2]); - if(buffer != nullptr && (len+offset) < buffer->data.size()) - { - - mbedtls_sha1_update((mbedtls_sha1_context*)native->GetPointer(),buffer->data.data() + offset, len); - } - } - return false; - }); - - dict->DeclareFunction(ls.GetGC(),"Finish","Get hash",{},[native](GCList& ls2,std::vector args2)->TObject{ - if(!native->GetDestroyed()) - { - TByteArray* ba = TByteArray::Create(ls2); - ba->data.resize(20); - - mbedtls_sha1_finish((mbedtls_sha1_context*)native->GetPointer(),ba->data.data()); - - return ba; - } - return false; - }); - - - /* - - - mbedtls_sha1_update(&ctx, (const unsigned char*)"Demi Lovato", 11); - unsigned char sha1[20]; - mbedtls_sha1_finish(&ctx,sha1); - - for(int i = 0;i<20;i++) - { - printf("%02x", sha1[i]); - } - printf("\n"); - - mbedtls_sha1_free(&ctx);*/ - - return dict; - } - static TObject Crypto_Sha256(GCList& ls, std::vector args) - { - - bool is224=false; - - if(args.size() == 1 && std::holds_alternative(args[0])) - { - is224 = std::get(args[1]); - } - - TDictionary* dict = TDictionary::Create(ls); - mbedtls_sha256_context* ctx = new mbedtls_sha256_context(); - mbedtls_sha256_init(ctx); - mbedtls_sha256_starts(ctx,is224 ? 1 : 0); - - - TNative* native = TNative::Create(ls,ctx,[](void* _ctx)->void{ - if(_ctx == nullptr) return; - mbedtls_sha256_context* ctx = (mbedtls_sha256_context*)_ctx; - mbedtls_sha256_free(ctx); - delete ctx; - }); - ls.GetGC()->BarrierBegin(); - dict->SetValue("_native",native); - ls.GetGC()->BarrierEnd(); - - dict->DeclareFunction(ls.GetGC(),"Update","Write more bytes",{"buffer","offset","size"},[native](GCList& ls2,std::vector args2)->TObject{ - if(!native->GetDestroyed() && args2.size() == 3 && std::holds_alternative(args2[0]) && std::holds_alternative(args2[1]) && std::holds_alternative(args2[2])) - { - auto buffer = dynamic_cast(std::get(args2[0]).obj); - auto offset = (size_t)std::get(args2[1]); - auto len = (size_t)std::get(args2[2]); - if(buffer != nullptr && (len+offset) < buffer->data.size()) - { - - mbedtls_sha256_update((mbedtls_sha256_context*)native->GetPointer(),buffer->data.data() + offset, len); - } - } - return false; - }); - - dict->DeclareFunction(ls.GetGC(),"Finish","Get hash",{},[native](GCList& ls2,std::vector args2)->TObject{ - if(!native->GetDestroyed()) - { - TByteArray* ba = TByteArray::Create(ls2); - ba->data.resize(32); - - mbedtls_sha256_finish((mbedtls_sha256_context*)native->GetPointer(),ba->data.data()); - - return ba; - } - return false; - }); - - - - return dict; - } - static TObject Crypto_Sha512(GCList& ls, std::vector args) - { - - bool is384=false; - - if(args.size() == 1 && std::holds_alternative(args[0])) - { - is384 = std::get(args[1]); - } - - TDictionary* dict = TDictionary::Create(ls); - mbedtls_sha512_context* ctx = new mbedtls_sha512_context(); - mbedtls_sha512_init(ctx); - mbedtls_sha512_starts(ctx,is384 ? 1 : 0); - - TNative* native = TNative::Create(ls,ctx,[](void* _ctx)->void{ - if(_ctx == nullptr) return; - mbedtls_sha512_context* ctx = (mbedtls_sha512_context*)_ctx; - mbedtls_sha512_free(ctx); - delete ctx; - }); - ls.GetGC()->BarrierBegin(); - dict->SetValue("_native",native); - ls.GetGC()->BarrierEnd(); - - dict->DeclareFunction(ls.GetGC(),"Update","Write more bytes",{"buffer","offset","size"},[native](GCList& ls2,std::vector args2)->TObject{ - if(!native->GetDestroyed() && args2.size() == 3 && std::holds_alternative(args2[0]) && std::holds_alternative(args2[1]) && std::holds_alternative(args2[2])) - { - auto buffer = dynamic_cast(std::get(args2[0]).obj); - auto offset = (size_t)std::get(args2[1]); - auto len = (size_t)std::get(args2[2]); - if(buffer != nullptr && (len+offset) < buffer->data.size()) - { - - mbedtls_sha512_update((mbedtls_sha512_context*)native->GetPointer(),buffer->data.data() + offset, len); - } - } - return Undefined(); - }); - - dict->DeclareFunction(ls.GetGC(),"Finish","Get hash",{},[native](GCList& ls2,std::vector args2)->TObject{ - if(!native->GetDestroyed()) - { - TByteArray* ba = TByteArray::Create(ls2); - ba->data.resize(64); - - mbedtls_sha512_finish((mbedtls_sha512_context*)native->GetPointer(),ba->data.data()); - - return ba; - } - return Undefined(); - }); - - - - return dict; - } static TObject Crypto_Base64Encode(GCList& ls, std::vector args) { TByteArray* byteArray; @@ -313,26 +98,22 @@ namespace Tesses::CrossLang } return nullptr; } - #endif void TStd::RegisterCrypto(GC* gc,TRootEnvironment* env) { env->permissions.canRegisterCrypto=true; - #if defined(TESSESFRAMEWORK_ENABLE_MBED) - + if(!HaveCrypto()) return; GCList ls(gc); TDictionary* dict = TDictionary::Create(ls); dict->DeclareFunction(gc, "PBKDF2","Hash passwords with PBKDF2",{"pass","salt","itterations","keylen","shanum"},Crypto_PBKDF2); dict->DeclareFunction(gc, "RandomBytes","Create bytearray but with random bytes in it instead of zeros (this uses mbedtls by the way)",{"byteCount","personalString"},Crypto_RandomBytes); - dict->DeclareFunction(gc, "Sha1","Sha1 Algorithm (needed for WebSocket handshake/BitTorrent etc) (don't use unless you have no other choice)",{},Crypto_Sha1); - dict->DeclareFunction(gc, "Sha256","Sha256 Algorithm",{"$is224"},Crypto_Sha256); - dict->DeclareFunction(gc, "Sha512","Sha512 Algorithm",{"$is384"},Crypto_Sha512); + dict->DeclareFunction(gc, "Base64Encode","Base64 encode",{"data"},Crypto_Base64Encode); dict->DeclareFunction(gc, "Base64Decode","Base64 decode",{"str"},Crypto_Base64Decode); gc->BarrierBegin(); env->DeclareVariable("Crypto", dict); gc->BarrierEnd(); - #endif + } } diff --git a/src/runtime_methods/env.cpp b/src/runtime_methods/env.cpp index da73308..7eb81ca 100644 --- a/src/runtime_methods/env.cpp +++ b/src/runtime_methods/env.cpp @@ -46,7 +46,8 @@ namespace Tesses::CrossLang using namespace Tesses::Framework::Filesystem; using namespace Tesses::Framework::Http; LocalFilesystem lfs; - if(!realPath.relative) return realPath; + if(!realPath.relative) return realPath.MakeAbsolute(); + if(lfs.FileExists(realPath)) return realPath.MakeAbsolute(); const char* path = std::getenv("PATH"); #if defined(_WIN32) const char* pathext = std::getenv("PATHEXT"); @@ -61,7 +62,7 @@ namespace Tesses::CrossLang if(lfs.FileExists(newPath)) return newPath; } } - return realPath.RelativeCurrentDirectory(); + return realPath; #else auto pathParts = HttpUtils::SplitString(path,":"); @@ -70,7 +71,7 @@ namespace Tesses::CrossLang auto newPath = lfs.SystemToVFSPath(item) / realPath; if(lfs.FileExists(newPath)) return newPath; } - return realPath.RelativeCurrentDirectory(); + return realPath.MakeAbsolute(); #endif } diff --git a/src/runtime_methods/net.cpp b/src/runtime_methods/net.cpp index eeba79b..5f3ab7c 100644 --- a/src/runtime_methods/net.cpp +++ b/src/runtime_methods/net.cpp @@ -239,6 +239,15 @@ namespace Tesses::CrossLang ctx->WithSingleHeader(key,value); return dict; }); + + dict->DeclareFunction(gc,"SendStream","Send stream",{"strm"},[ctx,dict](Tesses::CrossLang::GCList &ls2, std::vector args2)->TObject{ + TStreamHeapObject* strmHeapObj; + if(GetArgumentHeap(args2,0,strmHeapObj)) + { + ctx->SendStream(strmHeapObj->stream); + } + return nullptr; + }); dict->DeclareFunction(gc,"SendBytes","Send bytes",{"ba"},[ctx,dict](Tesses::CrossLang::GCList &ls2, std::vector args2)->TObject{ TByteArray* ba; if(GetArgumentHeap(args2,0,ba)) diff --git a/src/runtime_methods/std.cpp b/src/runtime_methods/std.cpp index e7df067..a1f0f83 100644 --- a/src/runtime_methods/std.cpp +++ b/src/runtime_methods/std.cpp @@ -468,6 +468,14 @@ namespace Tesses::CrossLang return TVMVersion(ba->data.data()+o); } return nullptr; + })), + TDItem("FromLong", TExternalMethod::Create(ls, "Create from long",{"longBasedVersion"},[](GCList& ls, std::vector args)->TObject { + int64_t num; + if(GetArgument(args,0,num)) + { + return TVMVersion((uint64_t)num); + } + return nullptr; })) })); env->DeclareVariable("InvokeMethod",MethodInvoker()); diff --git a/src/runtime_methods/time.cpp b/src/runtime_methods/time.cpp index d9cb20f..2819184 100644 --- a/src/runtime_methods/time.cpp +++ b/src/runtime_methods/time.cpp @@ -1,5 +1,6 @@ #include "CrossLang.hpp" +#if defined(CROSSLANG_ENABLE_TIME) #if defined(_WIN32) #include @@ -10,10 +11,12 @@ #include #endif #include "../HowardHinnant_date/date.h" +#endif namespace Tesses::CrossLang { +#if defined(CROSSLANG_ENABLE_TIME) static int64_t ToLocalTime(int64_t local) { #if defined(__SWITCH__) || defined(_WIN32) @@ -135,6 +138,53 @@ namespace Tesses::CrossLang return nullptr; } + static TObject Time_LocalUsSlashDate(GCList& ls, std::vector args) + { + + //THIS code isn't the best but should work + int64_t local; + if(GetArgument(args,0,local)) + { + local = ToLocalTime(local); + + std::chrono::duration local_epoch_time(local); + date::hh_mm_ss hms(local_epoch_time%86400); + auto epoch = date::sys_days{date::January/1/1970}; + epoch += date::days(local/86400); + + + + //date::days sys_days_since_epoch = date::days(gmt); + + + // Convert sys_days to year_month_day + date::year_month_day ymd = date::year_month_day(epoch); + + return std::to_string((uint32_t)ymd.month()) + "/" + std::to_string((uint32_t)ymd.day()) + "/" + std::to_string((int)ymd.year()); + } + return ""; + } + static TObject Time_UTCUsSlashDate(GCList& ls,std::vector args) + { + int64_t gmt; + if(GetArgument(args,0,gmt)) + { + + std::chrono::duration epoch_time(gmt); + date::hh_mm_ss hms(epoch_time%86400); + auto epoch = date::sys_days{date::January/1/1970}; + epoch += date::days(gmt/86400); + + + //date::days sys_days_since_epoch = date::days(gmt); + + + // Convert sys_days to year_month_day + date::year_month_day ymd = date::year_month_day(epoch); + return std::to_string((uint32_t)ymd.month()) + "/" + std::to_string((uint32_t)ymd.day()) + "/" + std::to_string((int)ymd.year()); + } + return ""; + } static TObject Time_GMTTime(GCList& ls, std::vector args) { int64_t gmt; @@ -166,9 +216,10 @@ namespace Tesses::CrossLang } return nullptr; } +#endif void TStd::RegisterTime(GC* gc,TRootEnvironment* env) { - + #if defined(CROSSLANG_ENABLE_TIME) env->permissions.canRegisterTime=true; @@ -178,7 +229,9 @@ namespace Tesses::CrossLang dict->DeclareFunction(gc, "GetGMTTime","Get the GMT time from epoch value",{"epoch"},Time_GMTTime); dict->DeclareFunction(gc, "getNow","Get the time right now, returns C's time(NULL) return value",{},Time_getNow); dict->DeclareFunction(gc, "Sleep","Sleep for a specified amount of milliseconds (multiply seconds by 1000 to get milliseconds)", {"ms"},Time_Sleep); - + dict->DeclareFunction(gc, "UTCUsSlashDate","Get a utc date like 8/20/1992 from epoch value",{"epoch"},Time_UTCUsSlashDate); + dict->DeclareFunction(gc, "LocalUsSlashDate","Get a local date like 8/20/1992 from epoch value",{"epoch"},Time_LocalUsSlashDate); + gc->BarrierBegin(); #if defined(__SWITCH__) || defined(_WIN32) dict->SetValue("Zone", (int64_t)-(_timezone)); @@ -190,5 +243,6 @@ namespace Tesses::CrossLang env->DeclareVariable("Time", dict); gc->BarrierEnd(); + #endif } } diff --git a/src/runtime_methods/vm.cpp b/src/runtime_methods/vm.cpp index ef20d92..95295e5 100644 --- a/src/runtime_methods/vm.cpp +++ b/src/runtime_methods/vm.cpp @@ -317,6 +317,7 @@ namespace Tesses::CrossLang dict->DeclareFunction(gc, "getRuntimeVersion","Get the runtime version",{},[](GCList& ls,std::vector args)->TObject { return TVMVersion(TVM_MAJOR,TVM_MINOR,TVM_PATCH,TVM_BUILD,TVM_VERSIONSTAGE); }); + gc->BarrierBegin(); env->DeclareVariable("VM", dict); gc->BarrierEnd(); diff --git a/src/types/streamheapobject.cpp b/src/types/streamheapobject.cpp index 2fb1d81..74093d2 100644 --- a/src/types/streamheapobject.cpp +++ b/src/types/streamheapobject.cpp @@ -10,6 +10,7 @@ namespace Tesses::CrossLang { this->ls = new GCList(gc); this->ls->Add(obj); + this->obj = obj; TDictionary* dict; if(GetObjectHeap(obj,dict)) { diff --git a/src/vm/vm.cpp b/src/vm/vm.cpp index f67f011..5dca259 100644 --- a/src/vm/vm.cpp +++ b/src/vm/vm.cpp @@ -69,6 +69,7 @@ namespace Tesses::CrossLang { { return !thrd->hasReturned; } + return true; } return false; @@ -873,12 +874,14 @@ namespace Tesses::CrossLang { auto left = cse.back()->Pop(ls); if(std::holds_alternative(left) && std::holds_alternative(right)) { - return true; + cse.back()->Push(gc,true); + return false; } else if(std::holds_alternative(left) && std::holds_alternative(right)) { - return true; + cse.back()->Push(gc,true); + return false; } else if(std::holds_alternative(left) && std::holds_alternative(right)) @@ -934,6 +937,7 @@ namespace Tesses::CrossLang { auto dict = dynamic_cast(obj); auto dynDict = dynamic_cast(obj); + auto native = dynamic_cast(obj); if(dict != nullptr) { gc->BarrierBegin(); @@ -953,20 +957,48 @@ namespace Tesses::CrossLang { return false; } } + else if(native != nullptr && std::holds_alternative(right)){ + cse.back()->Push(gc, native->GetDestroyed()); + return false; + } + if(std::holds_alternative(right)) { cse.back()->Push(gc,obj == std::get(right).obj); return false; } + + else if(std::holds_alternative(right)) + { + cse.back()->Push(gc, false); + return false; + } + else if(std::holds_alternative(right)) + { + cse.back()->Push(gc, false); + return false; + } else { cse.back()->Push(gc,Undefined()); } } + + else if(std::holds_alternative(right)) + { + cse.back()->Push(gc, false); + return false; + } + else if(std::holds_alternative(right)) + { + cse.back()->Push(gc, false); + return false; + } else { cse.back()->Push(gc, Undefined()); + } return false; @@ -980,10 +1012,12 @@ namespace Tesses::CrossLang { if(std::holds_alternative(left) && std::holds_alternative(right)) { + cse.back()->Push(gc,false); return false; } else if(std::holds_alternative(left) && std::holds_alternative(right)) { + cse.back()->Push(gc,false); return false; } else if(std::holds_alternative(left) && std::holds_alternative(right)) @@ -1033,6 +1067,7 @@ namespace Tesses::CrossLang { { auto obj = std::get(left).obj; auto dict = dynamic_cast(obj); + auto native = dynamic_cast(obj); auto dynDict = dynamic_cast(obj); if(dict != nullptr) @@ -1054,17 +1089,42 @@ namespace Tesses::CrossLang { return false; } } + else if(native != nullptr && std::holds_alternative(right)){ + cse.back()->Push(gc, !native->GetDestroyed()); + return false; + } if(std::holds_alternative(right)) { cse.back()->Push(gc,obj != std::get(right).obj); return false; } + else if(std::holds_alternative(right)) + { + cse.back()->Push(gc, true); + return false; + } + else if(std::holds_alternative(right)) + { + cse.back()->Push(gc, true); + return false; + } else { cse.back()->Push(gc,Undefined()); + } } + else if(std::holds_alternative(right)) + { + cse.back()->Push(gc, true); + return false; + } + else if(std::holds_alternative(right)) + { + cse.back()->Push(gc, true); + return false; + } else { cse.back()->Push(gc, Undefined()); @@ -3826,6 +3886,7 @@ namespace Tesses::CrossLang { } else { cse.back()->Push(gc, nullptr); + return false; } } cse.back()->Push(gc, Undefined()); @@ -4177,6 +4238,7 @@ namespace Tesses::CrossLang { auto value = stk->Pop(ls); auto key = stk->Pop(ls); + if(std::holds_alternative(key)) { gc->BarrierBegin(); @@ -4420,6 +4482,17 @@ namespace Tesses::CrossLang { { stk->Push(gc, tryC->Call(ls,{})); } + catch(TextException& ex) + { + + TDictionary* dict = TDictionary::Create(ls); + auto gc = ls.GetGC(); + gc->BarrierBegin(); + dict->SetValue("Type","NativeException"); + dict->SetValue("Text",ex.what()); + gc->BarrierEnd(); + stk->Push(gc, catchC->Call(ls,{dict})); + } catch(VMException& ex) {