From 9af802e6aaba0dcbd7c9920736b6a889ba4d13aa Mon Sep 17 00:00:00 2001 From: Mike Nolan Date: Sat, 3 May 2025 10:05:57 -0500 Subject: [PATCH] Do more docs --- CMakeLists.txt | 13 +- docs/CPP_LAYER.md | 1 + docs/EXTENDING_RUNTIME.md | 232 ++++++++++++++ docs/HANDBOOK.md | 605 ++++++++++++++++++++++++++++++++++++ docs/README.md | 566 +-------------------------------- src/compiler/parser.cpp | 10 +- src/runtime_methods/io.cpp | 101 +++--- src/runtime_methods/std.cpp | 73 ++++- 8 files changed, 968 insertions(+), 633 deletions(-) create mode 100644 docs/CPP_LAYER.md create mode 100644 docs/EXTENDING_RUNTIME.md create mode 100644 docs/HANDBOOK.md diff --git a/CMakeLists.txt b/CMakeLists.txt index 56c0d74..43356cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,7 +45,7 @@ endif() include(FetchContent) FetchContent_Declare( TessesFramework - GIT_REPOSITORY https://onedev.site.tesses.net/tesses-framework.git + GIT_REPOSITORY httpgc, envs://onedev.site.tesses.net/tesses-framework.git ) FetchContent_MakeAvailable(TessesFramework) else() @@ -155,12 +155,15 @@ endif() -include(GNUInstallDirs) +include(GNUInstallDirs)cmake_minimum_required(VERSION 3.16) -if(CROSSLANG_ENABLE_STATIC) +project(MyPlugin) -add_library(crosslang_static STATIC ${CROSSLANG_SOURCE}) -CROSSLANG_LINK_DEPS(crosslang_static) +find_package(TessesCrossLang REQUIRED) + +add_library(MyPlugin SHARED lib.cpp) + +target_link_libraries(MyPlugin PUBLIC TessesCrossLang::crosslang_shared) if(CROSSLANG_FETCHCONTENT) target_link_libraries(crosslang_static PUBLIC tessesframework) else() diff --git a/docs/CPP_LAYER.md b/docs/CPP_LAYER.md new file mode 100644 index 0000000..e0d2d83 --- /dev/null +++ b/docs/CPP_LAYER.md @@ -0,0 +1 @@ +# C++ Layer \ No newline at end of file diff --git a/docs/EXTENDING_RUNTIME.md b/docs/EXTENDING_RUNTIME.md new file mode 100644 index 0000000..4d609f8 --- /dev/null +++ b/docs/EXTENDING_RUNTIME.md @@ -0,0 +1,232 @@ +Extending the runtime +===================== + +See [this](CPP_LAYER.md) + +There are a couple ways of doing this + +- Creating a shared library (only works if using shared libs) +- Embeding the runtime +- Forking crosslang and adding functionality (I don't support pull requests at this time) + +### Shared Library + +This assumes you are on linux, if on windows use MyPlugin.dll on mac libMyPlugin.dylib in crosslang project (it would be a good idea to check Env.Platform) + +CMakeLists.txt: +```cmake +cmake_minimum_required(VERSION 3.16) + +project(MyPlugin) + +find_package(TessesCrossLang REQUIRED) + +add_library(MyPlugin SHARED lib.cpp) + +target_link_libraries(MyPlugin PUBLIC TessesCrossLang::crosslang_shared) +``` + +lib.cpp: +```c++ +#include + +using namespace Tesses::CrossLang; + +void MyPlugin(GC* gc, TRootEnvironment* env) +{ + GCList ls(gc); + TDictionary* dict = TDictionary::Create(ls,{ + TDItem("MyFunction", TExternalMethod::Create(ls, "A description for function",{"myLong","$optionalList"},[](GCList& ls, std::vector args)->TObject { + int64_t n; + if(GetArgument(args,0,n)) + { + TList* myLs; + if(GetArgumentHeap(args,1,myLs)) + { + //do something with the list + myLs->Add(n); + return TList::Create(ls, {n,myLs}); + } + + + return TList::Create(ls, {n}); + } + + return nullptr; + })) + }); + + gc->BarrierBegin(); + env->SetVariable("MyPlugin",dict); + gc->BarrierEnd(); +} + +CROSSLANG_PLUGIN(MyPlugin); + +``` + +main.tcross: +```go +func main(args) +{ + Reflection.LoadNativePlugin(FS.MakeFull("libMyPlugin.so")); + + var res = MyPlugin.MyFunction(42); + if(TypeOf(res) == "List") + { + Console.WriteLine(res); + } + + var res = MyPlugin.MyFunction(42,["Hello","John"]); + if(TypeOf(res) == "List") + { + Console.WriteLine(res); + } +} +``` + + +### Embeding the runtime + +CMakeLists.txt: +```cmake +cmake_minimum_required(VERSION 3.16) + +project(MyCrossLangBinary) + +find_package(TessesCrossLang REQUIRED) + +add_executable(MyCrossLangBinary SHARED app.cpp) + +target_link_libraries(MyCrossLangBinary PUBLIC TessesCrossLang::crosslang_shared) # or crosslang_static for static linking + + +``` + +app.cpp: +```c++ +#include + +using namespace Tesses::CrossLang; +using namespace Tesses::Framework; + +void MyPlugin(GC* gc, TRootEnvironment* env) +{ + GCList ls(gc); + TDictionary* dict = TDictionary::Create(ls,{ + TDItem("MyFunction", TExternalMethod::Create(ls, "A description for function",{"myLong","$optionalList"},[](GCList& ls, std::vector args)->TObject { + int64_t n; + if(GetArgument(args,0,n)) + { + TList* myLs; + if(GetArgumentHeap(args,1,myLs)) + { + //do something with the list + myLs->Add(n); + return TList::Create(ls, {n,myLs}); + } + + + return TList::Create(ls, {n}); + } + + return nullptr; + })) + }); + + gc->BarrierBegin(); + env->SetVariable("MyPlugin",dict); + gc->BarrierEnd(); +} + +int main(int argc, char** argv) +{ + TF_Init(); + if(argc < 2) + { + printf("USAGE: %s \n",argv[0]); + return 1; + } + GC gc; + gc.RegisterEverythingCallback(MyPlugin); + gc.Start(); + GCList ls(gc); + TRootEnvironment* env = TRootEnvironment::Create(ls,TDictionary::Create(ls)); + Tesses::Framework::Filesystem::LocalFilesystem fs; + TStd::RegisterStd(&gc,env); + env->LoadFileWithDependencies(&gc, &fs, fs.SystemToVFSPath(argv[1])); + + TList* args = TList::Create(ls); + for(int arg=1;argAdd(std::string(argv[arg])); + + auto res = env->CallFunction(ls,"main",{args}); + int64_t iresult; + if(GetObject(res,iresult)) + return (int)iresult; + return 0; +} +``` + +main.tcross (needs to be compiled with crossc): +```go +func main(args) +{ + //no reflection necessary + + var res = MyPlugin.MyFunction(42); + if(TypeOf(res) == "List") + { + Console.WriteLine(res); + } + + var res = MyPlugin.MyFunction(42,["Hello","John"]); + if(TypeOf(res) == "List") + { + Console.WriteLine(res); + } +} +``` + +### Forking (implementing it in runtime library itself) +- edit include/CrossLang.hpp +- in TStd class insert, static void RegisterMyPlugin(GC* gc, TRootEnvironment* env); where MyPlugin is whatever you want +- edit src/runtime_methods/std.cpp +- in TStd::RegisterStd before register everything call TStd::RegisterMyPlugin(gc, env); +- in CMakeLists.txt within list(APPEND CROSSLANG_SOURCE **Many Source Files**) you must add src/runtime_methods/myplugin.cpp + +myplugin.cpp: +```c++ +#include +namespace Tesses::CrossLang +{ + void TStd::RegisterMyPlugin(GC* gc, TRootEnvironment* env) + { + GCList ls(gc); + TDictionary* dict = TDictionary::Create(ls,{ + TDItem("MyFunction", TExternalMethod::Create(ls, "A description for function",{"myLong","$optionalList"},[](GCList& ls, std::vector args)->TObject { + int64_t n; + if(GetArgument(args,0,n)) + { + TList* myLs; + if(GetArgumentHeap(args,1,myLs)) + { + //do something with the list + myLs->Add(n); + return TList::Create(ls, {n,myLs}); + } + + + return TList::Create(ls, {n}); + } + + return nullptr; + })) + }); + + gc->BarrierBegin(); + env->SetVariable("MyPlugin",dict); + gc->BarrierEnd(); + } +} +``` \ No newline at end of file diff --git a/docs/HANDBOOK.md b/docs/HANDBOOK.md new file mode 100644 index 0000000..8ddcaab --- /dev/null +++ b/docs/HANDBOOK.md @@ -0,0 +1,605 @@ +## CrossLang Handbook + +This is a book for my language found [here](https://crosslang.tesseslanguage.com/) + + + +## Statements and loops + + + +#### Declare a variable (it is an expression) + +You can (the var is optional but should be used if the root environment has the same variable and you don't want to set it) + +```js +var myVariable = 42; +``` + +#### Set a variable (it is an expression) + +```js +myVariable = "I have set the variable to a string"; +``` + +#### If statements + +The {} are optional if you only need one statement + +```js +var myExpression = 0; +var myOtherExpression = { + mol = 42 +}; + +if(myExpression) +{ + Console.WriteLine("myExpression is truthy"); +} +else if(myOtherExpression) +{ + //this will run based on the condition + Console.WriteLine("myExpression is falsey but myOtherExpression is truthy"); +} +else { + Console.WriteLine("both myExpression and myOtherExpression are falsey"); +} +``` + +#### For loop + +Note: i will leak, also you can use curly brackets like if + +```js +for(var i = 0; i < 42; i++) + Console.WriteLine(i); +``` + +#### Each loop + +```js +enumerable MyItterator() +{ + yield 42; + yield 100; + yield 88; +} +each(var item : MyItterator()) +{ + Console.WriteLine(item); +} + + +each(var item : [3,7,12]) +{ + Console.WriteLine(item); +} + + +/* + This should output + 42 + 100 + 88 + 3 + 7 + 12 +*/ +``` + +#### While loop + +```js +while(conditionLikeIf) +{ + Console.WriteLine("Hi"); +} + +//If conditionLikeIf is falsey we never print Hi +//If conditionLikeIf is truthy we print Hi until it is falsey +``` + +#### Do loop + +```js +do(conditionLikeIf) +{ + Console.WriteLine("Hi"); +} + +//If conditionLikeIf is falsey we print Hi once +//If conditionLikeIf is truthy we print Hi until it is falsey +``` + + + +#### Try statement + +Note: you can't yield, return, break or continue from inside a try statement (also applys to catch and finally due to these being internally closures) + +```js +try { + Console.WriteLine("God is good."); + throw "my error"; + Console.WriteLine("I am lonely, I am never called."); +} +catch(ex) +{ + Console.WriteLine($"I got an error {ex}."); +} +finally { + Console.WriteLine("For God so loved the world that he gave his only begotten Son, that whoever believes in him should not perish but have eternal life."); +} + + +/* + This should print + God is good. + I got an error my error. + For God so loved the world that he gave his only begotten Son, that whoever believes in him should not perish but have eternal life. +*/ +``` + +#### Switch statement + +NOTE: you can use dictionaries with overloaded equals operator (we will pass the switch's expression as the argument) + +```js +o = { + operator== = (this,v) => { + //this is implicitly passed by runtime, but you must declare it if you want it + return (v * 7) == 42; + } +}; + +var val = 6; + +switch(val) +{ + case 4: + Console.WriteLine("I am four"); + break; + case 5: + Console.WriteLine("I am five"); + break; + case o: + Console.WriteLine("Hitchhiker's guide to the galaxy meaning of life"); + break; + default: + Console.WriteLine("We don't support it we are sorry"); +} +``` + + + +#### Function declaration + +```go +//you can create functions on dictionaries so long as they are on root environment (they are declared there before root scope is evaluated so this is possible) +My.Func.Joel = 59; //Because My.Func.Is.A.Few.Deep creates the dictionaries via runtime + +func My.Func.Is.A.Few.Deep(a, b) +{ + return a * b; +} +func main(args) +{ + Console.WriteLine(My.Func.Is.A.Few.Deep(6,7)); +} +``` + +#### Function inside a function + +Oh yeah functions can have exclusively a expression as a body which does not need a return keyword + +```go +func main(args) +{ + func MyOther(a,b) a * b; + Console.WriteLine(MyOther(a,b)); +} +``` + +#### Document a function + +```go +/^ + This function returns 42 +^/ +func MyFunction() 42; +``` + +#### Return statement + +You can also return nothing + +```js +return 42; +return; +``` + +#### Yield statement + +This will halt the function's execution and return the running function object (were you can pop the item and resume it) + +To automaticly make a function enumerable (with each statement) you must use ``enumerable`` instead of func when declaring your function + +```js +yield 42; +``` + +#### Throw statement + +You can catch this with ``try statement`` with ``catch`` clause + +```js +throw "You can replace me with another string or any other object"; +``` + +#### Defer statement + +Your code will be called when your scope is destroyed + +```go +{ + defer { + Console.WriteLine("In defer"); + } + Console.WriteLine("About to leave scope"); +} +Console.WriteLine("Left scope"); + + +/* +The output should be + +About to leave scope +In defer +Left scope +*/ +``` + +## Expressions + +We will place it as a parameter to a function ``doThing`` but these will work anywhere like setting a variable or another expression + +#### Undefined + +```js +doThing(undefined); +``` + +#### Null + +```js +doThing(null); +``` + +#### Boolean + +```js +doThing(false); +doThing(true); +``` + +#### Long + +A signed 64 bit number + +```js +doThing(94); +doThing(599929022); +``` + +#### Double + +A 64 bit floating pointer number + +```js +doThing(5.25); +doThing(942.90204); +``` + +#### Char + +Yes we support single character strings (this escapes a double quote) + +```csharp +doThing('\"'); +``` + +#### String + +Second is an interopolated string + +```csharp +doThing("Hi"); +doThing($"My name is {name}"); +``` + +#### List + +```js +doThing( + [4,5,9,42,true,"Hi",'\n'] +); +``` + +#### Dictionary + +```js +doThing( + { + a = "This is an option", + b = 42, + c = 9 + } +); +``` + +###### Enumerating a dictionary + +```js +var dict = { a = 5, b = 42}; +each(var item : Dictionary.Items(dict)) +{ + Console.WriteLine($"Key: {item.Key}"); + Console.WriteLine($"Value: {item.Value}"); + Console.WriteLine(); +} +/* +This prints +Key: a +Value: 5 + +Key: b +Value: 42 + +*/ +``` + + + +###### Getting a value from dictionary + +```go +func doThing(data) +{ + Console.WriteLine(data.b); //will print 42 +} +``` + +###### Setting a value to dictionary + +```go +func doThing(data) +{ + data.a = true; //replace a with a different value + data.someFieldThatDoesNotExist = 9; //define a new field entirely + +} +``` + +###### Methods + +Note that this is optional but if you do have it it must be the first argument, the this argument points to the dictionary + +```go +var item = { + myMethod = (this, a, b) => { + return a * b; + } +}; + +Console.WriteLine(item.myMethod(6,7)); //prints 42 +``` + +Or + +```go +var item = {}; +func item.myMethod(this, a, b) +{ + return a * b; +} +Console.WriteLine(item.myMethod(6,7)); //prints 42 +``` + +###### Properties + +They are declared like every other method except they are prefixed with ``get`` or ``set`` + +```go +var item = { + getProp = (this)=>{ + return 42; + }, + setProp = (this,v)=>{ + //do something with v + } +}; + +Console.WriteLine(item.Prop); //equivalent to Console.WriteLine(item.getProp()); + +item.Prop = 100; //equivalent to item.setProp(100); will do nothing in this example + +``` + +###### Operator overloading + +```js +var item = { + operator+ = (this,right) => { + return 4 + a; + } + operator- = (this,right) => { + return 4 - right; //be careful right may be undefined if the negate operator is used + } +}; + +Console.WriteLine(item + 5); //prints 9 +``` + +###### Array operator overloading + +```js +var item = { + GetAt = (index)=>{ + return index * 2; //we multiply index by two and return + }, + SetAt => (index,value)=>{ + Console.WriteLine($"The index was {index} and value was {value}"); + } +}; + +Console.WriteLine(item[21]); //prints 42 +item["Hi"] = "John"; //prints The index was Hi and value was John +``` + +#### ByteArray + +```js +var bA = new ByteArray(1); +ba[0] = 42; +Console.WriteLine(ba.ToString()); //prints * +``` + +###### ByteArray from string + +```js +var bA = new ByteArray("Hi"); +//we can get bytes like this bA[0] which will be 72 +//we can get length by bA.Count or bA.Length +``` + +#### Embedding files (you can't do this easily in C/C++ and C# not as easy as this) + +```js +var bA = embed("myFile.png"); //will embed the file res/myFile.png +//it is stored as a RESO in the crvm file and bA is a ByteArray +//note the argument MUST be a constant string +//note you can access a subdirectory of res as well just use dir/file.bin +``` + +#### Streams + + + +###### From an existing file + +```js +var strm = FS.Local.OpenFile("file.bin","rb"); + +var data = new ByteArray(1024); +var read = strm.Read(data,0,data.Count); +//use ReadBlock to ensure you have 1024 bytes if not near end of stream + +//use WriteBlock to ensure you write the amount you say you need + +strm.Close(); +``` + +#### VFS + +```js +var myVFS = FS.Local; //FS.Local is the local filesystem + + +myVFS.CreateDirectory("myDir"); //creates directory in current dir (only local) + +myVFS.DeleteDirectory("myDir"); //now we delete it + + +myVFS.CreateDirectory(Path.FromString(Env.Downloads) / "Folder in Downloads"); //Create a folder in downloads +``` + + + +#### Path + +```js +var path = / "Path" / "To" / "Directory"; +var relativePath = ./"another"/"path"; +var combined = path / relativePath; +var someString = "joel"; +var combinedAndStr = combined / someString; +var combinedWithExtension = combined + ".mp4"; +Console.WriteLine(combined); // outputs /Path/To/Directory/another/path +Console.WriteLine(combined); // outputs /Path/To/Directory/another/path/joel +Console.WriteLine(combinedWithExtension); //outputs /Path/To/Directory/another/path.mp4 +//these paths are used with vfs + + +//to get parent directory use combined.GetParent(); +//to get filename use combined.GetFileName(); +//to get extension use combinedWithExtension.GetExtension();, it will be the extension including . +//to change the extension use combinedWithExtension.ChangeExtension(".mkv"); +//to remove the extension use combinedWithExtension.ChangeExtension(null); or combinedWithExtension.ChangeExtension(); +``` + +#### Html Litterals +```go +//comments are just crosslang ones + +//we will be setting to myHtml for the html (returns a string) (it is already html escaped) +myHtml =

{someStringExpression}

; + +//a link, in this example we have a dictionary named node with string fields href and text +//lets say node.text equaled List and href contained ./mypage?name=Joel&file=steve.bin myHtml would be equal to "List<int>" (the \ are just there to make this a valid string for demonstration purposes, there aren't really backslashes) +myHtml = {node.text}; + + +//for (works like for loop but is in html form, the for part is not rendered in the final string) +myHtml = +
{i}
+ ; + +//each +myHtml = ; + +//do +myHtml = ; + +//while +myHtml = ; + +//if + +myHtml = + + //if expr is 42 + + + //if expr is not 42 + //you would use another if here instead of else if + +; + +myHtml = Hello; //would just be Hello (useful for templating as it works just like div but the null part is not emited into string) + +myHtml = ; //allowing you to emit unescaped html from string into html litteral (useful for templating) +``` + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/README.md b/docs/README.md index de7bb09..7fcb9ef 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,563 +1,7 @@ -## CrossLang Handbook - -This is a book for my language found [here](https://crosslang.tesseslanguage.com/) - - - -## Statements and loops - - - -#### Declare a variable (it is an expression) - -You can (the var is optional but should be used if the root environment has the same variable and you don't want to set it) - -```js -var myVariable = 42; -``` - -#### Set a variable (it is an expression) - -```js -myVariable = "I have set the variable to a string"; -``` - -#### If statements - -The {} are optional if you only need one statement - -```js -var myExpression = 0; -var myOtherExpression = { - mol = 42 -}; - -if(myExpression) -{ - Console.WriteLine("myExpression is truthy"); -} -else if(myOtherExpression) -{ - //this will run based on the condition - Console.WriteLine("myExpression is falsey but myOtherExpression is truthy"); -} -else { - Console.WriteLine("both myExpression and myOtherExpression are falsey"); -} -``` - -#### For loop - -Note: i will leak, also you can use curly brackets like if - -```js -for(var i = 0; i < 42; i++) - Console.WriteLine(i); -``` - -#### Each loop - -```js -enumerable MyItterator() -{ - yield 42; - yield 100; - yield 88; -} -each(var item : MyItterator()) -{ - Console.WriteLine(item); -} - - -each(var item : [3,7,12]) -{ - Console.WriteLine(item); -} - - -/* - This should output - 42 - 100 - 88 - 3 - 7 - 12 -*/ -``` - -#### While loop - -```js -while(conditionLikeIf) -{ - Console.WriteLine("Hi"); -} - -//If conditionLikeIf is falsey we never print Hi -//If conditionLikeIf is truthy we print Hi until it is falsey -``` - -#### Do loop - -```js -do(conditionLikeIf) -{ - Console.WriteLine("Hi"); -} - -//If conditionLikeIf is falsey we print Hi once -//If conditionLikeIf is truthy we print Hi until it is falsey -``` - - - -#### Try statement - -Note: you can't yield, return, break or continue from inside a try statement (also applys to catch and finally due to these being internally closures) - -```js -try { - Console.WriteLine("God is good."); - throw "my error"; - Console.WriteLine("I am lonely, I am never called."); -} -catch(ex) -{ - Console.WriteLine($"I got an error {ex}."); -} -finally { - Console.WriteLine("For God so loved the world that he gave his only begotten Son, that whoever believes in him should not perish but have eternal life."); -} - - -/* - This should print - God is good. - I got an error my error. - For God so loved the world that he gave his only begotten Son, that whoever believes in him should not perish but have eternal life. -*/ -``` - -#### Switch statement - -NOTE: you can use dictionaries with overloaded equals operator (we will pass the switch's expression as the argument) - -```js -o = { - operator== = (this,v) => { - //this is implicitly passed by runtime, but you must declare it if you want it - return (v * 7) == 42; - } -}; - -var val = 6; - -switch(val) -{ - case 4: - Console.WriteLine("I am four"); - break; - case 5: - Console.WriteLine("I am five"); - break; - case o: - Console.WriteLine("Hitchhiker's guide to the galaxy meaning of life"); - break; - default: - Console.WriteLine("We don't support it we are sorry"); -} -``` - - - -#### Function declaration - -```go -func My.Func.Is.A.Few.Deep(a, b) -{ - return a * b; -} -func main(args) -{ - Console.WriteLine(My.Func.Is.A.Few.Deep(6,7)); -} -``` - -#### Function inside a function - -Oh yeah functions can have exclusively a expression as a body which does not need a return keyword - -```go -func main(args) -{ - func MyOther(a,b) a * b; - Console.WriteLine(MyOther(a,b)); -} -``` - -#### Document a function - -```go -/^ - This function returns 42 -^/ -func MyFunction() 42; -``` - -#### Return statement - -You can also return nothing - -```js -return 42; -return; -``` - -#### Yield statement - -This will halt the function's execution and return the running function object (were you can pop the item and resume it) - -To automaticly make a function enumerable (with each statement) you must use ``enumerable`` instead of func when declaring your function - -```js -yield 42; -``` - -#### Throw statement - -You can catch this with ``try statement`` with ``catch`` clause - -```js -throw "You can replace me with another string or any other object"; -``` - -#### Defer statement - -Your code will be called when your scope is destroyed - -```go -{ - defer { - Console.WriteLine("In defer"); - } - Console.WriteLine("About to leave scope"); -} -Console.WriteLine("Left scope"); - - -/* -The output should be - -About to leave scope -In defer -Left scope -*/ -``` - -## Expressions - -We will place it as a parameter to a function ``doThing`` but these will work anywhere like setting a variable or another expression - -#### Undefined - -```js -doThing(undefined); -``` - -#### Null - -```js -doThing(null); -``` - -#### Boolean - -```js -doThing(false); -doThing(true); -``` - -#### Long - -A signed 64 bit number - -```js -doThing(94); -doThing(599929022); -``` - -#### Double - -A 64 bit floating pointer number - -```js -doThing(5.25); -doThing(942.90204); -``` - -#### Char - -Yes we support single character strings (this escapes a double quote) - -```csharp -doThing('\"'); -``` - -#### String - -Second is an interopolated string - -```csharp -doThing("Hi"); -doThing($"My name is {name}"); -``` - -#### List - -```js -doThing( - [4,5,9,42,true,"Hi",'\n'] -); -``` - -#### Dictionary - -```js -doThing( - { - a = "This is an option", - b = 42, - c = 9 - } -); -``` - -###### Enumerating a dictionary - -```js -var dict = { a = 5, b = 42}; -each(var item : Dictionary.Items(dict)) -{ - Console.WriteLine($"Key: {item.Key}"); - Console.WriteLine($"Value: {item.Value}"); - Console.WriteLine(); -} -/* -This prints -Key: a -Value: 5 - -Key: b -Value: 42 - -*/ -``` - - - -###### Getting a value from dictionary - -```go -func doThing(data) -{ - Console.WriteLine(data.b); //will print 42 -} -``` - -###### Setting a value to dictionary - -```go -func doThing(data) -{ - data.a = true; //replace a with a different value - data.someFieldThatDoesNotExist = 9; //define a new field entirely - -} -``` - -###### Methods - -Note that this is optional but if you do have it it must be the first argument, the this argument points to the dictionary - -```go -var item = { - myMethod = (this, a, b) => { - return a * b; - } -}; - -Console.WriteLine(item.myMethod(6,7)); //prints 42 -``` - -Or - -```go -var item = {}; -func item.myMethod(this, a, b) -{ - return a * b; -} -Console.WriteLine(item.myMethod(6,7)); //prints 42 -``` - -###### Properties - -They are declared like every other method except they are prefixed with ``get`` or ``set`` - -```go -var item = { - getProp = (this)=>{ - return 42; - }, - setProp = (this,v)=>{ - //do something with v - } -}; - -Console.WriteLine(item.Prop); //equivalent to Console.WriteLine(item.getProp()); - -item.Prop = 100; //equivalent to item.setProp(100); will do nothing in this example - -``` - -###### Operator overloading - -```js -var item = { - operator+ = (this,right) => { - return 4 + a; - } - operator- = (this,right) => { - return 4 - right; //be careful right may be undefined if the negate operator is used - } -}; - -Console.WriteLine(item + 5); //prints 9 -``` - -###### Array operator overloading - -```js -var item = { - GetAt = (index)=>{ - return index * 2; //we multiply index by two and return - }, - SetAt => (index,value)=>{ - Console.WriteLine($"The index was {index} and value was {value}"); - } -}; - -Console.WriteLine(item[21]); //prints 42 -item["Hi"] = "John"; //prints The index was Hi and value was John -``` - -#### ByteArray - -```js -var bA = ByteArray(1); -ba[0] = 42; -Console.WriteLine(ba.ToString()); //prints * -``` - -###### ByteArray from string - -```js -var bA = ByteArray("Hi"); -//we can get bytes like this bA[0] which will be 72 -//we can get length by bA.Count or bA.Length -``` - -#### Embedding files (you can't do this easily in C/C++ and C# not as easy as this) - -```js -var bA = embed("myFile.png"); //will embed the file res/myFile.png -//it is stored as a RESO in the crvm file and bA is a ByteArray -//note the argument MUST be a constant string -//note you can access a subdirectory of res as well just use dir/file.bin -``` - -#### Streams - - - -###### From an existing file - -```js -var strm = FS.Local.OpenFile("file.bin","rb"); - -var data = ByteArray(1024); -var read = strm.Read(data,0,data.Count); -//use ReadBlock to ensure you have 1024 bytes if not near end of stream - -//use WriteBlock to ensure you write the amount you say you need - -strm.Close(); -``` - -#### VFS - -```js -var myVFS = FS.Local; //FS.Local is the local filesystem - - -myVFS.CreateDirectory("myDir"); //creates directory in current dir (only local) - -myVFS.DeleteDirectory("myDir"); //now we delete it - - -myVFS.CreateDirectory(Path.FromString(Env.Downloads) / "Folder in Downloads"); //Create a folder in downloads -``` - - - -#### Path - -```js -var path = / "Path" / "To" / "Directory"; -var relativePath = ./"another"/"path"; -var combined = path / relativePath; -var someString = "joel"; -var combinedAndStr = combined / someString; -var combinedWithExtension = combined + ".mp4"; -Console.WriteLine(combined); // outputs /Path/To/Directory/another/path -Console.WriteLine(combined); // outputs /Path/To/Directory/another/path/joel -Console.WriteLine(combinedWithExtension); //outputs /Path/To/Directory/another/path.mp4 -//these paths are used with vfs - - -//to get parent directory use combined.GetParent(); -//to get filename use combined.GetFileName(); -//to get extension use combinedWithExtension.GetExtension();, it will be the extension including . -//to change the extension use combinedWithExtension.ChangeExtension(".mkv"); -//to remove the extension use combinedWithExtension.ChangeExtension(null); or combinedWithExtension.ChangeExtension(); -``` - - - - - - - - - - - - - - - - - - - - - - - - - - +# Tesses CrossLang +This is the docs for my language +- [Handbook](HANDBOOK.md) +- [C++ Layer](CPP_LAYER.md) +- [Extend the runtime](EXTENDING_RUNTIME.md) \ No newline at end of file diff --git a/src/compiler/parser.cpp b/src/compiler/parser.cpp index 04f0bf2..6c0200c 100644 --- a/src/compiler/parser.cpp +++ b/src/compiler/parser.cpp @@ -158,7 +158,7 @@ namespace Tesses::CrossLang } this->EnsureSymbol(">"); - if(tagName != "if" && tagName != "true" && tagName != "false" && tagName != "for" && tagName != "while" && tagName != "do" && tagName != "each") + if(tagName != "if" && tagName != "true" && tagName != "false" && tagName != "for" && tagName != "while" && tagName != "do" && tagName != "each" && tagName != "null") { std::string myVal = ""); + std::vector _nodes; + parseFn(_nodes,"null"); + nodes.push_back(AdvancedSyntaxNode::Create(ScopeNode,false,_nodes)); + + } else if(tagName == "while") { diff --git a/src/runtime_methods/io.cpp b/src/runtime_methods/io.cpp index 62434b8..92d0231 100644 --- a/src/runtime_methods/io.cpp +++ b/src/runtime_methods/io.cpp @@ -4,60 +4,6 @@ namespace Tesses::CrossLang { - static TObject FS_SubdirFilesystem(GCList& ls, std::vector args) - { - TVFSHeapObject* vfsho; - - Tesses::Framework::Filesystem::VFSPath path; - - if(GetArgumentHeap(args,0,vfsho) && GetArgumentAsPath(args,1,path)) - { - return TVFSHeapObject::Create(ls,new Tesses::Framework::Filesystem::SubdirFilesystem(new TObjectVFS(ls.GetGC(),vfsho),path,true)); - } - return nullptr; - } - static TObject FS_MountableFilesystem(GCList& ls, std::vector args) - { - TVFSHeapObject* vfsho; - - if(GetArgumentHeap(args,0,vfsho)) - { - return TVFSHeapObject::Create(ls,new Tesses::Framework::Filesystem::MountableFilesystem(new TObjectVFS(ls.GetGC(),vfsho),true)); - } - return nullptr; - } - static TObject FS_MemoryStream(GCList& ls, std::vector args) - { - bool writable; - if(GetArgument(args,0,writable)) - { - return TStreamHeapObject::Create(ls,new Tesses::Framework::Streams::MemoryStream(writable)); - } - return nullptr; - } - static TObject FS_CreateMemoryFilesystem(GCList& ls, std::vector args) - { - return TVFSHeapObject::Create(ls, new Tesses::Framework::Filesystem::MemoryFilesystem()); - } - - static TObject FS_CreateFilesystem(GCList& ls, std::vector args) - { - TDictionary* dict; - if(GetArgumentHeap(args,0,dict)) - { - return TVFSHeapObject::Create(ls, new TObjectVFS(ls.GetGC(),dict)); - } - return nullptr; - } - static TObject FS_CreateStream(GCList& ls, std::vector args) - { - TDictionary* dict; - if(GetArgumentHeap(args,0,dict)) - { - return TStreamHeapObject::Create(ls, new TObjectStream(ls.GetGC(),dict)); - } - return nullptr; - } static TObject FS_MakeFull(GCList& ls, std::vector args) { Tesses::Framework::Filesystem::VFSPath path; @@ -126,6 +72,27 @@ namespace Tesses::CrossLang } return ""; } + static TObject FS_ReadAllBytes(GCList& ls, std::vector args) + { + Tesses::Framework::Filesystem::VFSPath path; + TVFSHeapObject* vfs; + if(GetArgumentHeap(args,0,vfs) && GetArgumentAsPath(args,1,path)) + { + auto txtFile = vfs->vfs->OpenFile(path,"rb"); + if(txtFile == nullptr) return nullptr; + auto res = TByteArray::Create(ls); + std::array data; + size_t read; + do { + read = txtFile->ReadBlock(data.data(),data.size()); + res->data.insert(res->data.end(),data.begin(),data.begin()+read); + } while(read != 0); + delete txtFile; + return res; + } + return nullptr; + } + static TObject FS_WriteAllText(GCList& ls, std::vector args) { @@ -143,6 +110,22 @@ namespace Tesses::CrossLang } return nullptr; } + static TObject FS_WriteAllBytes(GCList& ls, std::vector args) + { + Tesses::Framework::Filesystem::VFSPath path; + + TVFSHeapObject* vfs; + + TByteArray* bArray; + if(GetArgumentHeap(args,0,vfs) && GetArgumentAsPath(args,1,path) && GetArgumentHeap(args,2,bArray)) + { + auto txtFile = vfs->vfs->OpenFile(path,"wb"); + if(txtFile == nullptr) return nullptr; + txtFile->WriteBlock(bArray->data.data(),bArray->data.size()); + delete txtFile; + } + return nullptr; + } static TObject FS_getCurrentPath(GCList& ls, std::vector args) { @@ -178,13 +161,9 @@ namespace Tesses::CrossLang dict->DeclareFunction(gc, "ReadAllText","Read all text from file", {"fs","filename"},FS_ReadAllText); dict->DeclareFunction(gc, "WriteAllText","Write all text to file", {"fs","filename","content"},FS_WriteAllText); - dict->DeclareFunction(gc, "MountableFilesystem","Create a mountable filesystem",{"root"}, FS_MountableFilesystem); - dict->DeclareFunction(gc, "SubdirFilesystem","Create a subdir filesystem",{"fs","subdir"}, FS_SubdirFilesystem); - dict->DeclareFunction(gc, "MemoryStream","Create a memory stream",{"writable"}, FS_MemoryStream); - dict->DeclareFunction(gc, "CreateStream","Create stream", {"strm"},FS_CreateStream); - dict->DeclareFunction(gc, "CreateFilesystem","Create filesystem", {"fs"},FS_CreateFilesystem); - - dict->DeclareFunction(gc, "CreateMemoryFilesystem","Create in memory filesystem", {},FS_CreateMemoryFilesystem); + dict->DeclareFunction(gc, "ReadAllBytes","Read all bytes from file", {"fs","filename"},FS_ReadAllBytes); + dict->DeclareFunction(gc, "WriteAllBytes","Write all bytes to file", {"fs","filename","content"},FS_WriteAllBytes); + dict->DeclareFunction(gc, "CreateArchive", "Create a crvm archive",{"fs","strm","name","version","info"},FS_CreateArchive); dict->DeclareFunction(gc,"ExtractArchive", "Extract a crvm archive",{"strm","vfs"},FS_ExtractArchive); env->DeclareVariable("FS", dict); diff --git a/src/runtime_methods/std.cpp b/src/runtime_methods/std.cpp index a1f0f83..0881a50 100644 --- a/src/runtime_methods/std.cpp +++ b/src/runtime_methods/std.cpp @@ -118,7 +118,61 @@ namespace Tesses::CrossLang TVFSHeapObject* vfs; return GetArgumentHeap(args,0,vfs); } - + static TObject New_SubdirFilesystem(GCList& ls, std::vector args) + { + TVFSHeapObject* vfsho; + + Tesses::Framework::Filesystem::VFSPath path; + + if(GetArgumentHeap(args,0,vfsho) && GetArgumentAsPath(args,1,path)) + { + return TVFSHeapObject::Create(ls,new Tesses::Framework::Filesystem::SubdirFilesystem(new TObjectVFS(ls.GetGC(),vfsho),path,true)); + } + return nullptr; + } + static TObject New_MountableFilesystem(GCList& ls, std::vector args) + { + TVFSHeapObject* vfsho; + + if(GetArgumentHeap(args,0,vfsho)) + { + return TVFSHeapObject::Create(ls,new Tesses::Framework::Filesystem::MountableFilesystem(new TObjectVFS(ls.GetGC(),vfsho),true)); + } + return nullptr; + } + static TObject New_MemoryStream(GCList& ls, std::vector args) + { + bool writable; + if(GetArgument(args,0,writable)) + { + return TStreamHeapObject::Create(ls,new Tesses::Framework::Streams::MemoryStream(writable)); + } + return nullptr; + } + static TObject New_MemoryFilesystem(GCList& ls, std::vector args) + { + return TVFSHeapObject::Create(ls, new Tesses::Framework::Filesystem::MemoryFilesystem()); + } + + static TObject New_Filesystem(GCList& ls, std::vector args) + { + TDictionary* dict; + if(GetArgumentHeap(args,0,dict)) + { + return TVFSHeapObject::Create(ls, new TObjectVFS(ls.GetGC(),dict)); + } + return nullptr; + } + static TObject New_Stream(GCList& ls, std::vector args) + { + TDictionary* dict; + if(GetArgumentHeap(args,0,dict)) + { + return TStreamHeapObject::Create(ls, new TObjectStream(ls.GetGC(),dict)); + } + return nullptr; + } + static TObject TypeOf(GCList& ls, std::vector args) { if(args.size() < 1) return "Undefined"; @@ -334,6 +388,15 @@ namespace Tesses::CrossLang env->permissions.canRegisterRoot=true; TDictionary* newTypes = TDictionary::Create(ls); + + newTypes->DeclareFunction(gc, "MountableFilesystem","Create a mountable filesystem",{"root"}, New_MountableFilesystem); + newTypes->DeclareFunction(gc, "SubdirFilesystem","Create a subdir filesystem",{"fs","subdir"}, New_SubdirFilesystem); + newTypes->DeclareFunction(gc, "MemoryStream","Create a memory stream",{"writable"}, New_MemoryStream); + newTypes->DeclareFunction(gc, "Stream","Create stream", {"strm"},New_Stream); + newTypes->DeclareFunction(gc, "Filesystem","Create filesystem", {"fs"},New_Filesystem); + + newTypes->DeclareFunction(gc, "MemoryFilesystem","Create in memory filesystem", {},New_MemoryFilesystem); + newTypes->DeclareFunction(gc,"Version","Create a version object",{"$major","$minor","$patch","$build","$stage"},[](GCList& ls, std::vector args)->TObject{ int64_t major=1; @@ -375,7 +438,7 @@ namespace Tesses::CrossLang env->DeclareFunction(gc, "TypeIsVFS","Get whether object is a virtual filesystem",{"object"},TypeIsVFS); - env->DeclareFunction(gc, "Regex", "Create regex object",{"regex"},[](GCList& ls,std::vector args)->TObject { + newTypes->DeclareFunction(gc, "Regex", "Create regex object",{"regex"},[](GCList& ls,std::vector args)->TObject { std::string str; if(GetArgument(args,0,str)) { @@ -384,7 +447,7 @@ namespace Tesses::CrossLang } return nullptr; }); - env->DeclareFunction(gc, "Mutex", "Create mutex",{}, [](GCList& ls,std::vector args)->TObject { + newTypes->DeclareFunction(gc, "Mutex", "Create mutex",{}, [](GCList& ls,std::vector args)->TObject { ls.GetGC()->BarrierBegin(); auto mtx = TDictionary::Create(ls); auto native = TNative::Create(ls, new Tesses::Framework::Threading::Mutex(),[](void* ptr)->void{ @@ -428,7 +491,7 @@ namespace Tesses::CrossLang ls.GetGC()->BarrierEnd(); return mtx; }); - env->DeclareFunction(gc, "Thread","Create thread",{"callback"},[](GCList& ls, std::vector args)-> TObject + newTypes->DeclareFunction(gc, "Thread","Create thread",{"callback"},[](GCList& ls, std::vector args)-> TObject { if(args.size() == 1 && std::holds_alternative(args[0])) { @@ -440,7 +503,7 @@ namespace Tesses::CrossLang } return Undefined(); }); - env->DeclareFunction(gc,"ByteArray","Create bytearray, with optional either size (to size it) or string argument (to fill byte array)",{"$data"},ByteArray); + newTypes->DeclareFunction(gc,"ByteArray","Create bytearray, with optional either size (to size it) or string argument (to fill byte array)",{"$data"},ByteArray); gc->BarrierBegin(); env->DeclareVariable("Version", TDictionary::Create(ls,{ TDItem("Parse",TExternalMethod::Create(ls,"Parse version from string",{"versionStr"},[](GCList& ls, std::vector args)->TObject{