Do more docs
This commit is contained in:
@ -45,7 +45,7 @@ endif()
|
|||||||
include(FetchContent)
|
include(FetchContent)
|
||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
TessesFramework
|
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)
|
FetchContent_MakeAvailable(TessesFramework)
|
||||||
else()
|
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})
|
find_package(TessesCrossLang REQUIRED)
|
||||||
CROSSLANG_LINK_DEPS(crosslang_static)
|
|
||||||
|
add_library(MyPlugin SHARED lib.cpp)
|
||||||
|
|
||||||
|
target_link_libraries(MyPlugin PUBLIC TessesCrossLang::crosslang_shared)
|
||||||
if(CROSSLANG_FETCHCONTENT)
|
if(CROSSLANG_FETCHCONTENT)
|
||||||
target_link_libraries(crosslang_static PUBLIC tessesframework)
|
target_link_libraries(crosslang_static PUBLIC tessesframework)
|
||||||
else()
|
else()
|
||||||
|
|||||||
1
docs/CPP_LAYER.md
Normal file
1
docs/CPP_LAYER.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
# C++ Layer
|
||||||
232
docs/EXTENDING_RUNTIME.md
Normal file
232
docs/EXTENDING_RUNTIME.md
Normal file
@ -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 <CrossLang.hpp>
|
||||||
|
|
||||||
|
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<TObject> 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 <CrossLang.hpp>
|
||||||
|
|
||||||
|
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<TObject> 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 <filename.crvm> <args...>\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;arg<argc;arg++)
|
||||||
|
args->Add(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 <CrossLang.hpp>
|
||||||
|
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<TObject> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
605
docs/HANDBOOK.md
Normal file
605
docs/HANDBOOK.md
Normal file
@ -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 = <h1>{someStringExpression}</h1>;
|
||||||
|
|
||||||
|
//a link, in this example we have a dictionary named node with string fields href and text
|
||||||
|
//lets say node.text equaled List<int> and href contained ./mypage?name=Joel&file=steve.bin myHtml would be equal to "<a target=\"_blank\" href=\"./mypage?name=Joel&file=steve.bin\">List<int></a>" (the \ are just there to make this a valid string for demonstration purposes, there aren't really backslashes)
|
||||||
|
myHtml = <a target="_blank" href={node.href}>{node.text}</a>;
|
||||||
|
|
||||||
|
|
||||||
|
//for (works like for loop but is in html form, the for part is not rendered in the final string)
|
||||||
|
myHtml = <for(var i = 0; i < 42; i++)>
|
||||||
|
<h6>{i}</h6>
|
||||||
|
</for>;
|
||||||
|
|
||||||
|
//each
|
||||||
|
myHtml = <each(var item : list)></each>;
|
||||||
|
|
||||||
|
//do
|
||||||
|
myHtml = <do(expr == 42)></do>;
|
||||||
|
|
||||||
|
//while
|
||||||
|
myHtml = <while(expr == 42)></while>;
|
||||||
|
|
||||||
|
//if
|
||||||
|
|
||||||
|
myHtml = <if(expr == 42)>
|
||||||
|
<true>
|
||||||
|
//if expr is 42
|
||||||
|
</true>
|
||||||
|
<false>
|
||||||
|
//if expr is not 42
|
||||||
|
//you would use another if here instead of else if
|
||||||
|
</false>
|
||||||
|
</if>;
|
||||||
|
|
||||||
|
myHtml = <null>Hello</null>; //would just be Hello (useful for templating as it works just like div but the null part is not emited into string)
|
||||||
|
|
||||||
|
myHtml = <raw(stringExpr)>; //allowing you to emit unescaped html from string into html litteral (useful for templating)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
566
docs/README.md
566
docs/README.md
@ -1,563 +1,7 @@
|
|||||||
## CrossLang Handbook
|
# Tesses CrossLang
|
||||||
|
|
||||||
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();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
This is the docs for my language
|
||||||
|
|
||||||
|
- [Handbook](HANDBOOK.md)
|
||||||
|
- [C++ Layer](CPP_LAYER.md)
|
||||||
|
- [Extend the runtime](EXTENDING_RUNTIME.md)
|
||||||
@ -158,7 +158,7 @@ namespace Tesses::CrossLang
|
|||||||
|
|
||||||
}
|
}
|
||||||
this->EnsureSymbol(">");
|
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::string myVal = "</";
|
||||||
myVal += tagName;
|
myVal += tagName;
|
||||||
@ -290,6 +290,14 @@ namespace Tesses::CrossLang
|
|||||||
}
|
}
|
||||||
nodes.push_back(AdvancedSyntaxNode::Create(IfStatement,false,{expr,truth,falsey}));
|
nodes.push_back(AdvancedSyntaxNode::Create(IfStatement,false,{expr,truth,falsey}));
|
||||||
}
|
}
|
||||||
|
else if(tagName == "null")
|
||||||
|
{
|
||||||
|
EnsureSymbol(">");
|
||||||
|
std::vector<SyntaxNode> _nodes;
|
||||||
|
parseFn(_nodes,"null");
|
||||||
|
nodes.push_back(AdvancedSyntaxNode::Create(ScopeNode,false,_nodes));
|
||||||
|
|
||||||
|
}
|
||||||
else if(tagName == "while")
|
else if(tagName == "while")
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@ -4,60 +4,6 @@
|
|||||||
|
|
||||||
namespace Tesses::CrossLang
|
namespace Tesses::CrossLang
|
||||||
{
|
{
|
||||||
static TObject FS_SubdirFilesystem(GCList& ls, std::vector<TObject> 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<TObject> 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<TObject> 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<TObject> args)
|
|
||||||
{
|
|
||||||
return TVFSHeapObject::Create(ls, new Tesses::Framework::Filesystem::MemoryFilesystem());
|
|
||||||
}
|
|
||||||
|
|
||||||
static TObject FS_CreateFilesystem(GCList& ls, std::vector<TObject> 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<TObject> 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<TObject> args)
|
static TObject FS_MakeFull(GCList& ls, std::vector<TObject> args)
|
||||||
{
|
{
|
||||||
Tesses::Framework::Filesystem::VFSPath path;
|
Tesses::Framework::Filesystem::VFSPath path;
|
||||||
@ -126,6 +72,27 @@ namespace Tesses::CrossLang
|
|||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
static TObject FS_ReadAllBytes(GCList& ls, std::vector<TObject> 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<uint8_t,1024> 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<TObject> args)
|
static TObject FS_WriteAllText(GCList& ls, std::vector<TObject> args)
|
||||||
{
|
{
|
||||||
@ -143,6 +110,22 @@ namespace Tesses::CrossLang
|
|||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
static TObject FS_WriteAllBytes(GCList& ls, std::vector<TObject> 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<TObject> args)
|
static TObject FS_getCurrentPath(GCList& ls, std::vector<TObject> args)
|
||||||
{
|
{
|
||||||
@ -178,13 +161,9 @@ namespace Tesses::CrossLang
|
|||||||
dict->DeclareFunction(gc, "ReadAllText","Read all text from file", {"fs","filename"},FS_ReadAllText);
|
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, "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, "ReadAllBytes","Read all bytes from file", {"fs","filename"},FS_ReadAllBytes);
|
||||||
dict->DeclareFunction(gc, "SubdirFilesystem","Create a subdir filesystem",{"fs","subdir"}, FS_SubdirFilesystem);
|
dict->DeclareFunction(gc, "WriteAllBytes","Write all bytes to file", {"fs","filename","content"},FS_WriteAllBytes);
|
||||||
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, "CreateArchive", "Create a crvm archive",{"fs","strm","name","version","info"},FS_CreateArchive);
|
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);
|
dict->DeclareFunction(gc,"ExtractArchive", "Extract a crvm archive",{"strm","vfs"},FS_ExtractArchive);
|
||||||
env->DeclareVariable("FS", dict);
|
env->DeclareVariable("FS", dict);
|
||||||
|
|||||||
@ -118,7 +118,61 @@ namespace Tesses::CrossLang
|
|||||||
TVFSHeapObject* vfs;
|
TVFSHeapObject* vfs;
|
||||||
return GetArgumentHeap(args,0,vfs);
|
return GetArgumentHeap(args,0,vfs);
|
||||||
}
|
}
|
||||||
|
static TObject New_SubdirFilesystem(GCList& ls, std::vector<TObject> 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<TObject> 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<TObject> 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<TObject> args)
|
||||||
|
{
|
||||||
|
return TVFSHeapObject::Create(ls, new Tesses::Framework::Filesystem::MemoryFilesystem());
|
||||||
|
}
|
||||||
|
|
||||||
|
static TObject New_Filesystem(GCList& ls, std::vector<TObject> 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<TObject> 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<TObject> args)
|
static TObject TypeOf(GCList& ls, std::vector<TObject> args)
|
||||||
{
|
{
|
||||||
if(args.size() < 1) return "Undefined";
|
if(args.size() < 1) return "Undefined";
|
||||||
@ -334,6 +388,15 @@ namespace Tesses::CrossLang
|
|||||||
|
|
||||||
env->permissions.canRegisterRoot=true;
|
env->permissions.canRegisterRoot=true;
|
||||||
TDictionary* newTypes = TDictionary::Create(ls);
|
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<TObject> args)->TObject{
|
newTypes->DeclareFunction(gc,"Version","Create a version object",{"$major","$minor","$patch","$build","$stage"},[](GCList& ls, std::vector<TObject> args)->TObject{
|
||||||
int64_t major=1;
|
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, "TypeIsVFS","Get whether object is a virtual filesystem",{"object"},TypeIsVFS);
|
||||||
|
|
||||||
|
|
||||||
env->DeclareFunction(gc, "Regex", "Create regex object",{"regex"},[](GCList& ls,std::vector<TObject> args)->TObject {
|
newTypes->DeclareFunction(gc, "Regex", "Create regex object",{"regex"},[](GCList& ls,std::vector<TObject> args)->TObject {
|
||||||
std::string str;
|
std::string str;
|
||||||
if(GetArgument(args,0,str))
|
if(GetArgument(args,0,str))
|
||||||
{
|
{
|
||||||
@ -384,7 +447,7 @@ namespace Tesses::CrossLang
|
|||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
});
|
});
|
||||||
env->DeclareFunction(gc, "Mutex", "Create mutex",{}, [](GCList& ls,std::vector<TObject> args)->TObject {
|
newTypes->DeclareFunction(gc, "Mutex", "Create mutex",{}, [](GCList& ls,std::vector<TObject> args)->TObject {
|
||||||
ls.GetGC()->BarrierBegin();
|
ls.GetGC()->BarrierBegin();
|
||||||
auto mtx = TDictionary::Create(ls);
|
auto mtx = TDictionary::Create(ls);
|
||||||
auto native = TNative::Create(ls, new Tesses::Framework::Threading::Mutex(),[](void* ptr)->void{
|
auto native = TNative::Create(ls, new Tesses::Framework::Threading::Mutex(),[](void* ptr)->void{
|
||||||
@ -428,7 +491,7 @@ namespace Tesses::CrossLang
|
|||||||
ls.GetGC()->BarrierEnd();
|
ls.GetGC()->BarrierEnd();
|
||||||
return mtx;
|
return mtx;
|
||||||
});
|
});
|
||||||
env->DeclareFunction(gc, "Thread","Create thread",{"callback"},[](GCList& ls, std::vector<TObject> args)-> TObject
|
newTypes->DeclareFunction(gc, "Thread","Create thread",{"callback"},[](GCList& ls, std::vector<TObject> args)-> TObject
|
||||||
{
|
{
|
||||||
if(args.size() == 1 && std::holds_alternative<THeapObjectHolder>(args[0]))
|
if(args.size() == 1 && std::holds_alternative<THeapObjectHolder>(args[0]))
|
||||||
{
|
{
|
||||||
@ -440,7 +503,7 @@ namespace Tesses::CrossLang
|
|||||||
}
|
}
|
||||||
return Undefined();
|
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();
|
gc->BarrierBegin();
|
||||||
env->DeclareVariable("Version", TDictionary::Create(ls,{
|
env->DeclareVariable("Version", TDictionary::Create(ls,{
|
||||||
TDItem("Parse",TExternalMethod::Create(ls,"Parse version from string",{"versionStr"},[](GCList& ls, std::vector<TObject> args)->TObject{
|
TDItem("Parse",TExternalMethod::Create(ls,"Parse version from string",{"versionStr"},[](GCList& ls, std::vector<TObject> args)->TObject{
|
||||||
|
|||||||
Reference in New Issue
Block a user