{"id":21498497,"url":"https://github.com/notapenguin0/pscript","last_synced_at":"2025-08-03T00:33:24.700Z","repository":{"id":39985675,"uuid":"507262304","full_name":"NotAPenguin0/pscript","owner":"NotAPenguin0","description":"Small custom programming language","archived":false,"fork":false,"pushed_at":"2022-07-06T09:42:23.000Z","size":533,"stargazers_count":5,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-30T01:35:03.266Z","etag":null,"topics":["runtime","scripting"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NotAPenguin0.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-06-25T08:59:23.000Z","updated_at":"2024-07-26T16:07:15.000Z","dependencies_parsed_at":"2022-07-12T18:01:15.879Z","dependency_job_id":null,"html_url":"https://github.com/NotAPenguin0/pscript","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NotAPenguin0%2Fpscript","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NotAPenguin0%2Fpscript/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NotAPenguin0%2Fpscript/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NotAPenguin0%2Fpscript/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NotAPenguin0","download_url":"https://codeload.github.com/NotAPenguin0/pscript/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250469958,"owners_count":21435734,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["runtime","scripting"],"created_at":"2024-11-23T16:39:08.482Z","updated_at":"2025-04-23T16:27:09.995Z","avatar_url":"https://github.com/NotAPenguin0.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pscript\n\n[![CMake](https://github.com/NotAPenguin0/pscript/actions/workflows/cmake.yml/badge.svg?branch=master)](https://github.com/NotAPenguin0/pscript/actions/workflows/cmake.yml)\n\n![lines](https://img.shields.io/tokei/lines/github/NotApenguin0/pscript) \n![release](https://img.shields.io/github/v/release/NotAPenguin0/pscript)\n\nCustom programming language built for scripting plugins in my engine.\n\n## Documentation\n\n### 0. Hello, World!\n\n```cpp\nimport std.io;\nstd.io.print(\"Hello, World!\");\n```\n\n### 1. Basic syntax\n\nVariables are defined using the `let` keyword. An initializer is required, from which the\ntype of the variable is inferred. Once declared, a variable cannot change type.\n\n```rust\nlet x = 1;\nlet y = \"abc\";\n```\n\nThere is an exception to this rule. Re-declaring a variable with a new type will shadow\nthe old variable in the current scope. \n\n```rust\nlet x = 1;\nlet x = \"abc\"; // x is now a string.\n```\n\nBasic control sequences are supported using `if`, `while`, `for` and `else` with similar \nsyntax to C and C++.\n\n```cpp\nif (condition) {\n    // statements\n} else if (other_condition) {\n    // other statements\n} else {\n    // final statements\n}\n```\n\n```cpp\nwhile(true) {\n    // do something\n}\n```\n\n```rust\nfor(let i = 0; i \u003c 10; ++i) {\n    // do something\n}\n```\n\n### 2. Types\n\nThe following builtin types are available:\n\n- `int`\n- `uint`\n- `float`\n- `bool`\n- `str`\n- `list`\n- `any`\n- `void`\n\n`any` refers to any posible type and is useful for passing generic arguments or\nexternal types around (see later). A string is created by wrapping text inside \nquotes (`\"abc\"`). A list can be created by using square braces:\n\n```rust\nlet my_list = [1, 2, 3];\nlet empty_list = [];\n\nempty_list.append(4);\n```\n\nAdditionally, new types can be defined using structs (see later).\n\n### 3. Functions\n\nA function is declared using the `fn` keyword, followed by its name, parameters and a\nreturn type.\n\n```rust\nfn foo() -\u003e int {\n    // execute foo()\n}\n\nfn bar(a: int, b: any) -\u003e void {\n    // do something with parameters a and b.\n}\n```\n\nA function can then easily be called using standard function call syntax:\n```rust\nbar(a, \"xyz\"); // since the type of b was 'any', we can pass any type.\n```\n\n### 4. Structures\n\nA struct allows for creating new types that can be used anywhere after the struct was declared.\nA struct declaration starts with the `struct` keyword and is followed by a block with member\ndeclarations. Note that like in C and C++, the struct declaration must end with a semicolon.\n\n```rust\nstruct MyStruct {\n    x: int = 0;\n    y: str = \"abc\";\n};\n```\n\nSimilar to variables, the initializer for struct members is required and must be convertible\nto the member type. An instance of a struct can be created as follows:\n\n```rust\nlet s = MyStruct { 42, \"The meaning of life\" };\nlet s2 = MyStruct { 42 }; // 'y' is initialized to \"abc\"\n```\n\nThe initializers are assigned to the members in order. You can pass fewer initializers to use\nthe default values for those members. To access a struct member, use the `-\u003e` operator.\n\n```rust\nlet s_x = s-\u003ex;\n```\n\n### 5. Type casting\n\nSometimes types do not match exactly. When this happens, the interpreter will try to cast\nto the type the called function, variable or struct initializer expects. If this is not\npossible, a `TypeError` will be reported. You can also cast between compatible types manually\nby using the same syntax as struct construction:\n\n```rust\nlet x = float { 5 };\nlet y = uint { 6 };\n```\n\nBecause this syntax for creating unsigned integers is cumbersome, a *literal suffix* `u`\nis also provided that does the same thing.\n\n```rust\nlet x = 6u;\n```\n\n### 6. Modules\n\nImporting code from other files (like the standard library) is made possible through\n*modules*. A module can be imported using the `import` statement.\n\n```cpp\nimport std.io;\n```\n\nThis will look in the provided module path (by default only includes the standard\n`pscript_modules/` folder) for a file `std/io.ps` and import it. After the import, \nfunctions, types and variables from the imported file can be accessed by prefixing\ntheir names with the module name.\n\n```cpp\nimport std.io;\n\nstd.io.print(\"Hello, World!\");\n```\n\n### 7. External objects\n\nThrough the C++ API, external functions and variables can be accessed inside a PScript \nprogram. To do this, the function or variable must be declared using the `extern` \nkeyword.\n\n```rust\nextern fn foo(x: int) -\u003e str;\nextern let bar: int;\n```\n\nAfter that, hook up the names to your C++ objects using the `ps::extern_library` class.\n\n```cpp\nps::string_type foo(int x) {\n    return ps::string_type { std::to_string(x) };\n}\n\nint bar = 42;\n\nps::context ctx(memsize);\n\nps::extern_library lib {};\nlib.add_function(ctx, \"foo\", \u0026foo);\nlib.add_variable(\"bar\", \u0026bar);\n\nps::execution_context exec {};\nexec.externs = \u0026lib;\n\nctx.execute(my_script, lib);\n```\n\nThese external functions and variables can now be used like normal PScript objects.\n\n### 8. Reference types\n\nSometimes it is useful to pass objects to functions by reference. This means no copy is\nmade, and changes to the parameter are also visible to the caller. By default, lists,\nstrings and structures are always reference types. To create a reference to another\ntype, prefix its name using an ampersand `\u0026`.\n\n```rust\nfn foo(x: int\u0026, a: int, b: int) -\u003e void {\n    a = a + 1;\n    x = a + b;\n}\n\nlet result = 0;\nlet a = 10;\nlet b = 9;\nfoo(result, a, b); // will set result to 20, but leave a unmodified.\n```\n\n### 9. Standard library reference\n\nWIP. for now, please refer to the source code in `modules/std`.\n\n### 10. C++ API\n\nPScript programs can be executed through the C++ API implemented in this repository.\nEverything is available with a single `#include \u003cpscript/context.hpp\u003e`. To execute a\nscript, first load it into a `ps::script` object.\n\n```cpp\nps::script source { \"let x = 0; let y = 10;\", ctx };\n```\n\nTo run scripts, you must create a `ps::context` object. This object signifies a single \nexecution context. Variables created in scripts are created in their context, imported\nmodules are imported for the entire context, etc. Each context has a memory pool, whose\nsize must be specified at creation (in bytes).\n\n```cpp\nstatic constexpr std::size_t mem_size = 1024 * 1024; // 1 MiB of memory.\nps::context ctx { mem_size };\n```\n\nTo execute a script on a context, simply call `context.execute(script);`. This function\nhas an additional optional parameter of type `ps::execution_context`. This parameter\nallows specifying various options to control execution.\n\n- Control standard input and output streams by setting `in`, `out` and `err`.\n- Provide an extern library by building a chain of `ps::extern_library` objects (see\n  also the provided `ps::extern_library_chain_builder`). This chain structure allows\n  linking multiple extern libraries to the same context.\n- Provide additional paths to search for modules by adding paths to `module_paths`.\n  Note that this also allows removing the path to the standard `pscript_modules/` folder.\n\n### 11. Advanced functionality\n\n#### a) Variadics\n\nFunctions can be made to accept any number of arguments by passing in a variadic parameter.\nThis parameter must always be the last parameter declared. In the function call, the\nvariable will behave as if it is a `list\u003cany\u003e`.\n\n```rust\nfn print_all(x...) {\n  for (let i = 0; i \u003c x.size(); ++i) {\n    std.io.print(x[i]);\n  }\n}\n\nprint_all(1, 2.2, 3, \"abc\");\n```\n\nAs you can see, a variadic parameter can store different types. Variadics can be passed around to other functions or constructors\nby expanding them.\n\n```rust\nfn printf(fmt: str, args...) {\n\t// pass all elements of args... through to the builtin format() function\n\tstd.io.print(fmt.format(args...));\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnotapenguin0%2Fpscript","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnotapenguin0%2Fpscript","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnotapenguin0%2Fpscript/lists"}