diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index a00f3ea8..61056f2f 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -14,8 +14,8 @@ -Resolves: - +Resolves: # + # ### Type of change: @@ -42,6 +42,8 @@ Resolves: - [ ] Documentation has been updated to reflect the changes done within this PR (if applicable). +# + ### Preview (Screenshots) : diff --git a/.gitignore b/.gitignore index cf8ed8c9..9f232d3a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,53 +1,31 @@ \.idea/ - build/ - \.DS_Store - dictu - dictu\.dSYM/Contents/Info\.plist - test/ history\.txt - text\.txt - *.py *.pyc - benchmark.sh - out.txt - time.du - test.du - coverage/ - compile_commands.json - venv/ - cmake-build-debug/CMakeFiles/clion-log.txt - docs/.jekyll-cache/ -docs/_site/ -cmake-build-debug/ - -cmake-build-release/ - -.vs/ - -dictu.exe - -dictu.exe.manifest - -CMakeSettings.json - -dictu.ilk - -dictu.pdb - +docs/_site/ +cmake-build-debug/ +cmake-build-release/ +.vs/ +dictu.exe +dictu.exe.manifest +CMakeSettings.json +dictu.ilk +dictu.pdb test1.du +\.vscode/ +/.env diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..cb70d814 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,129 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[emailing the project team][email]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org +[email]: mailto:jasonhall96686@gmail.com + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/Docker/DictuAlpineDockerfile b/Docker/DictuAlpineDockerfile index 98903ec5..5525d267 100644 --- a/Docker/DictuAlpineDockerfile +++ b/Docker/DictuAlpineDockerfile @@ -10,7 +10,7 @@ RUN cd Dictu \ && cmake -DCMAKE_BUILD_TYPE=Release -B build \ && cmake --build ./build \ && ./dictu tests/runTests.du \ - && cp dictu /usr/bin/ \ - && rm -rf * + && cp dictu /usr/bin/ \ + && rm -rf * CMD ["dictu"] diff --git a/Docker/DictuUbuntuDockerfile b/Docker/DictuUbuntuDockerfile index bef6140a..c0fddfc7 100644 --- a/Docker/DictuUbuntuDockerfile +++ b/Docker/DictuUbuntuDockerfile @@ -9,14 +9,14 @@ RUN apt-get update \ && apt-get update \ && apt-get install -y --reinstall ca-certificates \ && apt-get install -y --no-install-recommends git cmake libcurl4-gnutls-dev \ - && rm -rf /var/lib/apt/lists/* + && rm -rf /var/lib/apt/lists/* RUN git clone https://github.com/dictu-lang/Dictu.git RUN cd Dictu \ - && cmake -DCMAKE_BUILD_TYPE=Release -B build \ - && cmake --build ./build \ - && ./dictu tests/runTests.du \ + && cmake -DCMAKE_BUILD_TYPE=Release -B build \ + && cmake --build ./build \ + && ./dictu tests/runTests.du \ && cp dictu /usr/bin/ \ && rm -rf * diff --git a/docs/_config.yml b/docs/_config.yml index 7c3238b6..6c38844d 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -5,7 +5,7 @@ description: >- color_scheme: "dictu" # Custom theme logo: "/assets/images/dictu-logo/dictu-wordmark.svg" -version: "0.22.0" +version: "0.23.0" github_username: dictu-lang search_enabled: true diff --git a/docs/docs/classes.md b/docs/docs/classes.md index 99a87b43..e0dd3a3c 100644 --- a/docs/docs/classes.md +++ b/docs/docs/classes.md @@ -245,6 +245,21 @@ print(myObject.getAttribute("y", 100)); // 100 print(myObject.getAttribute("y")); // nil ``` +### getAttributes + +The `getAttributes` method returns all public attributes on the given instance of a class. + +```cs +class Test { + init() { + this.x = 10; + } +} + +var myObject = Test(); +print(myObject.getAttributes()); // ["x"] +``` + ### setAttribute Similar concept to `getAttribute` however this allows us to set an attribute on an instance. @@ -288,7 +303,18 @@ print(Test().someMethod()?.someOtherMethod()); // nil // If the operand is not nil the method / property must exist print(Test()?.unknownMethod()); // Undefined property 'unknownMethod'. -``` +``` + +### _class + +`_class` is a special attribute that is added to instances so that a reference to the class is kept on objects. This will be +useful for things like pulling class annotations from an object where it's class may be unknown until runtime. + +```cs +class Test {} + +print(Test()._class); // +``` ## Class variables diff --git a/docs/docs/modules.md b/docs/docs/modules.md index da30b2ab..624be053 100644 --- a/docs/docs/modules.md +++ b/docs/docs/modules.md @@ -37,9 +37,9 @@ If however you wish to import something specific from the module into the curren accepts a single identifier or multiple separated by a comma. ```cs -from Math import PI; +from Math import pi; -PI; // 3.14... +pi; // 3.14... // Import multiple things from JSON import parse, stringify; diff --git a/docs/docs/standard-lib/env.md b/docs/docs/standard-lib/env.md index 0b8cd607..0d67dabf 100644 --- a/docs/docs/standard-lib/env.md +++ b/docs/docs/standard-lib/env.md @@ -47,3 +47,24 @@ Env.set("key", "test"); Env.set("key", nil); // Remove env var Env.set("key", 10); // set() arguments must be a string or nil. ``` + +### Env.readFile(string: path -> optional) + +To read environment variables from a file this helper method is provided. +By default it will attempt to read `.env` unless a different path is supplied. +Returns a Result type and on success will unwrap to nil. + +Note: You are able to have comments in the `.env` file via the use of `#` (both in-line and first character). + +```env +# This is a comment +TEST=10 +TESTING=100 # In-line comment +``` + +```cs +Env.readFile(); // + +print(Env.get("TEST")); // 10 +print(Env.get("TESTING")); // 100 +``` \ No newline at end of file diff --git a/docs/docs/standard-lib/http.md b/docs/docs/standard-lib/http.md index 6d2442ab..d5f3d488 100644 --- a/docs/docs/standard-lib/http.md +++ b/docs/docs/standard-lib/http.md @@ -18,7 +18,7 @@ parent: Standard Library ## HTTP -To make use of the HTTP module an import is required. +To make use of the HTTP module an import is required. Along with the methods described below, this module also defines constants representing all standard response codes and their associated messages, the standard set of HTTP methods, and common request headers and values. ```cs import HTTP; diff --git a/docs/docs/standard-lib/inspect.md b/docs/docs/standard-lib/inspect.md new file mode 100644 index 00000000..6301f851 --- /dev/null +++ b/docs/docs/standard-lib/inspect.md @@ -0,0 +1,64 @@ +--- +layout: default +title: Inspect +nav_order: 14 +parent: Standard Library +--- + +# Inspect +{: .no_toc } + +## Table of contents +{: .no_toc .text-delta } + +1. TOC +{:toc} + +--- + +## Inspect +To make use of the Inspect module an import is required. + +```js +import Inspect; +``` + +### Inspect.getFrameCount() + +This gives you the current frame count of the VM at the point of calling. + +Note: This is 0-based. + +```cs +Inspect.getFrameCount(); // 0 + +def test() { + print(Inspect.getFrameCount()); +} + +test(); // 1 +``` + +### Inspect.getLine(number: count -> optional) + +This gives you the line number within the file that the function was called from. + +The optional argument passed is the amount of frames to traverse back up. If this number exceeds the +frame count an error is raised. + +```cs +Inspect.getLine(); // 1 +Inspect.getLine(1000); // Optional argument passed to getLine() exceeds the frame count. +``` + +### Inspect.getFile(number: count -> optional) + +This gives you the name of the file that the function was called from. + +The optional argument passed is the amount of frames to traverse back up. If this number exceeds the +frame count an error is raised. + +```cs +Inspect.getFile(); // repl +Inspect.getFile(); // myFile.du +``` \ No newline at end of file diff --git a/docs/docs/standard-lib/log.md b/docs/docs/standard-lib/log.md new file mode 100644 index 00000000..14ac250f --- /dev/null +++ b/docs/docs/standard-lib/log.md @@ -0,0 +1,113 @@ +--- +layout: default +title: Log +nav_order: 16 +parent: Standard Library +--- + +# Log +{: .no_toc } + +## Table of contents +{: .no_toc .text-delta } + +1. TOC +{:toc} + +--- + +## Log + +To make use of the Log module an import is required. + +```cs +import Log; +``` + +### Constants + +| Constant | Description | +| ---------- | --------------------------------------------------------------------- | +| Log.stdout | The default file descriptor where a process can write output. | +| Log.stderr | The default file descriptor where a process can write error messages. | +| Log.stdin | An input stream where data is sent to and read by a program. | + +### Log.print(string) + +```cs +Log.print("something extremely interesting"); // 2021/11/27 11:49:14 something extremely interesting +``` + +### Log.println(string) + +```cs +Log.println("hello, world! println"); // 2021/11/27 11:49:14 hello, world! print +``` + +### Log.fatal(string) + +Log the given output and exit the program with an exit code of 1. + +```cs +Log.fatal("we've met a tragic end"); // 2021/11/27 11:49:14 we've met a tragic end +``` + +### Log.fatalln(string) + +Log the given output with a new line and exit the program with an exit code of 1. + +```cs +Log.fatalln("hello, world! fatalln"); // 2021/11/27 11:49:14 hello, world! print +``` + +### Log.new(number) + +Create a new instance of a logger. + +```cs +const log = Log.new(Log.stdout).unwrap(); +``` + +### log.setPrefix(string) + +A prefix can be set on the logger that will be included in the output just before the user provided content. + +```cs +log.setPrefix("prefix"); +``` + +### log.unsetPrefix(string) + +Remove the prefix on the logger. This is a noop if there was no prefix previously set. + +```cs +log.unsetPrefix(); +``` + +### log.print(string) + +```cs +Log.print("something extremely interesting"); // 2021/11/27 11:49:14 something extremely interesting +``` + +### log.println(string) + +```cs +Log.println("hello, world! println"); // 2021/11/27 11:49:14 hello, world! print +``` + +### log.fatal(string) + +Log the given output and exit the program with an exit code of 1. + +```cs +log.fatal("we've met a tragic end"); // 2021/11/27 11:49:14 we've met a tragic end +``` + +### log.fatalln(string) + +Log the given output with a new line and exit the program with an exit code of 1. + +```cs +log.fatalln("hello, world! fatalln"); // 2021/11/27 11:49:14 hello, world! print +``` diff --git a/docs/docs/standard-lib/math.md b/docs/docs/standard-lib/math.md index ac98d6fe..b4fb1879 100644 --- a/docs/docs/standard-lib/math.md +++ b/docs/docs/standard-lib/math.md @@ -31,6 +31,13 @@ import Math; |--------------|--------------------------------------------------------| | Math.pi | The mathematical constant: 3.14159265358979 | | Math.e | The mathematical constant: 2.71828182845905 | +| Math.phi | The mathematical constant: 1.61803398874989 | +| Math.sqrt2 | The mathematical constant: 1.41421356237309 | +| Math.sqrte | The mathematical constant: 1.61803398874989 | +| Math.sqrtpi | The mathematical constant: 1.77245385090551 | +| Math.sqrtphi | The mathematical constant: 1.27201964951406 | +| Math.ln2 | The mathematical constant: 0.69314718055994 | +| Math.ln10 | The mathematical constant: 2.30258509299404 | ### Math.min(iterable) diff --git a/docs/docs/standard-lib/system.md b/docs/docs/standard-lib/system.md index 8587d279..d80b7314 100644 --- a/docs/docs/standard-lib/system.md +++ b/docs/docs/standard-lib/system.md @@ -26,7 +26,7 @@ import System; ### Constants | Constant | Description | -|-----------------|---------------------------------------------------------------------------------------------------| +| --------------- | ------------------------------------------------------------------------------------------------- | | System.argv | The list of command line arguments. The first element of the argv list is always the script name. | | System.platform | This string identifies the underlying system platform. | | System.version | Dictionary containing Dictu major, minor and patch versions. | @@ -44,10 +44,10 @@ import System; | System.S_IXOTH | Execute by others. | | System.S_ISUID | Set user ID on execution. | | System.S_ISGID | Set group ID on execution. | -| System.F_OK | Test for existence. -| System.X_OK | Test for execute permission. -| System.W_OK | Test for write permission. -| System.R_OK | Test for read permission. +| System.F_OK | Test for existence. | +| System.X_OK | Test for execute permission. | +| System.W_OK | Test for write permission. | +| System.R_OK | Test for read permission. | ### System.mkdir(string, number: mode -> optional) @@ -228,3 +228,21 @@ Shell ```bash $ echo $?; // 10 ``` + +### System.chmod(string, string) + +Set the permissions on a file or directory. + +```cs +System.chmod("/usr/local/share", "755"); +``` + +### System.chown(string, number, number) + +Set the ownership of a file or directory with the given path, uid, and gid. + +Note: This is not available on Windows systems. + +```cs +System.chown("/path/to/file", 0, 0); +``` diff --git a/docs/docs/standard-lib/unittest.md b/docs/docs/standard-lib/unittest.md new file mode 100644 index 00000000..21ce6ad0 --- /dev/null +++ b/docs/docs/standard-lib/unittest.md @@ -0,0 +1,334 @@ +--- +layout: default +title: UnitTest +nav_order: 15 +parent: Standard Library +--- + +# Unit Test +{: .no_toc } + +## Table of contents +{: .no_toc .text-delta } + +1. TOC +{:toc} + +--- + +## UnitTest +Unit testing is a very important part of software development and something we as developers +should always strive to complete. Dictu aims to make this slightly easier by having a unit test +framework built within the language. + +The framework works by first inheriting from the abstract `UnitTest` class provided +to us within this module. + +Note: If any tests fail then the suite will exit with an exit code of 1. This will allow CI/CD processes +to easily detect a failing test suite. + +```cs +from UnitTest import UnitTest; + +class Test < UnitTest { + +} +``` + +From here we add methods that will test different "units" of our code. +This is done through a range of helper methods provided (see table of contents) +to us by the `UnitTest` class. + +```cs +from UnitTest import UnitTest; + +def add(a, b) { + return a + b; +} + +class Test < UnitTest { + testAddFunction() { + this.assertEquals(add(2, 3), 5); + } +} + +Test().run(); +``` + +The framework will search any methods within the class and execute any that +begin with `test`, if a method within a class does not begin with `test` it is simply +ignored. The final step is to ensure we run the unit test by calling the `.run()` method. + +This will generate the following output. + +``` +file.du + testAddFunction() + Success. + +Results: + - 1 assertion(s) were successful. + - 0 assertion(s) were failures. + - 0 method(s) were skipped. +``` + +## Helpers + +Along with the list of assertion methods in the next section, there are a few +helper methods to make test writing easier. + +### setUp + +`setUp` is a method which is ran before a test method is executed. This is useful +if you need some data (for example) to be present before running a test. + +Note: If in use with providers `setUp` will run for every item passed into the array. + +```cs +class Test < UnitTest { + setUp() { + // Code + } +} +``` + +### tearDown + +`tearDown` very similar to `setUp` yet happens once a test has completed. Useful +for cleaning up any data created in `setUp` or the test itself (examples). + +Note: If in use with providers `tearDown` will run for every item passed into the array. + +```cs +class Test < UnitTest { + tearDown() { + // Code + } +} +``` + +### Providers + +Providers are special methods that allow you to pass data into tests +so that they can be made slightly more dynamic and reduce the boiler plate +needing to be written. They are denoted by appending a method name with `Provider`. + +Providers can pass a single value and you can use this within your test, or if +you return an array from a provider then the test will run for every item within the +array. + +```cs +from UnitTest import UnitTest; + +def add(a, b) { + return a + b; +} + +class Test < UnitTest { + testAddFunction(data) { + this.assertEquals(add(data["val1"], data["val2"]), data["expected"]); + } + + // This will not be ran as a test as it's marked as a provider + testAddFunctionProvider() { + return [ + {"val1": 1, "val2": 2, "expected": 3}, + {"val1": 2, "val2": 2, "expected": 4}, + {"val1": 3, "val2": 2, "expected": 5}, + {"val1": 4, "val2": 2, "expected": 6}, + {"val1": 5, "val2": 2, "expected": 7}, + {"val1": 6, "val2": 2, "expected": 8}, + ]; + } +} + +Test().run(); +``` + +As you can see, providers make it incredibly easy to add additional test cases without +the need to actually modify your test code. + +Output: +``` +file.du + testAddFunction() + Success. + Success. + Success. + Success. + Success. + Success. + +Results: + - 6 assertion(s) were successful. + - 0 assertion(s) were failures. + - 0 method(s) were skipped. +``` + +### Skipping Tests + +Sometimes we need to skip tests, be that because they're temporarily broken or +because they're testing functionality that has been disabled for whatever reason. + +This is done very simply by just appending `_skipped` to the method name. + + +```cs +from UnitTest import UnitTest; + +def add(a, b) { + return a + b; +} + +class Test < UnitTest { + testAddFunction_skipped() { + this.assertEquals(add(2, 3), 5); + } +} + +Test().run(); +``` + +Output: +``` +file.du + +Results: + - 0 assertion(s) were successful. + - 0 assertion(s) were failures. + - 1 method(s) were skipped. +``` + +## Settings +### Silencing Passing Tests +The default setting is that it will output `Success.` for tests that pass, but +sometimes that can cause finding errors or tests that fail slightly harder +so we can turn this off to only show output from failed tests. + +```cs +from UnitTest import UnitTest; + +def add(a, b) { + return a + b; +} + +class Test < UnitTest { + init() { + // Remember to call the parent constructor! + super.init(); + + // Set it so only errors display + this.onlyFailures = true; + } + + testAddFunction(data) { + this.assertEquals(add(data["val1"], data["val2"]), data["expected"]); + } + + // This will not be ran as a test as it's marked as a provider + testAddFunctionProvider() { + return [ + {"val1": 1, "val2": 2, "expected": 3}, + {"val1": 2, "val2": 2, "expected": 4}, + {"val1": 3, "val2": 2, "expected": 5}, + {"val1": 4, "val2": 2, "expected": 6}, + {"val1": 5, "val2": 2, "expected": 7}, + // This will fail + {"val1": 6, "val2": 2, "expected": 9}, + ]; + } +} + +Test().run(); +``` + +Output: +``` +file.du + testAddFunction() + Line: 17 - Failure: 8 is not equal to 9. + +Results: + - 5 assertion(s) were successful. + - 1 assertion(s) were failures. + - 0 method(s) were skipped. +``` + +### Exit On Failure +Sometimes we may want our test suite to stop as soon as a test fails. +This is done very similarly to silencing passing tests. + +```cs +from UnitTest import UnitTest; + +def add(a, b) { + return a + b; +} + +class Test < UnitTest { + init() { + // Remember to call the parent constructor! + super.init(); + + // Exit as soon as we get a failure + this.exitOnFailure = true; + } + + testAddFunction(data) { + this.assertEquals(add(data["val1"], data["val2"]), data["expected"]); + } + + // This will not be ran as a test as it's marked as a provider + testAddFunctionProvider() { + return [ + // This will fail + {"val1": 1, "val2": 2, "expected": 5}, + {"val1": 2, "val2": 2, "expected": 4}, + {"val1": 3, "val2": 2, "expected": 5}, + {"val1": 4, "val2": 2, "expected": 6}, + {"val1": 5, "val2": 2, "expected": 7}, + // This will fail + {"val1": 6, "val2": 2, "expected": 9}, + ]; + } +} + +Test().run(); +``` + +In this case the first dictionary in the data provider will cause our add test +to fail, with `exitOnFailure` set to true it will exit after the first assertion. + +Output: +``` +file.du + testAddFunction() + Line: 17 - Failure: 3 is not equal to 5. +``` + +## Assertions +### assertEquals(value, value) + +This helper method ensures that both values passed in equal each other. + +### assertTruthy(value) + +This helper method ensures that the value passed in would evaluate to true. + +Note: This is not the same as equaling `true`. + +### assertFalsey(value) + +This helper method ensures that the value passed in would evaluate to false. + +Note: This is not the same as equaling `false`. + +### assertSuccess(value) + +This helper method ensures that the value passed in is a `Result` type in +a `Success` state. + +### assertError(value) + +This helper method ensures that the value passed in is a `Result` type in +an `Error` state. diff --git a/docs/docs/strings.md b/docs/docs/strings.md index 0f8a5e3f..b2176af0 100644 --- a/docs/docs/strings.md +++ b/docs/docs/strings.md @@ -162,13 +162,19 @@ Returns true if a string ends with a given string. "Dictu".endsWith("U"); // false ``` -### string.split(delimiter) +### string.split(string: delimiter, number: splitCount -> optional) Returns a list of strings, split based on a given delimiter. Returns a list of all characters in a string if an empty string is passed as delimiter. + +An optional second argument can be passed which will determine the amount of times a split occurs, if negative it's the same +as not passing a value and will split by all occurrences of the delimiter. ```cs "Dictu is awesome!".split(" "); // ['Dictu', 'is', 'awesome!'] +"Dictu is awesome!".split(" ", -1); // ['Dictu', 'is', 'awesome!'] "Dictu is awesome!".split(""); // ["D", "i", "c", "t", "u", " ", "i", "s", " ", "a", "w", "e", "s", "o", "m", "e", "!"] +"Dictu is awesome!".split(" ", 0); // ['Dictu is awesome!'] +"Dictu is awesome!".split(" ", 1); // ['Dictu', 'is awesome!'] ``` ### string.replace(string: old, string: new) diff --git a/examples/README.md b/examples/README.md index a6024ccf..db7e1ac2 100644 --- a/examples/README.md +++ b/examples/README.md @@ -95,6 +95,8 @@ for (var i = 1; i < 101; i += 1) { ## Guessing Game ```js +import System; + const guess = 10; while { @@ -128,5 +130,3 @@ def fibonacci(num) { print(fibonacci(10)); ``` -[See here](https://github.com/Jason2605/Dictu/blob/develop/examples/recursiveFib.du) - diff --git a/examples/pathWalker.du b/examples/pathWalker.du index 0b155f70..4092cef9 100644 --- a/examples/pathWalker.du +++ b/examples/pathWalker.du @@ -13,11 +13,11 @@ class Walker { } next() { - var filtered = this.toAdd.filter(def (x) => !this.prune.contains(x)); + var filtered = this.toAdd.filter(def (x) => not this.prune.contains(x)); var joined = filtered.map(def (x) => Path.join(this.prev, x)); this.stack.extend(joined); - if (!this.stack.len()) return nil; + if (not this.stack.len()) return nil; var current = this.stack.pop(); var dirList = Path.listDir(current); @@ -43,7 +43,7 @@ class Walker { var results = []; while { var x = this.next(); - if (!x) break; + if (not x) break; results.push(x); } return results; @@ -53,7 +53,7 @@ class Walker { var results = []; while { var x = this.next(); - if (!x) break; + if (not x) break; results.push(x[0]); } return results; @@ -63,7 +63,7 @@ class Walker { var results = []; while { var x = this.next(); - if (!x) break; + if (not x) break; results.extend(x[2].map(def (file) => Path.join(x[0], file))); } return results; @@ -73,7 +73,7 @@ class Walker { var results = []; while { var x = this.next(); - if (!x) break; + if (not x) break; [1, 2].forEach(def (i) => results.extend(x[i].map(def (y) => Path.join(x[0], y)))); } return results; @@ -88,7 +88,7 @@ var walker = Walker(dictuDir, ['docs']); while { var tmp = walker.next(); - if (!tmp) break; + if (not tmp) break; var [dirpath, dirnames, filenames] = tmp; print([dirpath, dirnames, filenames]); diff --git a/src/vm/datatypes/generate.du b/scripts/generate.du similarity index 72% rename from src/vm/datatypes/generate.du rename to scripts/generate.du index 775efe8c..de479af3 100644 --- a/src/vm/datatypes/generate.du +++ b/scripts/generate.du @@ -2,8 +2,8 @@ import Path; /** * This file is used to generate the source of any datatype - * methods that are actually written in Dictu ready to be loaded - * by the VM on init. + * methods / stdlib modules that are actually written in + * Dictu ready to be loaded by the VM. */ def generate(filename) { @@ -26,9 +26,11 @@ def generate(filename) { } var files = [ - 'lists/list', - 'dicts/dict', - 'result/result' + 'src/vm/datatypes/lists/list', + 'src/vm/datatypes/dicts/dict', + 'src/vm/datatypes/result/result', + 'src/optionals/unittest/unittest', + 'src/optionals/env/env' ]; for (var i = 0; i < files.len(); i += 1) { diff --git a/src/include/dictu_include.h b/src/include/dictu_include.h index bddbb8ae..06faee5c 100644 --- a/src/include/dictu_include.h +++ b/src/include/dictu_include.h @@ -4,7 +4,7 @@ #include #define DICTU_MAJOR_VERSION "0" -#define DICTU_MINOR_VERSION "22" +#define DICTU_MINOR_VERSION "23" #define DICTU_PATCH_VERSION "0" #define DICTU_STRING_VERSION "Dictu Version: " DICTU_MAJOR_VERSION "." DICTU_MINOR_VERSION "." DICTU_PATCH_VERSION "\n" diff --git a/src/optionals/base64.c b/src/optionals/base64.c index 7a7d7629..e133a8f3 100644 --- a/src/optionals/base64.c +++ b/src/optionals/base64.c @@ -48,7 +48,7 @@ static Value decode(DictuVM *vm, int argCount, Value *args) { return OBJ_VAL(string); } -ObjModule *createBase64Module(DictuVM *vm) { +Value createBase64Module(DictuVM *vm) { ObjString *name = copyString(vm, "Base64", 6); push(vm, OBJ_VAL(name)); ObjModule *module = newModule(vm, name); @@ -63,5 +63,5 @@ ObjModule *createBase64Module(DictuVM *vm) { pop(vm); pop(vm); - return module; + return OBJ_VAL(module); } \ No newline at end of file diff --git a/src/optionals/base64.h b/src/optionals/base64.h index 41f83685..9ae5e050 100644 --- a/src/optionals/base64.h +++ b/src/optionals/base64.h @@ -4,6 +4,6 @@ #include "base64/base64Lib.h" #include "optionals.h" -ObjModule *createBase64Module(DictuVM *vm); +Value createBase64Module(DictuVM *vm); #endif //dictu_base64_h diff --git a/src/optionals/datetime.c b/src/optionals/datetime.c index 6a218a26..1d870ed1 100644 --- a/src/optionals/datetime.c +++ b/src/optionals/datetime.c @@ -154,7 +154,7 @@ static Value strptimeNative(DictuVM *vm, int argCount, Value *args) { } #endif -ObjModule *createDatetimeModule(DictuVM *vm) { +Value createDatetimeModule(DictuVM *vm) { ObjString *name = copyString(vm, "Datetime", 8); push(vm, OBJ_VAL(name)); ObjModule *module = newModule(vm, name); @@ -173,5 +173,5 @@ ObjModule *createDatetimeModule(DictuVM *vm) { pop(vm); pop(vm); - return module; + return OBJ_VAL(module); } diff --git a/src/optionals/datetime.h b/src/optionals/datetime.h index f150cfb3..85c48bc2 100644 --- a/src/optionals/datetime.h +++ b/src/optionals/datetime.h @@ -12,6 +12,6 @@ #include "optionals.h" -ObjModule *createDatetimeModule(DictuVM *vm); +Value createDatetimeModule(DictuVM *vm); #endif //dictu_datetime_h diff --git a/src/optionals/env/env-source.h b/src/optionals/env/env-source.h new file mode 100644 index 00000000..78b80b2d --- /dev/null +++ b/src/optionals/env/env-source.h @@ -0,0 +1,32 @@ +#define DICTU_ENV_SOURCE "import Env;\n" \ +"\n" \ +"def readFile(path='.env') {\n" \ +" const SPLIT_DELIMITER = '=';\n" \ +" const COMMENT = '#';\n" \ +"\n" \ +" with(path, 'r') {\n" \ +" var line;\n" \ +" var lineCount = 0;\n" \ +" // When you reach the end of the file, nil is returned\n" \ +" while((line = file.readLine()) != nil) {\n" \ +" lineCount = lineCount + 1;\n" \ +" if (not line or line.startsWith(COMMENT))\n" \ +" continue;\n" \ +"\n" \ +" if (not line.contains('='))\n" \ +" return Error('Malformed entry on line {}'.format(lineCount));\n" \ +"\n" \ +" const [variable, rawValue] = line.split(SPLIT_DELIMITER, 1);\n" \ +" // Strip out any in-line comments\n" \ +" const value = rawValue.split(COMMENT, 1);\n" \ +" const result = Env.set(variable.strip(), value[0].strip());\n" \ +"\n" \ +" if (not result.success()) {\n" \ +" return result;\n" \ +" }\n" \ +" }\n" \ +" }\n" \ +"\n" \ +" return Success(nil);\n" \ +"}\n" \ + diff --git a/src/optionals/env.c b/src/optionals/env/env.c similarity index 82% rename from src/optionals/env.c rename to src/optionals/env/env.c index e125e1b7..7d2458a6 100644 --- a/src/optionals/env.c +++ b/src/optionals/env/env.c @@ -1,5 +1,7 @@ #include "env.h" +#include "env-source.h" + #ifdef _WIN32 #define unsetenv(NAME) _putenv_s(NAME, "") int setenv(const char *name, const char *value, int overwrite) { @@ -25,7 +27,7 @@ static Value get(DictuVM *vm, int argCount, Value *args) { runtimeError(vm, "get() arguments must be a string."); return EMPTY_VAL; } - + char *value = getenv(AS_CSTRING(args[0])); if (argCount == 2) { @@ -76,20 +78,22 @@ static Value set(DictuVM *vm, int argCount, Value *args) { return newResultSuccess(vm, NIL_VAL); } -ObjModule *createEnvModule(DictuVM *vm) { - ObjString *name = copyString(vm, "Env", 3); - push(vm, OBJ_VAL(name)); - ObjModule *module = newModule(vm, name); - push(vm, OBJ_VAL(module)); +Value createEnvModule(DictuVM *vm) { + ObjClosure *closure = compileModuleToClosure(vm, "Env", DICTU_ENV_SOURCE); + + if (closure == NULL) { + return EMPTY_VAL; + } + + push(vm, OBJ_VAL(closure)); /** * Define Env methods */ - defineNative(vm, &module->values, "get", get); - defineNative(vm, &module->values, "set", set); + defineNative(vm, &closure->function->module->values, "get", get); + defineNative(vm, &closure->function->module->values, "set", set); - pop(vm); pop(vm); - return module; + return OBJ_VAL(closure); } diff --git a/src/optionals/env/env.du b/src/optionals/env/env.du new file mode 100644 index 00000000..433c7290 --- /dev/null +++ b/src/optionals/env/env.du @@ -0,0 +1,31 @@ +import Env; + +def readFile(path='.env') { + const SPLIT_DELIMITER = '='; + const COMMENT = '#'; + + with(path, 'r') { + var line; + var lineCount = 0; + // When you reach the end of the file, nil is returned + while((line = file.readLine()) != nil) { + lineCount = lineCount + 1; + if (not line or line.startsWith(COMMENT)) + continue; + + if (not line.contains('=')) + return Error('Malformed entry on line {}'.format(lineCount)); + + const [variable, rawValue] = line.split(SPLIT_DELIMITER, 1); + // Strip out any in-line comments + const value = rawValue.split(COMMENT, 1); + const result = Env.set(variable.strip(), value[0].strip()); + + if (not result.success()) { + return result; + } + } + } + + return Success(nil); +} \ No newline at end of file diff --git a/src/optionals/env.h b/src/optionals/env/env.h similarity index 54% rename from src/optionals/env.h rename to src/optionals/env/env.h index a53100b0..bd8948e8 100644 --- a/src/optionals/env.h +++ b/src/optionals/env/env.h @@ -4,9 +4,9 @@ #include #include -#include "optionals.h" -#include "../vm/vm.h" +#include "../optionals.h" +#include "../../vm/vm.h" -ObjModule *createEnvModule(DictuVM *vm); +Value createEnvModule(DictuVM *vm); #endif //dictu_env_h diff --git a/src/optionals/hashlib.c b/src/optionals/hashlib.c index 9ff4aa04..87177bfa 100644 --- a/src/optionals/hashlib.c +++ b/src/optionals/hashlib.c @@ -148,7 +148,7 @@ static Value verify(DictuVM *vm, int argCount, Value *args) { return BOOL_VAL(_compare((const uint8_t *) stringA->chars, (const uint8_t *) stringB->chars, stringA->length) == 0); } -ObjModule *createHashlibModule(DictuVM *vm) { +Value createHashlibModule(DictuVM *vm) { ObjString *name = copyString(vm, "Hashlib", 7); push(vm, OBJ_VAL(name)); ObjModule *module = newModule(vm, name); @@ -166,5 +166,5 @@ ObjModule *createHashlibModule(DictuVM *vm) { pop(vm); pop(vm); - return module; + return OBJ_VAL(module); } \ No newline at end of file diff --git a/src/optionals/hashlib.h b/src/optionals/hashlib.h index 8f3811a0..6a33f44f 100644 --- a/src/optionals/hashlib.h +++ b/src/optionals/hashlib.h @@ -7,6 +7,6 @@ #include "hashlib/hmac.h" #include "hashlib/bcrypt/bcrypt.h" -ObjModule *createHashlibModule(DictuVM *vm); +Value createHashlibModule(DictuVM *vm); #endif //dictu_hashlib_h diff --git a/src/optionals/http.c b/src/optionals/http.c index 66c89009..01398f29 100644 --- a/src/optionals/http.c +++ b/src/optionals/http.c @@ -1,5 +1,236 @@ +#include +#include +#include + #include "http.h" +#define HTTP_METHOD_GET "GET" +#define HTTP_METHOD_POST "POST" +#define HTTP_METHOD_PUT "PUT" +#define HTTP_METHOD_DELETE "DELETE" +#define HTTP_METHOD_HEAD "HEAD" +#define HTTP_METHOD_CONNECT "CONNECT" +#define HTTP_METHOD_OPTIONS "OPTIONS" +#define HTTP_METHOD_TRACE "TRACE" +#define HTTP_METHOD_PATCH "PATCH" + +#define HTTP_STATUS_CODE_CONTINUE 100 // RFC 7231, 6.2.1 +#define HTTP_STATUS_CODE_SWITCHING_PROTOCOLS 101 // RFC 7231, 6.2.2 +#define HTTP_STATUS_CODE_PROCESSING 102 // RFC 2518, 10.1 +#define HTTP_STATUS_CODE_EARLY_HINTS 103 // RFC 8297 + +#define HTTP_STATUS_CODE_OK 200 // RFC 7231, 6.3.1 +#define HTTP_STATUS_CODE_CREATED 201 // RFC 7231, 6.3.2 +#define HTTP_STATUS_CODE_ACCEPTED 202 // RFC 7231, 6.3.3 +#define HTTP_STATUS_CODE_NONAUTHORITATIVE_INFO 203 // RFC 7231, 6.3.4 +#define HTTP_STATUS_CODE_NO_CONTENT 204 // RFC 7231, 6.3.5 +#define HTTP_STATUS_CODE_RESET_CONTENT 205 // RFC 7231, 6.3.6 +#define HTTP_STATUS_CODE_PARTIAL_CONTENT 206 // RFC 7233, 4.1 +#define HTTP_STATUS_CODE_MULTI_STATUS 207 // RFC 4918, 11.1 +#define HTTP_STATUS_CODE_ALREADY_REPORTED 208 // RFC 5842, 7.1 +#define HTTP_STATUS_CODE_IM_USED 226 // RFC 3229, 10.4.1 + +#define HTTP_STATUS_CODE_MULTIPLE_CHOICES 300 // RFC 7231, 6.4.1 +#define HTTP_STATUS_CODE_MOVED_PERMANENTLY 301 // RFC 7231, 6.4.2 +#define HTTP_STATUS_CODE_FOUND 302 // RFC 7231, 6.4.3 +#define HTTP_STATUS_CODE_SEE_OTHER 303 // RFC 7231, 6.4.4 +#define HTTP_STATUS_CODE_NOT_MODIFIED 304 // RFC 7232, 4.1 +#define HTTP_STATUS_CODE_USE_PROXY 305 // RFC 7231, 6.4.5 +#define HTTP_STATUS_CODE_TEMPORARY_REDIRECT 307 // RFC 7231, 6.4.7 +#define HTTP_STATUS_CODE_PERMANENT_REDIRECT 308 // RFC 7538, 3 + +#define HTTP_STATUS_CODE_BAD_REQUEST 400 // RFC 7231, 6.5.1 +#define HTTP_STATUS_CODE_UNAUTHORIZED 401 // RFC 7235, 3.1 +#define HTTP_STATUS_CODE_PAYMENT_REQUIRED 402 // RFC 7231, 6.5.2 +#define HTTP_STATUS_CODE_FORBIDDEN 403 // RFC 7231, 6.5.3 +#define HTTP_STATUS_CODE_NOT_FOUND 404 // RFC 7231, 6.5.4 +#define HTTP_STATUS_CODE_METHOD_NOT_ALLOWED 405 // RFC 7231, 6.5.5 +#define HTTP_STATUS_CODE_NOT_ACCEPTABLE 406 // RFC 7231, 6.5.6 +#define HTTP_STATUS_CODE_PROXY_AUTH_REQUIRED 407 // RFC 7235, 3.2 +#define HTTP_STATUS_CODE_REQUEST_TIMEOUT 408 // RFC 7231, 6.5.7 +#define HTTP_STATUS_CODE_CONFLICT 409 // RFC 7231, 6.5.8 +#define HTTP_STATUS_CODE_GONE 410 // RFC 7231, 6.5.9 +#define HTTP_STATUS_CODE_LENGTH_REQUIRED 411 // RFC 7231, 6.5.10 +#define HTTP_STATUS_CODE_PRECONDITION_FAILED 412 // RFC 7232, 4.2 +#define HTTP_STATUS_CODE_REQUEST_ENTITY_TOO_LARGE 413 // RFC 7231, 6.5.11 +#define HTTP_STATUS_CODE_REQUEST_URI_TOO_LONG 414 // RFC 7231, 6.5.12 +#define HTTP_STATUS_CODE_UNSUPPORTED_MEDIA_TYPE 415 // RFC 7231, 6.5.13 +#define HTTP_STATUS_CODE_REQUESTED_RANGE_NOT_SATISFIABLE 416 // RFC 7233, 4.4 +#define HTTP_STATUS_CODE_EXPECTATION_FAILED 417 // RFC 7231, 6.5.14 +#define HTTP_STATUS_CODE_TEAPOT 418 // RFC 7168, 2.3.3 +#define HTTP_STATUS_CODE_MISDIRECTED_REQUEST 421 // RFC 7540, 9.1.2 +#define HTTP_STATUS_CODE_UNPROCESSABLE_ENTITY 422 // RFC 4918, 11.2 +#define HTTP_STATUS_CODE_LOCKED 423 // RFC 4918, 11.3 +#define HTTP_STATUS_CODE_FAILED_DEPENDENCY 424 // RFC 4918, 11.4 +#define HTTP_STATUS_CODE_TOO_EARLY 425 // RFC 8470, 5.2. +#define HTTP_STATUS_CODE_UPGRADE_REQUIRED 426 // RFC 7231, 6.5.15 +#define HTTP_STATUS_CODE_PRECONDITION_REQUIRED 428 // RFC 6585, 3 +#define HTTP_STATUS_CODE_TOO_MANY_REQUESTS 429 // RFC 6585, 4 +#define HTTP_STATUS_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE 431 // RFC 6585, 5 +#define HTTP_STATUS_CODE_UNAVAILABLE_FOR_LEGAL_REASONS 451 // RFC 7725, 3 + +#define HTTP_STATUS_CODE_INTERNAL_SERVER_ERROR 500 // RFC 7231, 6.6.1 +#define HTTP_STATUS_CODE_NOT_IMPLEMENTED 501 // RFC 7231, 6.6.2 +#define HTTP_STATUS_CODE_BAD_GATEWAY 502 // RFC 7231, 6.6.3 +#define HTTP_STATUS_CODE_SERVICE_UNAVAILABLE 503 // RFC 7231, 6.6.4 +#define HTTP_STATUS_CODE_GATEWAY_TIMEOUT 504 // RFC 7231, 6.6.5 +#define HTTP_STATUS_CODE_HTTP_VERSION_NOT_SUPPORTED 505 // RFC 7231, 6.6.6 +#define HTTP_STATUS_CODE_VARIANT_ALSO_NEGOTIATES 506 // RFC 2295, 8.1 +#define HTTP_STATUS_CODE_INSUFFICIENT_STORAGE 507 // RFC 4918, 11.5 +#define HTTP_STATUS_CODE_LOOP_DETECTED 508 // RFC 5842, 7.2 +#define HTTP_STATUS_CODE_NOT_EXTENDED 510 // RFC 2774, 7 +#define HTTP_STATUS_CODE_NETWORK_AUTHENTICATION_REQUIRED 511 // RFC 6585, 6 + +#define HTTP_STATUS_MESSAGE_CONTINUE "Continue" +#define HTTP_STATUS_MESSAGE_SWITCHING_PROTOCOLS "Switching Protocols" +#define HTTP_STATUS_MESSAGE_PROCESS "Processing" +#define HTTP_STATUS_MESSAGE_EARLY_HINTS "Early Hints" +#define HTTP_STATUS_MESSAGE_OK "OK" +#define HTTP_STATUS_MESSAGE_CREATED "Created" +#define HTTP_STATUS_MESSAGE_ACCEPTED "Accepted" +#define HTTP_STATUS_MESSAGE_NONAUTHORITATIVE_INFO "Non-Authoritative Information" +#define HTTP_STATUS_MESSAGE_NO_CONTENT "No Content" +#define HTTP_STATUS_MESSAGE_RESET_CONTENT "Reset Content" +#define HTTP_STATUS_MESSAGE_PARTIAL_CONTENT "Partial Content" +#define HTTP_STATUS_MESSAGE_MULTI_STATUS "Multi-Status" +#define HTTP_STATUS_MESSAGE_ALREADY_REPORTED "Already Reported" +#define HTTP_STATUS_MESSAGE_IM_USED "IM Used" +#define HTTP_STATUS_MESSAGE_MULTIPLE_CHOICES "Multiple Choices" +#define HTTP_STATUS_MESSAGE_MOVED_PERMANENTLY "Moved Permanently" +#define HTTP_STATUS_MESSAGE_FOUND "Found" +#define HTTP_STATUS_MESSAGE_SEE_OTHER "See Other" +#define HTTP_STATUS_MESSAGE_NOT_MODIFIED "Not Modified" +#define HTTP_STATUS_MESSAGE_USE_PROXY "Use Proxy" +#define HTTP_STATUS_MESSAGE_TEMPORARY_REDIRECT "Temporary Redirect" +#define HTTP_STATUS_MESSAGE_PERMANENT_REDIRECT "Permanent Redirect" +#define HTTP_STATUS_MESSAGE_BAD_REQUEST "Bad Request" +#define HTTP_STATUS_MESSAGE_UNAUTHORIZED "Unauthorized" +#define HTTP_STATUS_MESSAGE_PAYMENT_REQUIRED "Payment Required" +#define HTTP_STATUS_MESSAGE_FORBIDDEN "Forbidden" +#define HTTP_STATUS_MESSAGE_NOT_FOUND "Not Found" +#define HTTP_STATUS_MESSAGE_METHOD_NOT_ALLOWED "Method Not Allowed" +#define HTTP_STATUS_MESSAGE_NOT_ACCEPTABLE "Not Acceptable" +#define HTTP_STATUS_MESSAGE_PROXY_AUTH_REQUIRED "Proxy Authentication Required" +#define HTTP_STATUS_MESSAGE_REQUEST_TIMEOUT "Request Timeout" +#define HTTP_STATUS_MESSAGE_CONFLICT "Conflict" +#define HTTP_STATUS_MESSAGE_GONE "Gone" +#define HTTP_STATUS_MESSAGE_LENGTH_REQUIRED "Length Required" +#define HTTP_STATUS_MESSAGE_PRECONDITION_FAILED "Precondition Failed" +#define HTTP_STATUS_MESSAGE_REQUEST_ENTITY_TOO_LARGE "Request Entity Too Large" +#define HTTP_STATUS_MESSAGE_REQUEST_URI_TOO_LONG "Request URI Too Long" +#define HTTP_STATUS_MESSAGE_UNSUPPORTED_MEDIA_TYPE "Unsupported Media Type" +#define HTTP_STATUS_MESSAGE_REQUESTED_RANGE_NOT_SATISFIABLE "Requested Range Not Satisfiable" +#define HTTP_STATUS_MESSAGE_EXPECTATION_FAILED "Expectation Failed" +#define HTTP_STATUS_MESSAGE_TEAPOT "I'm a teapot" +#define HTTP_STATUS_MESSAGE_MISDIRECTED_REQUEST "Misdirected Request" +#define HTTP_STATUS_MESSAGE_UNPROCESSABLE_ENTITY "Unprocessable Entity" +#define HTTP_STATUS_MESSAGE_LOCKED "Locked" +#define HTTP_STATUS_MESSAGE_FAILED_DEPENDENCY "Failed Dependency" +#define HTTP_STATUS_MESSAGE_TOO_EARLY "Too Early" +#define HTTP_STATUS_MESSAGE_UPGRADE_REQIUIRED "Upgrade Required" +#define HTTP_STATUS_MESSAGE_PRECONDITION_REQUIRED "Precondition Required" +#define HTTP_STATUS_MESSAGE_TOO_MANY_REQUESTS "Too Many Requests" +#define HTTP_STATUS_MESSAGE_REQUEST_HEADER_FIELDS_TOO_LARGE "Request Header Fields Too Large" +#define HTTP_STATUS_MESSAGE_UNAVAILABLE_FOR_LEGAL_REASONS "Unavailable For Legal Reasons" +#define HTTP_STATUS_MESSAGE_INTERNAL_SERVER_ERROR "Internal Server Error" +#define HTTP_STATUS_MESSAGE_NOT_IMPLEMENTED "Not Implemented" +#define HTTP_STATUS_MESSAGE_BAD_GATEWAY "Bad Gateway" +#define HTTP_STATUS_MESSAGE_UNAVAILABLE "Service Unavailable" +#define HTTP_STATUS_MESSAGE_GATEWAY_TIMEOUT "Gateway Timeout" +#define HTTP_STATUS_MESSAGE_HTTP_VERSION_NOT_SUPPORTED "HTTP Version Not Supported" +#define HTTP_STATUS_MESSAGE_VARIAN_ALSO_NEGOTIATES "Variant Also Negotiates" +#define HTTP_STATUS_MESSAGE_INSUFFICIENT_STORAGE "Insufficient Storage" +#define HTTP_STATUS_MESSAGE_LOOP_DETECTED "Loop Detected" +#define HTTP_STATUS_MESSAGE_NOT_EXTENDED "Not Extended" +#define HTTP_STATUS_MESSAGE_NETWORK_AUTHENTICATION_REQUIRED "Network Authentication Required" + +#define HTTP_REQUEST_HEADER_AIM "A-IM" +#define HTTP_REQUEST_HEADER_ACCEPT "Accept" +#define HTTP_REQUEST_HEADER_ACCEPT_CHARSET "Accept-Charset" +#define HTTP_REQUEST_HEADER_ACCEPT_DATETIME "Accept-Datetime" +#define HTTP_REQUEST_HEADER_ACCEPT_ENCODING "Accept-Encoding" +#define HTTP_REQUEST_HEADER_ACCEPT_LANGUAGE "Accept-Language" +#define HTTP_REQUEST_HEADER_ACCEPT_CONTROL_REQUEST_METHOD "Access-Control-Request-Method" +#define HTTP_REQUEST_HEADER_ACCEPT_CONTROL_REQUEST_HEADERS "Access-Control-Request-Headers" +#define HTTP_REQUEST_HEADER_AUTHORIZATION "Authorization" +#define HTTP_REQUEST_HEADER_CACHE_CONTROL "Cache-Control" +#define HTTP_REQUEST_HEADER_CONNECTION "Connection" +#define HTTP_REQUEST_HEADER_CONTENT_ENCODING "Content-Encoding" +#define HTTP_REQUEST_HEADER_CONTENT_LENGTH "Content-Length" +#define HTTP_REQUEST_HEADER_CONTENT_MD5 "Content-MD5" +#define HTTP_REQUEST_HEADER_CONTENT_TYPE "Content-Type" +#define HTTP_REQUEST_HEADER_COOKIE "Cookie" +#define HTTP_REQUEST_HEADER_DATE "Date" +#define HTTP_REQUEST_HEADER_EXPECT "Expect" +#define HTTP_REQUEST_HEADER_FORWARDED "Forwarded" +#define HTTP_REQUEST_HEADER_FROM "From" +#define HTTP_REQUEST_HEADER_HOST "Host" +#define HTTP_REQUEST_HEADER_HTTP2_SETTINGS "HTTP2-Settings" +#define HTTP_REQUEST_HEADER_IF_MATCH "If-Match" +#define HTTP_REQUEST_HEADER_IF_MODIFIED_SINCE "If-Modified-Since" +#define HTTP_REQUEST_HEADER_IF_NONE_MATCH "If-None-Match" +#define HTTP_REQUEST_HEADER_IF_RANGE "If-Range" +#define HTTP_REQUEST_HEADER_IF_UNMODIFIED_SINCE "If-Unmodified-Since" +#define HTTP_REQUEST_HEADER_MAX_FORWARDS "Max-Forwards" +#define HTTP_REQUEST_HEADER_PRAGMA "Pragma" +#define HTTP_REQUEST_HEADER_PROXY_AUTHORIZATION "Proxy-Authorization" +#define HTTP_REQUEST_HEADER_RANGE "Range" +#define HTTP_REQUEST_HEADER_REFERRER "Referer" +#define HTTP_REQUEST_HEADER_TE "TE" +#define HTTP_REQUEST_HEADER_TRAILER "Trailer" +#define HTTP_REQUEST_HEADER_TRANSFER_ENCODING "Transfer-Encoding" +#define HTTP_REQUEST_HEADER_USER_AGENT "User-Agent" +#define HTTP_REQUEST_HEADER_UPGRADE "Upgrade" +#define HTTP_REQUEST_HEADER_WARNING "Warning" + +#define HTTP_RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN "Access-Control-Allow-Origin" +#define HTTP_RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS HTTP_RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN +#define HTTP_RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS HTTP_RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN +#define HTTP_RESPONSE_HEADER_ACCESS_CONTROL_MAX_AGE HTTP_RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN +#define HTTP_RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_METHODS HTTP_RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN +#define HTTP_RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_HEADERS HTTP_RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN +#define HTTP_RESPONSE_HEADER_ACCEPT_PATCH "Accept-Patch" +#define HTTP_RESPONSE_HEADER_ACCEPT_RANGES "Accept-Ranges" +#define HTTP_RESPONSE_HEADER_AGE "Age" +#define HTTP_RESPONSE_HEADER_ALLOW "Allow" +#define HTTP_RESPONSE_HEADER_ALT_SVC "Alt-Svc" +#define HTTP_RESPONSE_HEADER_CACHE_CONTROL "Cache-Control" +#define HTTP_RESPONSE_HEADER_CONNECTION "Connection" +#define HTTP_RESPONSE_HEADER_CONTENT_DISPOSITION "Content-Disposition" +#define HTTP_RESPONSE_HEADER_CONTENT_ENCODING "Content-Encoding" +#define HTTP_RESPONSE_HEADER_CONTENT_LANGUAGE "Content-Language" +#define HTTP_RESPONSE_HEADER_CONTENT_LENGTH "Content-Length" +#define HTTP_RESPONSE_HEADER_CONTENT_LOCATION "Content-Location" +#define HTTP_RESPONSE_HEADER_CONTENT_MD5 "Content-MD5" +#define HTTP_RESPONSE_HEADER_CONTENT_RANGE "Content-Range" +#define HTTP_RESPONSE_HEADER_CONTENT_TYPE "Content-Type" +#define HTTP_RESPONSE_HEADER_DATE "Date" +#define HTTP_RESPONSE_HEADER_DELTA_BASE "Delta-Base" +#define HTTP_RESPONSE_HEADER_ETAG "ETag" +#define HTTP_RESPONSE_HEADER_EXPIRES "Expires" +#define HTTP_RESPONSE_HEADER_IM "IM" +#define HTTP_RESPONSE_HEADER_LAST_MODIFIED "Last-Modified" +#define HTTP_RESPONSE_HEADER_LINK "Link" +#define HTTP_RESPONSE_HEADER_LOCATION "Location" +#define HTTP_RESPONSE_HEADER_P3P "P3P" +#define HTTP_RESPONSE_HEADER_PRAGMA "Pragma" +#define HTTP_RESPONSE_HEADER_PROXY_AUTHENTICATE "Proxy-Authenticate" +#define HTTP_RESPONSE_HEADER_PUBLIC_KEY_PINS "Public-Key-Pins" +#define HTTP_RESPONSE_HEADER_RETRY_AFTER "Retry-After" +#define HTTP_RESPONSE_HEADER_SET_COOKIE "Set-Cookie" +#define HTTP_RESPONSE_HEADER_STRICT_TRANSPORT_SECURITY "Strict-Transport-Security" +#define HTTP_RESPONSE_HEADER_TRAILER "Trailer" +#define HTTP_RESPONSE_HEADER_TRANSFER_ENCODING "Transfer-Encoding" +#define HTTP_RESPONSE_HEADER_TK "Tk" +#define HTTP_RESPONSE_HEADER_UPGRADE "Upgrade" +#define HTTP_RESPONSE_HEADER_VARY "Vary" +#define HTTP_RESPONSE_HEADER_VIA "Via" +#define HTTP_RESPONSE_HEADER_WARNING "Warning" +#define HTTP_RESPONSE_HEADER_WWW_AUTHENTICATE "WWW-Authenticate" +#define HTTP_RESPONSE_HEADER_X_FRAME_OPTIONS "X-Frame-Options" + +#define HTTP_MAX_HEADER_BYTES 1 << 20 // 1 MB + static void createResponse(DictuVM *vm, Response *response) { response->vm = vm; response->headers = newList(vm); @@ -368,12 +599,239 @@ static Value post(DictuVM *vm, int argCount, Value *args) { return newResultError(vm, errorString); } -ObjModule *createHTTPModule(DictuVM *vm) { +Value createHTTPModule(DictuVM *vm) { ObjString *name = copyString(vm, "HTTP", 4); push(vm, OBJ_VAL(name)); ObjModule *module = newModule(vm, name); push(vm, OBJ_VAL(module)); + defineNativeProperty(vm, &module->values, "METHOD_GET", OBJ_VAL(copyString(vm, HTTP_METHOD_GET, strlen(HTTP_METHOD_GET)))); + defineNativeProperty(vm, &module->values, "METHOD_POST", OBJ_VAL(copyString(vm, HTTP_METHOD_POST, strlen(HTTP_METHOD_POST)))); + defineNativeProperty(vm, &module->values, "METHOD_DELETE", OBJ_VAL(copyString(vm, HTTP_METHOD_DELETE, strlen(HTTP_METHOD_DELETE)))); + defineNativeProperty(vm, &module->values, "METHOD_PUT", OBJ_VAL(copyString(vm, HTTP_METHOD_PUT, strlen(HTTP_METHOD_PUT)))); + defineNativeProperty(vm, &module->values, "METHOD_HEAD", OBJ_VAL(copyString(vm, HTTP_METHOD_HEAD, strlen(HTTP_METHOD_HEAD)))); + defineNativeProperty(vm, &module->values, "METHOD_CONNECT", OBJ_VAL(copyString(vm, HTTP_METHOD_CONNECT, strlen(HTTP_METHOD_CONNECT)))); + defineNativeProperty(vm, &module->values, "METHOD_OPTIONS", OBJ_VAL(copyString(vm, HTTP_METHOD_OPTIONS, strlen(HTTP_METHOD_OPTIONS)))); + defineNativeProperty(vm, &module->values, "METHOD_TRACE", OBJ_VAL(copyString(vm, HTTP_METHOD_TRACE, strlen(HTTP_METHOD_TRACE)))); + defineNativeProperty(vm, &module->values, "METHOD_PATCH", OBJ_VAL(copyString(vm, HTTP_METHOD_PATCH, strlen(HTTP_METHOD_PATCH)))); + + defineNativeProperty(vm, &module->values, "STATUS_CODE_CONTINUE", NUMBER_VAL(HTTP_STATUS_CODE_CONTINUE)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_SWITCHING_PROTOCOLS", NUMBER_VAL(HTTP_STATUS_CODE_SWITCHING_PROTOCOLS)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_PROCESSING", NUMBER_VAL(HTTP_STATUS_CODE_PROCESSING)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_EARLY_HINTS", NUMBER_VAL(HTTP_STATUS_CODE_EARLY_HINTS)); + defineNativeProperty(vm, &module->values, "STATUS_OK", NUMBER_VAL(HTTP_STATUS_CODE_OK)); + + defineNativeProperty(vm, &module->values, "STATUS_CODE_CREATED", NUMBER_VAL(HTTP_STATUS_CODE_CREATED)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_ACCEPTED", NUMBER_VAL(HTTP_STATUS_CODE_ACCEPTED)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_NONAUTHORITATIVE_INFO", NUMBER_VAL(HTTP_STATUS_CODE_NONAUTHORITATIVE_INFO)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_NO_CONTENT", NUMBER_VAL(HTTP_STATUS_CODE_NO_CONTENT)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_RESET_CONTENT", NUMBER_VAL(HTTP_STATUS_CODE_RESET_CONTENT)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_PARTIAL_CONTENT", NUMBER_VAL(HTTP_STATUS_CODE_PARTIAL_CONTENT)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_MULTI_STATUS", NUMBER_VAL(HTTP_STATUS_CODE_MULTI_STATUS)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_ALREADY_REPORTED", NUMBER_VAL(HTTP_STATUS_CODE_ALREADY_REPORTED)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_IM_USED", NUMBER_VAL(HTTP_STATUS_CODE_IM_USED)); + + defineNativeProperty(vm, &module->values, "STATUS_CODE_MULTIPLE_CHOICES", NUMBER_VAL(HTTP_STATUS_CODE_MULTIPLE_CHOICES)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_MOVED_PERMANENTLY", NUMBER_VAL(HTTP_STATUS_CODE_MOVED_PERMANENTLY)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_FOUND", NUMBER_VAL(HTTP_STATUS_CODE_FOUND)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_SEE_OTHER", NUMBER_VAL(HTTP_STATUS_CODE_SEE_OTHER)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_NOT_MODIFIED", NUMBER_VAL(HTTP_STATUS_CODE_NOT_MODIFIED)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_USE_PROXY", NUMBER_VAL(HTTP_STATUS_CODE_USE_PROXY)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_TEMPORARY_REDIRECT", NUMBER_VAL(HTTP_STATUS_CODE_TEMPORARY_REDIRECT)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_PERMANENT_REDIRECT", NUMBER_VAL(HTTP_STATUS_CODE_PERMANENT_REDIRECT)); + + defineNativeProperty(vm, &module->values, "STATUS_CODE_BAD_REQUEST", NUMBER_VAL(HTTP_STATUS_CODE_BAD_REQUEST)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_UNAUTHORIZED", NUMBER_VAL(HTTP_STATUS_CODE_UNAUTHORIZED)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_PAYMENT_REQUIRED", NUMBER_VAL(HTTP_STATUS_CODE_PAYMENT_REQUIRED)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_FORBIDDEN", NUMBER_VAL(HTTP_STATUS_CODE_FORBIDDEN)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_NOT_FOUND", NUMBER_VAL(HTTP_STATUS_CODE_NOT_FOUND)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_METHOD_NOT_ALLOWED", NUMBER_VAL(HTTP_STATUS_CODE_METHOD_NOT_ALLOWED)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_NOT_ACCEPTABLE", NUMBER_VAL(HTTP_STATUS_CODE_NOT_ACCEPTABLE)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_PROXY_AUTH_REQUIRED", NUMBER_VAL(HTTP_STATUS_CODE_PROXY_AUTH_REQUIRED)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_REQUEST_TIMEOUT", NUMBER_VAL(HTTP_STATUS_CODE_REQUEST_TIMEOUT)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_CONFLICT", NUMBER_VAL(HTTP_STATUS_CODE_CONFLICT)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_GONE", NUMBER_VAL(HTTP_STATUS_CODE_GONE)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_LENGTH_REQUIRED", NUMBER_VAL(HTTP_STATUS_CODE_LENGTH_REQUIRED)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_PRECONDITION_FAILED", NUMBER_VAL(HTTP_STATUS_CODE_PRECONDITION_FAILED)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_REQUEST_ENTITY_TOO_LARGE", NUMBER_VAL(HTTP_STATUS_CODE_REQUEST_ENTITY_TOO_LARGE)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_REQUEST_URI_TOO_LONG", NUMBER_VAL(HTTP_STATUS_CODE_REQUEST_URI_TOO_LONG)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_UNSUPPORTED_MEDIA_TYPE", NUMBER_VAL(HTTP_STATUS_CODE_UNSUPPORTED_MEDIA_TYPE)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_REQUESTED_RANGE_NOT_SATISFIABLE", NUMBER_VAL(HTTP_STATUS_CODE_REQUESTED_RANGE_NOT_SATISFIABLE)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_EXPECTATION_FAILED", NUMBER_VAL(HTTP_STATUS_CODE_EXPECTATION_FAILED)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_TEAPOT", NUMBER_VAL(HTTP_STATUS_CODE_TEAPOT)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_MISDIRECTED_REQUEST", NUMBER_VAL(HTTP_STATUS_CODE_MISDIRECTED_REQUEST)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_UNPROCESSABLE_ENTITY", NUMBER_VAL(HTTP_STATUS_CODE_UNPROCESSABLE_ENTITY)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_LOCKED", NUMBER_VAL(HTTP_STATUS_CODE_LOCKED)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_FAILED_DEPENDENCY", NUMBER_VAL(HTTP_STATUS_CODE_FAILED_DEPENDENCY)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_TOO_EARLY", NUMBER_VAL(HTTP_STATUS_CODE_TOO_EARLY)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_UPGRADE_REQUIRED", NUMBER_VAL(HTTP_STATUS_CODE_UPGRADE_REQUIRED)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_PRECONDITION_REQUIRED", NUMBER_VAL(HTTP_STATUS_CODE_PRECONDITION_REQUIRED)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_TOO_MANY_REQUESTS", NUMBER_VAL(HTTP_STATUS_CODE_TOO_MANY_REQUESTS)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE", NUMBER_VAL(HTTP_STATUS_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_UNAVAILABLE_FOR_LEGAL_REASONS", NUMBER_VAL(HTTP_STATUS_CODE_UNAVAILABLE_FOR_LEGAL_REASONS)); + + defineNativeProperty(vm, &module->values, "STATUS_CODE_INTERNAL_SERVER_ERROR", NUMBER_VAL(HTTP_STATUS_CODE_INTERNAL_SERVER_ERROR)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_NOT_IMPLEMENTED", NUMBER_VAL(HTTP_STATUS_CODE_NOT_IMPLEMENTED)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_BAD_GATEWAY", NUMBER_VAL(HTTP_STATUS_CODE_BAD_GATEWAY)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_SERVICE_UNAVAILABLE", NUMBER_VAL(HTTP_STATUS_CODE_SERVICE_UNAVAILABLE)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_GATEWAY_TIMEOUT", NUMBER_VAL(HTTP_STATUS_CODE_GATEWAY_TIMEOUT)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_HTTP_VERSION_NOT_SUPPORTED", NUMBER_VAL(HTTP_STATUS_CODE_HTTP_VERSION_NOT_SUPPORTED)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_VARIANT_ALSO_NEGOTIATES", NUMBER_VAL(HTTP_STATUS_CODE_VARIANT_ALSO_NEGOTIATES)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_INSUFFICIENT_STORAGE", NUMBER_VAL(HTTP_STATUS_CODE_INSUFFICIENT_STORAGE)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_LOOP_DETECTED", NUMBER_VAL(HTTP_STATUS_CODE_LOOP_DETECTED)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_NOT_EXTENDED", NUMBER_VAL(HTTP_STATUS_CODE_NOT_EXTENDED)); + defineNativeProperty(vm, &module->values, "STATUS_CODE_NETWORK_AUTHENTICATION_REQUIRED", NUMBER_VAL(HTTP_STATUS_CODE_NETWORK_AUTHENTICATION_REQUIRED)); + + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_CONTINUE", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_CONTINUE, strlen(HTTP_STATUS_MESSAGE_CONTINUE)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_SWITCHING_PROTOCOLS", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_SWITCHING_PROTOCOLS, strlen(HTTP_STATUS_MESSAGE_SWITCHING_PROTOCOLS)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_PROCESS", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_PROCESS, strlen(HTTP_STATUS_MESSAGE_PROCESS)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_EARLY_HINTS", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_EARLY_HINTS, strlen(HTTP_STATUS_MESSAGE_EARLY_HINTS)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_OK", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_OK, strlen(HTTP_STATUS_MESSAGE_OK)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_CREATED", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_CREATED, strlen(HTTP_STATUS_MESSAGE_CREATED)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_ACCEPTED", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_ACCEPTED, strlen(HTTP_STATUS_MESSAGE_ACCEPTED)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_NONAUTHORITATIVE_INFO", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_NONAUTHORITATIVE_INFO, strlen(HTTP_STATUS_MESSAGE_NONAUTHORITATIVE_INFO)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_NO_CONTENT", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_NO_CONTENT, strlen(HTTP_STATUS_MESSAGE_NO_CONTENT)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_RESET_CONTENT", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_RESET_CONTENT, strlen(HTTP_STATUS_MESSAGE_RESET_CONTENT)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_PARTIAL_CONTENT", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_PARTIAL_CONTENT, strlen(HTTP_STATUS_MESSAGE_PARTIAL_CONTENT)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_MULTI_STATUS", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_MULTI_STATUS, strlen(HTTP_STATUS_MESSAGE_MULTI_STATUS)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_ALREADY_REPORTED", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_ALREADY_REPORTED, strlen(HTTP_STATUS_MESSAGE_ALREADY_REPORTED)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_IM_USED", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_IM_USED, strlen(HTTP_STATUS_MESSAGE_IM_USED)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_MULTIPLE_CHOICES", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_MULTIPLE_CHOICES, strlen(HTTP_STATUS_MESSAGE_MULTIPLE_CHOICES)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_MOVED_PERMANENTLY", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_MOVED_PERMANENTLY, strlen(HTTP_STATUS_MESSAGE_MOVED_PERMANENTLY)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_FOUND", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_FOUND, strlen(HTTP_STATUS_MESSAGE_FOUND)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_SEE_OTHER", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_SEE_OTHER, strlen(HTTP_STATUS_MESSAGE_SEE_OTHER)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_NOT_MODIFIED", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_NOT_MODIFIED, strlen(HTTP_STATUS_MESSAGE_NOT_MODIFIED)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_USE_PROXY", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_USE_PROXY, strlen(HTTP_STATUS_MESSAGE_USE_PROXY)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_TEMPORARY_REDIRECT", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_TEMPORARY_REDIRECT, strlen(HTTP_STATUS_MESSAGE_TEMPORARY_REDIRECT)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_PERMANENT_REDIRECT", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_PERMANENT_REDIRECT, strlen(HTTP_STATUS_MESSAGE_PERMANENT_REDIRECT)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_BAD_REQUEST", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_BAD_REQUEST, strlen(HTTP_STATUS_MESSAGE_BAD_REQUEST)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_UNAUTHORIZED", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_UNAUTHORIZED, strlen(HTTP_STATUS_MESSAGE_UNAUTHORIZED)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_PAYMENT_REQUIRED", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_PAYMENT_REQUIRED, strlen(HTTP_STATUS_MESSAGE_PAYMENT_REQUIRED)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_FORBIDDEN", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_FORBIDDEN, strlen(HTTP_STATUS_MESSAGE_FORBIDDEN)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_NOT_FOUND", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_NOT_FOUND, strlen(HTTP_STATUS_MESSAGE_NOT_FOUND)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_METHOD_NOT_ALLOWED", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_METHOD_NOT_ALLOWED, strlen(HTTP_STATUS_MESSAGE_METHOD_NOT_ALLOWED)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_NOT_ACCEPTABLE", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_NOT_ACCEPTABLE, strlen(HTTP_STATUS_MESSAGE_NOT_ACCEPTABLE)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_PROXY_AUTH_REQUIRED", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_PROXY_AUTH_REQUIRED, strlen(HTTP_STATUS_MESSAGE_PROXY_AUTH_REQUIRED)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_REQUEST_TIMEOUT", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_REQUEST_TIMEOUT, strlen(HTTP_STATUS_MESSAGE_REQUEST_TIMEOUT)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_CONFLICT", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_CONFLICT, strlen(HTTP_STATUS_MESSAGE_CONFLICT)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_GONE", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_GONE, strlen(HTTP_STATUS_MESSAGE_GONE)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_LENGTH_REQUIRED", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_LENGTH_REQUIRED, strlen(HTTP_STATUS_MESSAGE_LENGTH_REQUIRED)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_PRECONDITION_FAILED", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_PRECONDITION_FAILED, strlen(HTTP_STATUS_MESSAGE_PRECONDITION_FAILED)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_REQUEST_ENTITY_TOO_LARGE", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_REQUEST_ENTITY_TOO_LARGE, strlen(HTTP_STATUS_MESSAGE_REQUEST_ENTITY_TOO_LARGE)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_REQUEST_URI_TOO_LONG", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_REQUEST_URI_TOO_LONG, strlen(HTTP_STATUS_MESSAGE_REQUEST_URI_TOO_LONG)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_UNSUPPORTED_MEDIA_TYPE", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_UNSUPPORTED_MEDIA_TYPE, strlen(HTTP_STATUS_MESSAGE_UNSUPPORTED_MEDIA_TYPE)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_REQUESTED_RANGE_NOT_SATISFIABLE", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_REQUESTED_RANGE_NOT_SATISFIABLE, strlen(HTTP_STATUS_MESSAGE_REQUESTED_RANGE_NOT_SATISFIABLE)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_EXPECTATION_FAILED", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_EXPECTATION_FAILED, strlen(HTTP_STATUS_MESSAGE_EXPECTATION_FAILED)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_TEAPOT", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_TEAPOT, strlen(HTTP_STATUS_MESSAGE_TEAPOT)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_MISDIRECTED_REQUEST", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_MISDIRECTED_REQUEST, strlen(HTTP_STATUS_MESSAGE_MISDIRECTED_REQUEST)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_UNPROCESSABLE_ENTITY", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_UNPROCESSABLE_ENTITY, strlen(HTTP_STATUS_MESSAGE_UNPROCESSABLE_ENTITY)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_LOCKED", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_LOCKED, strlen(HTTP_STATUS_MESSAGE_LOCKED)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_FAILED_DEPENDENCY", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_FAILED_DEPENDENCY, strlen(HTTP_STATUS_MESSAGE_FAILED_DEPENDENCY)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_TOO_EARLY", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_TOO_EARLY, strlen(HTTP_STATUS_MESSAGE_TOO_EARLY)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_UPGRADE_REQIUIRED", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_UPGRADE_REQIUIRED, strlen(HTTP_STATUS_MESSAGE_UPGRADE_REQIUIRED)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_PRECONDITION_REQUIRED", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_PRECONDITION_REQUIRED, strlen(HTTP_STATUS_MESSAGE_PRECONDITION_REQUIRED)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_TOO_MANY_REQUESTS", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_TOO_MANY_REQUESTS, strlen(HTTP_STATUS_MESSAGE_TOO_MANY_REQUESTS)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_REQUEST_HEADER_FIELDS_TOO_LARGE", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_REQUEST_HEADER_FIELDS_TOO_LARGE, strlen(HTTP_STATUS_MESSAGE_REQUEST_HEADER_FIELDS_TOO_LARGE)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_UNAVAILABLE_FOR_LEGAL_REASONS", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_UNAVAILABLE_FOR_LEGAL_REASONS, strlen(HTTP_STATUS_MESSAGE_UNAVAILABLE_FOR_LEGAL_REASONS)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_INTERNAL_SERVER_ERROR", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_INTERNAL_SERVER_ERROR, strlen(HTTP_STATUS_MESSAGE_INTERNAL_SERVER_ERROR)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_NOT_IMPLEMENTED", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_NOT_IMPLEMENTED, strlen(HTTP_STATUS_MESSAGE_NOT_IMPLEMENTED)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_BAD_GATEWAY", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_BAD_GATEWAY, strlen(HTTP_STATUS_MESSAGE_BAD_GATEWAY)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_UNAVAILABLE", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_UNAVAILABLE, strlen(HTTP_STATUS_MESSAGE_UNAVAILABLE)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_GATEWAY_TIMEOUT", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_GATEWAY_TIMEOUT, strlen(HTTP_STATUS_MESSAGE_GATEWAY_TIMEOUT)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_HTTP_VERSION_NOT_SUPPORTED", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_HTTP_VERSION_NOT_SUPPORTED, strlen(HTTP_STATUS_MESSAGE_HTTP_VERSION_NOT_SUPPORTED)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_VARIAN_ALSO_NEGOTIATES", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_VARIAN_ALSO_NEGOTIATES, strlen(HTTP_STATUS_MESSAGE_VARIAN_ALSO_NEGOTIATES)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_INSUFFICIENT_STORAGE", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_INSUFFICIENT_STORAGE, strlen(HTTP_STATUS_MESSAGE_INSUFFICIENT_STORAGE)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_LOOP_DETECTED", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_LOOP_DETECTED, strlen(HTTP_STATUS_MESSAGE_LOOP_DETECTED)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_NOT_EXTENDED", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_NOT_EXTENDED, strlen(HTTP_STATUS_MESSAGE_NOT_EXTENDED)))); + defineNativeProperty(vm, &module->values, "STATUS_MESSAGE_NETWORK_AUTHENTICATION_REQUIRED", OBJ_VAL(copyString(vm, HTTP_STATUS_MESSAGE_NETWORK_AUTHENTICATION_REQUIRED, strlen(HTTP_STATUS_MESSAGE_NETWORK_AUTHENTICATION_REQUIRED)))); + + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_AIM", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_AIM, strlen(HTTP_REQUEST_HEADER_AIM)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_ACCEPT", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_ACCEPT, strlen(HTTP_REQUEST_HEADER_ACCEPT)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_ACCEPT_CHARSET", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_ACCEPT_CHARSET, strlen(HTTP_REQUEST_HEADER_ACCEPT_CHARSET)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_ACCEPT_DATETIME", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_ACCEPT_DATETIME, strlen(HTTP_REQUEST_HEADER_ACCEPT_DATETIME)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_ACCEPT_ENCODING", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_ACCEPT_ENCODING, strlen(HTTP_REQUEST_HEADER_ACCEPT_ENCODING)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_ACCEPT_LANGUAGE", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_ACCEPT_LANGUAGE, strlen(HTTP_REQUEST_HEADER_ACCEPT_LANGUAGE)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_ACCEPT_CONTROL_REQUEST_METHOD", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_ACCEPT_CONTROL_REQUEST_METHOD, strlen(HTTP_REQUEST_HEADER_ACCEPT_CONTROL_REQUEST_METHOD)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_ACCEPT_CONTROL_REQUEST_HEADERS", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_ACCEPT_CONTROL_REQUEST_HEADERS, strlen(HTTP_REQUEST_HEADER_ACCEPT_CONTROL_REQUEST_HEADERS)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_AUTHORIZATION", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_AUTHORIZATION, strlen(HTTP_REQUEST_HEADER_AUTHORIZATION)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_CACHE_CONTROL", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_CACHE_CONTROL, strlen(HTTP_REQUEST_HEADER_CACHE_CONTROL)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_CONNECTION", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_CONNECTION, strlen(HTTP_REQUEST_HEADER_CONNECTION)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_CONTENT_ENCODING", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_CONTENT_ENCODING, strlen(HTTP_REQUEST_HEADER_CONTENT_ENCODING)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_CONTENT_LENGTH", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_CONTENT_LENGTH, strlen(HTTP_REQUEST_HEADER_CONTENT_LENGTH)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_CONTENT_MD5", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_CONTENT_MD5, strlen(HTTP_REQUEST_HEADER_CONTENT_MD5)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_CONTENT_TYPE", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_CONTENT_TYPE, strlen(HTTP_REQUEST_HEADER_CONTENT_TYPE)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_COOKIE", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_COOKIE, strlen(HTTP_REQUEST_HEADER_COOKIE)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_DATE", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_DATE, strlen(HTTP_REQUEST_HEADER_DATE)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_EXPECT", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_EXPECT, strlen(HTTP_REQUEST_HEADER_EXPECT)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_FORWARDED", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_FORWARDED, strlen(HTTP_REQUEST_HEADER_FORWARDED)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_FROM", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_FROM, strlen(HTTP_REQUEST_HEADER_FROM)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_HOST", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_HOST, strlen(HTTP_REQUEST_HEADER_HOST)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_HTTP2_SETTINGS", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_HTTP2_SETTINGS, strlen(HTTP_REQUEST_HEADER_HTTP2_SETTINGS)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_IF_MATCH", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_IF_MATCH, strlen(HTTP_REQUEST_HEADER_IF_MATCH)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_IF_MODIFIED_SINCE", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_IF_MODIFIED_SINCE, strlen(HTTP_REQUEST_HEADER_IF_MODIFIED_SINCE)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_IF_NONE_MATCH", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_IF_NONE_MATCH, strlen(HTTP_REQUEST_HEADER_IF_NONE_MATCH)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_IF_RANGE", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_IF_RANGE, strlen(HTTP_REQUEST_HEADER_IF_RANGE)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_IF_UNMODIFIED_SINCE", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_IF_UNMODIFIED_SINCE, strlen(HTTP_REQUEST_HEADER_IF_UNMODIFIED_SINCE)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_MAX_FORWARDS", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_MAX_FORWARDS, strlen(HTTP_REQUEST_HEADER_MAX_FORWARDS)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_PRAGMA", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_PRAGMA, strlen(HTTP_REQUEST_HEADER_PRAGMA)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_PROXY_AUTHORIZATION", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_PROXY_AUTHORIZATION, strlen(HTTP_REQUEST_HEADER_PROXY_AUTHORIZATION)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_RANGE", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_RANGE, strlen(HTTP_REQUEST_HEADER_RANGE)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_REFERRER", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_REFERRER, strlen(HTTP_REQUEST_HEADER_REFERRER)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_TE", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_TE, strlen(HTTP_REQUEST_HEADER_TE)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_TRAILER", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_TRAILER, strlen(HTTP_REQUEST_HEADER_TRAILER)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_TRANSFER_ENCODING", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_TRANSFER_ENCODING, strlen(HTTP_REQUEST_HEADER_TRANSFER_ENCODING)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_USER_AGENT", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_USER_AGENT, strlen(HTTP_REQUEST_HEADER_USER_AGENT)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_UPGRADE", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_UPGRADE, strlen(HTTP_REQUEST_HEADER_UPGRADE)))); + defineNativeProperty(vm, &module->values, "REQUEST_HEADER_WARNING", OBJ_VAL(copyString(vm, HTTP_REQUEST_HEADER_WARNING, strlen(HTTP_REQUEST_HEADER_WARNING)))); + + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, strlen(HTTP_RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS, strlen(HTTP_RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS, strlen(HTTP_RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_ACCESS_CONTROL_MAX_AGE", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_ACCESS_CONTROL_MAX_AGE, strlen(HTTP_RESPONSE_HEADER_ACCESS_CONTROL_MAX_AGE)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_METHODS", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_METHODS, strlen(HTTP_RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_METHODS)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_HEADERS", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_HEADERS, strlen(HTTP_RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_HEADERS)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_ACCEPT_PATCH", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_ACCEPT_PATCH, strlen(HTTP_RESPONSE_HEADER_ACCEPT_PATCH)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_ACCEPT_RANGES", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_ACCEPT_RANGES, strlen(HTTP_RESPONSE_HEADER_ACCEPT_RANGES)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_AGE", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_AGE, strlen(HTTP_RESPONSE_HEADER_AGE)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_ALLOW", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_ALLOW, strlen(HTTP_RESPONSE_HEADER_ALLOW)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_ALT_SVC", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_ALT_SVC, strlen(HTTP_RESPONSE_HEADER_ALT_SVC)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_CACHE_CONTROL", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_CACHE_CONTROL, strlen(HTTP_RESPONSE_HEADER_CACHE_CONTROL)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_CONNECTION", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_CONNECTION, strlen(HTTP_RESPONSE_HEADER_CONNECTION)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_CONTENT_DISPOSITION", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_CONTENT_DISPOSITION, strlen(HTTP_RESPONSE_HEADER_CONTENT_DISPOSITION)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_CONTENT_ENCODING", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_CONTENT_ENCODING, strlen(HTTP_RESPONSE_HEADER_CONTENT_ENCODING)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_CONTENT_LANGUAGE", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_CONTENT_LANGUAGE, strlen(HTTP_RESPONSE_HEADER_CONTENT_LANGUAGE)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_CONTENT_LENGTH", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_CONTENT_LENGTH, strlen(HTTP_RESPONSE_HEADER_CONTENT_LENGTH)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_CONTENT_LOCATION", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_CONTENT_LOCATION, strlen(HTTP_RESPONSE_HEADER_CONTENT_LOCATION)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_CONTENT_MD5", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_CONTENT_MD5, strlen(HTTP_RESPONSE_HEADER_CONTENT_MD5)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_CONTENT_RANGE", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_CONTENT_RANGE, strlen(HTTP_RESPONSE_HEADER_CONTENT_RANGE)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_CONTENT_TYPE", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_CONTENT_TYPE, strlen(HTTP_RESPONSE_HEADER_CONTENT_TYPE)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_DATE", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_DATE, strlen(HTTP_RESPONSE_HEADER_DATE)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_DELTA_BASE", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_DELTA_BASE, strlen(HTTP_RESPONSE_HEADER_DELTA_BASE)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_ETAG", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_ETAG, strlen(HTTP_RESPONSE_HEADER_ETAG)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_EXPIRES", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_EXPIRES, strlen(HTTP_RESPONSE_HEADER_EXPIRES)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_IM", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_IM, strlen(HTTP_RESPONSE_HEADER_IM)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_LAST_MODIFIED", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_LAST_MODIFIED, strlen(HTTP_RESPONSE_HEADER_LAST_MODIFIED)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_LINK", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_LINK, strlen(HTTP_RESPONSE_HEADER_LINK)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_LOCATION", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_LOCATION, strlen(HTTP_RESPONSE_HEADER_LOCATION)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_P3P", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_P3P, strlen(HTTP_RESPONSE_HEADER_P3P)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_PRAGMA", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_PRAGMA, strlen(HTTP_RESPONSE_HEADER_PRAGMA)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_PROXY_AUTHENTICATE", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_PROXY_AUTHENTICATE, strlen(HTTP_RESPONSE_HEADER_PROXY_AUTHENTICATE)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_PUBLIC_KEY_PINS", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_PUBLIC_KEY_PINS, strlen(HTTP_RESPONSE_HEADER_PUBLIC_KEY_PINS)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_RETRY_AFTER", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_RETRY_AFTER, strlen(HTTP_RESPONSE_HEADER_RETRY_AFTER)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_SET_COOKIE", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_SET_COOKIE, strlen(HTTP_RESPONSE_HEADER_SET_COOKIE)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_STRICT_TRANSPORT_SECURITY", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_STRICT_TRANSPORT_SECURITY, strlen(HTTP_RESPONSE_HEADER_STRICT_TRANSPORT_SECURITY)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_TRAILER", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_TRAILER, strlen(HTTP_RESPONSE_HEADER_TRAILER)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_TRANSFER_ENCODING", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_TRANSFER_ENCODING, strlen(HTTP_RESPONSE_HEADER_TRANSFER_ENCODING)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_TK", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_TK, strlen(HTTP_RESPONSE_HEADER_TK)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_UPGRADE", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_UPGRADE, strlen(HTTP_RESPONSE_HEADER_UPGRADE)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_VARY", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_VARY, strlen(HTTP_RESPONSE_HEADER_VARY)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_VIA", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_VIA, strlen(HTTP_RESPONSE_HEADER_VIA)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_WARNING", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_WARNING, strlen(HTTP_RESPONSE_HEADER_WARNING)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_WWW_AUTHENTICATE", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_WWW_AUTHENTICATE, strlen(HTTP_RESPONSE_HEADER_WWW_AUTHENTICATE)))); + defineNativeProperty(vm, &module->values, "RESPONSE_HEADER_X_FRAME_OPTIONS", OBJ_VAL(copyString(vm, HTTP_RESPONSE_HEADER_X_FRAME_OPTIONS, strlen(HTTP_RESPONSE_HEADER_X_FRAME_OPTIONS)))); + + defineNativeProperty(vm, &module->values, "MAX_HEADER_BYTES", AS_NUMBER(HTTP_MAX_HEADER_BYTES)); + /** * Define Http methods */ @@ -383,5 +841,5 @@ ObjModule *createHTTPModule(DictuVM *vm) { pop(vm); pop(vm); - return module; + return OBJ_VAL(module); } diff --git a/src/optionals/http.h b/src/optionals/http.h index 96a873ad..c00f3e8c 100644 --- a/src/optionals/http.h +++ b/src/optionals/http.h @@ -16,6 +16,6 @@ typedef struct response { long statusCode; } Response; -ObjModule *createHTTPModule(DictuVM *vm); +Value createHTTPModule(DictuVM *vm); #endif //dictu_http_h \ No newline at end of file diff --git a/src/optionals/inspect.c b/src/optionals/inspect.c new file mode 100644 index 00000000..4bc4d42f --- /dev/null +++ b/src/optionals/inspect.c @@ -0,0 +1,99 @@ +#include "inspect.h" + +static Value getLine(DictuVM *vm, int argCount, Value *args) { + if (argCount != 0 && argCount != 1) { + runtimeError(vm, "getLine() takes takes 0 or 1 arguments (%d given)", argCount); + return EMPTY_VAL; + } + + int count = 0; + + if (argCount == 1) { + if (!IS_NUMBER(args[0])) { + runtimeError(vm, "Optional argument passed to getLine() must be a number."); + return EMPTY_VAL; + } + + count = AS_NUMBER(args[0]); + + if (count < 0) { + runtimeError(vm, "Optional argument passed to getLine() must be 0 or more."); + return EMPTY_VAL; + } + + if (count > vm->frameCount - 1) { + runtimeError(vm, "Optional argument passed to getLine() exceeds the frame count."); + return EMPTY_VAL; + } + } + + CallFrame *frame = &vm->frames[vm->frameCount - 1 - count]; + ObjFunction *function = frame->closure->function; + size_t instruction = frame->ip - function->chunk.code - 1; + + return NUMBER_VAL(function->chunk.lines[instruction]); +} + +static Value getFile(DictuVM *vm, int argCount, Value *args) { + if (argCount != 0 && argCount != 1) { + runtimeError(vm, "getFile() takes takes 0 or 1 arguments (%d given)", argCount); + return EMPTY_VAL; + } + + int count = 0; + + if (argCount == 1) { + if (!IS_NUMBER(args[0])) { + runtimeError(vm, "Optional argument passed to getFile() must be a number."); + return EMPTY_VAL; + } + + count = AS_NUMBER(args[0]); + + if (count < 0) { + runtimeError(vm, "Optional argument passed to getFile() must be 0 or more."); + return EMPTY_VAL; + } + + if (count > vm->frameCount - 1) { + runtimeError(vm, "Optional argument passed to getFile() exceeds the frame count."); + return EMPTY_VAL; + } + } + + CallFrame *frame = &vm->frames[vm->frameCount - 1 - count]; + ObjFunction *function = frame->closure->function; + + return OBJ_VAL(function->module->name); +} + +static Value getFrameCount(DictuVM *vm, int argCount, Value *args) { + UNUSED(args); + + if (argCount != 0) { + runtimeError(vm, "getFrameCount() takes takes 0 arguments (%d given)", argCount); + return EMPTY_VAL; + } + + // -1 as it's pointing to the next frame + return NUMBER_VAL(vm->frameCount - 1); +} + +Value createInspectModule(DictuVM *vm) { + ObjString *name = copyString(vm, "Inspect", 7); + push(vm, OBJ_VAL(name)); + ObjModule *module = newModule(vm, name); + push(vm, OBJ_VAL(module)); + + /** + * Define Inspect methods + */ + defineNative(vm, &module->values, "getLine", getLine); + defineNative(vm, &module->values, "getFile", getFile); + defineNative(vm, &module->values, "getFrameCount", getFrameCount); + + pop(vm); + pop(vm); + + return OBJ_VAL(module); +} \ No newline at end of file diff --git a/src/optionals/inspect.h b/src/optionals/inspect.h new file mode 100644 index 00000000..69824b26 --- /dev/null +++ b/src/optionals/inspect.h @@ -0,0 +1,8 @@ +#ifndef dictu_inspect_h +#define dictu_inspect_h + +#include "optionals.h" + +Value createInspectModule(DictuVM *vm); + +#endif //dictu_inspect_h diff --git a/src/optionals/json.c b/src/optionals/json.c index 0ac89a45..b7bac074 100644 --- a/src/optionals/json.c +++ b/src/optionals/json.c @@ -221,7 +221,7 @@ static Value stringify(DictuVM *vm, int argCount, Value *args) { return newResultSuccess(vm, OBJ_VAL(string)); } -ObjModule *createJSONModule(DictuVM *vm) { +Value createJSONModule(DictuVM *vm) { ObjString *name = copyString(vm, "JSON", 4); push(vm, OBJ_VAL(name)); ObjModule *module = newModule(vm, name); @@ -236,5 +236,5 @@ ObjModule *createJSONModule(DictuVM *vm) { pop(vm); pop(vm); - return module; + return OBJ_VAL(module); } diff --git a/src/optionals/json.h b/src/optionals/json.h index 97cbe799..02697a79 100644 --- a/src/optionals/json.h +++ b/src/optionals/json.h @@ -6,6 +6,6 @@ #include "optionals.h" #include "../vm/vm.h" -ObjModule *createJSONModule(DictuVM *vm); +Value createJSONModule(DictuVM *vm); #endif //dictu_json_h diff --git a/src/optionals/log.c b/src/optionals/log.c new file mode 100644 index 00000000..94f69532 --- /dev/null +++ b/src/optionals/log.c @@ -0,0 +1,302 @@ +#include +#include + +#include "log.h" +#include "optionals.h" +#include "../vm/vm.h" + +typedef struct { + FILE *of; + char *prefix; +} Log; + +#define AS_LOG(v) ((Log*)AS_ABSTRACT(v)->data) + +static struct tm* timeNow() { + time_t rawtime = time(NULL); + if (rawtime == -1) { + return NULL; + } + + struct tm *ptm = localtime(&rawtime); + if (ptm == NULL) { + return NULL; + } + + return ptm; +} + +static Value printLog(DictuVM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "print() takes 1 argument (%d given)", argCount); + return EMPTY_VAL; + } + + struct tm *ptm = timeNow(); + + char *msg = AS_CSTRING(args[0]); + printf("%04d/%02d/%02d %02d:%02d:%02d %s", + ptm->tm_year+2000-100, ptm->tm_mon, ptm->tm_mday, + ptm->tm_hour, ptm->tm_min, ptm->tm_sec, msg); + + return NIL_VAL; +} + +static Value printlnLog(DictuVM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "println() takes 1 argument (%d given)", argCount); + return EMPTY_VAL; + } + + struct tm *ptm = timeNow(); + + char *msg = AS_CSTRING(args[0]); + printf("%04d/%02d/%02d %02d:%02d:%02d %s\n", + ptm->tm_year+2000-100, ptm->tm_mon, ptm->tm_mday, + ptm->tm_hour, ptm->tm_min, ptm->tm_sec, msg); + + return NIL_VAL; +} + +static Value fatalLog(DictuVM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "fatal() takes 1 argument (%d given)", argCount); + return EMPTY_VAL; + } + + struct tm *ptm = timeNow(); + + char *msg = AS_CSTRING(args[0]); + printf("%04d/%02d/%02d %02d:%02d:%02d %s", + ptm->tm_year+2000-100, ptm->tm_mon, ptm->tm_mday, + ptm->tm_hour, ptm->tm_min, ptm->tm_sec, msg); + + exit(1); +} + +static Value fatallnLog(DictuVM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "fatalln() takes 1 argument (%d given)", argCount); + return EMPTY_VAL; + } + + struct tm *ptm = timeNow(); + + char *msg = AS_CSTRING(args[0]); + printf("%04d/%02d/%02d %02d:%02d:%02d %s\n", + ptm->tm_year+2000-100, ptm->tm_mon, ptm->tm_mday, + ptm->tm_hour, ptm->tm_min, ptm->tm_sec, msg); + + exit(1); +} + +void freeLog(DictuVM *vm, ObjAbstract *abstract) { + Log *log = (Log*)abstract->data; + + if (log->prefix != NULL) { + free(log->prefix); + } + + FREE(vm, Log, abstract->data); +} + +static Value logObjSetPrefix(DictuVM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "setPrefix() takes 1 argument (%d given)", argCount); + return EMPTY_VAL; + } + + Log *log = AS_LOG(args[0]); + char *prefix = AS_CSTRING(args[1]); + + if (strlen(prefix) == 0) { + return NIL_VAL; + } + + log->prefix = strdup(prefix); + + return NIL_VAL; +} + +static Value logObjUnsetPrefix(DictuVM *vm, int argCount, Value *args) { + if (argCount != 0) { + runtimeError(vm, "unsetPrefix() takes 0 argument (%d given)", argCount); + return EMPTY_VAL; + } + + Log *log = AS_LOG(args[0]); + if (log->prefix != NULL) { + free(log->prefix); + log->prefix = NULL; + } + + return NIL_VAL; +} + +static Value logObjPrint(DictuVM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "print() takes 1 argument (%d given)", argCount); + return EMPTY_VAL; + } + + Log *log = AS_LOG(args[0]); + + struct tm *ptm = timeNow(); + char *msg = AS_CSTRING(args[1]); + + if (log->prefix != NULL && strlen(log->prefix) != 0) { + fprintf(log->of, "%04d/%02d/%02d %02d:%02d:%02d %s %s", + ptm->tm_year+2000-100, ptm->tm_mon, ptm->tm_mday, + ptm->tm_hour, ptm->tm_min, ptm->tm_sec, log->prefix, msg); + + return NIL_VAL; + } + + fprintf(log->of, "%04d/%02d/%02d %02d:%02d:%02d %s", + ptm->tm_year+2000-100, ptm->tm_mon, ptm->tm_mday, + ptm->tm_hour, ptm->tm_min, ptm->tm_sec, msg); + + return NIL_VAL; +} + +static Value logObjPrintln(DictuVM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "println() takes 1 argument (%d given)", argCount); + return EMPTY_VAL; + } + + Log *log = AS_LOG(args[0]); + + struct tm *ptm = timeNow(); + char *msg = AS_CSTRING(args[1]); + + if (log->prefix != NULL && strlen(log->prefix) != 0) { + fprintf(log->of, "%04d/%02d/%02d %02d:%02d:%02d %s %s\n", + ptm->tm_year+2000-100, ptm->tm_mon, ptm->tm_mday, + ptm->tm_hour, ptm->tm_min, ptm->tm_sec, log->prefix, msg); + + return NIL_VAL; + } + + fprintf(log->of, "%04d/%02d/%02d %02d:%02d:%02d %s\n", + ptm->tm_year+2000-100, ptm->tm_mon, ptm->tm_mday, + ptm->tm_hour, ptm->tm_min, ptm->tm_sec, msg); + + return NIL_VAL; +} + +static Value logObjFatal(DictuVM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "fatal() takes 1 argument (%d given)", argCount); + return EMPTY_VAL; + } + + Log *log = AS_LOG(args[0]); + + struct tm *ptm = timeNow(); + + char *msg = AS_CSTRING(args[1]); + fprintf(log->of, "%04d/%02d/%02d %02d:%02d:%02d %s\n", + ptm->tm_year+2000-100, ptm->tm_mon, ptm->tm_mday, + ptm->tm_hour, ptm->tm_min, ptm->tm_sec, msg); + + exit(1); +} + +static Value logObjFatalln(DictuVM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "fatalln() takes 1 argument (%d given)", argCount); + return EMPTY_VAL; + } + + Log *log = AS_LOG(args[0]); + + struct tm *ptm = timeNow(); + + char *msg = AS_CSTRING(args[1]); + fprintf(log->of, "%04d/%02d/%02d %02d:%02d:%02d %s\n", + ptm->tm_year+2000-100, ptm->tm_mon, ptm->tm_mday, + ptm->tm_hour, ptm->tm_min, ptm->tm_sec, msg); + + exit(1); +} + +ObjAbstract* newLogObj(DictuVM *vm) { + ObjAbstract *abstract = newAbstract(vm, freeLog); + push(vm, OBJ_VAL(abstract)); + + Log *log = ALLOCATE(vm, Log, 1); + log->of = NULL; + log->prefix = NULL; + + /** + * Setup Log object methods + */ + defineNative(vm, &abstract->values, "setPrefix", logObjSetPrefix); + defineNative(vm, &abstract->values, "unsetPrefix", logObjUnsetPrefix); + + defineNative(vm, &abstract->values, "print", logObjPrint); + defineNative(vm, &abstract->values, "println", logObjPrintln); + defineNative(vm, &abstract->values, "fatal", logObjFatal); + defineNative(vm, &abstract->values, "fatalln", logObjFatalln); + + abstract->data = log; + pop(vm); + + return abstract; +} + +static Value newLog(DictuVM *vm, int argCount, Value *args) { + if (argCount != 1) { + runtimeError(vm, "new() takes 1 argument (%d given).", argCount); + return EMPTY_VAL; + } + + ObjAbstract *abstract = newLogObj(vm); + Log *log = abstract->data; + + int output = AS_NUMBER(args[0]); + switch(output) { + case STDOUT_FILENO: + log->of = stdout; + break; + case STDERR_FILENO: + log->of = stderr; + break; + default: + return newResultError(vm, "invalid output destination"); + } + abstract->data = log; + + push(vm, OBJ_VAL(abstract)); + Value success = newResultSuccess(vm, OBJ_VAL(abstract)); + pop(vm); + + return success; +} + +Value createLogModule(DictuVM *vm) { + ObjString *name = copyString(vm, "Log", 3); + push(vm, OBJ_VAL(name)); + ObjModule *module = newModule(vm, name); + push(vm, OBJ_VAL(module)); + + defineNativeProperty(vm, &module->values, "stdin", NUMBER_VAL(STDIN_FILENO)); + defineNativeProperty(vm, &module->values, "stdout", NUMBER_VAL(STDOUT_FILENO)); + defineNativeProperty(vm, &module->values, "stderr", NUMBER_VAL(STDERR_FILENO)); + + /** + * Define Log methods + */ + defineNative(vm, &module->values, "print", printLog); + defineNative(vm, &module->values, "println", printlnLog); + defineNative(vm, &module->values, "fatal", fatalLog); + defineNative(vm, &module->values, "fatalln", fatallnLog); + + defineNative(vm, &module->values, "new", newLog); + + pop(vm); + pop(vm); + + return OBJ_VAL(module); +} diff --git a/src/optionals/log.h b/src/optionals/log.h new file mode 100644 index 00000000..f827043b --- /dev/null +++ b/src/optionals/log.h @@ -0,0 +1,14 @@ +#ifndef _dictu_log_h +#define _dictu_log_h + +#include "../vm/vm.h" + +#ifdef _WIN32 +#define STDIN_FILENO 0 /* standard input file descriptor */ +#define STDOUT_FILENO 1 /* standard output file descriptor */ +#define STDERR_FILENO 2 /* standard error file descriptor */ +#endif + +Value createLogModule(DictuVM *vm); + +#endif //_dictu_log_h diff --git a/src/optionals/math.c b/src/optionals/math.c index d9b84dda..2899cf12 100644 --- a/src/optionals/math.c +++ b/src/optionals/math.c @@ -327,7 +327,7 @@ static Value lcmNative(DictuVM *vm, int argCount, Value *args) { return NUMBER_VAL(result); } -ObjModule *createMathsModule(DictuVM *vm) { +Value createMathsModule(DictuVM *vm) { ObjString *name = copyString(vm, "Math", 4); push(vm, OBJ_VAL(name)); ObjModule *module = newModule(vm, name); @@ -356,8 +356,15 @@ ObjModule *createMathsModule(DictuVM *vm) { */ defineNativeProperty(vm, &module->values, "pi", NUMBER_VAL(3.14159265358979)); defineNativeProperty(vm, &module->values, "e", NUMBER_VAL(2.71828182845905)); + defineNativeProperty(vm, &module->values, "phi", NUMBER_VAL(1.61803398874989)); + defineNativeProperty(vm, &module->values, "sqrt2", NUMBER_VAL(1.41421356237309)); + defineNativeProperty(vm, &module->values, "sqrte", NUMBER_VAL(1.61803398874989)); + defineNativeProperty(vm, &module->values, "sqrtpi", NUMBER_VAL(1.77245385090551)); + defineNativeProperty(vm, &module->values, "sqrtphi", NUMBER_VAL(1.27201964951406)); + defineNativeProperty(vm, &module->values, "ln2", NUMBER_VAL(0.69314718055994)); + defineNativeProperty(vm, &module->values, "ln10", NUMBER_VAL(2.30258509299404)); pop(vm); pop(vm); - return module; + return OBJ_VAL(module); } diff --git a/src/optionals/math.h b/src/optionals/math.h index 46b0aca6..bd585ca1 100644 --- a/src/optionals/math.h +++ b/src/optionals/math.h @@ -9,6 +9,6 @@ #define FLOAT_TOLERANCE 0.00001 -ObjModule *createMathsModule(DictuVM *vm); +Value createMathsModule(DictuVM *vm); #endif //dictu_math_h diff --git a/src/optionals/optionals.c b/src/optionals/optionals.c index 9c455ea7..deb3650c 100644 --- a/src/optionals/optionals.c +++ b/src/optionals/optionals.c @@ -1,31 +1,36 @@ #include "optionals.h" BuiltinModules modules[] = { - {"Math", &createMathsModule}, - {"Env", &createEnvModule}, - {"JSON", &createJSONModule}, - {"Path", &createPathModule}, - {"Datetime", &createDatetimeModule}, - {"Socket", &createSocketModule}, - {"Random", &createRandomModule}, - {"Base64", &createBase64Module}, - {"Hashlib", &createHashlibModule}, - {"Sqlite", &createSqliteModule}, - {"Process", &createProcessModule}, - {"System", &createSystemModule}, + {"Math", &createMathsModule, false}, + {"Env", &createEnvModule, true}, + {"JSON", &createJSONModule, false}, + {"Log", &createLogModule, false}, + {"Path", &createPathModule, false}, + {"Datetime", &createDatetimeModule, false}, + {"Socket", &createSocketModule, false}, + {"Random", &createRandomModule, false}, + {"Base64", &createBase64Module, false}, + {"Hashlib", &createHashlibModule, false}, + {"Sqlite", &createSqliteModule, false}, + {"Process", &createProcessModule, false}, + {"System", &createSystemModule, false}, + {"UnitTest", &createUnitTestModule, true}, + {"Inspect", &createInspectModule, false}, #ifndef DISABLE_HTTP - {"HTTP", &createHTTPModule}, + {"HTTP", &createHTTPModule, false}, #endif - {NULL, NULL} + {NULL, NULL, false} }; -ObjModule *importBuiltinModule(DictuVM *vm, int index) { +Value importBuiltinModule(DictuVM *vm, int index) { return modules[index].module(vm); } -int findBuiltinModule(char *name, int length) { +int findBuiltinModule(char *name, int length, bool *dictuSource) { for (int i = 0; modules[i].module != NULL; ++i) { if (strncmp(modules[i].name, name, length) == 0) { + *dictuSource = modules[i].dictuSource; + return i; } } diff --git a/src/optionals/optionals.h b/src/optionals/optionals.h index 2f7d0f05..24eecd07 100644 --- a/src/optionals/optionals.h +++ b/src/optionals/optionals.h @@ -3,9 +3,10 @@ #include "../vm/util.h" #include "math.h" -#include "env.h" +#include "env/env.h" #include "system.h" #include "json.h" +#include "log.h" #include "http.h" #include "path.h" #include "c.h" @@ -16,16 +17,19 @@ #include "hashlib.h" #include "sqlite.h" #include "process.h" +#include "inspect.h" +#include "unittest/unittest.h" -typedef ObjModule *(*BuiltinModule)(DictuVM *vm); +typedef Value (*BuiltinModule)(DictuVM *vm); typedef struct { char *name; BuiltinModule module; + bool dictuSource; } BuiltinModules; -ObjModule *importBuiltinModule(DictuVM *vm, int index); +Value importBuiltinModule(DictuVM *vm, int index); -int findBuiltinModule(char *name, int length); +int findBuiltinModule(char *name, int length, bool *dictuSource); #endif //dictu_optionals_h diff --git a/src/optionals/path.c b/src/optionals/path.c index 7833ab73..87a1aae2 100644 --- a/src/optionals/path.c +++ b/src/optionals/path.c @@ -312,7 +312,7 @@ static Value joinNative(DictuVM *vm, int argCount, Value *args) { return OBJ_VAL(takeString(vm, str, resultSize)); } -ObjModule *createPathModule(DictuVM *vm) { +Value createPathModule(DictuVM *vm) { ObjString *name = copyString(vm, "Path", 4); push(vm, OBJ_VAL(name)); ObjModule *module = newModule(vm, name); @@ -343,5 +343,5 @@ ObjModule *createPathModule(DictuVM *vm) { pop(vm); pop(vm); - return module; + return OBJ_VAL(module); } diff --git a/src/optionals/path.h b/src/optionals/path.h index 7369a03b..1ee91402 100644 --- a/src/optionals/path.h +++ b/src/optionals/path.h @@ -15,6 +15,6 @@ #include #endif -ObjModule *createPathModule(DictuVM *vm); +Value createPathModule(DictuVM *vm); #endif //dictu_path_h diff --git a/src/optionals/process.c b/src/optionals/process.c index 6c853598..a66b3944 100644 --- a/src/optionals/process.c +++ b/src/optionals/process.c @@ -268,7 +268,7 @@ static Value runNative(DictuVM* vm, int argCount, Value* args) { return execute(vm, argList, true); } -ObjModule* createProcessModule(DictuVM* vm) { +Value createProcessModule(DictuVM* vm) { ObjString* name = copyString(vm, "Process", 7); push(vm, OBJ_VAL(name)); ObjModule* module = newModule(vm, name); @@ -287,5 +287,5 @@ ObjModule* createProcessModule(DictuVM* vm) { pop(vm); pop(vm); - return module; + return OBJ_VAL(module); } \ No newline at end of file diff --git a/src/optionals/process.h b/src/optionals/process.h index b6f09891..81bd0645 100644 --- a/src/optionals/process.h +++ b/src/optionals/process.h @@ -1,12 +1,12 @@ #ifndef dictu_process_h #define dictu_process_h -#ifndef _WIN32 -#include +#ifndef _WIN32 +#include #endif // !_WIN32 #include "optionals.h" -ObjModule *createProcessModule(DictuVM *vm); +Value createProcessModule(DictuVM *vm); #endif //dictu_process_h diff --git a/src/optionals/random.c b/src/optionals/random.c index ae215ea3..05afe321 100644 --- a/src/optionals/random.c +++ b/src/optionals/random.c @@ -49,7 +49,7 @@ static Value randomSelect(DictuVM *vm, int argCount, Value *args) { return args[index]; } -ObjModule *createRandomModule(DictuVM *vm) { +Value createRandomModule(DictuVM *vm) { ObjString *name = copyString(vm, "Random", 6); push(vm, OBJ_VAL(name)); ObjModule *module = newModule(vm, name); @@ -67,5 +67,5 @@ ObjModule *createRandomModule(DictuVM *vm) { pop(vm); pop(vm); - return module; + return OBJ_VAL(module); } \ No newline at end of file diff --git a/src/optionals/random.h b/src/optionals/random.h index d38569bb..32f715f0 100644 --- a/src/optionals/random.h +++ b/src/optionals/random.h @@ -7,6 +7,6 @@ #include "optionals.h" #include "../vm/vm.h" -ObjModule *createRandomModule(DictuVM *vm); +Value createRandomModule(DictuVM *vm); #endif //dictu_random_h diff --git a/src/optionals/socket.c b/src/optionals/socket.c index 9e904ba9..470c9285 100644 --- a/src/optionals/socket.c +++ b/src/optionals/socket.c @@ -321,7 +321,7 @@ void cleanupSockets(void) { } #endif -ObjModule *createSocketModule(DictuVM *vm) { +Value createSocketModule(DictuVM *vm) { #ifdef _WIN32 #include "windowsapi.h" @@ -352,5 +352,5 @@ ObjModule *createSocketModule(DictuVM *vm) { pop(vm); pop(vm); - return module; + return OBJ_VAL(module); } diff --git a/src/optionals/socket.h b/src/optionals/socket.h index 4bd7127a..146eff3f 100644 --- a/src/optionals/socket.h +++ b/src/optionals/socket.h @@ -10,6 +10,6 @@ #include #endif -ObjModule *createSocketModule(DictuVM *vm); +Value createSocketModule(DictuVM *vm); #endif //dictu_socket_h diff --git a/src/optionals/sqlite.c b/src/optionals/sqlite.c index 4edadab7..3c9ba9b6 100644 --- a/src/optionals/sqlite.c +++ b/src/optionals/sqlite.c @@ -238,7 +238,7 @@ ObjAbstract *newSqlite(DictuVM *vm) { return abstract; } -ObjModule *createSqliteModule(DictuVM *vm) { +Value createSqliteModule(DictuVM *vm) { ObjString *name = copyString(vm, "Sqlite", 6); push(vm, OBJ_VAL(name)); ObjModule *module = newModule(vm, name); @@ -252,5 +252,5 @@ ObjModule *createSqliteModule(DictuVM *vm) { pop(vm); pop(vm); - return module; + return OBJ_VAL(module); } \ No newline at end of file diff --git a/src/optionals/sqlite.h b/src/optionals/sqlite.h index 71397ce4..59ee190e 100644 --- a/src/optionals/sqlite.h +++ b/src/optionals/sqlite.h @@ -11,6 +11,6 @@ #include "optionals.h" #include "../vm/vm.h" -ObjModule *createSqliteModule(DictuVM *vm); +Value createSqliteModule(DictuVM *vm); #endif //dictu_sqlite_h diff --git a/src/optionals/sqlite/sqlite3.c b/src/optionals/sqlite/sqlite3.c index 69cbd683..89371880 100644 --- a/src/optionals/sqlite/sqlite3.c +++ b/src/optionals/sqlite/sqlite3.c @@ -114499,7 +114499,7 @@ SQLITE_PRIVATE void sqlite3DefaultRowEst(Index *pIdx){ if( x<99 ){ pIdx->pTable->nRowLogEst = x = 99; } - if( pIdx->pPartIdxWhere!=0 ) x -= 10; assert( 10==sqlite3LogEst(2) ); + if( pIdx->pPartIdxWhere!=0 ) { x -= 10; assert( 10==sqlite3LogEst(2) ); } a[0] = x; /* Estimate that a[1] is 10, a[2] is 9, a[3] is 8, a[4] is 7, a[5] is diff --git a/src/optionals/system.c b/src/optionals/system.c index 483c25f3..db6622be 100644 --- a/src/optionals/system.c +++ b/src/optionals/system.c @@ -3,6 +3,7 @@ #ifdef _WIN32 #define rmdir(DIRNAME) _rmdir(DIRNAME) #define chdir(DIRNAME) _chdir(DIRNAME) +#define chmod(FILENAME, MODE) _chmod(FILENAME, MODE) #define getcwd(BUFFER, MAXLEN) _getcwd(BUFFER, MAXLEN) #endif @@ -72,6 +73,33 @@ static Value getpidNative(DictuVM *vm, int argCount, Value *args) { return NUMBER_VAL(getpid()); } + +static Value chownNative(DictuVM *vm, int argCount, Value *args) { + if (argCount != 3) { + runtimeError(vm, "chown() takes 3 arguments (%d given).", argCount); + return EMPTY_VAL; + } + + if (!IS_STRING(args[0])) { + runtimeError(vm, "first chown() argument must be a string."); + return EMPTY_VAL; + } + + if (!IS_NUMBER(args[1]) || !IS_NUMBER(args[2])) { + runtimeError(vm, "second and third chown() arguments must be numbers."); + return EMPTY_VAL; + } + + ObjString *file = AS_STRING(args[0]); + int uid = AS_NUMBER(args[1]); + int gid = AS_NUMBER(args[2]); + + if (chown(file->chars, uid, gid) == -1) { + ERROR_RESULT; + } + + return newResultSuccess(vm, EMPTY_VAL); +} #endif static Value rmdirNative(DictuVM *vm, int argCount, Value *args) { @@ -184,12 +212,12 @@ static Value removeNative(DictuVM *vm, int argCount, Value *args) { static Value setCWDNative(DictuVM *vm, int argCount, Value *args) { if (argCount != 1) { - runtimeError(vm, "setcwd() takes 1 argument (%d given)", argCount); + runtimeError(vm, "setCWD() takes 1 argument (%d given)", argCount); return EMPTY_VAL; } if (!IS_STRING(args[0])) { - runtimeError(vm, "setcwd() argument must be a string"); + runtimeError(vm, "setCWD() argument must be a string"); return EMPTY_VAL; } @@ -281,6 +309,29 @@ static Value exitNative(DictuVM *vm, int argCount, Value *args) { return EMPTY_VAL; /* satisfy the tcc compiler */ } +static Value chmodNative(DictuVM *vm, int argCount, Value *args) { + if (argCount != 2) { + runtimeError(vm, "chmod() takes 2 arguments (%d given).", argCount); + return EMPTY_VAL; + } + + if (!IS_STRING(args[0]) || !IS_STRING(args[1])) { + runtimeError(vm, "chmod() arguments must be strings."); + return EMPTY_VAL; + } + + ObjString *file = AS_STRING(args[0]); + ObjString *mode = AS_STRING(args[1]); + + int i = strtol(mode->chars, 0, 8); + + if (chmod(file->chars, i) == -1) { + ERROR_RESULT; + } + + return newResultSuccess(vm, NIL_VAL); +} + void initArgv(DictuVM *vm, Table *table, int argc, char **argv) { ObjList *list = newList(vm); push(vm, OBJ_VAL(list)); @@ -346,7 +397,7 @@ void setVersion(DictuVM *vm, Table *table) { pop(vm); } -ObjModule *createSystemModule(DictuVM *vm) { +Value createSystemModule(DictuVM *vm) { ObjString *name = copyString(vm, "System", 6); push(vm, OBJ_VAL(name)); ObjModule *module = newModule(vm, name); @@ -362,6 +413,7 @@ ObjModule *createSystemModule(DictuVM *vm) { defineNative(vm, &module->values, "geteuid", geteuidNative); defineNative(vm, &module->values, "getppid", getppidNative); defineNative(vm, &module->values, "getpid", getpidNative); + defineNative(vm, &module->values, "chown", chownNative); #endif defineNative(vm, &module->values, "rmdir", rmdirNative); defineNative(vm, &module->values, "mkdir", mkdirNative); @@ -376,6 +428,7 @@ ObjModule *createSystemModule(DictuVM *vm) { defineNative(vm, &module->values, "collect", collectNative); defineNative(vm, &module->values, "sleep", sleepNative); defineNative(vm, &module->values, "exit", exitNative); + defineNative(vm, &module->values, "chmod", chmodNative); /** * Define System properties @@ -412,5 +465,5 @@ ObjModule *createSystemModule(DictuVM *vm) { pop(vm); pop(vm); - return module; + return OBJ_VAL(module); } diff --git a/src/optionals/system.h b/src/optionals/system.h index 9eb183d7..89fa2647 100644 --- a/src/optionals/system.h +++ b/src/optionals/system.h @@ -24,6 +24,6 @@ #include "../vm/vm.h" #include "../vm/memory.h" -ObjModule *createSystemModule(DictuVM *vm); +Value createSystemModule(DictuVM *vm); #endif //dictu_system_h diff --git a/src/optionals/unittest/unittest-source.h b/src/optionals/unittest/unittest-source.h new file mode 100644 index 00000000..ee1d6b0d --- /dev/null +++ b/src/optionals/unittest/unittest-source.h @@ -0,0 +1,127 @@ +#define DICTU_UNITTEST_SOURCE "import Inspect;\n" \ +"import System;\n" \ +"\n" \ +"abstract class UnitTest {\n" \ +" var METHOD_NAME_PADDING = ' ';\n" \ +" var RESULTS_PADDING = ' ';\n" \ +" var ASSERTION_PADDING = ' ';\n" \ +" var results = {\n" \ +" 'passed': 0,\n" \ +" 'failed': 0,\n" \ +" 'skipped': 0\n" \ +" };\n" \ +"\n" \ +" init(var onlyFailures = false, var exitOnFailure = false) {}\n" \ +"\n" \ +" filterMethods() {\n" \ +" return this.methods().filter(def (method) => {\n" \ +" if (method.startsWith('test') and !method.endsWith('Provider') and !method.endsWith('_skipped')) {\n" \ +" return method;\n" \ +" }\n" \ +"\n" \ +" if (method.endsWith('_skipped')) {\n" \ +" this.results['skipped'] += 1;\n" \ +" }\n" \ +" });\n" \ +" }\n" \ +"\n" \ +" setUp() {\n" \ +"\n" \ +" }\n" \ +"\n" \ +" tearDown() {\n" \ +"\n" \ +" }\n" \ +"\n" \ +" run() {\n" \ +" const methods = this.filterMethods();\n" \ +"\n" \ +" print(Inspect.getFile(1));\n" \ +" methods.forEach(def (method) => {\n" \ +" print('{}{}()'.format(UnitTest.METHOD_NAME_PADDING, method));\n" \ +"\n" \ +" const providerMethodName = '{}Provider'.format(method);\n" \ +"\n" \ +" if (this.hasAttribute(providerMethodName)) {\n" \ +" const testValue = this.getAttribute(providerMethodName)();\n" \ +"\n" \ +" if (type(testValue) == 'list') {\n" \ +" testValue.forEach(def (val) => {\n" \ +" this.setUp();\n" \ +" this.getAttribute(method)(val);\n" \ +" this.tearDown();\n" \ +" });\n" \ +" } else {\n" \ +" this.setUp();\n" \ +" this.getAttribute(method)(testValue);\n" \ +" this.tearDown();\n" \ +" }\n" \ +" } else {\n" \ +" this.setUp();\n" \ +" this.getAttribute(method)();\n" \ +" this.tearDown();\n" \ +" }\n" \ +" });\n" \ +" print('\nResults:\n{}- {} assertion(s) were successful.\n{}- {} assertion(s) were failures.\n{}- {} method(s) were skipped.\n'.format(\n" \ +" UnitTest.RESULTS_PADDING,\n" \ +" this.results['passed'],\n" \ +" UnitTest.RESULTS_PADDING,\n" \ +" this.results['failed'],\n" \ +" UnitTest.RESULTS_PADDING,\n" \ +" this.results['skipped']\n" \ +" ));\n" \ +"\n" \ +" if (this.results['failed'] > 0) {\n" \ +" System.exit(1);\n" \ +" }\n" \ +" }\n" \ +"\n" \ +" printResult(success, errorMsg) {\n" \ +" if (success) {\n" \ +" this.results['passed'] += 1;\n" \ +"\n" \ +" if (!this.onlyFailures) {\n" \ +" print('{}Success.'.format(UnitTest.ASSERTION_PADDING));\n" \ +" }\n" \ +" } else {\n" \ +" this.results['failed'] += 1;\n" \ +"\n" \ +" print('{}Line: {} - {}'.format(UnitTest.ASSERTION_PADDING, Inspect.getLine(2), errorMsg));\n" \ +"\n" \ +" if (this.exitOnFailure) {\n" \ +" System.exit(1);\n" \ +" }\n" \ +" }\n" \ +" }\n" \ +"\n" \ +" assertEquals(value, expected) {\n" \ +" this.printResult(value == expected, 'Failure: {} is not equal to {}.'.format(value, expected));\n" \ +" }\n" \ +"\n" \ +" assertTruthy(value) {\n" \ +" this.printResult(value, 'Failure: {} is not Truthy.'.format(value));\n" \ +" }\n" \ +"\n" \ +" assertFalsey(value) {\n" \ +" this.printResult(!value, 'Failure: {} is not Falsey.'.format(value));\n" \ +" }\n" \ +"\n" \ +" assertSuccess(value) {\n" \ +" if (type(value) != 'result') {\n" \ +" this.printResult(false, 'Failure: {} is not a Result type.'.format(value));\n" \ +" return;\n" \ +" }\n" \ +"\n" \ +" this.printResult(value.success(), 'Failure: {} is not a Result type in a success state.'.format(value));\n" \ +" }\n" \ +"\n" \ +" assertError(value) {\n" \ +" if (type(value) != 'result') {\n" \ +" this.printResult(false, 'Failure: {} is not a Result type.'.format(value));\n" \ +" return;\n" \ +" }\n" \ +"\n" \ +" this.printResult(!value.success(), 'Failure: {} is not a Result type in an error state.'.format(value));\n" \ +" }\n" \ +"}\n" \ + diff --git a/src/optionals/unittest/unittest.c b/src/optionals/unittest/unittest.c new file mode 100644 index 00000000..fe876ae3 --- /dev/null +++ b/src/optionals/unittest/unittest.c @@ -0,0 +1,13 @@ +#include "unittest.h" + +#include "unittest-source.h" + +Value createUnitTestModule(DictuVM *vm) { + ObjClosure *module = compileModuleToClosure(vm, "UnitTest", DICTU_UNITTEST_SOURCE); + + if (module == NULL) { + return EMPTY_VAL; + } + + return OBJ_VAL(module); +} diff --git a/src/optionals/unittest/unittest.du b/src/optionals/unittest/unittest.du new file mode 100644 index 00000000..aecd3096 --- /dev/null +++ b/src/optionals/unittest/unittest.du @@ -0,0 +1,126 @@ +import Inspect; +import System; + +abstract class UnitTest { + var METHOD_NAME_PADDING = ' '; + var RESULTS_PADDING = ' '; + var ASSERTION_PADDING = ' '; + var results = { + 'passed': 0, + 'failed': 0, + 'skipped': 0 + }; + + init(var onlyFailures = false, var exitOnFailure = false) {} + + filterMethods() { + return this.methods().filter(def (method) => { + if (method.startsWith('test') and not method.endsWith('Provider') and not method.endsWith('_skipped')) { + return method; + } + + if (method.endsWith('_skipped')) { + this.results['skipped'] += 1; + } + }); + } + + setUp() { + + } + + tearDown() { + + } + + run() { + const methods = this.filterMethods(); + + print(Inspect.getFile(1)); + methods.forEach(def (method) => { + print('{}{}()'.format(UnitTest.METHOD_NAME_PADDING, method)); + + const providerMethodName = '{}Provider'.format(method); + + if (this.hasAttribute(providerMethodName)) { + const testValue = this.getAttribute(providerMethodName)(); + + if (type(testValue) == 'list') { + testValue.forEach(def (val) => { + this.setUp(); + this.getAttribute(method)(val); + this.tearDown(); + }); + } else { + this.setUp(); + this.getAttribute(method)(testValue); + this.tearDown(); + } + } else { + this.setUp(); + this.getAttribute(method)(); + this.tearDown(); + } + }); + print('\nResults:\n{}- {} assertion(s) were successful.\n{}- {} assertion(s) were failures.\n{}- {} method(s) were skipped.\n'.format( + UnitTest.RESULTS_PADDING, + this.results['passed'], + UnitTest.RESULTS_PADDING, + this.results['failed'], + UnitTest.RESULTS_PADDING, + this.results['skipped'] + )); + + if (this.results['failed'] > 0) { + System.exit(1); + } + } + + printResult(success, errorMsg) { + if (success) { + this.results['passed'] += 1; + + if (not this.onlyFailures) { + print('{}Success.'.format(UnitTest.ASSERTION_PADDING)); + } + } else { + this.results['failed'] += 1; + + print('{}Line: {} - {}'.format(UnitTest.ASSERTION_PADDING, Inspect.getLine(2), errorMsg)); + + if (this.exitOnFailure) { + System.exit(1); + } + } + } + + assertEquals(value, expected) { + this.printResult(value == expected, 'Failure: {} is not equal to {}.'.format(value, expected)); + } + + assertTruthy(value) { + this.printResult(value, 'Failure: {} is not Truthy.'.format(value)); + } + + assertFalsey(value) { + this.printResult(not value, 'Failure: {} is not Falsey.'.format(value)); + } + + assertSuccess(value) { + if (type(value) != 'result') { + this.printResult(false, 'Failure: {} is not a Result type.'.format(value)); + return; + } + + this.printResult(value.success(), 'Failure: {} is not a Result type in a success state.'.format(value)); + } + + assertError(value) { + if (type(value) != 'result') { + this.printResult(false, 'Failure: {} is not a Result type.'.format(value)); + return; + } + + this.printResult(not value.success(), 'Failure: {} is not a Result type in an error state.'.format(value)); + } +} \ No newline at end of file diff --git a/src/optionals/unittest/unittest.h b/src/optionals/unittest/unittest.h new file mode 100644 index 00000000..79bcb331 --- /dev/null +++ b/src/optionals/unittest/unittest.h @@ -0,0 +1,8 @@ +#ifndef DICTU_API_UNITTEST_H +#define DICTU_API_UNITTEST_H + +#include "../optionals.h" + +Value createUnitTestModule(DictuVM *vm); + +#endif //DICTU_API_UNITTEST_H diff --git a/src/vm/compiler.c b/src/vm/compiler.c index 7c684401..43017723 100644 --- a/src/vm/compiler.c +++ b/src/vm/compiler.c @@ -1344,7 +1344,7 @@ static bool foldUnary(Compiler *compiler, TokenType operatorType) { TokenType valueToken = compiler->parser->previous.type; switch (operatorType) { - case TOKEN_BANG: { + case TOKEN_NOT: { if (valueToken == TOKEN_TRUE) { Chunk *chunk = currentChunk(compiler); chunk->code[chunk->count - 1] = OP_FALSE; @@ -1387,7 +1387,7 @@ static void unary(Compiler *compiler, bool canAssign) { } switch (operatorType) { - case TOKEN_BANG: + case TOKEN_NOT: emitByte(compiler, OP_NOT); break; case TOKEN_MINUS: @@ -1428,7 +1428,7 @@ ParseRule rules[] = { {NULL, NULL, PREC_NONE}, // TOKEN_AMPERSAND_EQUALS {NULL, NULL, PREC_NONE}, // TOKEN_CARET_EQUALS {NULL, NULL, PREC_NONE}, // TOKEN_PIPE_EQUALS - {unary, NULL, PREC_NONE}, // TOKEN_BANG + {unary, NULL, PREC_NONE}, // TOKEN_NOT {NULL, binary, PREC_EQUALITY}, // TOKEN_BANG_EQUAL {NULL, NULL, PREC_NONE}, // TOKEN_EQUAL {NULL, binary, PREC_EQUALITY}, // TOKEN_EQUAL_EQUAL @@ -1973,8 +1973,11 @@ static int getArgCount(uint8_t *code, const ValueArray constants, int ip) { case OP_IMPORT_BUILTIN: return 2; - case OP_IMPORT_BUILTIN_VARIABLE: - return 3; + case OP_IMPORT_BUILTIN_VARIABLE: { + int argCount = code[ip + 2]; + + return 2 + argCount; + } case OP_CLOSURE: { int constant = code[ip + 1]; @@ -2219,14 +2222,19 @@ static void importStatement(Compiler *compiler) { emitByte(compiler, OP_IMPORT_VARIABLE); defineVariable(compiler, importName, false); } + + emitByte(compiler, OP_IMPORT_END); } else { consume(compiler, TOKEN_IDENTIFIER, "Expect import identifier."); uint8_t importName = identifierConstant(compiler, &compiler->parser->previous); declareVariable(compiler, &compiler->parser->previous); + bool dictuSource = false; + int index = findBuiltinModule( - (char *) compiler->parser->previous.start, - compiler->parser->previous.length - compiler->parser->current.length + (char *) compiler->parser->previous.start, + compiler->parser->previous.length - compiler->parser->current.length, + &dictuSource ); if (index == -1) { @@ -2236,11 +2244,15 @@ static void importStatement(Compiler *compiler) { emitBytes(compiler, OP_IMPORT_BUILTIN, index); emitByte(compiler, importName); + if (dictuSource) { + emitByte(compiler, OP_POP); + emitByte(compiler, OP_IMPORT_VARIABLE); + } + defineVariable(compiler, importName, false); } consume(compiler, TOKEN_SEMICOLON, "Expect ';' after import."); - emitByte(compiler, OP_IMPORT_END); } static void fromImportStatement(Compiler *compiler) { @@ -2293,9 +2305,12 @@ static void fromImportStatement(Compiler *compiler) { consume(compiler, TOKEN_IDENTIFIER, "Expect import identifier."); uint8_t importName = identifierConstant(compiler, &compiler->parser->previous); + bool dictuSource; + int index = findBuiltinModule( (char *) compiler->parser->previous.start, - compiler->parser->previous.length + compiler->parser->previous.length, + &dictuSource ); consume(compiler, TOKEN_IMPORT, "Expect 'import' after identifier"); @@ -2319,7 +2334,11 @@ static void fromImportStatement(Compiler *compiler) { } } while (match(compiler, TOKEN_COMMA)); - emitBytes(compiler, OP_IMPORT_BUILTIN_VARIABLE, index); + emitBytes(compiler, OP_IMPORT_BUILTIN, index); + emitByte(compiler, importName); + emitByte(compiler, OP_POP); + + emitByte(compiler, OP_IMPORT_BUILTIN_VARIABLE); emitBytes(compiler, importName, varCount); for (int i = 0; i < varCount; ++i) { @@ -2547,4 +2566,4 @@ void grayCompilerRoots(DictuVM *vm) { grayTable(vm, &compiler->stringConstants); compiler = compiler->enclosing; } -} \ No newline at end of file +} diff --git a/src/vm/datatypes/class.c b/src/vm/datatypes/class.c index 6c787d81..3b0e01bf 100644 --- a/src/vm/datatypes/class.c +++ b/src/vm/datatypes/class.c @@ -27,7 +27,7 @@ static Value methods(DictuVM *vm, int argCount, Value *args) { ObjList *list = newList(vm); push(vm, OBJ_VAL(list)); - for (int i = 0; i < klass->publicMethods.capacityMask; ++i) { + for (int i = 0; i <= klass->publicMethods.capacityMask; ++i) { if (klass->publicMethods.entries[i].key == NULL) { continue; } diff --git a/src/vm/datatypes/instance.c b/src/vm/datatypes/instance.c index 95ea5502..2159c8f5 100644 --- a/src/vm/datatypes/instance.c +++ b/src/vm/datatypes/instance.c @@ -100,6 +100,85 @@ static Value getAttribute(DictuVM *vm, int argCount, Value *args) { return defaultValue; } +static Value getAttributes(DictuVM *vm, int argCount, Value *args) { + if (argCount > 0) { + runtimeError(vm, "getAttributes() takes 0 arguments (%d given)", argCount); + return EMPTY_VAL; + } + + ObjInstance *instance = AS_INSTANCE(args[0]); + + ObjDict *dict = newDict(vm); + push(vm, OBJ_VAL(dict)); + + ObjList *classVariables = newList(vm); + push(vm, OBJ_VAL(classVariables)); + + ObjClass *klass = instance->klass; + + // Walk the inheritance chain + while (klass != NULL) { + for (int i = 0; i < klass->publicProperties.capacityMask + 1; i++) { + if (klass->publicProperties.entries[i].key == NULL) { + continue; + } + + writeValueArray(vm, &classVariables->values, OBJ_VAL(klass->publicProperties.entries[i].key)); + } + + klass = klass->superclass; + } + + ObjString *cv = copyString(vm, "classVariables", 14); + push(vm, OBJ_VAL(cv)); + + dictSet(vm, dict, OBJ_VAL(cv), OBJ_VAL(classVariables)); + + pop(vm); // "classVariables" string + pop(vm); // "classVariables" list + + ObjList *properties = newList(vm); + push(vm, OBJ_VAL(properties)); + + for (int i = 0; i < instance->publicFields.capacityMask + 1; i++) { + if (instance->publicFields.entries[i].key == NULL) { + continue; + } + + writeValueArray(vm, &properties->values, OBJ_VAL(instance->publicFields.entries[i].key)); + } + + ObjString *pv = copyString(vm, "properties", 10); + push(vm, OBJ_VAL(pv)); + + dictSet(vm, dict, OBJ_VAL(pv), OBJ_VAL(properties)); + + pop(vm); // "properties" string + pop(vm); // "properties" list + + ObjList *methods = newList(vm); + push(vm, OBJ_VAL(methods)); + + for (int i = 0; i <= instance->klass->publicMethods.capacityMask; ++i) { + if (instance->klass->publicMethods.entries[i].key == NULL) { + continue; + } + + writeValueArray(vm, &methods->values, OBJ_VAL(instance->klass->publicMethods.entries[i].key)); + } + ObjString *mv = copyString(vm, "methods", 7); + push(vm, OBJ_VAL(mv)); + + dictSet(vm, dict, OBJ_VAL(mv), OBJ_VAL(methods)); + + pop(vm); // "methods" string + pop(vm); // "methods" list + + pop(vm); // dict + + return OBJ_VAL(dict); +} + static Value setAttribute(DictuVM *vm, int argCount, Value *args) { if (argCount != 2) { runtimeError(vm, "setAttribute() takes 2 arguments (%d given)", argCount); @@ -182,7 +261,7 @@ static Value methods(DictuVM *vm, int argCount, Value *args) { ObjList *list = newList(vm); push(vm, OBJ_VAL(list)); - for (int i = 0; i < instance->klass->publicMethods.capacityMask; ++i) { + for (int i = 0; i <= instance->klass->publicMethods.capacityMask; ++i) { if (instance->klass->publicMethods.entries[i].key == NULL) { continue; } @@ -198,6 +277,7 @@ void declareInstanceMethods(DictuVM *vm) { defineNative(vm, &vm->instanceMethods, "toString", toString); defineNative(vm, &vm->instanceMethods, "hasAttribute", hasAttribute); defineNative(vm, &vm->instanceMethods, "getAttribute", getAttribute); + defineNative(vm, &vm->instanceMethods, "getAttributes", getAttributes); defineNative(vm, &vm->instanceMethods, "setAttribute", setAttribute); defineNative(vm, &vm->instanceMethods, "isInstance", isInstance); defineNative(vm, &vm->instanceMethods, "copy", copyShallow); diff --git a/src/vm/datatypes/strings.c b/src/vm/datatypes/strings.c index 604102b3..e234239a 100644 --- a/src/vm/datatypes/strings.c +++ b/src/vm/datatypes/strings.c @@ -122,8 +122,8 @@ static Value formatString(DictuVM *vm, int argCount, Value *args) { } static Value splitString(DictuVM *vm, int argCount, Value *args) { - if (argCount != 1) { - runtimeError(vm, "split() takes 1 argument (%d given)", argCount); + if (argCount != 1 && argCount != 2) { + runtimeError(vm, "split() takes 1 or 2 arguments (%d given)", argCount); return EMPTY_VAL; } @@ -134,6 +134,18 @@ static Value splitString(DictuVM *vm, int argCount, Value *args) { ObjString *string = AS_STRING(args[0]); char *delimiter = AS_CSTRING(args[1]); + int maxSplit = string->length + 1; + + if (argCount == 2) { + if (!AS_NUMBER(args[1])) { + runtimeError(vm, "Argument passed to split() must be a number"); + return EMPTY_VAL; + } + + if (AS_NUMBER(args[2]) >= 0) { + maxSplit = AS_NUMBER(args[2]); + } + } char *tmp = ALLOCATE(vm, char, string->length + 1); char *tmpFree = tmp; @@ -144,9 +156,13 @@ static Value splitString(DictuVM *vm, int argCount, Value *args) { ObjList *list = newList(vm); push(vm, OBJ_VAL(list)); + int count = 0; + if (delimiterLength == 0) { - for (int tokenCount = 0; tokenCount < string->length; tokenCount++) { - *(tmp) = string->chars[tokenCount]; + int tokenIndex = 0; + for (; tokenIndex < string->length && count < maxSplit; tokenIndex++) { + count++; + *(tmp) = string->chars[tokenIndex]; *(tmp + 1) = '\0'; Value str = OBJ_VAL(copyString(vm, tmp, 1)); // Push to stack to avoid GC @@ -154,8 +170,15 @@ static Value splitString(DictuVM *vm, int argCount, Value *args) { writeValueArray(vm, &list->values, str); pop(vm); } - } else { + + if (tokenIndex != string->length && count >= maxSplit) { + tmp = (string->chars) + tokenIndex; + } else { + tmp = NULL; + } + } else if (maxSplit > 0) { do { + count++; token = strstr(tmp, delimiter); if (token) *token = '\0'; @@ -168,8 +191,22 @@ static Value splitString(DictuVM *vm, int argCount, Value *args) { pop(vm); tmp = token + delimiterLength; - } while (token != NULL); + } while (token != NULL && count < maxSplit); + + if (token == NULL) { + tmp = NULL; + } } + + if (tmp != NULL && count >= maxSplit) { + Value remainingStr = OBJ_VAL(copyString(vm, tmp, strlen(tmp))); + + // Push to stack to avoid GC + push(vm, remainingStr); + writeValueArray(vm, &list->values, remainingStr); + pop(vm); + } + pop(vm); FREE_ARRAY(vm, char, tmpFree, string->length + 1); diff --git a/src/vm/debug.c b/src/vm/debug.c index 8c68184d..e375f8fe 100644 --- a/src/vm/debug.c +++ b/src/vm/debug.c @@ -32,6 +32,16 @@ static int invokeInstruction(const char* name, Chunk* chunk, return offset + 3; } +static int importFromInstruction(const char *name, Chunk *chunk, + int offset) { + uint8_t constant = chunk->code[offset + 1]; + uint8_t argCount = chunk->code[offset + 2]; + printf("%-16s %4d '", name, constant); + printValue(chunk->constants.values[constant]); + printf("'\n"); + return offset + 1 + argCount; +} + static int builtinImportInstruction(const char* name, Chunk* chunk, int offset) { uint8_t module = chunk->code[offset + 2]; @@ -43,12 +53,12 @@ static int builtinImportInstruction(const char* name, Chunk* chunk, static int builtinFromImportInstruction(const char* name, Chunk* chunk, int offset) { - uint8_t module = chunk->code[offset + 2]; - uint8_t argCount = chunk->code[offset + 3]; + uint8_t module = chunk->code[offset + 1]; + uint8_t argCount = chunk->code[offset + 2]; printf("%-16s '", name); printValue(chunk->constants.values[module]); printf("'\n"); - return offset + 3 + argCount; + return offset + 2 + argCount; } static int classInstruction(const char* name, Chunk* chunk, @@ -210,7 +220,7 @@ int disassembleInstruction(Chunk *chunk, int offset) { case OP_IMPORT_VARIABLE: return simpleInstruction("OP_IMPORT_VARIABLE", offset); case OP_IMPORT_FROM: - return constantInstruction("OP_IMPORT_FROM", chunk, offset); + return importFromInstruction("OP_IMPORT_FROM", chunk, offset); case OP_IMPORT_END: return simpleInstruction("OP_IMPORT_END", offset); case OP_NEW_LIST: diff --git a/src/vm/natives.c b/src/vm/natives.c index eb402575..99772f60 100644 --- a/src/vm/natives.c +++ b/src/vm/natives.c @@ -149,7 +149,9 @@ static Value isDefinedNative(DictuVM *vm, int argCount, Value *args) { if (tableGet(&vm->globals, string, &value)) return TRUE_VAL; - if (findBuiltinModule(string->chars, string->length) != -1) + bool _; + + if (findBuiltinModule(string->chars, string->length, &_) != -1) return TRUE_VAL; return FALSE_VAL; @@ -205,7 +207,6 @@ void defineAllNatives(DictuVM *vm) { generateErrorResult }; - for (uint8_t i = 0; i < sizeof(nativeNames) / sizeof(nativeNames[0]); ++i) { defineNative(vm, &vm->globals, nativeNames[i], nativeFunctions[i]); } diff --git a/src/vm/object.c b/src/vm/object.c index cfc9ba45..27e26fd6 100644 --- a/src/vm/object.c +++ b/src/vm/object.c @@ -117,6 +117,14 @@ ObjInstance *newInstance(DictuVM *vm, ObjClass *klass) { instance->klass = klass; initTable(&instance->publicFields); initTable(&instance->privateFields); + + push(vm, OBJ_VAL(instance)); + ObjString *classString = copyString(vm, "_class", 6); + push(vm, OBJ_VAL(classString)); + tableSet(vm, &instance->publicFields, classString, OBJ_VAL(klass)); + pop(vm); + pop(vm); + return instance; } diff --git a/src/vm/scanner.c b/src/vm/scanner.c index 90db8691..1b5e6223 100644 --- a/src/vm/scanner.c +++ b/src/vm/scanner.c @@ -210,7 +210,7 @@ static TokenType identifierType(Scanner *scanner) { if (scanner->current - scanner->start > 1) { switch (scanner->start[1]) { case 'o': - return checkKeyword(scanner, 2, 1, "t", TOKEN_BANG); + return checkKeyword(scanner, 2, 1, "t", TOKEN_NOT); case 'i': return checkKeyword(scanner, 2, 1, "l", TOKEN_NIL); } @@ -420,7 +420,10 @@ Token scanToken(Scanner *scanner) { case '|': return makeToken(scanner, match(scanner, '=') ? TOKEN_PIPE_EQUALS : TOKEN_PIPE); case '!': - return makeToken(scanner, match(scanner, '=') ? TOKEN_BANG_EQUAL : TOKEN_BANG); + if (match(scanner, '=')) { + return makeToken(scanner, TOKEN_BANG_EQUAL); + } + break; case '=': if (match(scanner, '=')) { return makeToken(scanner, TOKEN_EQUAL_EQUAL); diff --git a/src/vm/scanner.h b/src/vm/scanner.h index 206d7e7c..08c1659d 100644 --- a/src/vm/scanner.h +++ b/src/vm/scanner.h @@ -22,7 +22,7 @@ typedef enum { TOKEN_AMPERSAND_EQUALS, TOKEN_CARET_EQUALS, TOKEN_PIPE_EQUALS, // One or two character tokens. - TOKEN_BANG, TOKEN_BANG_EQUAL, + TOKEN_NOT, TOKEN_BANG_EQUAL, TOKEN_EQUAL, TOKEN_EQUAL_EQUAL, TOKEN_GREATER, TOKEN_GREATER_EQUAL, TOKEN_LESS, TOKEN_LESS_EQUAL, diff --git a/src/vm/vm.c b/src/vm/vm.c index 41422d1b..2cb391ea 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -186,6 +186,23 @@ Value peek(DictuVM *vm, int distance) { return vm->stackTop[-1 - distance]; } +ObjClosure *compileModuleToClosure(DictuVM *vm, char *name, char *source) { + ObjString *pathObj = copyString(vm, name, strlen(name)); + push(vm, OBJ_VAL(pathObj)); + ObjModule *module = newModule(vm, pathObj); + pop(vm); + push(vm, OBJ_VAL(module)); + ObjFunction *function = compile(vm, module, source); + pop(vm); + + if (function == NULL) return NULL; + push(vm, OBJ_VAL(function)); + ObjClosure *closure = newClosure(vm, function); + pop(vm); + + return closure; +} + static bool call(DictuVM *vm, ObjClosure *closure, int argCount) { if (argCount < closure->function->arity || argCount > closure->function->arity + closure->function->arityOptional) { runtimeError(vm, "Function '%s' expected %d argument(s) but got %d.", @@ -1458,18 +1475,33 @@ static DictuInterpretResult run(DictuVM *vm) { // If we have imported this module already, skip. if (tableGet(&vm->modules, fileName, &moduleVal)) { + vm->lastModule = AS_MODULE(moduleVal); push(vm, moduleVal); DISPATCH(); } - ObjModule *module = importBuiltinModule(vm, index); + Value module = importBuiltinModule(vm, index); + + if (IS_EMPTY(module)) { + return INTERPRET_COMPILE_ERROR; + } + + push(vm, module); + + if (IS_CLOSURE(module)) { + frame->ip = ip; + call(vm, AS_CLOSURE(module), 0); + frame = &vm->frames[vm->frameCount - 1]; + ip = frame->ip; + + tableGet(&vm->modules, fileName, &module); + vm->lastModule = AS_MODULE(module); + } - push(vm, OBJ_VAL(module)); DISPATCH(); } CASE_CODE(IMPORT_BUILTIN_VARIABLE): { - int index = READ_BYTE(); ObjString *fileName = READ_STRING(); int varCount = READ_BYTE(); @@ -1479,7 +1511,7 @@ static DictuInterpretResult run(DictuVM *vm) { if (tableGet(&vm->modules, fileName, &moduleVal)) { module = AS_MODULE(moduleVal); } else { - module = importBuiltinModule(vm, index); + RUNTIME_ERROR("ERROR!!"); } for (int i = 0; i < varCount; i++) { diff --git a/src/vm/vm.h b/src/vm/vm.h index 82aa072d..1fb7aa19 100644 --- a/src/vm/vm.h +++ b/src/vm/vm.h @@ -65,4 +65,6 @@ Value pop(DictuVM *vm); bool isFalsey(Value value); +ObjClosure *compileModuleToClosure(DictuVM *vm, char *name, char *source); + #endif diff --git a/tests/benchmarks/dict-methods/exists.du b/tests/benchmarks/dict-methods/exists.du index 24705376..1f07f1a5 100644 --- a/tests/benchmarks/dict-methods/exists.du +++ b/tests/benchmarks/dict-methods/exists.du @@ -1,7 +1,6 @@ var start = System.clock(); var x = {"dictu": "is great!"}; - for (var i = 0; i < 10000; i += 1) { x.exists("dictu"); } diff --git a/tests/benchmarks/methodCall.du b/tests/benchmarks/methodCall.du index 16035eff..c7b27ecb 100644 --- a/tests/benchmarks/methodCall.du +++ b/tests/benchmarks/methodCall.du @@ -6,7 +6,7 @@ class Toggle { value() { return this.state; } activate() { - this.state = !this.state; + this.state = not this.state; return this; } } diff --git a/tests/classes/copy.du b/tests/classes/copy.du index 1d3615b8..ad4d6660 100644 --- a/tests/classes/copy.du +++ b/tests/classes/copy.du @@ -28,7 +28,7 @@ assert(objCopy.z == {1: 2}); objCopy.y = 10; assert(objCopy.y == 10); -assert(!obj.hasAttribute('y')); +assert(not obj.hasAttribute('y')); obj.y = Test(); @@ -45,5 +45,5 @@ assert(obj.y.x == [1, 2]); deepCopy.i = 10; assert(deepCopy.i == 10); -assert(!obj.hasAttribute('i')); +assert(not obj.hasAttribute('i')); diff --git a/tests/classes/getAttribute.du b/tests/classes/getAttribute.du new file mode 100644 index 00000000..33f57146 --- /dev/null +++ b/tests/classes/getAttribute.du @@ -0,0 +1,48 @@ +/** + * getAttribute.du + * + * Testing the obj.getAttribute() method + * + */ + +class Test { + var x = 10; + + init(private priv = 10, var pub = 20) { + this.prop = 30; + } + + private privMethod() {} + + publicMethod() {} +} + +assert(Test().getAttribute("x") == 10); +assert(Test().getAttribute("x", nil) == 10); +assert(Test().getAttribute("priv") == nil); +assert(Test().getAttribute("priv", 10) == 10); +assert(Test().getAttribute("privMethod") == nil); +assert(Test().getAttribute("privMethod", 10) == 10); +assert(Test().getAttribute("pub") == 20); +assert(Test().getAttribute("pub", nil) == 20); +assert(Test().getAttribute("prop") == 30); +assert(Test().getAttribute("prop", nil) == 30); +assert(type(Test().getAttribute("publicMethod") == "function")); +assert(type(Test().getAttribute("publicMethod", nil) == "function")); + +class Inherit < Test { + +} + +assert(Inherit().getAttribute("x") == 10); +assert(Inherit().getAttribute("x", nil) == 10); +assert(Inherit().getAttribute("priv") == nil); +assert(Inherit().getAttribute("priv", 10) == 10); +assert(Inherit().getAttribute("privMethod") == nil); +assert(Inherit().getAttribute("privMethod", 10) == 10); +assert(Inherit().getAttribute("pub") == 20); +assert(Inherit().getAttribute("pub", nil) == 20); +assert(Inherit().getAttribute("prop") == 30); +assert(Inherit().getAttribute("prop", nil) == 30); +assert(type(Inherit().getAttribute("publicMethod") == "function")); +assert(type(Inherit().getAttribute("publicMethod", nil) == "function")); diff --git a/tests/classes/getAttributes.du b/tests/classes/getAttributes.du index d7b081a4..2cbf0a76 100644 --- a/tests/classes/getAttributes.du +++ b/tests/classes/getAttributes.du @@ -1,7 +1,7 @@ /** * getAttributes.du * - * Testing the obj.getAttribute() method + * Testing the obj.getAttributes() method * */ @@ -17,32 +17,26 @@ class Test { publicMethod() {} } -assert(Test().getAttribute("x") == 10); -assert(Test().getAttribute("x", nil) == 10); -assert(Test().getAttribute("priv") == nil); -assert(Test().getAttribute("priv", 10) == 10); -assert(Test().getAttribute("privMethod") == nil); -assert(Test().getAttribute("privMethod", 10) == 10); -assert(Test().getAttribute("pub") == 20); -assert(Test().getAttribute("pub", nil) == 20); -assert(Test().getAttribute("prop") == 30); -assert(Test().getAttribute("prop", nil) == 30); -assert(type(Test().getAttribute("publicMethod") == "function")); -assert(type(Test().getAttribute("publicMethod", nil) == "function")); +assert(Test().getAttributes().len() == 3); +assert(Test().getAttributes().exists("properties")); +assert(Test().getAttributes().exists("classVariables")); +assert(Test().getAttributes().exists("methods")); +// 3 because of the implicit "_class" property +assert(Test().getAttributes().get("properties").len() == 3); +assert(Test().getAttributes().get("classVariables").len() == 1); +assert(Test().getAttributes().get("methods").len() == 2); +assert(Test().getAttributes().get("methods").find(def (item) => item == "publicMethod")); class Inherit < Test { } -assert(Inherit().getAttribute("x") == 10); -assert(Inherit().getAttribute("x", nil) == 10); -assert(Inherit().getAttribute("priv") == nil); -assert(Inherit().getAttribute("priv", 10) == 10); -assert(Inherit().getAttribute("privMethod") == nil); -assert(Inherit().getAttribute("privMethod", 10) == 10); -assert(Inherit().getAttribute("pub") == 20); -assert(Inherit().getAttribute("pub", nil) == 20); -assert(Inherit().getAttribute("prop") == 30); -assert(Inherit().getAttribute("prop", nil) == 30); -assert(type(Inherit().getAttribute("publicMethod") == "function")); -assert(type(Inherit().getAttribute("publicMethod", nil) == "function")); \ No newline at end of file +assert(Inherit().getAttributes().len() == 3); +assert(Inherit().getAttributes().exists("properties")); +assert(Inherit().getAttributes().exists("classVariables")); +assert(Inherit().getAttributes().exists("methods")); +// 3 because of the implicit "_class" property +assert(Inherit().getAttributes().get("properties").len() == 3); +assert(Inherit().getAttributes().get("classVariables").len() == 1); +assert(Inherit().getAttributes().get("methods").len() == 2); +assert(Inherit().getAttributes().get("methods").find(def (item) => item == "publicMethod")); diff --git a/tests/classes/isInstance.du b/tests/classes/isInstance.du index 1d62551e..57866b0d 100644 --- a/tests/classes/isInstance.du +++ b/tests/classes/isInstance.du @@ -14,12 +14,12 @@ class AnotherTest < Test {} assert(AnotherTest().isInstance(Test)); assert(AnotherTest().isInstance(AnotherTest)); -assert(!Test().isInstance(AnotherTest)); +assert(not Test().isInstance(AnotherTest)); class Unrelated {} -assert(!Test().isInstance(Unrelated)); -assert(!AnotherTest().isInstance(Unrelated)); +assert(not Test().isInstance(Unrelated)); +assert(not AnotherTest().isInstance(Unrelated)); abstract class AbstractBase {} diff --git a/tests/classes/methods.du b/tests/classes/methods.du index 1288ea8e..1ac16eb0 100644 --- a/tests/classes/methods.du +++ b/tests/classes/methods.du @@ -27,7 +27,7 @@ expectedMethods.forEach(def (method) => { assert(Test.methods().contains(method)); }); -assert(!Test.methods().contains("somePrivateFunc")); +assert(not Test.methods().contains("somePrivateFunc")); /** * Test instance @@ -37,7 +37,7 @@ expectedMethods.forEach(def (method) => { assert(Test().methods().contains(method)); }); -assert(!Test().methods().contains("somePrivateFunc")); +assert(not Test().methods().contains("somePrivateFunc")); /** * Inheritance @@ -61,7 +61,7 @@ expectedMethodsInheritance.forEach(def (method) => { assert(AnotherTest.methods().contains(method)); }); -assert(!AnotherTest.methods().contains("somePrivateFunc")); +assert(not AnotherTest.methods().contains("somePrivateFunc")); /** * Test instance @@ -71,4 +71,4 @@ expectedMethodsInheritance.forEach(def (method) => { assert(AnotherTest().methods().contains(method)); }); -assert(!AnotherTest().methods().contains("somePrivateFunc")); \ No newline at end of file +assert(not AnotherTest().methods().contains("somePrivateFunc")); \ No newline at end of file diff --git a/tests/classes/properties.du b/tests/classes/properties.du index ea071ef8..4dab7ba5 100644 --- a/tests/classes/properties.du +++ b/tests/classes/properties.du @@ -29,7 +29,7 @@ obj.x /= 10; assert(obj.x == 20); assert(obj.hasAttribute("x")); -assert(!obj.hasAttribute("y")); +assert(not obj.hasAttribute("y")); assert(obj.getAttribute("x") == 20); assert(obj.getAttribute("x", 50) == 20); @@ -41,4 +41,10 @@ assert(obj.getAttribute("y") == 10); obj.setAttribute("x", 10); assert(obj.hasAttribute("x")); -assert(obj.getAttribute("x") == 10); \ No newline at end of file +assert(obj.getAttribute("x") == 10); + +assert(obj._class == Test); + +class Inherit < Test {} + +assert(Inherit()._class == Inherit); \ No newline at end of file diff --git a/tests/dicts/exists.du b/tests/dicts/exists.du index 39870b86..5699fadd 100644 --- a/tests/dicts/exists.du +++ b/tests/dicts/exists.du @@ -19,9 +19,9 @@ assert(myDict.exists(false)); assert(myDict.exists(nil)); assert(myDict.exists(10)); assert(myDict.exists(10.5)); -assert(!(myDict.exists("unknown"))); -assert(!(myDict.exists(100))); -assert(!(myDict.exists(100.5))); +assert(not myDict.exists("unknown")); +assert(not myDict.exists(100)); +assert(not myDict.exists(100.5)); -assert(!{}.exists(true)); +assert(not {}.exists(true)); assert({true: true}.exists(true)); \ No newline at end of file diff --git a/tests/env/.env b/tests/env/.env new file mode 100644 index 00000000..ea1b6f9f --- /dev/null +++ b/tests/env/.env @@ -0,0 +1,5 @@ +TEST=10 +INLINE=100 # in-line comment +# IGNORE=1000 +SPACES = 30 +MALFORMED \ No newline at end of file diff --git a/tests/env/env.du b/tests/env/env.du index 063f4ace..ee8a07db 100644 --- a/tests/env/env.du +++ b/tests/env/env.du @@ -2,7 +2,7 @@ * end.du * * Testing the Env functions: - * - get(), set() + * - get(), set(), readFile() * */ import Env; @@ -15,3 +15,14 @@ assert(Env.set("test", "test").success() == true); assert(Env.get("test") == "test"); assert(Env.set("test", nil).success() == true); assert(Env.get("test") == nil); + +import Path; + +const result = Env.readFile(Path.dirname(__file__) + '/.env'); +assert(Env.get("TEST") == "10"); +assert(Env.get("INLINE") == "100"); +assert(Env.get("IGNORE") == nil); +assert(Env.get("MALFORMED") == nil); + +assert(not result.success()); +assert(result.unwrapError() == "Malformed entry on line 5"); diff --git a/tests/http/import.du b/tests/http/import.du index e0f34b10..13688e22 100644 --- a/tests/http/import.du +++ b/tests/http/import.du @@ -5,4 +5,5 @@ */ import "get.du"; -import "post.du"; \ No newline at end of file +import "post.du"; +import "status.du"; diff --git a/tests/http/status.du b/tests/http/status.du new file mode 100644 index 00000000..24b1fda9 --- /dev/null +++ b/tests/http/status.du @@ -0,0 +1,3 @@ +import HTTP; + +assert(HTTP.STATUS_OK == 200); diff --git a/tests/inspect/getFile.du b/tests/inspect/getFile.du new file mode 100644 index 00000000..5c6a39ff --- /dev/null +++ b/tests/inspect/getFile.du @@ -0,0 +1,10 @@ +/** + * getFile.du + * + * Testing the Inspect.getFile() method + */ + +import Inspect; + +assert(Inspect.getFile().endsWith("getFile.du")); +assert(Inspect.getFile(1).endsWith("import.du")); \ No newline at end of file diff --git a/tests/inspect/getFrameCount.du b/tests/inspect/getFrameCount.du new file mode 100644 index 00000000..de89a0f6 --- /dev/null +++ b/tests/inspect/getFrameCount.du @@ -0,0 +1,44 @@ +/** + * getFrameCount.du + * + * Testing the Inspect.getFrameCount() method + */ + +import Inspect; + +/** + * This is required as the imports have increased the frame count. + */ +const ADDED_FRAME_COUNT = 2; + +assert(Inspect.getFrameCount() == 0 + ADDED_FRAME_COUNT); + +def test() { + assert(Inspect.getFrameCount() == 1 + ADDED_FRAME_COUNT); +} + +test(); + +def anotherTest() { + def inner() { + assert(Inspect.getFrameCount() == 2 + ADDED_FRAME_COUNT); + } + + inner(); +} + +anotherTest(); + +def anotherDeeperTest() { + def middle() { + def inner() { + assert(Inspect.getFrameCount() == 3 + ADDED_FRAME_COUNT); + } + + inner(); + } + + middle(); +} + +anotherDeeperTest(); \ No newline at end of file diff --git a/tests/inspect/getLine.du b/tests/inspect/getLine.du new file mode 100644 index 00000000..ca0e63ee --- /dev/null +++ b/tests/inspect/getLine.du @@ -0,0 +1,24 @@ +/** + * getLine.du + * + * Testing the Inspect.getLine() method + */ + +import Inspect; + +assert(Inspect.getLine() == 9); + +def func(framesToWalk=0, expectedLine=12) { + assert(Inspect.getLine(framesToWalk) == expectedLine); +} + +func(); +func(1, 16); + +def anotherFunc(framesToWalk=0, expectedLine=12) { + func(framesToWalk, expectedLine); +} + +anotherFunc(); +anotherFunc(1, 19); +anotherFunc(2, 24); \ No newline at end of file diff --git a/tests/inspect/import.du b/tests/inspect/import.du new file mode 100644 index 00000000..49848890 --- /dev/null +++ b/tests/inspect/import.du @@ -0,0 +1,9 @@ +/** + * import.du + * + * General import file for all the Inspect tests + */ + +import "getLine.du"; +import "getFile.du"; +import "getFrameCount.du"; diff --git a/tests/lists/contains.du b/tests/lists/contains.du index 48a8081b..442fa575 100644 --- a/tests/lists/contains.du +++ b/tests/lists/contains.du @@ -19,12 +19,11 @@ assert(x.contains(4)); assert(x.contains(5)); assert(x.contains([6, 7])); -// Test ! .contains() -assert(!(x.contains(6))); -assert(!(x.contains(7))); -assert(!(x.contains(10))); -assert(!(x.contains(11))); -assert(!(x.contains(12))); -assert(!(x.contains(13))); -assert(!(x.contains(14))); - +// Test not .contains() +assert(not x.contains(6)); +assert(not x.contains(7)); +assert(not x.contains(10)); +assert(not x.contains(11)); +assert(not x.contains(12)); +assert(not x.contains(13)); +assert(not x.contains(14)); diff --git a/tests/log/import.du b/tests/log/import.du new file mode 100644 index 00000000..753dbfd3 --- /dev/null +++ b/tests/log/import.du @@ -0,0 +1,7 @@ +/** + * import.du + * + * General import file for all the Log methods + */ + +import "log.du"; diff --git a/tests/log/log.du b/tests/log/log.du new file mode 100644 index 00000000..d602797c --- /dev/null +++ b/tests/log/log.du @@ -0,0 +1,25 @@ +/** + * log.du + * + * Testing the Log functions: + * - print(), println(), new(), setPrefix + * + */ +import Log; + +Log.print("hello, world! print\n"); +Log.println("hello, world! println"); + +var log = Log.new(Log.stdout).unwrap(); +assert(log != nil); + +log.setPrefix(""); // noop +log.setPrefix("PREFIX"); +log.println("println output to stdout with prefix"); +log.unsetPrefix(); +log.print("print output to stdout without prefix\n"); + +var log = Log.new(Log.stderr).unwrap(); +assert(log != nil); + +log.print("print output to stderr\n"); diff --git a/tests/maths/maths.du b/tests/maths/maths.du index f25871c3..3750521e 100644 --- a/tests/maths/maths.du +++ b/tests/maths/maths.du @@ -66,4 +66,11 @@ assert(Math.lcm(32, 24, 12) == 96); assert(Math.lcm([32, 24, 12]) == 96); assert(Math.pi == 3.14159265358979); -assert(Math.e == 2.71828182845905); \ No newline at end of file +assert(Math.e == 2.71828182845905); +assert(Math.phi == 1.61803398874989); +assert(Math.sqrt2 == 1.41421356237309); +assert(Math.sqrte == 1.61803398874989); +assert(Math.sqrtpi == 1.77245385090551); +assert(Math.sqrtphi == 1.27201964951406); +assert(Math.ln2 == 0.69314718055994); +assert(Math.ln10 == 2.30258509299404); diff --git a/tests/operators/equality.du b/tests/operators/equality.du index cbd3af01..d6bb925b 100644 --- a/tests/operators/equality.du +++ b/tests/operators/equality.du @@ -8,10 +8,10 @@ assert(10 == 10); assert(11 != 10); assert(10 >= 10); -assert(!(10 >= 11)); +assert(not (10 >= 11)); assert(9 <= 10); -assert(!(10 <= 9)); +assert(not (10 <= 9)); assert("test" == "test"); assert("test" != "testing"); diff --git a/tests/operators/not.du b/tests/operators/not.du index 95a963c6..360df26f 100644 --- a/tests/operators/not.du +++ b/tests/operators/not.du @@ -7,5 +7,5 @@ assert((not true) == false); assert(not false); -assert(!true == false); -assert(!false); \ No newline at end of file +assert(not (true == false)); +assert(not false); \ No newline at end of file diff --git a/tests/operators/ternary.du b/tests/operators/ternary.du index 0276fbde..00c5952c 100644 --- a/tests/operators/ternary.du +++ b/tests/operators/ternary.du @@ -14,7 +14,7 @@ assert(false ? 1 + 2 + 3 : 4 + 5 + 6 == 15); var condition = true; assert(condition ? [1, 2] : [3, 4] == [1, 2]); -assert(!condition ? [1, 2] : [3, 4] == [3, 4]); +assert(not condition ? [1, 2] : [3, 4] == [3, 4]); // Nested def nestedTernary(num) { diff --git a/tests/result/matchWrap.du b/tests/result/matchWrap.du index 97a63a0f..b9ac4217 100644 --- a/tests/result/matchWrap.du +++ b/tests/result/matchWrap.du @@ -18,7 +18,7 @@ assert(result.success()); assert(result.unwrap() == 10); result = Error("Error!").matchWrap(success, error); -assert(!result.success()); +assert(not result.success()); assert(result.unwrapError() == "Error!"); @@ -41,5 +41,5 @@ assert(number.success()); assert(number.unwrap() == 10); var number = "test".toNumber().matchWrap(success, error); -assert(!number.success()); +assert(not number.success()); assert(number.unwrapError() == "Can not convert 'test' to number"); diff --git a/tests/runTests.du b/tests/runTests.du index 05d73dae..5fed0503 100644 --- a/tests/runTests.du +++ b/tests/runTests.du @@ -19,11 +19,13 @@ import "datetime/import.du"; import "env/import.du"; import "system/import.du"; import "json/import.du"; +import "log/import.du"; import "path/import.du"; import "sockets/import.du"; import "base64/import.du"; import "sqlite/import.du"; import "process/import.du"; +import "inspect/import.du"; if (isDefined("HTTP")) { import "http/import.du"; diff --git a/tests/sets/remove.du b/tests/sets/remove.du index bb29a707..d838cfc1 100644 --- a/tests/sets/remove.du +++ b/tests/sets/remove.du @@ -32,25 +32,25 @@ mySet.remove("dictu"); assert(mySet.len() == 7); -assert(!mySet.contains("dictu")); +assert(not mySet.contains("dictu")); assert(mySet.contains("is")); assert(mySet.contains("great!")); mySet.remove(true); -assert(!mySet.contains(true)); +assert(not mySet.contains(true)); mySet.remove(false); -assert(!mySet.contains(false)); +assert(not mySet.contains(false)); mySet.remove(10); -assert(!mySet.contains(10)); +assert(not mySet.contains(10)); mySet.remove(10.5); -assert(!mySet.contains(10.5)); +assert(not mySet.contains(10.5)); mySet.remove(nil); -assert(!mySet.contains(nil)); +assert(not mySet.contains(nil)); assert(mySet.contains("is")); assert(mySet.contains("great!")); @@ -73,25 +73,25 @@ mySet.remove("dictu"); assert(mySet.len() == 7); -assert(!mySet.contains("dictu")); +assert(not mySet.contains("dictu")); assert(mySet.contains("is")); assert(mySet.contains("great!")); mySet.remove(true); -assert(!mySet.contains(true)); +assert(not mySet.contains(true)); mySet.remove(false); -assert(!mySet.contains(false)); +assert(not mySet.contains(false)); mySet.remove(10); -assert(!mySet.contains(10)); +assert(not mySet.contains(10)); mySet.remove(10.5); -assert(!mySet.contains(10.5)); +assert(not mySet.contains(10.5)); mySet.remove(nil); -assert(!mySet.contains(nil)); +assert(not mySet.contains(nil)); assert(mySet.contains("is")); assert(mySet.contains("great!")); \ No newline at end of file diff --git a/tests/strings/contains.du b/tests/strings/contains.du index fdb89c40..7fcc1cf5 100644 --- a/tests/strings/contains.du +++ b/tests/strings/contains.du @@ -7,9 +7,9 @@ */ assert("Dictu is great!".contains("is")); -assert(!("Dictu is great!".contains("test"))); -assert(!("Dictu is great!".contains("12345"))); -assert(!("Dictu is great!".contains("@£$%"))); +assert(not ("Dictu is great!".contains("test"))); +assert(not ("Dictu is great!".contains("12345"))); +assert(not ("Dictu is great!".contains("@£$%"))); assert("Dictu is great!".contains("!")); assert("1Dictu is great!1".contains("1")); assert(("1Dictu " + "is great!").contains("1")); \ No newline at end of file diff --git a/tests/strings/endsWith.du b/tests/strings/endsWith.du index b1797243..91a15aef 100644 --- a/tests/strings/endsWith.du +++ b/tests/strings/endsWith.du @@ -9,9 +9,9 @@ assert("test".endsWith("t")); assert("test".endsWith("st")); assert("test".endsWith("test")); -assert(!("test".endsWith("e"))); -assert(!("test".endsWith("he"))); -assert(!("test".endsWith("hello"))); -assert(!("test".endsWith("1234"))); -assert(!("test".endsWith("!@£$%"))); -assert(!("test".endsWith("123456789"))); \ No newline at end of file +assert(not ("test".endsWith("e"))); +assert(not ("test".endsWith("he"))); +assert(not ("test".endsWith("hello"))); +assert(not ("test".endsWith("1234"))); +assert(not ("test".endsWith("!@£$%"))); +assert(not ("test".endsWith("123456789"))); \ No newline at end of file diff --git a/tests/strings/split.du b/tests/strings/split.du index c28deb29..065008f6 100644 --- a/tests/strings/split.du +++ b/tests/strings/split.du @@ -14,3 +14,21 @@ assert("Dictu is great!".split(",") == ["Dictu is great!"]); assert("Dictu is great!".split("12345") == ["Dictu is great!"]); assert("Dictu is great!".split("!@£$%^") == ["Dictu is great!"]); assert("Dictu is great!".split("") == ["D", "i", "c", "t", "u", " ", "i", "s", " ", "g", "r", "e", "a", "t", "!"]); + +// Test optional split amount +assert("Dictu is great!".split(" ", -2) == ["Dictu", "is", "great!"]); +assert("Dictu is great!".split(" ", -1) == ["Dictu", "is", "great!"]); +assert("Dictu is great!".split(" ", 0) == ["Dictu is great!"]); +assert("Dictu is great!".split(" ", 1) == ["Dictu", "is great!"]); +assert("Dictu is great!".split(" ", 2) == ["Dictu", "is", "great!"]); +assert("Dictu is great!".split(" ", 3) == ["Dictu", "is", "great!"]); +assert("Dictu is great!".split(" ", 4) == ["Dictu", "is", "great!"]); + +assert("Dictu is great!".split("", -2) == ["D", "i", "c", "t", "u", " ", "i", "s", " ", "g", "r", "e", "a", "t", "!"]); +assert("Dictu is great!".split("", -1) == ["D", "i", "c", "t", "u", " ", "i", "s", " ", "g", "r", "e", "a", "t", "!"]); +assert("Dictu is great!".split("", 0) == ["Dictu is great!"]); +assert("Dictu is great!".split("", 1) == ["D", "ictu is great!"]); +assert("Dictu is great!".split("", 2) == ["D", "i", "ctu is great!"]); +assert("Dictu is great!".split("", 3) == ["D", "i", "c", "tu is great!"]); +assert("Dictu is great!".split("", 4) == ["D", "i", "c", "t", "u is great!"]); + diff --git a/tests/strings/startsWith.du b/tests/strings/startsWith.du index 4bcdbd7f..4df68ba5 100644 --- a/tests/strings/startsWith.du +++ b/tests/strings/startsWith.du @@ -9,9 +9,9 @@ assert("test".startsWith("t")); assert("test".startsWith("te")); assert("test".startsWith("test")); -assert(!("test".startsWith("e"))); -assert(!("test".startsWith("he"))); -assert(!("test".startsWith("hello"))); -assert(!("test".startsWith("1234"))); -assert(!("test".startsWith("!@£$%"))); -assert(!("test".startsWith("123456789"))); +assert(not ("test".startsWith("e"))); +assert(not ("test".startsWith("he"))); +assert(not ("test".startsWith("hello"))); +assert(not ("test".startsWith("1234"))); +assert(not ("test".startsWith("!@£$%"))); +assert(not ("test".startsWith("123456789"))); diff --git a/tests/system/access.du b/tests/system/access.du index 5b3e5871..85ca120b 100644 --- a/tests/system/access.du +++ b/tests/system/access.du @@ -7,33 +7,30 @@ */ import System; -var - F_OK = System.F_OK, - X_OK = System.X_OK, - W_OK = System.W_OK, - R_OK = System.R_OK; +if (System.platform != "windows") { + const + F_OK = System.F_OK, + X_OK = System.X_OK, + W_OK = System.W_OK, + R_OK = System.R_OK; -assert(F_OK == 0); -assert(X_OK == 1); -assert(W_OK == 2); -assert(R_OK == 4); -assert(System.access("/", F_OK) == 0); -assert(System.access("/", R_OK) == 0); -assert(System.access("/", X_OK) == 0); -assert(System.access("/", W_OK) == -1); -assert(System.errno == C.EACCES); -assert(System.access("/", F_OK|R_OK|W_OK) == -1); -assert(System.errno == C.EACCES); -assert(System.access("/", F_OK|R_OK|W_OK|X_OK) == -1); -assert(System.errno == C.EACCES); -assert(System.access("/", F_OK|R_OK|X_OK) == 0); -assert(System.errno == 0); -assert(System.access(__file__, X_OK) == -1); -assert(System.errno == C.EACCES); + assert(F_OK == 0); + assert(X_OK == 1); + assert(W_OK == 2); + assert(R_OK == 4); + assert(System.access("/", F_OK).success()); + assert(System.access("/", R_OK).success()); + assert(System.access("/", X_OK).success()); + assert(not System.access("/", W_OK).success()); + assert(not System.access("/", F_OK|R_OK|W_OK).success()); + assert(not System.access("/", F_OK|R_OK|W_OK|X_OK).success()); + assert(System.access("/", F_OK|R_OK|X_OK).success()); + assert(not System.access(__file__, X_OK).success()); -import Path; + import Path; -if (!Path.exists ("/__NOT_EXISTANT__")) { - assert(System.access("/NOT_EXISTANT__", F_OK) == -1); - assert(System.errno == C.ENOENT); + if (not Path.exists ("/__NOT_EXISTANT__")) { + assert(not System.access("/NOT_EXISTANT__", F_OK).success()); + } } + diff --git a/tests/system/chmod.du b/tests/system/chmod.du new file mode 100644 index 00000000..e7a37b26 --- /dev/null +++ b/tests/system/chmod.du @@ -0,0 +1,9 @@ +/** + * chmod.du + * + * Testing the System.chmod() function + * + * chmod(). + */ +import System; + diff --git a/tests/system/chown.du b/tests/system/chown.du new file mode 100644 index 00000000..16cc734d --- /dev/null +++ b/tests/system/chown.du @@ -0,0 +1,9 @@ +/** + * chown.du + * + * Testing the System.chown() function + * + * chown(). + */ +import System; + diff --git a/tests/system/import.du b/tests/system/import.du index 0c46200a..b36c1a40 100644 --- a/tests/system/import.du +++ b/tests/system/import.du @@ -4,6 +4,7 @@ * General import file for all the System methods */ +import "access.du"; import "version.du"; import "sleep.du"; import "getCWD.du"; @@ -14,3 +15,5 @@ import "remove.du"; import "process.du"; import "mkdir.du"; import "constants.du"; +import "chmod.du"; +import "chown.du"; diff --git a/tests/system/setCWD.du b/tests/system/setCWD.du index 2e039f0b..5edeb9d0 100644 --- a/tests/system/setCWD.du +++ b/tests/system/setCWD.du @@ -21,3 +21,6 @@ assert(System.setCWD(cwd).success() == true); assert(System.getCWD().unwrap() == cwd); assert(System.setCWD("some/directory/that/doesnt/exist").success() == false); assert(System.setCWD("some/directory/that/doesnt/exist").unwrapError() == "No such file or directory"); + +// Reset +System.setCWD(cwd);