From c75c79e3e0fbce174ac825075cb20d81d597fa00 Mon Sep 17 00:00:00 2001 From: Mike Nolan Date: Sun, 31 Aug 2025 05:03:04 -0500 Subject: [PATCH] --help --- Packaging/Linux/build-debian-amd64.sh | 14 + Packaging/Linux/debian/control-amd64 | 8 + Packaging/Linux/pack-debian-amd64.sh | 6 + src/assembler/asm.cpp | 1282 +++++++++++++++++++++++++ src/assembler/disasm.cpp | 894 +++++++++++++++++ src/assembler/merge.cpp | 171 ++++ 6 files changed, 2375 insertions(+) create mode 100644 Packaging/Linux/build-debian-amd64.sh create mode 100644 Packaging/Linux/debian/control-amd64 create mode 100644 Packaging/Linux/pack-debian-amd64.sh create mode 100644 src/assembler/asm.cpp create mode 100644 src/assembler/disasm.cpp create mode 100644 src/assembler/merge.cpp diff --git a/Packaging/Linux/build-debian-amd64.sh b/Packaging/Linux/build-debian-amd64.sh new file mode 100644 index 0000000..221a1c6 --- /dev/null +++ b/Packaging/Linux/build-debian-amd64.sh @@ -0,0 +1,14 @@ +#!/bin/bash +mkdir -p build +cd build +mkdir build-deb-amd64 +cmake -S ../../../ -B build-deb-amd64 -DCMAKE_INSTALL_PREFIX=/usr -DCROSSLANG_FETCHCONTENT=OFF -DCROSSLANG_ENABLE_FFI=ON +cd build-deb-amd64 +make -j`nproc` +make install DESTDIR=../crosslang_1.0.0_amd64 +mkdir -p ../crosslang_1.0.0_amd64/DEBIAN +mkdir -p ../crosslang_1.0.0_amd64/usr/share/Tesses/CrossLang +wget -O ../crosslang_1.0.0_amd64/usr/share/Tesses/CrossLang/Tesses.CrossLang.ShellPackage-1.0.0.0-prod.crvm https://downloads.tesses.net/ShellPackage.crvm +cp ../../debian/control-amd64 ../crosslang_1.0.0_amd64/DEBIAN/control +cd ../ +dpkg-deb --build crosslang_1.0.0_amd64 \ No newline at end of file diff --git a/Packaging/Linux/debian/control-amd64 b/Packaging/Linux/debian/control-amd64 new file mode 100644 index 0000000..12ba53d --- /dev/null +++ b/Packaging/Linux/debian/control-amd64 @@ -0,0 +1,8 @@ +Package: crosslang +Version: 1.0.0 +Architecture: amd64 +Essential: no +Priority: optional +Depends: tessesframework, libffi-dev +Maintainer: Mike Nolan +Description: A programming language diff --git a/Packaging/Linux/pack-debian-amd64.sh b/Packaging/Linux/pack-debian-amd64.sh new file mode 100644 index 0000000..9ae8699 --- /dev/null +++ b/Packaging/Linux/pack-debian-amd64.sh @@ -0,0 +1,6 @@ +#!/bin/bash +curl --user tesses50:$GITEA_AUTH -X DELETE \ + https://git.tesseslanguage.com/api/packages/tesses50/debian/pool/jammy/main/crosslang/1.0.0/amd64 +curl --user tesses50:$GITEA_AUTH \ + --upload-file build/crosslang_1.0.0_amd64.deb \ + https://git.tesseslanguage.com/api/packages/tesses50/debian/pool/jammy/main/upload diff --git a/src/assembler/asm.cpp b/src/assembler/asm.cpp new file mode 100644 index 0000000..b1b5ddf --- /dev/null +++ b/src/assembler/asm.cpp @@ -0,0 +1,1282 @@ +#include "CrossLang.hpp" +using namespace Tesses::Framework::Serialization::Json; + +namespace Tesses::CrossLang { + + class ChunkInstruction { + + public: + virtual ~ChunkInstruction() {} + virtual size_t Size()=0; + }; + + class SimpleChunkInstruction : public ChunkInstruction + { + public: + SimpleChunkInstruction(Instruction instr) : instruction(instr) + { + + } + Instruction instruction; + size_t Size() + { + return 1; + } + + }; + + class PushLongChunkInstruction : public ChunkInstruction { + public: + PushLongChunkInstruction(int64_t v) : value(v) + {} + int64_t value; + + size_t Size() + { + return 9; + } + }; + class PushDoubleChunkInstruction : public ChunkInstruction { + public: + PushDoubleChunkInstruction(double v) : value(v) + {} + double value; + + size_t Size() + { + return 9; + } + }; + + + class PushStringChunkInstruction : public ChunkInstruction { + public: + PushStringChunkInstruction(std::string v) : value(v) + {} + std::string value; + + size_t Size() + { + return 5; + } + }; + + class PushCharChunkInstruction : public ChunkInstruction { + public: + PushCharChunkInstruction(char v) : value(v) + {} + char value; + size_t Size() + { + return 2; + } + }; + + class PushResourceChunkInstruction : public ChunkInstruction { + public: + PushResourceChunkInstruction(std::string v) : value(v) + {} + std::string value; + size_t Size() + { + return 5; + } + }; + + class LabelChunkInstruction : public ChunkInstruction { + public: + LabelChunkInstruction(std::string lbl) : lbl(lbl) + {} + std::string lbl; + size_t Size() + { + return 0; + } + }; + class JumpStyleChunkInstruction : public ChunkInstruction { + public: + JumpStyleChunkInstruction(Instruction instr,std::string lbl) : instr(instr), lbl(lbl) + { + + } + Instruction instr; + std::string lbl; + size_t Size() + { + return 5; + } + }; + + class ScopeEndTimesChunkInstruction : public ChunkInstruction { + public: + ScopeEndTimesChunkInstruction(uint32_t times) : times(times) + {} + uint32_t times; + size_t Size() + { + return 5; + } + }; + + class ChunkData { + public: + std::vector args; + std::vector> instructions; + + }; + class ClosureChunkInstruction : public ChunkInstruction { + public: + ClosureChunkInstruction(bool scoped) : scoped(scoped) + { + + } + ChunkData data; + bool scoped; + size_t Size() + { + return 5; + } + }; + + + class FunctionData { + public: + std::vector name; + ChunkData chunk; + + }; + class ClassDataEntry { + public: + ChunkData data; + std::string documentation; + std::string name; + TClassModifier modifier; + bool isFunction; + bool isAbstract; + }; + class ClassData { + public: + std::string documentation; + std::vector name; + std::vector inherits; + std::vector entries; + }; + class AssemblerParser { + std::vector& tokens; + size_t i=0; + std::string ReadIdent() + { + if(i >= tokens.size()) throw std::out_of_range("End of file"); + if(tokens[i].type != LexTokenType::Identifier) throw std::runtime_error("Expected an identifier"); + return tokens[i++].text; + } + bool IsIdent(std::string val,bool pop=true) + { + if(i >= tokens.size()) return false; + if(tokens[i].type != LexTokenType::Identifier) return false; + if(tokens[i].text != val) return false; + if(pop) i++; + return true; + } + bool IsSym(std::string val,bool pop=true) + { + if(i >= tokens.size()) return false; + if(tokens[i].type != LexTokenType::Symbol) return false; + if(tokens[i].text != val) return false; + if(pop) i++; + return true; + } + + void ParseClosureInstructions(std::vector>& instrs, std::string lbl_prefix) + { + while(!IsSym("}",false)) + { + auto name = ReadIdent(); + if(name == "add") + { + instrs.push_back(std::make_shared(ADD)); + } + else if(name == "sub") + { + instrs.push_back(std::make_shared(SUB)); + } + else if(name == "mul") + { + instrs.push_back(std::make_shared(TIMES)); + } + else if(name == "div") + { + instrs.push_back(std::make_shared(DIVIDE)); + } + else if(name == "mod") + { + instrs.push_back(std::make_shared(MODULO)); + } + else if(name == "lsh") + { + instrs.push_back(std::make_shared(LEFTSHIFT)); + } + else if(name == "rsh") + { + instrs.push_back(std::make_shared(RIGHTSHIFT)); + } + else if(name == "bor") + { + instrs.push_back(std::make_shared(BITWISEOR)); + } + else if(name == "band") + { + instrs.push_back(std::make_shared(BITWISEAND)); + } + else if(name == "bnot") + { + instrs.push_back(std::make_shared(BITWISENOT)); + } + else if(name == "lt") + { + instrs.push_back(std::make_shared(LESSTHAN)); + } + else if(name == "gt") + { + instrs.push_back(std::make_shared(GREATERTHAN)); + } + else if(name == "lte") + { + instrs.push_back(std::make_shared(LESSTHANEQ)); + } + else if(name == "gte") + { + instrs.push_back(std::make_shared(GREATERTHANEQ)); + } + else if(name == "eq") + { + instrs.push_back(std::make_shared(EQ)); + } + else if(name == "neq") + { + instrs.push_back(std::make_shared(NEQ)); + } + else if(name == "not") + { + instrs.push_back(std::make_shared(NOT)); + } + else if(name == "neg") + { + instrs.push_back(std::make_shared(NEGATIVE)); + } + else if(name == "xor") + { + instrs.push_back(std::make_shared(XOR)); + } + else if(name == "pop") + { + instrs.push_back(std::make_shared(POP)); + } + else if(name == "dup") + { + instrs.push_back(std::make_shared(DUP)); + } + else if(name == "nop") + { + instrs.push_back(std::make_shared(NOP)); + } + else if(name == "pushclosure") + { + auto closure = std::make_shared(true); + EnsureSymbol("("); + while(!IsSym(")",false) && i < tokens.size()) + { + closure->data.args.push_back(ReadIdent()); + IsSym(","); + } + EnsureSymbol(")"); + EnsureSymbol("{"); + ParseClosureInstructions(closure->data.instructions,"closure"); + EnsureSymbol("}"); + instrs.push_back(closure); + //instrs.push_back(std::make_shared(JMPC,lblName)); + } + else if(name == "createdict") + { + instrs.push_back(std::make_shared(CREATEDICTIONARY)); + } + else if(name == "createarray") + { + instrs.push_back(std::make_shared(CREATEARRAY)); + } + else if(name == "appendlist") + { + instrs.push_back(std::make_shared(APPENDLIST)); + } + else if(name == "appenddict") + { + instrs.push_back(std::make_shared(APPENDDICT)); + } + else if(name == "embed") + { + if(i < tokens.size() && tokens[i].type == LexTokenType::String) + { + std::string str = tokens[i++].text; + instrs.push_back(std::make_shared(str)); + } + } + else if(name == "push") + { + if(IsSym(".")) + { + instrs.push_back(std::make_shared(PUSHRELATIVEPATH)); + } + else if(IsSym("/")) + { + instrs.push_back(std::make_shared(PUSHROOTPATH)); + } + else if(IsSym("}",false)) + { + throw std::runtime_error("Push needs an argument"); + } + else if(i < tokens.size() && tokens[i].type == LexTokenType::Identifier) + { + + std::string token = tokens[i].text; + + i++; + + bool hasNumber=true; + int64_t lngNum = 0; + + if(token.size() == 1 && token[0] == '0') + { + lngNum = 0; + } + else if(token.size() > 0 && token[0] == '0') + { + if(token.size() > 1 && token[1] == 'x') + { + lngNum = std::stoll(token.substr(2),nullptr,16); + } + else if(token.size() > 1 && token[1] == 'b') + { + lngNum = std::stoll(token.substr(2),nullptr,2); + } + else + { + lngNum = std::stoll(token.substr(1),nullptr,8); + } + + } + else if(token.size() > 0 && token[0] >= '0' && token[0] <= '9') + { + lngNum=std::stoll(token,nullptr,10); + } + else + { + hasNumber = false; + } + + if(hasNumber && this->IsSym(".",false) && i+1 < tokens.size() && tokens[i+1].type == LexTokenType::Identifier) + { + std::string myToken = tokens[i+1].text; + if(myToken.size() > 0 && myToken[0] >= '0' && myToken[0] <= '9') + { + i+=2; + std::string myN = std::to_string(lngNum) + "." + myToken; + + double v = std::stod(myN,nullptr); + + + instrs.push_back(std::make_shared(v)); + } + else + { + + instrs.push_back(std::make_shared(lngNum)); + } + } + else if(hasNumber) + { + + instrs.push_back(std::make_shared(lngNum)); + } + + if(!hasNumber) + { + if(token == "true") + instrs.push_back(std::make_shared(PUSHTRUE)); + if(token == "false") + instrs.push_back(std::make_shared(PUSHFALSE)); + if(token == "null") + instrs.push_back(std::make_shared(PUSHNULL)); + if(token == "undefined") + instrs.push_back(std::make_shared(PUSHUNDEFINED)); + if(token == "break") + instrs.push_back(std::make_shared(PUSHBREAK)); + + if(token == "continue") + instrs.push_back(std::make_shared(PUSHCONTINUE)); + } + } + else if(i < tokens.size() && tokens[i].type == LexTokenType::String) + { + instrs.push_back(std::make_shared(tokens[i++].text)); + + } + else if(i < tokens.size() && tokens[i].type == LexTokenType::Char) + { + instrs.push_back(std::make_shared(tokens[i++].text[0])); + } + } + else if(name == "scopebegin") + { + instrs.push_back(std::make_shared(SCOPEBEGIN)); + } + else if(name == "scopeend") + { + instrs.push_back(std::make_shared(SCOPEEND)); + } + else if(name == "scopeendtimes") + { + if(tokens[i].type == LexTokenType::Identifier) + instrs.push_back(std::make_shared((uint32_t)std::stoul(tokens[i++].text))); + } + else if(name == "setvariable") + { + instrs.push_back(std::make_shared(SETVARIABLE)); + } + else if(name == "getvariable") + { + instrs.push_back(std::make_shared(GETVARIABLE)); + } + else if(name == "declarevariable") + { + instrs.push_back(std::make_shared(DECLAREVARIABLE)); + } + else if(name == "setfield") + { + instrs.push_back(std::make_shared(SETFIELD)); + } + else if(name == "getfield") + { + instrs.push_back(std::make_shared(GETFIELD)); + } + else if(name == "callfunction") + { + instrs.push_back(std::make_shared(CALLFUNCTION)); + } + else if(name == "callmethod") + { + instrs.push_back(std::make_shared(CALLMETHOD)); + } + else if(name == "ret") + { + instrs.push_back(std::make_shared(RET)); + } + else if(name == "jmpc") + { + auto lblName = lbl_prefix + "_" + ReadIdent(); + instrs.push_back(std::make_shared(JMPC,lblName)); + } + else if(name == "jmp") + { + auto lblName = lbl_prefix + "_" + ReadIdent(); + instrs.push_back(std::make_shared(JMP,lblName)); + } + else if(name == "jmpundefined") + { + auto lblName = lbl_prefix + "_" + ReadIdent(); + instrs.push_back(std::make_shared(JMPUNDEFINED,lblName)); + } + else if(name == "defer") + { + instrs.push_back(std::make_shared(DEFER)); + } + else if(name == "trycatch") + { + instrs.push_back(std::make_shared(TRYCATCH)); + } + else if(name == "throw") + { + instrs.push_back(std::make_shared(THROW)); + } + else if(name == "pushscopelessclosure") + { + auto closure = std::make_shared(false); + EnsureSymbol("("); + while(!IsSym(")",false) && i < tokens.size()) + { + closure->data.args.push_back(ReadIdent()); + IsSym(","); + } + EnsureSymbol(")"); + EnsureSymbol("{"); + ParseClosureInstructions(closure->data.instructions,"closure"); + EnsureSymbol("}"); + instrs.push_back(closure); + //instrs.push_back(std::make_shared(JMPC,lblName)); + } + else if(name == "yield") + { + instrs.push_back(std::make_shared(YIELD)); + } + else if(name == "breakpoint") + { + instrs.push_back(std::make_shared(BREAKPOINT)); + } + else if(name == "jmpifbreak") + { + auto lblName = lbl_prefix + "_" + ReadIdent(); + instrs.push_back(std::make_shared(JMPIFBREAK,lblName)); + } + else if(name == "jmpifcontinue") + { + auto lblName = lbl_prefix + "_" + ReadIdent(); + instrs.push_back(std::make_shared(JMPIFCONTINUE,lblName)); + } + else if(name == "jmpifdefined") + { + auto lblName = lbl_prefix + "_" + ReadIdent(); + instrs.push_back(std::make_shared(JMPIFDEFINED,lblName)); + } + else if(name == "lbl") + { + auto lblName = lbl_prefix + "_" + ReadIdent(); + instrs.push_back(std::make_shared(lblName)); + } + } + } + void EnsureSymbol(std::string txt) + { + if(i < tokens.size()) + { + if(tokens[i].type != LexTokenType::Symbol) + { + throw SyntaxException(tokens[i].lineInfo, "expected the symbol \"" + txt + "\" but did not get a symbol at all."); + } + if(tokens[i].text != txt) + { + + throw SyntaxException(tokens[i].lineInfo, "expected the symbol \"" + txt + "\" but got the symbol \"" + tokens[i].text + "\""); + } + i++; + return; + } + throw std::out_of_range("End of file"); + } + std::string ParseDocumentation() + { + if(i >= tokens.size()) return {}; + if(tokens[i].type != LexTokenType::Documentation) return {}; + return tokens[i++].text; + } + public: + AssemblerParser(std::vector& tokens) : tokens(tokens) + { + size_t root=0; + + while(i < tokens.size()) + { + auto doc = ParseDocumentation(); + + if(IsIdent("root")) { + EnsureSymbol("{"); + ParseClosureInstructions(this->root.instructions, "root_" + std::to_string(root)); + EnsureSymbol("}"); + root++; + } + else if(IsIdent("func")) { + FunctionData fdata; + fdata.name.push_back(doc); + fdata.name.push_back(ReadIdent()); + while(IsSym(".")) + { + fdata.name.push_back(ReadIdent()); + } + EnsureSymbol("("); + while(!IsSym(")",false) && i < tokens.size()) + { + fdata.chunk.args.push_back(ReadIdent()); + IsSym(","); + } + EnsureSymbol(")"); + + EnsureSymbol("{"); + ParseClosureInstructions(fdata.chunk.instructions, "func_" + std::to_string(root)); + EnsureSymbol("}"); + this->functions.push_back(fdata); + } + else if(IsIdent("class")) + { + ClassData cdata; + cdata.documentation = doc; + cdata.name.push_back(ReadIdent()); + while(IsSym(".")) + { + cdata.name.push_back(ReadIdent()); + } + if(IsSym(":")) + { + cdata.inherits.push_back(ReadIdent()); + while(IsSym(".")) + { + cdata.inherits.push_back(ReadIdent()); + } + } + else { + cdata.inherits.push_back("ClassObject"); + } + + EnsureSymbol("{"); + + while(i < tokens.size() && !IsSym("}",false)) + { + + ClassDataEntry ent; + ent.documentation = ParseDocumentation(); + + + if(IsIdent("public")) + { + ent.modifier = TClassModifier::Public; + } + else if(IsIdent("protected")) + { + ent.modifier = TClassModifier::Protected; + } + else if(IsIdent("private")) + { + ent.modifier = TClassModifier::Private; + } + else if(IsIdent("static")) + { + ent.modifier = TClassModifier::Static; + } + else throw std::runtime_error("Invalid modifier on class entry"); + + if(IsIdent("abstract")) + { + ent.name = ReadIdent(); + ent.isAbstract = true; + ent.isFunction=true; + + + EnsureSymbol("("); + while(!IsSym(")",false) && i < tokens.size()) + { + ent.data.args.push_back(ReadIdent()); + IsSym(","); + } + EnsureSymbol(")"); + EnsureSymbol(";"); + } + else { + ent.name = ReadIdent(); + if(IsSym("(")) + { + ent.isFunction=true; + ent.isAbstract=false; + while(!IsSym(")",false) && i < tokens.size()) + { + ent.data.args.push_back(ReadIdent()); + IsSym(","); + } + EnsureSymbol(")"); + EnsureSymbol("{"); + ParseClosureInstructions(ent.data.instructions,"cls_fn"); + EnsureSymbol("}"); + } + else if(IsSym(";")) + { + + ent.isFunction=false; + ent.isAbstract=true; + } + else if(IsSym("{")) + { + + ent.isFunction=false; + ent.isAbstract=false; + EnsureSymbol("{"); + ParseClosureInstructions(ent.data.instructions,"cls_field"); + EnsureSymbol("}"); + + } + } + cdata.entries.push_back(ent); + } + + EnsureSymbol("}"); + + this->classes.push_back(cdata); + } + } + } + + ChunkData root; + std::vector functions; + std::vector classes; + + + }; + + class CodeGen2 { + public: + void Write(Tesses::Framework::Streams::Stream* strm, uint8_t* buffer, size_t len) + { + strm->WriteBlock(buffer,len); + } + void WriteInt(Tesses::Framework::Streams::Stream* strm,uint32_t v) + { + uint8_t buffer[4]; + BitConverter::FromUint32BE(buffer[0],v); + Write(strm,buffer,4); + } + void WriteString(Tesses::Framework::Streams::Stream* strm,std::string v) + { + WriteInt(strm,(uint32_t)v.size()); + Write(strm,(uint8_t*)v.data(),v.size()); + } + void Save(Tesses::Framework::Filesystem::VFS* vfs, Tesses::Framework::Streams::Stream* stream) + { + + TVMVersion runtime_version(TVM_MAJOR,TVM_MINOR,TVM_PATCH,TVM_BUILD,TVM_VERSIONSTAGE); + uint8_t buffer[18]; + memcpy(buffer,"TCROSSVM",8); + runtime_version.ToArray(buffer+8); + version.ToArray(buffer+13); + Write(stream,buffer,18); + uint32_t sections=5; + uint32_t name = GetString(this->name); + uint32_t info = GetString(this->info); + + for(auto& dep : this->dependencies) + { + GetString(dep.first); + sections++; + } + for(auto& tool : this->tools) + { + GetString(tool.first); + sections++; + } + if(!this->icon.empty()) + { + this->GetResource(this->icon); + + } + for(auto& sec : this->sections) + sections++; + for(auto& vm : this->vms) + { + this->GetString(vm.first); + this->GetString(vm.second); + sections++; + } + + for(auto& res : this->res) + sections++; + + if(!this->icon.empty()) + sections++; + if(!this->classes.empty()) + sections++; + + + WriteInt(stream,sections); + uint32_t strSz=4; + for(auto& s : this->strs) + { + strSz += (uint32_t)s.size() + 4; + } + memcpy(buffer,"STRS",4); + Write(stream,buffer,4); + WriteInt(stream,strSz); //even though its ignored + WriteInt(stream,this->strs.size()); + for(auto& str : this->strs) + WriteString(stream,str); + memcpy(buffer,"NAME",4); + Write(stream,buffer,4); + WriteInt(stream,4); + WriteInt(stream,name); + memcpy(buffer,"INFO",4); + Write(stream,buffer,4); + WriteInt(stream,4); + WriteInt(stream,info); + + for(auto& dep : this->dependencies) + { + memcpy(buffer,"DEPS",4); + Write(stream,buffer,4); + WriteInt(stream,9); //even though its ignored + WriteInt(stream,GetString(dep.first)); + dep.second.ToArray(buffer); + Write(stream,buffer,5); + } + for(auto& tool : this->tools) + { + memcpy(buffer,"TOOL",4); + Write(stream,buffer,4); + WriteInt(stream,9); //even though its ignored + WriteInt(stream,GetString(tool.first)); + tool.second.ToArray(buffer); + Write(stream,buffer,5); + } + + for(auto& vm : this->vms) + { + memcpy(buffer,"MACH",4); + Write(stream,buffer,4); + WriteInt(stream,8); //even though its ignored + WriteInt(stream,GetString(vm.first)); + WriteInt(stream,GetString(vm.second)); + + } + + uint32_t fnLen=4; + + for(auto& fn : this->funcs) + { + fnLen += (fn.first.size() + 2) * 4; + } + + memcpy(buffer,"FUNS",4); + Write(stream,buffer,4); + WriteInt(stream,fnLen); + WriteInt(stream,(uint32_t)this->funcs.size()); + for(auto& fn : this->funcs) + { + WriteInt(stream,(uint32_t)fn.first.size()); + for(auto namePart : fn.first) + { + WriteInt(stream,namePart); + } + WriteInt(stream,fn.second); + } + + uint32_t clength = 4; + + memcpy(buffer,"CHKS",4); + Write(stream,buffer,4); + WriteInt(stream,clength); + WriteInt(stream,(uint32_t)this->chunks.size()); + for(auto& chunk : this->chunks) + { + std::vector buffer; + WriteInt(stream,(uint32_t)chunk.first.size()); + for(auto arg : chunk.first) + { + WriteInt(stream,arg); + } + for(auto instr : chunk.second) + { + instr->Write(buffer); + } + WriteInt(stream,(uint32_t)buffer.size()); + Write(stream,buffer.data(),buffer.size()); + } + + if(!classes.empty()) + { + uint32_t len = 4; + for(auto& cls : classes) + { + len += 8; + len += cls.name.size() * 4; + len += 4; + len += cls.inherits.size() * 4; + len += 4; + for(auto& clsEnt : cls.entries) + { + len += 17; + for(auto& arg : clsEnt.arguments) len+=4; + } + } + + memcpy(buffer,"CLSS",4); + Write(stream,buffer,4); + WriteInt(stream,len); + WriteInt(stream,(uint32_t)classes.size()); + for(auto& cls : classes) + { + WriteInt(stream,cls.documentation); + WriteInt(stream,(uint32_t)cls.name.size()); + for(auto namePart : cls.name) WriteInt(stream,namePart); + + WriteInt(stream,(uint32_t)cls.inherits.size()); + for(auto inhPart : cls.inherits) WriteInt(stream,inhPart); + + WriteInt(stream,(uint32_t)cls.entries.size()); + + for(auto& ent : cls.entries) + { + buffer[0] = ent.type; + Write(stream,buffer,1); + WriteInt(stream,ent.documentation); + WriteInt(stream,ent.name); + WriteInt(stream,(uint32_t)ent.arguments.size()); + for(auto ar : ent.arguments) + { + WriteInt(stream,ar); + } + WriteInt(stream,ent.closure); + } + } + } + + for(auto& reso : res) + { + memcpy(buffer,"RESO",4); + Write(stream,buffer,4); + + auto strm = vfs->OpenFile(reso,"rb"); + if(strm->EndOfStream()) + { + WriteInt(stream,0); + + } + else { + WriteInt(stream, (uint32_t)strm->GetLength()); + strm->CopyTo(stream); + } + + delete strm; + } + if(!this->icon.empty()) + { + memcpy(buffer,"ICON",4); + Write(stream,buffer,4); + WriteInt(stream,4); + WriteInt(stream,this->GetResource(this->icon)); + + } + for(auto& sect : this->sections) + { + memcpy(buffer,sect.first.data(),4); + Write(stream,buffer,4); + WriteInt(stream,(uint32_t)sect.second.size()); + Write(stream,sect.second.data(),sect.second.size()); + } + } + + + uint32_t GetResource(std::string resource) + { + for(uint32_t i = 0; i < (uint32_t)this->res.size();i++) + { + if(this->res[i] == resource) return i; + } + uint32_t resI = (uint32_t)this->res.size(); + this->res.push_back(resource); + return resI; + } + uint32_t GetString(std::string str) + { + for(uint32_t i = 0; i < (uint32_t)this->strs.size();i++) + { + if(this->strs[i] == str) return i; + } + uint32_t strI = (uint32_t)this->strs.size(); + this->strs.push_back(str); + return strI; + } + + std::vector strs; + std::vector res; + std::vector,uint32_t>> funcs; + std::vector classes; + std::vector, std::vector>>> chunks; + + std::vector> dependencies; + std::vector> tools; + std::vector>> sections; + std::vector> vms; + TVMVersion version; + std::string name; + std::string info; + std::string icon; + + uint32_t PushChunk(ChunkData& data) + { + uint32_t chunkId = chunks.size(); + + chunks.push_back(std::pair,std::vector>>()); + chunks[chunkId].first.resize(data.args.size()); + for(size_t i = 0; i < data.args.size(); i++) + { + chunks[chunkId].first[i] = GetString(data.args[i]); + } + + std::map labels; + + uint32_t offset=0; + + for(auto item : data.instructions) + { + offset += item->Size(); + + auto lbl = std::dynamic_pointer_cast(item); + auto simp = std::dynamic_pointer_cast(item); + auto lng = std::dynamic_pointer_cast(item); + auto dbl = std::dynamic_pointer_cast(item); + auto str = std::dynamic_pointer_cast(item); + auto chr = std::dynamic_pointer_cast(item); + auto chk = std::dynamic_pointer_cast(item); + auto reso = std::dynamic_pointer_cast(item); + auto jmp = std::dynamic_pointer_cast(item); + auto scopeend = std::dynamic_pointer_cast(item); + if(lbl) + { + labels[lbl->lbl] = offset; + } + if(simp) + { + chunks[chunkId].second.push_back(std::make_shared(simp->instruction)); + } + if(lng) + { + chunks[chunkId].second.push_back(std::make_shared(lng->value)); + } + if(dbl) + { + chunks[chunkId].second.push_back(std::make_shared(dbl->value)); + } + if(str) + { + chunks[chunkId].second.push_back(std::make_shared(GetString(str->value))); + } + if(chr) + { + chunks[chunkId].second.push_back(std::make_shared(chr->value)); + } + if(chk) + { + chunks[chunkId].second.push_back(std::make_shared(PushChunk(chk->data),chk->scoped)); + } + if(reso) + { + chunks[chunkId].second.push_back(std::make_shared(GetResource(reso->value))); + } + if(jmp) + { + chunks[chunkId].second.push_back(std::make_shared(jmp->instr,jmp->lbl)); + } + if(scopeend) + { + chunks[chunkId].second.push_back(std::make_shared(scopeend->times)); + } + } + + for(auto& item : chunks[chunkId].second) + { + auto jmp = std::dynamic_pointer_cast(item); + if(jmp) + { + jmp->n = labels[jmp->label]; + } + } + return chunkId; + } + }; + + + + + + Tesses::Framework::Filesystem::VFSPath Assemble(Tesses::Framework::Filesystem::VFS* vfs) + { + using namespace Tesses::Framework::Filesystem; + using namespace Tesses::Framework::TextStreams; + std::function getSrc; + + std::vector tokens; + + getSrc = [&](VFSPath path)->void { + if(vfs->DirectoryExists(path)) + for(auto item : vfs->EnumeratePaths(path)) + { + if(vfs->DirectoryExists(item)) + { + getSrc(item); + } + else { + if(item.GetExtension() == ".tcasm") + { + StreamReader reader(vfs->OpenFile(item,"rb"),true); + + std::stringstream strm(reader.ReadToEnd(),std::ios_base::binary | std::ios_base::in); + Lex(item.ToString(), strm, tokens); + } + } + } + }; + + getSrc(VFSPath() / "src"); + + AssemblerParser parser(tokens); + CodeGen2 cg2; + cg2.name = "out"; + cg2.version = TVMVersion(1,0,0,0,TVMVersionStage::ProductionVersion); + + + auto confFile = VFSPath() / "crossapp.json"; + if(vfs->FileExists(confFile)) + { + Tesses::Framework::TextStreams::StreamReader reader(vfs->OpenFile(confFile,"rb"),true); + auto jobj = Json::Decode(reader.ReadToEnd()); + JObject main; + if(TryGetJToken(jobj,main)) + { + TVMVersion version; + std::string str0; + std::string str1; + JObject dict0; + JArray array0; + + main.TryGetValueAsType("name",cg2.name); + if(main.TryGetValueAsType("version",str0)) + TVMVersion::TryParse(str0, cg2.version); + + if(main.TryGetValueAsType("info",dict0)) + cg2.info = Json::Encode(dict0,false); + + main.TryGetValueAsType("icon", cg2.icon); + + if(main.TryGetValueAsType("dependencies",array0)) + { + for(auto item : array0) + { + if(TryGetJToken(item,dict0)) + { + if(dict0.TryGetValueAsType("version", str1) && TVMVersion::TryParse(str1,version) && dict0.TryGetValueAsType("name",str0)) + { + cg2.dependencies.emplace_back(str0, version); + } + + } + } + } + if(main.TryGetValueAsType("tools",array0)) + { + for(auto item : array0) + { + if(TryGetJToken(item,dict0)) + { + if(dict0.TryGetValueAsType("version", str1) && TVMVersion::TryParse(str1,version) && dict0.TryGetValueAsType("name",str0)) + { + cg2.tools.emplace_back(str0, version); + } + + } + } + } + if(main.TryGetValueAsType("vms",array0)) + { + for(auto item : array0) + { + if(TryGetJToken(item,dict0)) + { + if(dict0.TryGetValueAsType("name", str0) && dict0.TryGetValueAsType("how_to_get",str1)) + { + cg2.vms.emplace_back(str0, str1); + } + + } + } + } + + } + } + + cg2.PushChunk(parser.root); + for(auto& fn : parser.functions) + { + std::vector nameParts; + nameParts.resize(fn.name.size()); + for(size_t i = 0; i < fn.name.size(); i++) + { + nameParts[i] = cg2.GetString(fn.name[i]); + } + + auto chunk = cg2.PushChunk(fn.chunk); + cg2.funcs.push_back(std::pair,uint32_t>(nameParts,chunk)); + } + for(auto& cls : parser.classes) + { + CodeGenClass cls2; + cls2.name.resize(cls.name.size()); + for(size_t i = 0; i < cls.name.size(); i++) + { + cls2.name[i] = cg2.GetString(cls.name[i]); + } + + cls2.inherits.resize(cls.inherits.size()); + for(size_t i = 0; i < cls.inherits.size(); i++) + { + cls2.inherits[i] = cg2.GetString(cls.inherits[i]); + } + + cls2.documentation = cg2.GetString(cls.documentation); + + for(auto clsItem : cls.entries) + { + CodeGenClassEntry ent; + ent.type = 0; + ent.type = (uint8_t)clsItem.modifier & 3; + if(clsItem.isAbstract) ent.type |= 0b00001000; + if(!clsItem.isFunction) ent.type |= 0b00000100; + ent.documentation = cg2.GetString(clsItem.documentation); + ent.name = cg2.GetString(clsItem.name); + ent.closure = 0; + ent.arguments.resize(clsItem.data.args.size()); + for(size_t i = 0; i < clsItem.data.args.size(); i++) + { + ent.arguments[i] = cg2.GetString(clsItem.data.args[i]); + } + if(!clsItem.isAbstract) + { + ent.closure = cg2.PushChunk(clsItem.data); + + } + + cls2.entries.push_back(ent); + } + cg2.classes.push_back(cls2); + } + vfs->CreateDirectory(VFSPath() / "res"); + vfs->CreateDirectory(VFSPath() / "bin"); + vfs->CreateDirectory(VFSPath() / "sections"); + + SubdirFilesystem sectionsdir(vfs,VFSPath() / "sections",false); + SubdirFilesystem resdir(vfs,VFSPath() / "res",false); + + for(auto file : sectionsdir.EnumeratePaths(VFSPath())) + { + if(file.GetExtension() == ".tsec" && sectionsdir.FileExists(file)) + { + auto strm0 = sectionsdir.OpenFile(file,"rb"); + int64_t len = strm0->GetLength(); + if(len > 4) + { + std::string name(4,' '); + strm0->ReadBlock((uint8_t*)name.data(),4); + + size_t off = cg2.sections.size(); + cg2.sections.push_back(std::pair>(name,{})); + cg2.sections[off].second.resize((size_t)len-4); + strm0->ReadBlock(cg2.sections[off].second.data(), cg2.sections[off].second.size()); + } + + delete strm0; + } + } + + + + auto strm = vfs->OpenFile(VFSPath() / "bin" / cg2.name + "-" + cg2.version.ToString() + ".crvm","wb"); + cg2.Save(&resdir, strm); + delete strm; + + + return VFSPath() / "bin" / cg2.name + "-" + cg2.version.ToString() + ".crvm"; + + } + +} diff --git a/src/assembler/disasm.cpp b/src/assembler/disasm.cpp new file mode 100644 index 0000000..14b72b4 --- /dev/null +++ b/src/assembler/disasm.cpp @@ -0,0 +1,894 @@ +#include "CrossLang.hpp" + +namespace Tesses::CrossLang { + class CrossLangFileReader { + Tesses::Framework::Streams::Stream* strm; + + void Ensure(uint8_t* buffer, size_t len) + { + auto read = this->strm->ReadBlock(buffer, len); + if(read < len) throw VMException("End of file, could not read " + std::to_string((int64_t)len) + " byte(s)., offset=" + std::to_string(strm->GetLength())); + } + + uint32_t ReadInt() + { + uint8_t buffer[4]; + Ensure(buffer,4); + return BitConverter::ToUint32BE(buffer[0]); + } + std::string ReadString() + { + auto len = ReadInt(); + if(len == 0) return {}; + std::string str={}; + str.resize((size_t)len); + Ensure((uint8_t*)str.data(),str.size()); + return str; + } + + std::string GetString() + { + uint32_t index=ReadInt(); + if(index >= this->strings.size()) throw VMException("String does not exist in TCrossVM file, expected string index: " + std::to_string(index) + ", total strings: " + std::to_string(this->strings.size())); + return this->strings[index]; + } + + public: + CrossLangFileReader(Tesses::Framework::Streams::Stream* strm) + { + this->strm = strm; + + + + uint8_t main_header[18]; + Ensure(main_header,sizeof(main_header)); + if(strncmp((const char*)main_header,"TCROSSVM",8) != 0) throw VMException("Invalid TCrossVM image."); + TVMVersion version(main_header+8); + if(version.CompareToRuntime() == 1) + { + throw VMException("Runtime is too old."); + } + TVMVersion v2(main_header+13); + this->version = v2; + + size_t _len = (size_t)ReadInt(); + + char table_name[4]; + + for(size_t i = 0;i < _len; i++) + { + Ensure((uint8_t*)table_name,sizeof(table_name)); + size_t tableLen = (size_t)ReadInt(); + if(strncmp(table_name,"NAME",4) == 0) + { + this->name = GetString(); + } + else if(strncmp(table_name,"INFO",4) == 0) + { + this->info = GetString(); + } + else if(strncmp(table_name,"DEPS",4) == 0) //dependencies + { + std::string name = GetString(); + uint8_t version_bytes[5]; + Ensure(version_bytes,sizeof(version_bytes)); + TVMVersion depVersion(version_bytes); + this->dependencies.push_back(std::pair(name, depVersion)); + } + else if(strncmp(table_name,"TOOL",4) == 0) //compile tools (for package manager) + { + std::string name = GetString(); + uint8_t version_bytes[5]; + Ensure(version_bytes,sizeof(version_bytes)); + TVMVersion depVersion(version_bytes); + this->tools.push_back(std::pair(name, depVersion)); + } + else if(strncmp(table_name,"RESO",4) == 0) //resources (using embed) + { + std::vector data; + data.resize(tableLen); + Ensure(data.data(), tableLen); + this->resources.push_back(data); + } + else if(strncmp(table_name,"CHKS",4) == 0) //chunks + { + size_t chunkCount = (size_t)ReadInt(); + for(size_t j = 0; j < chunkCount; j++) + { + std::vector args; + + + size_t argCount = (size_t)ReadInt(); + for(size_t k = 0; k < argCount; k++) + { + args.push_back(GetString()); + } + std::vector code; + size_t len = (size_t)ReadInt(); + code.resize(len); + Ensure(code.data(),len); + //reader.ReadIntoBuffer(chunk->code); + + this->chunks.emplace(this->chunks.end(),args,code); + } + + } + else if(strncmp(table_name,"FUNS",4) == 0) //functions + { + size_t funLength = (size_t)ReadInt(); + + for(size_t j = 0; j < funLength;j++) + { + std::vector fnParts; + uint32_t fnPartsC = ReadInt(); + for(uint32_t k = 0; k < fnPartsC; k++) + { + fnParts.push_back(GetString()); + } + + uint32_t fnNumber = ReadInt(); + this->functions.push_back(std::pair,uint32_t>(fnParts,fnNumber)); + + } + + } + else if(strncmp(table_name,"STRS",4) == 0) //strings + { + size_t strsLen = (size_t)ReadInt(); + for(size_t j = 0;j < strsLen;j++) + { + this->strings.push_back(ReadString()); + } + } + else if(strncmp(table_name,"ICON",4) == 0) //icon + { + this->icon = (int32_t)ReadInt(); + } + else if(strncmp(table_name,"MACH",4) == 0) //machine + { + std::string name = GetString(); + std::string howToGet = GetString(); + this->vms.push_back(std::pair(name,howToGet)); + } + else if(strncmp(table_name,"CLSS",4) == 0) //classes + { + uint32_t clsCnt = ReadInt(); + for(uint32_t j = 0; j < clsCnt; j++) + { + TClass cls; + cls.documentation= GetString(); + uint32_t name_cnt = ReadInt(); + for(uint32_t k = 0; k < name_cnt; k++) + { + cls.name.push_back(GetString()); + } + name_cnt = ReadInt(); + for(uint32_t k = 0; k < name_cnt; k++) + { + cls.inherits.push_back(GetString()); + } + name_cnt = ReadInt(); + for(uint32_t k = 0; k < name_cnt; k++) + { + TClassEntry ent; + Ensure(main_header,1); + uint8_t sig = main_header[0]; + ent.isAbstract = (sig & 0b00001000) != 0; + ent.isFunction = (sig & 0b00000100) == 0; + ent.modifier = (TClassModifier)(sig & 3); + ent.documentation = GetString(); + ent.name = GetString(); + uint32_t arglen = ReadInt(); + for(uint32_t l = 0; l < arglen; l++) + ent.args.push_back(GetString()); + ent.chunkId = ReadInt(); + cls.entry.push_back(ent); + } + this->classes.push_back(cls); + } + } + else + { + std::vector data; + data.resize(tableLen); + Ensure(data.data(), tableLen); + std::string key(std::string(table_name), 4); + this->sections.push_back(std::pair>(key,data)); + } + } + } + std::vector strings; + std::vector,std::vector>> chunks; + std::vector> vms; + std::vector, uint32_t>> functions; + std::vector> dependencies; + std::vector> tools; + std::vector>> sections; + std::vector> resources; + std::vector classes; + std::string name; + TVMVersion version; + std::string info; + int32_t icon; + + std::string Chunk2String(size_t chunkId,size_t tab) + { + if(chunkId >= this->chunks.size()) return {}; + auto& code = this->chunks[chunkId].second; + + std::string buffer = {}; + std::vector labels; + size_t i = 0; + while(i < code.size()) + { + switch(code[i++]) + { + case PUSHRESOURCE: + case PUSHSTRING: + case SCOPEENDTIMES: + case PUSHCLOSURE: + case PUSHSCOPELESSCLOSURE: + i+=4; + break; + case PUSHLONG: + case PUSHDOUBLE: + i+=8; + break; + case PUSHCHAR: + i+=1; + break; + case JMP: + case JMPC: + case JMPIFBREAK: + case JMPIFCONTINUE: + case JMPIFDEFINED: + case JMPUNDEFINED: + { + bool already = false; + for(auto item : labels) + { + if(item == i) { + already=true; + break; + } + } + if(!already) + labels.push_back(BitConverter::ToUint32BE(code[i])); + i+=4; + } + break; + + default: + break; + } + } + i=0; + + while(i < code.size()) + { + for(size_t j = 0; j < labels.size(); j++) + { + if(labels[j] == i) + { + buffer.append(tab,'\t'); + buffer.append("lbl l" + std::to_string((uint32_t)j) + "\n"); + break; + } + } + + buffer.append(tab,'\t'); + + + switch(code[i++]) + { + case ADD: + buffer.append("add"); + break; + case SUB: + buffer.append("sub"); + break; + case TIMES: + buffer.append("mul"); + break; + case DIVIDE: + buffer.append("div"); + break; + case MODULO: + buffer.append("mod"); + break; + case LEFTSHIFT: + buffer.append("lsh"); + break; + case RIGHTSHIFT: + buffer.append("rsh"); + break; + case BITWISEOR: + buffer.append("bor"); + break; + case BITWISEAND: + buffer.append("band"); + break; + case BITWISENOT: + buffer.append("bnot"); + break; + case LESSTHAN: + buffer.append("lt"); + break; + case GREATERTHAN: + buffer.append("gt"); + break; + case LESSTHANEQ: + buffer.append("lte"); + break; + case GREATERTHANEQ: + buffer.append("gte"); + break; + case EQ: + buffer.append("eq"); + break; + case NEQ: + buffer.append("neq"); + break; + case NOT: + buffer.append("not"); + break; + case NEGATIVE: + buffer.append("neg"); + break; + case XOR: + buffer.append("xor"); + break; + case POP: + buffer.append("pop"); + break; + case DUP: + buffer.append("dup"); + break; + case PUSHCLOSURE: + { + uint32_t clId = (uint32_t)code[i++] << 24; + clId |= (uint32_t)code[i++] << 16; + clId |= (uint32_t)code[i++] << 8; + clId |= (uint32_t)code[i++]; + buffer.append("pushclosure ("); + bool first=true; + + for(auto item : this->chunks.at(clId).first) + { + if(!first) + buffer.append(", "); + buffer.append(item); + first=false; + } + + + + buffer.append(") {\n"); + buffer.append(Chunk2String((size_t)clId,tab+1)); + buffer.append(tab,'\t'); + buffer.append("}"); + } + break; + case CREATEDICTIONARY: + buffer.append("createdict"); + break; + case CREATEARRAY: + buffer.append("createarray"); + break; + case APPENDLIST: + buffer.append("appendlist"); + break; + case APPENDDICT: + buffer.append("appenddict"); + break; + case PUSHRESOURCE: + { + uint32_t clId = (uint32_t)code[i++] << 24; + clId |= (uint32_t)code[i++] << 16; + clId |= (uint32_t)code[i++] << 8; + clId |= (uint32_t)code[i++]; + buffer.append("embed "); + buffer.append(EscapeString(name + "-" + version.ToString()+"_"+ std::to_string(clId) + ".bin",true)); + } + break; + case PUSHLONG: + { + uint64_t number = (uint64_t)code[i++] << 56; + number |= (uint64_t)code[i++] << 48; + number |= (uint64_t)code[i++] << 40; + number |= (uint64_t)code[i++] << 32; + number |= (uint64_t)code[i++] << 24; + number |= (uint64_t)code[i++] << 16; + number |= (uint64_t)code[i++] << 8; + number |= (uint64_t)code[i++]; + buffer.append("push " + std::to_string(number)); + + } + break; + case PUSHCHAR: + { + buffer.append("push '" + EscapeString(std::string({(char)code[i++]}),false) + "'"); + } + break; + case PUSHDOUBLE: + { + + auto res = BitConverter::ToDoubleBE(code[i]); + i+=8; + buffer.append("push " + std::to_string(res)); + } + break; + case PUSHSTRING: + { + auto res = BitConverter::ToUint32BE(code[i]); + i+=4; + buffer.append("push " + EscapeString(strings[res],true)); + } + break; + case PUSHNULL: + buffer.append("push null"); + break; + case PUSHUNDEFINED: + buffer.append("push undefined"); + break; + case SCOPEBEGIN: + buffer.append("scopebegin"); + break; + case SCOPEEND: + buffer.append("scopeend"); + break; + case SCOPEENDTIMES: + { + auto res = BitConverter::ToUint32BE(code[i]); + i+=4; + buffer.append("scopeendtimes " + std::to_string(res)); + } + break; + case PUSHFALSE: + buffer.append("push false"); + break; + case PUSHTRUE: + buffer.append("push true"); + break; + case SETVARIABLE: + buffer.append("setvariable"); + break; + case GETVARIABLE: + buffer.append("getvariable"); + break; + case DECLAREVARIABLE: + buffer.append("declarevariable"); + break; + case SETFIELD: + buffer.append("setfield"); + break; + case GETFIELD: + buffer.append("getfield"); + break; + case CALLFUNCTION: + buffer.append("callfunction"); + break; + case CALLMETHOD: + buffer.append("callmethod"); + break; + case RET: + buffer.append("ret"); + break; + case JMPC: + { + auto res = BitConverter::ToUint32BE(code[i]); + i+=4; + buffer.append("jmpc l"); + for(size_t j = 0; j < labels.size(); j++) + { + if(labels[j] == res) + { + buffer.append(std::to_string((uint32_t)j)); + break; + } + } + + } + break; + case JMP: + { + auto res = BitConverter::ToUint32BE(code[i]); + i+=4; + buffer.append("jmp l"); + for(size_t j = 0; j < labels.size(); j++) + { + if(labels[j] == res) + { + buffer.append(std::to_string((uint32_t)j)); + break; + } + } + + } + break; + case JMPUNDEFINED: + { + auto res = BitConverter::ToUint32BE(code[i]); + i+=4; + buffer.append("jmpundefined l"); + for(size_t j = 0; j < labels.size(); j++) + { + if(labels[j] == res) + { + buffer.append(std::to_string((uint32_t)j)); + break; + } + } + + } + break; + case DEFER: + buffer.append("defer"); + break; + case TRYCATCH: + buffer.append("trycatch"); + break; + case THROW: + buffer.append("throw"); + break; + case PUSHSCOPELESSCLOSURE: + { + uint32_t clId = (uint32_t)code[i++] << 24; + clId |= (uint32_t)code[i++] << 16; + clId |= (uint32_t)code[i++] << 8; + clId |= (uint32_t)code[i++]; + buffer.append("pushscopelessclosure ("); + bool first=true; + + for(auto item : this->chunks.at(clId).first) + { + if(!first) + buffer.append(", "); + buffer.append(item); + first=false; + } + + + + buffer.append(") {\n"); + buffer.append(Chunk2String((size_t)clId,tab+1)); + buffer.append(tab,'\t'); + buffer.append("}"); + } + break; + case YIELD: + buffer.append("yield"); + break; + case PUSHROOTPATH: + buffer.append("push /"); + break; + case PUSHRELATIVEPATH: + buffer.append("push ."); + break; + case BREAKPOINT: + buffer.append("breakpoint"); + break; + case PUSHBREAK: + buffer.append("push break"); + break; + case PUSHCONTINUE: + buffer.append("push continue"); + break; + case JMPIFBREAK: + { + auto res = BitConverter::ToUint32BE(code[i]); + i+=4; + buffer.append("jmpifbreak l"); + for(size_t j = 0; j < labels.size(); j++) + { + if(labels[j] == res) + { + buffer.append(std::to_string((uint32_t)j)); + break; + } + } + + } + break; + case JMPIFCONTINUE: + { + auto res = BitConverter::ToUint32BE(code[i]); + i+=4; + buffer.append("jmpifcontinue l"); + for(size_t j = 0; j < labels.size(); j++) + { + if(labels[j] == res) + { + buffer.append(std::to_string((uint32_t)j)); + break; + } + } + + } + break; + case JMPIFDEFINED: + { + auto res = BitConverter::ToUint32BE(code[i]); + i+=4; + buffer.append("jmpifdefined l"); + for(size_t j = 0; j < labels.size(); j++) + { + if(labels[j] == res) + { + buffer.append(std::to_string((uint32_t)j)); + break; + } + } + + } + break; + } + + buffer.push_back('\n'); + } + + if(code.empty()) buffer.push_back('\n'); + + return buffer; + } + }; + + + + + void Disassemble(Tesses::Framework::Streams::Stream* src,Tesses::Framework::Filesystem::VFS* vfs, bool generateJSON,bool extractResources) + { + using namespace Tesses::Framework::Filesystem; + CrossLangFileReader file(src); + if(extractResources) + { + std::string resdir = "res"; + VFSPath path=resdir; + vfs->CreateDirectory(path); + for(size_t i = 0; i < file.resources.size(); i++) + { + auto path2 = path / file.name + "-" + file.version.ToString()+"_"+ std::to_string((uint32_t)i) + ".bin"; + auto strm = vfs->OpenFile(path2,"wb"); + strm->WriteBlock(file.resources[i].data(),file.resources[i].size()); + delete strm; + } + + std::string secdir = "sections"; + VFSPath secpath=secdir; + vfs->CreateDirectory(secpath); + + for(size_t i = 0; i < file.sections.size(); i++) + { + auto path2 = secpath / file.name + "-" + file.version.ToString()+"_"+ std::to_string((uint32_t)i) + ".tsec"; + + if(file.sections[i].first.size() != 4) { + throw std::runtime_error("Chunk name is not 4 bytes"); + } + auto strm = vfs->OpenFile(path2,"wb"); + strm->WriteBlock((const uint8_t*)file.sections[i].first.data(), 4); + strm->WriteBlock(file.sections[i].second.data(),file.sections[i].second.size()); + delete strm; + } + } + + std::string srcFile = {}; + + srcFile.append("root {\n"); + srcFile.append(file.Chunk2String(0,1)); + srcFile.append("}\n"); + + for(auto& item : file.functions) + { + if(!item.first[0].empty()) + { + srcFile.append("/^" + Tesses::Framework::Http::HttpUtils::Replace(item.first[0],"^","^^") +"^/\n"); + } + srcFile.append("func "); + for(size_t i = 1; i < item.first.size(); i++) + { + if(i > 1) + { + srcFile.push_back('.'); + } + srcFile.append(item.first[i]); + } + srcFile.append("("); + auto& chunk = file.chunks.at(item.second); + for(size_t i = 0; i < chunk.first.size(); i++) + { + if(i > 0) + { + srcFile.append(", "); + } + srcFile.append(chunk.first[i]); + } + srcFile.append(") {\n"); + srcFile.append(file.Chunk2String((size_t)item.second,1)); + srcFile.append("}\n"); + } + for(auto& cls : file.classes) + { + if(!cls.documentation.empty()) + { + srcFile.append("/^" + Tesses::Framework::Http::HttpUtils::Replace(cls.documentation,"^","^^") +"^/\n"); + } + srcFile.append("class "); + for(size_t i = 0; i < cls.name.size(); i++) + { + if(i > 0) + { + srcFile.push_back('.'); + } + srcFile.append(cls.name[i]); + } + + if(!(cls.inherits.size() == 1 && cls.inherits[0] == "ClassObject")) + { + srcFile.append(" : "); + for(size_t i = 0; i < cls.inherits.size(); i++) + { + if(i > 0) + { + srcFile.push_back('.'); + } + srcFile.append(cls.inherits[i]); + } + } + srcFile.append(" {\n"); + for(auto& item : cls.entry) + { + if(!item.documentation.empty()) + srcFile.append("\t/^"+Tesses::Framework::Http::HttpUtils::Replace(item.documentation,"^","^^")+"^/\n"); + + switch(item.modifier) + { + case TClassModifier::Private: + srcFile.append("\tprivate "); + break; + case TClassModifier::Protected: + srcFile.append("\tprotected "); + break; + case TClassModifier::Public: + srcFile.append("\tpublic "); + break; + case TClassModifier::Static: + srcFile.append("\tstatic "); + default: + break; + } + + if(item.isFunction) + { + + if(item.isAbstract) + { + srcFile.append("abstract " + item.name); + srcFile.append("("); + for(size_t i = 0; i < item.args.size(); i++) + { + if(i > 0) + { + srcFile.append(", "); + } + srcFile.append(item.args[i]); + } + srcFile.append(");\n"); + } + else { + srcFile.append(item.name); + srcFile.append("("); + for(size_t i = 0; i < item.args.size(); i++) + { + if(i > 0) + { + srcFile.append(", "); + } + srcFile.append(item.args[i]); + } + srcFile.append(") {\n"); + srcFile.append(file.Chunk2String(item.chunkId,2)); + srcFile.append("\t}\n"); + } + } + else { + srcFile.append(item.name); + if(item.isAbstract) + { + + srcFile.append(";\n"); + } + else { + srcFile.append(" {\n"); + srcFile.append(file.Chunk2String(item.chunkId,2)); + srcFile.append("\t}\n"); + } + } + } + srcFile.append("}\n"); + } + std::string srcdirs = "src"; + VFSPath srcdir=srcdirs; + vfs->CreateDirectory(srcdir); + Tesses::Framework::TextStreams::StreamWriter writer(vfs->OpenFile(srcdir / file.name + "-" + file.version.ToString() + ".tcasm","wb"),true); + writer.Write(srcFile); + + if(generateJSON) + { + using namespace Tesses::Framework::Serialization::Json; + + JObject json_data { + JOItem{"name", file.name}, + JOItem{"version",file.version.ToString()} + }; + if(!file.info.empty()) + { + json_data.SetValue("info",Json::Decode(file.info)); + + } + + if(file.icon > -1 && file.icon < file.resources.size()) + { + json_data.SetValue("icon",file.name + "-" + file.version.ToString()+"_"+ std::to_string(file.icon) + ".bin"); + } + if(!file.dependencies.empty()) + { + JArray array; + for(auto& item : file.dependencies) + { + array.Add(JObject { + JOItem { + "name", + item.first + }, + JOItem { + "version", + item.second.ToString() + } + }); + } + json_data.SetValue("dependencies", array); + } + if(!file.tools.empty()) + { + JArray array; + for(auto& item : file.tools) + { + array.Add(JObject { + JOItem { + "name", + item.first + }, + JOItem { + "version", + item.second.ToString() + } + }); + } + json_data.SetValue("tools", array); + } + if(!file.vms.empty()) + { + JArray array; + for(auto& item : file.vms) + { + array.Add(JObject { + JOItem { + "name", + item.first + }, + JOItem { + "how_to_get", + item.second + } + }); + } + json_data.SetValue("vms", array); + } + + + Tesses::Framework::TextStreams::StreamWriter json_writer(vfs->OpenFile(VFSPath() / "crossapp.json","wb" ),true); + json_writer.WriteLine(Json::Encode(json_data,true)); + + } + } +} \ No newline at end of file diff --git a/src/assembler/merge.cpp b/src/assembler/merge.cpp new file mode 100644 index 0000000..9ccf5de --- /dev/null +++ b/src/assembler/merge.cpp @@ -0,0 +1,171 @@ +#include "CrossLang.hpp" +using namespace Tesses::Framework::Serialization::Json; +namespace Tesses::CrossLang { + static void LoadDependency(Tesses::Framework::Filesystem::VFS* srcFS, Tesses::Framework::Filesystem::VFSPath sourceDir, std::pair dep, std::vector>& files, std::vector>& tools); + static void LoadDependencies(Tesses::Framework::Filesystem::VFS* srcFS, Tesses::Framework::Filesystem::VFSPath sourceDir,TFile* file, std::vector>& files, std::vector>& tools); + static void LoadDependency(Tesses::Framework::Filesystem::VFS* srcFS, Tesses::Framework::Filesystem::VFSPath sourceDir, std::pair dep, std::vector>& files, std::vector>& tools) + { + for(auto index = files.begin(); index != files.end(); index++) + { + if(index->first == dep.first) + { + if(index->second.CompareTo(dep.second) >= 0) return; + files.erase(index); + break; + } + } + std::string name = {}; + name.append(dep.first); + name.push_back('-'); + name.append(dep.second.ToString()); + name.append(".crvm"); + auto filename= sourceDir / name; + + if(srcFS->RegularFileExists(filename)) + { + Tesses::Framework::Streams::Stream* file = srcFS->OpenFile(filename,"rb"); + + TFile f; + f.Load(nullptr, file); + delete file; + LoadDependencies(srcFS,sourceDir,&f,files,tools); + } + else throw VMException("Could not open file: \"" + name + "\"."); + + } + static void LoadDependencies(Tesses::Framework::Filesystem::VFS* srcFS, Tesses::Framework::Filesystem::VFSPath sourceDir,TFile* file, std::vector>& files, std::vector>& tools) + { + files.push_back(std::pair(file->name,file->version)); + for(auto item : file->tools) + { + bool exists = false; + for(auto& itm2 : tools) + { + if(itm2.first == item.first) + { + exists=true; + if(itm2.second.CompareTo(item.second) < 0) { + itm2.second = item.second; + } + break; + } + } + if(!exists) + tools.push_back(item); + } + for(auto item : file->dependencies) + { + LoadDependency(srcFS,sourceDir,item,files,tools); + } + } + static void EnumerateCRVM(Tesses::Framework::Filesystem::VFS* srcFS, Tesses::Framework::Filesystem::VFSPath sourceDir,std::string filename, std::vector>& files, Tesses::Framework::Filesystem::VFS* destFS) + { + + + TFile file; + auto strm = srcFS->OpenFile(sourceDir / filename,"rb"); + if(strm->EndOfStream()) { + + delete strm; + + throw std::runtime_error("File does not exist: " + (sourceDir / filename).ToString() ); + } + file.Load(nullptr,strm); + + + delete strm; + + std::vector> tools; + LoadDependencies(srcFS,sourceDir,&file,files,tools); + + + + + + JObject json_data { + JOItem{"name", file.name}, + JOItem{"version",file.version.ToString()} + + }; + if(!file.info.empty()) + { + json_data.SetValue("info",Json::Decode(file.info)); + + } + + if(file.icon > -1 && file.icon < file.resources.size()) + { + json_data.SetValue("icon",file.name + "-" + file.version.ToString()+"_"+ std::to_string(file.icon) + ".bin"); + } + if(!file.tools.empty()) + { + JArray array; + for(auto& item : file.tools) + { + array.Add(JObject { + JOItem { + "name", + item.first + }, + JOItem { + "version", + item.second.ToString() + } + }); + } + json_data.SetValue("tools", array); + } + if(!file.vms.empty()) + { + JArray array; + for(auto& item : file.vms) + { + array.Add(JObject { + JOItem { + "name", + item.first + }, + JOItem { + "how_to_get", + item.second + } + }); + } + json_data.SetValue("vms", array); + } + + + Tesses::Framework::TextStreams::StreamWriter json_writer(destFS->OpenFile(Tesses::Framework::Filesystem::VFSPath() / "crossapp.json","wb" ),true); + json_writer.WriteLine(Json::Encode(json_data,true)); + } + Tesses::Framework::Filesystem::VFSPath Merge(Tesses::Framework::Filesystem::VFS* srcFS, Tesses::Framework::Filesystem::VFSPath sourcePath, Tesses::Framework::Filesystem::VFS* destFS) + { + std::vector> files; + + for(auto ent : destFS->EnumeratePaths(Tesses::Framework::Filesystem::VFSPath())) + { + if(destFS->DirectoryExists(ent)) + { + destFS->DeleteDirectoryRecurse(ent); + } + else { + destFS->DeleteFile(ent); + } + } + + + EnumerateCRVM(srcFS,sourcePath.GetParent(), sourcePath.GetFileName(),files,destFS); + + for(auto item : files) + { + auto filePath = sourcePath.GetParent() / item.first + "-" + item.second.ToString() + ".crvm"; + if(srcFS->RegularFileExists(filePath)) + { + auto strm = srcFS->OpenFile(filePath,"rb"); + Disassemble(strm,destFS,false); + } + } + + return Assemble(destFS); + } +} \ No newline at end of file