mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-10 22:50:58 +08:00
Merge pull request #39 from ThakeeNathees/style-guide
some style guides were enforced.
This commit is contained in:
commit
d74a06eacf
31
README.md
31
README.md
@ -3,9 +3,9 @@
|
||||
<img src="https://user-images.githubusercontent.com/41085900/117528974-88fa8d00-aff2-11eb-8001-183c14786362.png" width="500" >
|
||||
</p>
|
||||
|
||||
**Pocketlang** is a small (~3000 semicollons) and fast functional programming
|
||||
language written in C. It's syntactically similar to Ruby and it can be learned
|
||||
in [less than an hour](https://thakeenathees.github.io/pocketlang/getting-started-learn-in-5-minutes.html).
|
||||
**Pocketlang** is a small (~3000 semicollons) and [fast](https://github.com/ThakeeNathees/pocketlang#performance)
|
||||
functional language written in C. It's syntactically similar to Ruby and it can
|
||||
be learned [within 15 minutes](https://thakeenathees.github.io/pocketlang/getting-started-learn-in-15-minutes.html).
|
||||
Including the compiler, bytecode VM and runtime, it's a standalone executable
|
||||
with zero external dependecies just as it's self descriptive name. The pocketlang
|
||||
VM can be embedded in another hosting program very easily.
|
||||
@ -32,6 +32,19 @@ for i in 0..10
|
||||
end
|
||||
```
|
||||
|
||||
## Try It Now
|
||||
|
||||
You can [try pocketlang on your browser](https://thakeenathees.github.io/pocketlang/getting-started-try-it-now.html).
|
||||
It's a [WebAssembly](https://webassembly.org/) build of the VM compiled using [emscripten](https://emscripten.org/).
|
||||
Note that in the webassembly version of the language, some features (input, relative import, etc.) have disabled, has
|
||||
limited memory allocations, and the IO calls might be slower.
|
||||
|
||||
## Documentation
|
||||
|
||||
The pocketlang documentation is hosted at https://thakeenathees.github.io/pocketlang/ which
|
||||
is built from the `docs` branch generated by a little python script at `docs/generate.py`.
|
||||
Note that the documentations are WIP and might not be up to date.
|
||||
|
||||
## Performance
|
||||
|
||||
All the tests are ran on, Windows10 (64bit), ASUS N552VX, Intel Core i7-6700HQ 2.6GHz
|
||||
@ -45,9 +58,11 @@ directory. They were ran using a small python script in the test directory.
|
||||
|
||||
## Building From Source
|
||||
|
||||
See [build documentation](https://thakeenathees.github.io/pocketlang/getting-started-build-from-source.html#using-a-build-script)
|
||||
for using an optional build script (Makefile, batch script for MSVC, SCons found in the `build/` directory).
|
||||
It can be build from source easily without any depencency, or additional
|
||||
requirenments except for a c99 compatible compiler, (and an optional build
|
||||
system). It can be compiled with the following command.
|
||||
requirenments except for a c99 compatible compiler. It can be compiled with the
|
||||
following command.
|
||||
|
||||
#### GCC
|
||||
```
|
||||
@ -68,16 +83,14 @@ cl /Fepocket cli/*.c src/*.c /Isrc/include && rm *.obj
|
||||
5. Compile.
|
||||
|
||||
If you weren't able to compile it, please report by [opening an issue](https://github.com/ThakeeNathees/pocketlang/issues/new).
|
||||
In addition you can use some of our build scripts (Makefile, batch script for MSVC, SCons)
|
||||
in the `build/` directory. For more see [build from source docs](https://thakeenathees.github.io/pocketlang/Getting%20Started/build%20from%20source.html).
|
||||
|
||||
|
||||
## References
|
||||
- Bob Nystrom.(2021) *craftinginterpreters* [online] Available at: www.craftinginterpreters.com/ (Accessed January 2021)
|
||||
|
||||
- Leonard schütz. (2020) *Dynamic Typing and NaN Boxing* [online] Available at: https://leonardschuetz.ch/blog/nan-boxing/ (Accessed December 2020)
|
||||
- Leonard schütz.(2020) *Dynamic Typing and NaN Boxing* [online] Available at: https://leonardschuetz.ch/blog/nan-boxing/ (Accessed December 2020)
|
||||
|
||||
- Bob Nystrom.(2011) *Pratt Parsers: Expression Parsing Made Easy* [online] Avaliable at: http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/ (Accessed December 2020)
|
||||
|
||||
- Carol E. Wolf of Pace University, adapted by P. Oser. *The Shunting Yard Algorithm* [online] Available at: http://mathcenter.oxford.emory.edu/site/cs171/shuntingYardAlgorithm/ (Accessed September 2020)
|
||||
- Carol E. (Wolf of Pace University), P. Oser. *The Shunting Yard Algorithm* [online] Available at: http://mathcenter.oxford.emory.edu/site/cs171/shuntingYardAlgorithm/ (Accessed September 2020)
|
||||
|
||||
|
@ -119,7 +119,7 @@ int main(int argc, char** argv) {
|
||||
"PocketLang " PK_VERSION_STRING " (https://github.com/ThakeeNathees/pocketlang/)\n"
|
||||
"Copyright(c) 2020 - 2021 ThakeeNathees.\n"
|
||||
"Free and open source software under the terms of the MIT license.\n";
|
||||
const char* help = "Usage: pocketlang <source_path>\n";
|
||||
const char* help = "usage: pocket [-c cmd | file]\n";
|
||||
|
||||
|
||||
// TODO: implement arg parse, REPL.
|
||||
|
@ -98,7 +98,7 @@ static inline bool pathIsExists(const char* path) {
|
||||
|
||||
static void _pathGetCWD(PKVM* vm) {
|
||||
char cwd[FILENAME_MAX];
|
||||
char* res = get_cwd(cwd, sizeof(cwd)); // Check if res is NULL.
|
||||
get_cwd(cwd, sizeof(cwd)); // Check if res is NULL.
|
||||
pkReturnString(vm, cwd);
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ static void _pathAbspath(PKVM* vm) {
|
||||
if (!pkGetArgString(vm, 1, &path)) return;
|
||||
|
||||
char cwd[FILENAME_MAX];
|
||||
char* res = get_cwd(cwd, sizeof(cwd)); // Check if res is NULL.
|
||||
get_cwd(cwd, sizeof(cwd)); // Check if res is NULL.
|
||||
|
||||
char abspath[FILENAME_MAX];
|
||||
size_t len = cwk_path_get_absolute(cwd, path, abspath, sizeof(abspath));
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
from markdown import markdown
|
||||
from os.path import join
|
||||
import os, sys, shutil
|
||||
import os, sys, shutil, re
|
||||
|
||||
## TODO: This is a quick and dirty script to generate html
|
||||
## from markdown. Refactor this file in the future.
|
||||
@ -40,7 +40,7 @@ WASM_SOURCE_FILES = '''\
|
||||
PAGES = [
|
||||
('Getting-Started', [
|
||||
TRY_PAGE,
|
||||
'learn-in-5-minutes.md',
|
||||
'learn-in-15-minutes.md',
|
||||
'build-from-source.md',
|
||||
'contributing.md',
|
||||
]),
|
||||
@ -179,13 +179,15 @@ def relative_static_dir(dst):
|
||||
def path_to_content(src):
|
||||
|
||||
text = ''
|
||||
with open(src, 'r') as home_md:
|
||||
text = home_md.read()
|
||||
with open(src, 'r') as f:
|
||||
text = f.read()
|
||||
|
||||
## If html file we're done.
|
||||
if get_validated_ext(src) == '.html':
|
||||
return text
|
||||
|
||||
assert(src.endswith('.md'))
|
||||
text = custom_md_override(text)
|
||||
content = markdown(text, extensions=['codehilite', 'fenced_code'])
|
||||
|
||||
## A wakey way to inject html overrides to highlight out language
|
||||
@ -194,6 +196,17 @@ def path_to_content(src):
|
||||
## of this script.
|
||||
return custom_html_override(src, content)
|
||||
|
||||
## Inject our custom markdown text override.
|
||||
def custom_md_override(text):
|
||||
|
||||
## Add html anchor.
|
||||
for pre in ('#', '##', '###'):
|
||||
pattern = '(^' + pre + r' \s*%%(.*)%%\n)'
|
||||
for match, title in re.findall(pattern, text, flags=re.MULTILINE):
|
||||
link = title.strip().lower().replace(' ', '-')
|
||||
text = text.replace(match, f'{pre} {title} <a href="#{link}" name="{link}" class="anchor">#</a>')
|
||||
return text
|
||||
|
||||
## Inject our custom html overrides.
|
||||
def custom_html_override(src, content):
|
||||
## FIXME: I should create a pygment lexer.
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
# Build From Source
|
||||
# %% Build From Source %%
|
||||
|
||||
## Without a build script
|
||||
## %% Without a build script %%
|
||||
|
||||
It can be build from source easily without any depencency, or additional
|
||||
requirenments except for a c99 compatible compiler. And build systems are
|
||||
@ -27,14 +27,14 @@ For other compiler/IDE
|
||||
|
||||
If you weren't able to compile it, please report by [opening an issue](https://github.com/ThakeeNathees/pocketlang/issues/new).
|
||||
|
||||
## Using a build script
|
||||
## %% Using a build script %%
|
||||
|
||||
You could find some of the build script for different build system in the `build/`
|
||||
direcroty. If you don't find a scirpt for your prifered system, request on github
|
||||
by [opening an issue](https://github.com/ThakeeNathees/pocketlang/issues/new) or
|
||||
feel free to contribute one.
|
||||
|
||||
## Makefile
|
||||
## %% Makefile %%
|
||||
|
||||
```
|
||||
cd build && make
|
||||
@ -45,7 +45,7 @@ And the makefile is still need to be imporved. If you find any problems with the
|
||||
have any improvements, let us know in [github](https://github.com/ThakeeNathees/pocketlang).
|
||||
|
||||
|
||||
## Batch script with MSVC
|
||||
## %% Batch script with MSVC %%
|
||||
|
||||
```
|
||||
cd build && build
|
||||
@ -55,7 +55,7 @@ You don't need to run the script from a Visual Studio .NET Command Prompt,
|
||||
It'll search for the MSVS installation path and use it to compile. But if it
|
||||
can't, try running on VS command prompt.
|
||||
|
||||
## SCons
|
||||
## %% SCons %%
|
||||
|
||||
pocketlang mainly focused on [scons](https://www.scons.org/), and it's
|
||||
recommended to use it for contributions since it has various configurations
|
||||
|
@ -1,3 +1,3 @@
|
||||
# Contributing
|
||||
# %% Contributing %%
|
||||
|
||||
TODO
|
||||
|
@ -1,12 +1,11 @@
|
||||
|
||||
# Learn pocketlang in 5 minutes
|
||||
# %% Learn pocketlang in 15 minutes %%
|
||||
|
||||
```ruby
|
||||
|
||||
# This is a comment.
|
||||
|
||||
x = 0 # Creating a variable.
|
||||
# The value '0' is a type of number.
|
||||
|
||||
# In pocketlang statements should end with a new line
|
||||
# or a semicollon. White space characters except for new
|
||||
@ -16,13 +15,10 @@ a = 1; b = 2;
|
||||
# Data types.
|
||||
# -----------
|
||||
|
||||
# Primitive types
|
||||
null # A null type.
|
||||
true; false # Booleans.
|
||||
42; 3.14 # Numbers.
|
||||
0..10; 10..0 # Range (0..10 = 0 <= r < 10).
|
||||
|
||||
# Object types
|
||||
"hello"; 'world' # Strings (support multiline).
|
||||
[42, 'foo', null] # Lists.
|
||||
{ 'Key':'value' } # Maps.
|
@ -1,4 +1,4 @@
|
||||
|
||||
# Functions
|
||||
# %% Functions %%
|
||||
|
||||
TODO
|
||||
|
@ -1,8 +1,8 @@
|
||||
# Import statements and Modules
|
||||
# %% Import statements and Modules %%
|
||||
|
||||
Each source file in pocketlang itself is a module that can be imported in another module, which makes it easier to split and share the project. There are two major kinds of modules in pocketlang. First one is core modules which are builtin to the VM and the second one is the local modlues where it's a written script file you'll import it with the path of it.
|
||||
|
||||
## Importing a core module
|
||||
## %% Importing a core module %%
|
||||
|
||||
The import statement of the pocketlang is highly inspired from python's import syntax. Here how it looks like.
|
||||
|
||||
@ -25,7 +25,7 @@ from math import *
|
||||
|
||||
```
|
||||
|
||||
## Importing a local module
|
||||
## %% Importing a local module %%
|
||||
|
||||
Importing a local module is same as importing a core module but instead of using the module name, you have to use it's path (either relative or absolute).
|
||||
|
||||
@ -58,7 +58,7 @@ bar.fn()
|
||||
|
||||
```
|
||||
|
||||
## The module keyword.
|
||||
## %% The module keyword. %%
|
||||
|
||||
We can define a name to a modlue with the `module` keyword. The name will become the namespace for that module's functions and global variables when importing it.
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
# Variables
|
||||
# %% Variables %%
|
||||
|
||||
|
||||
TODO
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
## PocketLang is a small, fast and friendly language for scripting and embedding.
|
||||
## %% PocketLang is a small, fast and friendly language for scripting and embedding. %%
|
||||
|
||||
With mixed syntax of ruby and python, that can be learned in less than an hour, and it's easy to embed into another application for scripting.
|
||||
|
||||
|
10
docs/static/main.css
vendored
10
docs/static/main.css
vendored
@ -32,6 +32,14 @@
|
||||
background-size: 100px;
|
||||
}
|
||||
|
||||
.anchor {
|
||||
color:white;
|
||||
}
|
||||
|
||||
.anchor:hover {
|
||||
color:#ccc;
|
||||
}
|
||||
|
||||
#main {
|
||||
display: table;
|
||||
table-layout: fixed;
|
||||
@ -169,7 +177,7 @@ pre {
|
||||
}
|
||||
|
||||
code {
|
||||
background-color:#dde4e9;
|
||||
background-color:#e9e9e9;
|
||||
padding: 0 5px ;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
@ -3,21 +3,18 @@
|
||||
* Licensed under: MIT License
|
||||
*/
|
||||
|
||||
// var rs = Module.cwrap('runSource', 'number', ['string'])
|
||||
|
||||
#include <emscripten.h>
|
||||
#include <pocketlang.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <emscripten.h>
|
||||
#include "pocketlang.h"
|
||||
|
||||
// IO api functions.
|
||||
extern void js_errorPrint(int type, int line, const char* message);
|
||||
extern void js_writeFunction(const char* message);
|
||||
extern const char* js_loadScript();
|
||||
|
||||
void errorPrint(PKVM* vm, PkErrorType type, const char* file, int line,
|
||||
const char* message) {
|
||||
// No need to pass file (since there is only script that'll ever run on the
|
||||
// browser.
|
||||
// No need to pass the file (since there is only script that'll ever run on
|
||||
// the browser.
|
||||
js_errorPrint((int)type, line, message);
|
||||
}
|
||||
|
||||
|
@ -11,45 +11,45 @@
|
||||
// The buffer "template" implementation of diferent types, defined in the
|
||||
// buffers.h header.
|
||||
|
||||
#define DEFINE_BUFFER(M__NAME, M__NAME_L, M__TYPE) \
|
||||
void M__NAME_L##BufferInit(M__NAME##Buffer* self) { \
|
||||
self->data = NULL; \
|
||||
self->count = 0; \
|
||||
self->capacity = 0; \
|
||||
} \
|
||||
\
|
||||
void M__NAME_L##BufferClear(M__NAME##Buffer* self, \
|
||||
PKVM* vm) { \
|
||||
vmRealloc(vm, self->data, self->capacity * sizeof(M__TYPE), 0); \
|
||||
self->data = NULL; \
|
||||
self->count = 0; \
|
||||
self->capacity = 0; \
|
||||
} \
|
||||
\
|
||||
void M__NAME_L##BufferReserve(M__NAME##Buffer* self, PKVM* vm, size_t size) { \
|
||||
if (self->capacity < size) { \
|
||||
int capacity = utilPowerOf2Ceil((int)size); \
|
||||
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY; \
|
||||
self->data = (M__TYPE*)vmRealloc(vm, self->data, \
|
||||
self->capacity * sizeof(M__TYPE), capacity * sizeof(M__TYPE)); \
|
||||
self->capacity = capacity; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
void M__NAME_L##BufferFill(M__NAME##Buffer* self, PKVM* vm, \
|
||||
M__TYPE data, int count) { \
|
||||
\
|
||||
M__NAME_L##BufferReserve(self, vm, self->count + count); \
|
||||
\
|
||||
for (int i = 0; i < count; i++) { \
|
||||
self->data[self->count++] = data; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
void M__NAME_L##BufferWrite(M__NAME##Buffer* self, \
|
||||
PKVM* vm, M__TYPE data) { \
|
||||
M__NAME_L##BufferFill(self, vm, data, 1); \
|
||||
} \
|
||||
#define DEFINE_BUFFER(m_name, m_name_l, m_type) \
|
||||
void m_name_l##BufferInit(m_name##Buffer* self) { \
|
||||
self->data = NULL; \
|
||||
self->count = 0; \
|
||||
self->capacity = 0; \
|
||||
} \
|
||||
\
|
||||
void m_name_l##BufferClear(m_name##Buffer* self, \
|
||||
PKVM* vm) { \
|
||||
vmRealloc(vm, self->data, self->capacity * sizeof(m_type), 0); \
|
||||
self->data = NULL; \
|
||||
self->count = 0; \
|
||||
self->capacity = 0; \
|
||||
} \
|
||||
\
|
||||
void m_name_l##BufferReserve(m_name##Buffer* self, PKVM* vm, size_t size) { \
|
||||
if (self->capacity < size) { \
|
||||
int capacity = utilPowerOf2Ceil((int)size); \
|
||||
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY; \
|
||||
self->data = (m_type*)vmRealloc(vm, self->data, \
|
||||
self->capacity * sizeof(m_type), capacity * sizeof(m_type)); \
|
||||
self->capacity = capacity; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
void m_name_l##BufferFill(m_name##Buffer* self, PKVM* vm, \
|
||||
m_type data, int count) { \
|
||||
\
|
||||
m_name_l##BufferReserve(self, vm, self->count + count); \
|
||||
\
|
||||
for (int i = 0; i < count; i++) { \
|
||||
self->data[self->count++] = data; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
void m_name_l##BufferWrite(m_name##Buffer* self, \
|
||||
PKVM* vm, m_type data) { \
|
||||
m_name_l##BufferFill(self, vm, data, 1); \
|
||||
} \
|
||||
|
||||
void ByteBufferAddString(ByteBuffer* self, PKVM* vm, const char* str,
|
||||
uint32_t length) {
|
||||
|
@ -18,30 +18,30 @@
|
||||
// it's last capacity.
|
||||
|
||||
|
||||
#define DECLARE_BUFFER(M__NAME, M__NAME_L, M__TYPE) \
|
||||
typedef struct { \
|
||||
M__TYPE* data; \
|
||||
uint32_t count; \
|
||||
uint32_t capacity; \
|
||||
} M__NAME##Buffer; \
|
||||
\
|
||||
/* Initialize a new buffer int instance. */ \
|
||||
void M__NAME_L##BufferInit(M__NAME##Buffer* self); \
|
||||
\
|
||||
/* Clears the allocated elementes from the VM's realloc function. */ \
|
||||
void M__NAME_L##BufferClear(M__NAME##Buffer* self, PKVM* vm); \
|
||||
\
|
||||
/* Ensure the capacity is greater than [size], if not resize. */ \
|
||||
void M__NAME_L##BufferReserve(M__NAME##Buffer* self, PKVM* vm, size_t size); \
|
||||
\
|
||||
/* Fill the buffer at the end of it with provided data if the capacity */ \
|
||||
/* isn't enough using VM's realloc function. */ \
|
||||
void M__NAME_L##BufferFill(M__NAME##Buffer* self, PKVM* vm, \
|
||||
M__TYPE data, int count); \
|
||||
\
|
||||
/* Write to the buffer with provided data at the end of the buffer.*/ \
|
||||
void M__NAME_L##BufferWrite(M__NAME##Buffer* self, \
|
||||
PKVM* vm, M__TYPE data); \
|
||||
#define DECLARE_BUFFER(m_name, m_name_l, m_type) \
|
||||
typedef struct { \
|
||||
m_type* data; \
|
||||
uint32_t count; \
|
||||
uint32_t capacity; \
|
||||
} m_name##Buffer; \
|
||||
\
|
||||
/* Initialize a new buffer int instance. */ \
|
||||
void m_name_l##BufferInit(m_name##Buffer* self); \
|
||||
\
|
||||
/* Clears the allocated elementes from the VM's realloc function. */ \
|
||||
void m_name_l##BufferClear(m_name##Buffer* self, PKVM* vm); \
|
||||
\
|
||||
/* Ensure the capacity is greater than [size], if not resize. */ \
|
||||
void m_name_l##BufferReserve(m_name##Buffer* self, PKVM* vm, size_t size); \
|
||||
\
|
||||
/* Fill the buffer at the end of it with provided data if the capacity */ \
|
||||
/* isn't enough using VM's realloc function. */ \
|
||||
void m_name_l##BufferFill(m_name##Buffer* self, PKVM* vm, \
|
||||
m_type data, int count); \
|
||||
\
|
||||
/* Write to the buffer with provided data at the end of the buffer.*/ \
|
||||
void m_name_l##BufferWrite(m_name##Buffer* self, \
|
||||
PKVM* vm, m_type data); \
|
||||
|
||||
|
||||
DECLARE_BUFFER(Uint, uint, uint32_t)
|
||||
@ -57,4 +57,4 @@ void ByteBufferAddString(ByteBuffer* self, PKVM* vm, const char* str,
|
||||
|
||||
#undef DECLARE_BUFFER
|
||||
|
||||
#endif // BUFFERS_TEMPLATE_H
|
||||
#endif // BUFFERS_TEMPLATE_H
|
||||
|
10
src/common.h
10
src/common.h
@ -89,6 +89,8 @@
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define NO_OP do {} while (false)
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#ifdef _MSC_VER
|
||||
@ -111,9 +113,9 @@
|
||||
|
||||
#else
|
||||
|
||||
#define DEBUG_BREAK() ((void*)0)
|
||||
#define ASSERT(condition, message) ((void*)0)
|
||||
#define ASSERT_INDEX(index, size) ((void*)0)
|
||||
#define DEBUG_BREAK() NO_OP
|
||||
#define ASSERT(condition, message) NO_OP
|
||||
#define ASSERT_INDEX(index, size) NO_OP
|
||||
|
||||
// Reference : https://github.com/wren-lang/
|
||||
#if defined( _MSC_VER )
|
||||
@ -121,7 +123,7 @@
|
||||
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
|
||||
#define UNREACHABLE() __builtin_unreachable()
|
||||
#else
|
||||
#define UNREACHABLE() ((void*)0)
|
||||
#define UNREACHABLE() NO_OP
|
||||
#endif
|
||||
|
||||
#endif // DEBUG
|
||||
|
@ -322,7 +322,7 @@ struct Compiler {
|
||||
const char* current_char; //< Current char position in the source.
|
||||
int current_line; //< Line number of the current char.
|
||||
Token previous, current, next; //< Currently parsed tokens.
|
||||
bool has_errors; //< True if any syntex error occured at compile time.
|
||||
bool has_errors; //< True if any syntex error occured at.
|
||||
|
||||
// Current depth the compiler in (-1 means top level) 0 means function
|
||||
// level and > 0 is inner scope.
|
||||
@ -372,8 +372,8 @@ static void reportError(Compiler* compiler, const char* file, int line,
|
||||
compiler->has_errors = true;
|
||||
char message[ERROR_MESSAGE_SIZE];
|
||||
int length = vsprintf(message, fmt, args);
|
||||
ASSERT(length < ERROR_MESSAGE_SIZE, "Error message buffer should not exceed "
|
||||
"the buffer");
|
||||
__ASSERT(length < ERROR_MESSAGE_SIZE, "Error message buffer should not exceed "
|
||||
"the buffer");
|
||||
|
||||
if (vm->config.error_fn == NULL) return;
|
||||
vm->config.error_fn(vm, PK_ERROR_COMPILE, file, line, message);
|
||||
@ -465,8 +465,8 @@ static void eatString(Compiler* compiler, bool single_quote) {
|
||||
}
|
||||
|
||||
// '\0' will be added by varNewSring();
|
||||
Var string = VAR_OBJ(&newStringLength(compiler->vm, (const char*)buff.data,
|
||||
(uint32_t)buff.count)->_super);
|
||||
Var string = VAR_OBJ(newStringLength(compiler->vm, (const char*)buff.data,
|
||||
(uint32_t)buff.count));
|
||||
|
||||
byteBufferClear(&buff, compiler->vm);
|
||||
|
||||
@ -578,10 +578,11 @@ static void setNextTwoCharToken(Compiler* compiler, char c, TokenType one,
|
||||
|
||||
// Initialize the next token as the type.
|
||||
static void setNextToken(Compiler* compiler, TokenType type) {
|
||||
compiler->next.type = type;
|
||||
compiler->next.start = compiler->token_start;
|
||||
compiler->next.length = (int)(compiler->current_char - compiler->token_start);
|
||||
compiler->next.line = compiler->current_line - ((type == TK_LINE) ? 1 : 0);
|
||||
Token* next = &compiler->next;
|
||||
next->type = type;
|
||||
next->start = compiler->token_start;
|
||||
next->length = (int)(compiler->current_char - compiler->token_start);
|
||||
next->line = compiler->current_line - ((type == TK_LINE) ? 1 : 0);
|
||||
}
|
||||
|
||||
// Initialize the next token as the type and assign the value.
|
||||
@ -711,12 +712,12 @@ static void lexToken(Compiler* compiler) {
|
||||
* PARSING *
|
||||
*****************************************************************************/
|
||||
|
||||
// Returns current token type.
|
||||
// Returns current token type without lexing a new token.
|
||||
static TokenType peek(Compiler* self) {
|
||||
return self->current.type;
|
||||
}
|
||||
|
||||
// Returns next token type.
|
||||
// Returns next token type without lexing a new token.
|
||||
static TokenType peekNext(Compiler* self) {
|
||||
return self->next.type;
|
||||
}
|
||||
@ -905,7 +906,7 @@ static int emitByte(Compiler* compiler, int byte);
|
||||
static int emitShort(Compiler* compiler, int arg);
|
||||
|
||||
static void patchJump(Compiler* compiler, int addr_index);
|
||||
static void patchForward(Compiler* compiler, Fn* fn, int addr_index, int name);
|
||||
static void patchForward(Compiler* compiler, Fn* fn, int index, int name);
|
||||
|
||||
static int compilerAddConstant(Compiler* compiler, Var value);
|
||||
static int compilerGetVariable(Compiler* compiler, const char* name,
|
||||
@ -1066,15 +1067,15 @@ static void exprFunc(Compiler* compiler, bool can_assign) {
|
||||
// Local/global variables, script/native/builtin functions name.
|
||||
static void exprName(Compiler* compiler, bool can_assign) {
|
||||
|
||||
const char* name_start = compiler->previous.start;
|
||||
int name_len = compiler->previous.length;
|
||||
int name_line = compiler->previous.line;
|
||||
NameSearchResult result = compilerSearchName(compiler, name_start, name_len);
|
||||
const char* start = compiler->previous.start;
|
||||
int length = compiler->previous.length;
|
||||
int line = compiler->previous.line;
|
||||
NameSearchResult result = compilerSearchName(compiler, start, length);
|
||||
|
||||
if (result.type == NAME_NOT_DEFINED) {
|
||||
if (can_assign && match(compiler, TK_EQ)) {
|
||||
int index = compilerAddVariable(compiler, name_start, name_len,
|
||||
name_line);
|
||||
int index = compilerAddVariable(compiler, start, length,
|
||||
line);
|
||||
compileExpression(compiler);
|
||||
if (compiler->scope_depth == DEPTH_GLOBAL) {
|
||||
emitStoreVariable(compiler, index, true);
|
||||
@ -1091,10 +1092,10 @@ static void exprName(Compiler* compiler, bool can_assign) {
|
||||
if (peek(compiler) == TK_LPARAN) {
|
||||
emitOpcode(compiler, OP_PUSH_FN);
|
||||
int index = emitShort(compiler, result.index);
|
||||
compilerAddForward(compiler, index, _FN, name_start, name_len,
|
||||
name_line);
|
||||
compilerAddForward(compiler, index, _FN, start, length,
|
||||
line);
|
||||
} else {
|
||||
parseError(compiler, "Name '%.*s' is not defined.", name_len, name_start);
|
||||
parseError(compiler, "Name '%.*s' is not defined.", length, start);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1104,12 +1105,12 @@ static void exprName(Compiler* compiler, bool can_assign) {
|
||||
switch (result.type) {
|
||||
case NAME_LOCAL_VAR:
|
||||
case NAME_GLOBAL_VAR: {
|
||||
const bool is_global = result.type == NAME_GLOBAL_VAR;
|
||||
|
||||
if (can_assign && matchAssignment(compiler)) {
|
||||
TokenType assignment = compiler->previous.type;
|
||||
if (assignment != TK_EQ) {
|
||||
emitPushVariable(compiler, result.index,
|
||||
result.type == NAME_GLOBAL_VAR);
|
||||
emitPushVariable(compiler, result.index, is_global);
|
||||
compileExpression(compiler);
|
||||
|
||||
switch (assignment) {
|
||||
@ -1126,10 +1127,10 @@ static void exprName(Compiler* compiler, bool can_assign) {
|
||||
compileExpression(compiler);
|
||||
}
|
||||
|
||||
emitStoreVariable(compiler, result.index, result.type == NAME_GLOBAL_VAR);
|
||||
emitStoreVariable(compiler, result.index, is_global);
|
||||
|
||||
} else {
|
||||
emitPushVariable(compiler, result.index, result.type == NAME_GLOBAL_VAR);
|
||||
emitPushVariable(compiler, result.index, is_global);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -1607,14 +1608,6 @@ static void emitOpcode(Compiler* compiler, Opcode opcode) {
|
||||
}
|
||||
}
|
||||
|
||||
// Emits a constant value if it doesn't exists on the current script it'll
|
||||
// make one.
|
||||
static void emitConstant(Compiler* compiler, Var value) {
|
||||
int index = compilerAddConstant(compiler, value);
|
||||
emitOpcode(compiler, OP_PUSH_CONSTANT);
|
||||
emitShort(compiler, index);
|
||||
}
|
||||
|
||||
// Update the jump offset.
|
||||
static void patchJump(Compiler* compiler, int addr_index) {
|
||||
int offset = (int)_FN->opcodes.count - (addr_index + 2 /*bytes index*/);
|
||||
@ -1624,9 +1617,9 @@ static void patchJump(Compiler* compiler, int addr_index) {
|
||||
_FN->opcodes.data[addr_index + 1] = offset & 0xff;
|
||||
}
|
||||
|
||||
static void patchForward(Compiler* compiler, Fn* fn, int addr_index, int name) {
|
||||
fn->opcodes.data[addr_index] = (name >> 8) & 0xff;
|
||||
fn->opcodes.data[addr_index + 1] = name & 0xff;
|
||||
static void patchForward(Compiler* compiler, Fn* fn, int index, int name) {
|
||||
fn->opcodes.data[index] = (name >> 8) & 0xff;
|
||||
fn->opcodes.data[index + 1] = name & 0xff;
|
||||
}
|
||||
|
||||
// Jump back to the start of the loop.
|
||||
@ -1781,7 +1774,8 @@ static Script* importFile(Compiler* compiler, const char* path) {
|
||||
// Resolve the path.
|
||||
pkStringPtr resolved = { path, NULL, NULL };
|
||||
if (vm->config.resolve_path_fn != NULL) {
|
||||
resolved = vm->config.resolve_path_fn(vm, compiler->script->path->data, path);
|
||||
resolved = vm->config.resolve_path_fn(vm, compiler->script->path->data,
|
||||
path);
|
||||
}
|
||||
|
||||
if (resolved.string == NULL) {
|
||||
@ -1796,7 +1790,7 @@ static Script* importFile(Compiler* compiler, const char* path) {
|
||||
if (resolved.on_done != NULL) resolved.on_done(vm, resolved);
|
||||
|
||||
// Check if the script already exists.
|
||||
Var entry = mapGet(vm->scripts, VAR_OBJ(&path_name->_super));
|
||||
Var entry = mapGet(vm->scripts, VAR_OBJ(path_name));
|
||||
if (!IS_UNDEF(entry)) {
|
||||
ASSERT(AS_OBJ(entry)->type == OBJ_SCRIPT, OOPS);
|
||||
|
||||
@ -1823,8 +1817,7 @@ static Script* importFile(Compiler* compiler, const char* path) {
|
||||
// Make a new script and to compile it.
|
||||
Script* scr = newScript(vm, path_name);
|
||||
vmPushTempRef(vm, &scr->_super); // scr.
|
||||
mapSet(vm, vm->scripts, VAR_OBJ(&path_name->_super),
|
||||
VAR_OBJ(&scr->_super));
|
||||
mapSet(vm, vm->scripts, VAR_OBJ(path_name), VAR_OBJ(scr));
|
||||
vmPopTempRef(vm); // scr.
|
||||
|
||||
// Push the script on the stack.
|
||||
@ -1851,7 +1844,7 @@ static Script* importCoreLib(Compiler* compiler, const char* name_start,
|
||||
name_start, name_length);
|
||||
String* module = compiler->script->names.data[index];
|
||||
|
||||
Var entry = mapGet(compiler->vm->core_libs, VAR_OBJ(&module->_super));
|
||||
Var entry = mapGet(compiler->vm->core_libs, VAR_OBJ(module));
|
||||
if (IS_UNDEF(entry)) {
|
||||
parseError(compiler, "No module named '%s' exists.", module->data);
|
||||
return NULL;
|
||||
@ -1876,7 +1869,7 @@ static inline Script* compilerImport(Compiler* compiler) {
|
||||
|
||||
} else if (match(compiler, TK_STRING)) { //< Local library.
|
||||
Var var_path = compiler->previous.value;
|
||||
ASSERT(IS_OBJ(var_path) && AS_OBJ(var_path)->type == OBJ_STRING, OOPS);
|
||||
ASSERT(IS_OBJ_TYPE(var_path, OBJ_STRING), OOPS);
|
||||
String* path = (String*)AS_OBJ(var_path);
|
||||
return importFile(compiler, path->data);
|
||||
}
|
||||
@ -2168,19 +2161,18 @@ static void compileForStatement(Compiler* compiler) {
|
||||
|
||||
consume(compiler, TK_IN, "Expected 'in' after iterator name.");
|
||||
|
||||
// Compile and store container.
|
||||
int container = compilerAddVariable(compiler, "@container", 10, iter_line);
|
||||
// Compile and store sequence.
|
||||
compilerAddVariable(compiler, "@Sequence", 9, iter_line); // Sequence
|
||||
compileExpression(compiler);
|
||||
|
||||
// Add iterator to locals. It's an increasing integer indicating that the
|
||||
// current loop is nth starting from 0.
|
||||
int iterator = compilerAddVariable(compiler, "@iterator", 9, iter_line);
|
||||
compilerAddVariable(compiler, "@iterator", 9, iter_line); // Iterator.
|
||||
emitOpcode(compiler, OP_PUSH_0);
|
||||
|
||||
// Add the iteration value. It'll be updated to each element in an array of
|
||||
// each character in a string etc.
|
||||
int iter_value = compilerAddVariable(compiler, iter_name, iter_len,
|
||||
iter_line);
|
||||
compilerAddVariable(compiler, iter_name, iter_len, iter_line); // Iter value.
|
||||
emitOpcode(compiler, OP_PUSH_NULL);
|
||||
|
||||
// Start the iteration, and check if the sequence is iterable.
|
||||
@ -2363,7 +2355,8 @@ bool compile(PKVM* vm, Script* script, const char* source) {
|
||||
if (index != -1) {
|
||||
patchForward(compiler, forward->func, forward->instruction, index);
|
||||
} else {
|
||||
resolveError(compiler, forward->line, "Name '%.*s' is not defined.", length, name);
|
||||
resolveError(compiler, forward->line, "Name '%.*s' is not defined.",
|
||||
length, name);
|
||||
}
|
||||
}
|
||||
|
||||
|
96
src/core.c
96
src/core.c
@ -29,7 +29,7 @@ static void moduleAddFunctionInternal(PKVM* vm, Script* script,
|
||||
// pkNewModule implementation (see pocketlang.h for description).
|
||||
PkHandle* pkNewModule(PKVM* vm, const char* name) {
|
||||
Script* module = newModuleInternal(vm, name);
|
||||
return vmNewHandle(vm, VAR_OBJ(&module->_super));
|
||||
return vmNewHandle(vm, VAR_OBJ(module));
|
||||
}
|
||||
|
||||
// pkModuleAddFunction implementation (see pocketlang.h for description).
|
||||
@ -38,8 +38,7 @@ void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name,
|
||||
__ASSERT(module != NULL, "Argument module was NULL.");
|
||||
|
||||
Var scr = module->value;
|
||||
__ASSERT(IS_OBJ(scr) && AS_OBJ(scr)->type == OBJ_SCRIPT,
|
||||
"Given handle is not a module");
|
||||
__ASSERT(IS_OBJ_TYPE(scr, OBJ_SCRIPT), "Given handle is not a module");
|
||||
|
||||
moduleAddFunctionInternal(vm, (Script*)AS_OBJ(scr), name, fptr, arity);
|
||||
}
|
||||
@ -63,11 +62,13 @@ void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name,
|
||||
} while (false)
|
||||
|
||||
// Check for errors in before calling the get arg public api function.
|
||||
#define CHECK_GET_ARG_API_ERRORS() \
|
||||
__ASSERT(vm->fiber != NULL, "This function can only be called at runtime."); \
|
||||
__ASSERT(arg > 0 || arg <= ARGC, "Invalid argument index."); \
|
||||
__ASSERT(value != NULL, "Parameter [value] was NULL."); \
|
||||
((void*)0)
|
||||
#define CHECK_GET_ARG_API_ERRORS() \
|
||||
do { \
|
||||
__ASSERT(vm->fiber != NULL, \
|
||||
"This function can only be called at runtime."); \
|
||||
__ASSERT(arg > 0 || arg <= ARGC, "Invalid argument index."); \
|
||||
__ASSERT(value != NULL, "Parameter [value] was NULL."); \
|
||||
} while (false)
|
||||
|
||||
// Set error for incompatible type provided as an argument.
|
||||
#define ERR_INVALID_ARG_TYPE(m_type) \
|
||||
@ -79,13 +80,13 @@ do { \
|
||||
} while (false)
|
||||
|
||||
// pkGetArgc implementation (see pocketlang.h for description).
|
||||
int pkGetArgc(PKVM* vm) {
|
||||
int pkGetArgc(const PKVM* vm) {
|
||||
__ASSERT(vm->fiber != NULL, "This function can only be called at runtime.");
|
||||
return ARGC;
|
||||
}
|
||||
|
||||
// pkGetArg implementation (see pocketlang.h for description).
|
||||
PkVar pkGetArg(PKVM* vm, int arg) {
|
||||
PkVar pkGetArg(const PKVM* vm, int arg) {
|
||||
__ASSERT(vm->fiber != NULL, "This function can only be called at runtime.");
|
||||
__ASSERT(arg > 0 || arg <= ARGC, "Invalid argument index.");
|
||||
|
||||
@ -125,7 +126,7 @@ bool pkGetArgString(PKVM* vm, int arg, const char** value) {
|
||||
CHECK_GET_ARG_API_ERRORS();
|
||||
|
||||
Var val = ARG(arg);
|
||||
if (IS_OBJ(val) && AS_OBJ(val)->type == OBJ_STRING) {
|
||||
if (IS_OBJ_TYPE(val, OBJ_STRING)) {
|
||||
*value = ((String*)AS_OBJ(val))->data;
|
||||
|
||||
} else {
|
||||
@ -169,12 +170,12 @@ void pkReturnNumber(PKVM* vm, double value) {
|
||||
|
||||
// pkReturnString implementation (see pocketlang.h for description).
|
||||
void pkReturnString(PKVM* vm, const char* value) {
|
||||
RET(VAR_OBJ(&newString(vm, value)->_super));
|
||||
RET(VAR_OBJ(newString(vm, value)));
|
||||
}
|
||||
|
||||
// pkReturnStringLength implementation (see pocketlang.h for description).
|
||||
void pkReturnStringLength(PKVM* vm, const char* value, size_t length) {
|
||||
RET(VAR_OBJ(&newStringLength(vm, value, (uint32_t)length)->_super));
|
||||
RET(VAR_OBJ(newStringLength(vm, value, (uint32_t)length)));
|
||||
}
|
||||
|
||||
// pkReturnValue implementation (see pocketlang.h for description).
|
||||
@ -255,7 +256,7 @@ static inline bool validateIndex(PKVM* vm, int32_t index, int32_t size,
|
||||
/*****************************************************************************/
|
||||
|
||||
// findBuiltinFunction implementation (see core.h for description).
|
||||
int findBuiltinFunction(PKVM* vm, const char* name, uint32_t length) {
|
||||
int findBuiltinFunction(const PKVM* vm, const char* name, uint32_t length) {
|
||||
for (int i = 0; i < vm->builtins_count; i++) {
|
||||
if (length == vm->builtins[i].length &&
|
||||
strncmp(name, vm->builtins[i].name, length) == 0) {
|
||||
@ -266,22 +267,22 @@ int findBuiltinFunction(PKVM* vm, const char* name, uint32_t length) {
|
||||
}
|
||||
|
||||
// getBuiltinFunction implementation (see core.h for description).
|
||||
Function* getBuiltinFunction(PKVM* vm, int index) {
|
||||
Function* getBuiltinFunction(const PKVM* vm, int index) {
|
||||
ASSERT_INDEX(index, vm->builtins_count);
|
||||
return vm->builtins[index].fn;
|
||||
}
|
||||
|
||||
// getBuiltinFunctionName implementation (see core.h for description).
|
||||
const char* getBuiltinFunctionName(PKVM* vm, int index) {
|
||||
const char* getBuiltinFunctionName(const PKVM* vm, int index) {
|
||||
ASSERT_INDEX(index, vm->builtins_count);
|
||||
return vm->builtins[index].name;
|
||||
}
|
||||
|
||||
// getCoreLib implementation (see core.h for description).
|
||||
Script* getCoreLib(PKVM* vm, String* name) {
|
||||
Var lib = mapGet(vm->core_libs, VAR_OBJ(&name->_super));
|
||||
Script* getCoreLib(const PKVM* vm, String* name) {
|
||||
Var lib = mapGet(vm->core_libs, VAR_OBJ(name));
|
||||
if (IS_UNDEF(lib)) return NULL;
|
||||
ASSERT(IS_OBJ(lib) && AS_OBJ(lib)->type == OBJ_SCRIPT, OOPS);
|
||||
ASSERT(IS_OBJ_TYPE(lib, OBJ_SCRIPT), OOPS);
|
||||
return (Script*)AS_OBJ(lib);
|
||||
}
|
||||
|
||||
@ -294,14 +295,14 @@ Script* getCoreLib(PKVM* vm, String* name) {
|
||||
RET(VAR_BOOL(check(ARG1))); \
|
||||
}
|
||||
|
||||
#define FN_IS_OBJ_TYPE(name, _enum) \
|
||||
void coreIs##name(PKVM* vm) { \
|
||||
Var arg1 = ARG1; \
|
||||
if (IS_OBJ(arg1) && AS_OBJ(arg1)->type == _enum) { \
|
||||
RET(VAR_TRUE); \
|
||||
} else { \
|
||||
RET(VAR_FALSE); \
|
||||
} \
|
||||
#define FN_IS_OBJ_TYPE(name, _enum) \
|
||||
void coreIs##name(PKVM* vm) { \
|
||||
Var arg1 = ARG1; \
|
||||
if (IS_OBJ_TYPE(arg1, _enum)) { \
|
||||
RET(VAR_TRUE); \
|
||||
} else { \
|
||||
RET(VAR_FALSE); \
|
||||
} \
|
||||
}
|
||||
|
||||
FN_IS_PRIMITE_TYPE(Null, IS_NULL)
|
||||
@ -319,7 +320,7 @@ FN_IS_OBJ_TYPE(UserObj, OBJ_USER)
|
||||
PK_DOC(coreTypeName,
|
||||
"type_name(value:var) -> string\n"
|
||||
"Returns the type name of the of the value.") {
|
||||
RET(VAR_OBJ(&newString(vm, varTypeName(ARG1))->_super));
|
||||
RET(VAR_OBJ(newString(vm, varTypeName(ARG1))));
|
||||
}
|
||||
|
||||
PK_DOC(coreAssert,
|
||||
@ -353,7 +354,7 @@ PK_DOC(coreAssert,
|
||||
PK_DOC(coreToString,
|
||||
"to_string(value:var) -> string\n"
|
||||
"Returns the string representation of the value.") {
|
||||
RET(VAR_OBJ(&toString(vm, ARG1)->_super));
|
||||
RET(VAR_OBJ(toString(vm, ARG1)));
|
||||
}
|
||||
|
||||
PK_DOC(corePrint,
|
||||
@ -369,7 +370,7 @@ PK_DOC(corePrint,
|
||||
for (int i = 1; i <= ARGC; i++) {
|
||||
Var arg = ARG(i);
|
||||
// If it's already a string don't allocate a new string instead use it.
|
||||
if (IS_OBJ(arg) && AS_OBJ(arg)->type == OBJ_STRING) {
|
||||
if (IS_OBJ_TYPE(arg, OBJ_STRING)) {
|
||||
str = (String*)AS_OBJ(arg);
|
||||
} else {
|
||||
str = toString(vm, arg);
|
||||
@ -396,7 +397,7 @@ PK_DOC(coreStrLower,
|
||||
// Since the string is modified re-hash it.
|
||||
result->hash = utilHashString(result->data);
|
||||
|
||||
RET(VAR_OBJ(&result->_super));
|
||||
RET(VAR_OBJ(result));
|
||||
}
|
||||
|
||||
PK_DOC(coreStrUpper,
|
||||
@ -411,7 +412,7 @@ PK_DOC(coreStrUpper,
|
||||
// Since the string is modified re-hash it.
|
||||
result->hash = utilHashString(result->data);
|
||||
|
||||
RET(VAR_OBJ(&result->_super));
|
||||
RET(VAR_OBJ(result));
|
||||
}
|
||||
|
||||
PK_DOC(coreStrStrip,
|
||||
@ -423,12 +424,12 @@ PK_DOC(coreStrStrip,
|
||||
|
||||
const char* start = str->data;
|
||||
while (*start && isspace(*start)) start++;
|
||||
if (*start == '\0') RET(VAR_OBJ(&newStringLength(vm, NULL, 0)->_super));
|
||||
if (*start == '\0') RET(VAR_OBJ(newStringLength(vm, NULL, 0)));
|
||||
|
||||
const char* end = str->data + str->length - 1;
|
||||
while (isspace(*end)) end--;
|
||||
|
||||
RET(VAR_OBJ(&newStringLength(vm, start, (uint32_t)(end - start + 1))->_super));
|
||||
RET(VAR_OBJ(newStringLength(vm, start, (uint32_t)(end - start + 1))));
|
||||
}
|
||||
|
||||
PK_DOC(coreStrChr,
|
||||
@ -438,7 +439,7 @@ PK_DOC(coreStrChr,
|
||||
if (!validateInteger(vm, ARG1, &num, "Argument 1")) return;
|
||||
|
||||
char c = (char)num;
|
||||
RET(VAR_OBJ(&newStringLength(vm, &c, 1)->_super));
|
||||
RET(VAR_OBJ(newStringLength(vm, &c, 1)));
|
||||
}
|
||||
|
||||
PK_DOC(coreStrOrd,
|
||||
@ -466,7 +467,7 @@ PK_DOC(coreListAppend,
|
||||
Var elem = ARG(2);
|
||||
|
||||
varBufferWrite(&list->elements, vm, elem);
|
||||
RET(VAR_OBJ(&list->_super));
|
||||
RET(VAR_OBJ(list));
|
||||
}
|
||||
|
||||
// Map functions.
|
||||
@ -496,7 +497,7 @@ static Script* newModuleInternal(PKVM* vm, const char* name) {
|
||||
|
||||
// Check if any module with the same name already exists and assert to the
|
||||
// hosting application.
|
||||
if (!IS_UNDEF(mapGet(vm->core_libs, VAR_OBJ(&_name->_super)))) {
|
||||
if (!IS_UNDEF(mapGet(vm->core_libs, VAR_OBJ(_name)))) {
|
||||
vmPopTempRef(vm); // _name
|
||||
__ASSERT(false, stringFormat(vm, "A module named '$' already exists",
|
||||
name)->data);
|
||||
@ -508,7 +509,7 @@ static Script* newModuleInternal(PKVM* vm, const char* name) {
|
||||
|
||||
// Add the script to core_libs.
|
||||
vmPushTempRef(vm, &scr->_super);
|
||||
mapSet(vm, vm->core_libs, VAR_OBJ(&_name->_super), VAR_OBJ(&scr->_super));
|
||||
mapSet(vm, vm->core_libs, VAR_OBJ(_name), VAR_OBJ(scr));
|
||||
vmPopTempRef(vm);
|
||||
|
||||
return scr;
|
||||
@ -569,7 +570,7 @@ void stdLangWrite(PKVM* vm) {
|
||||
for (int i = 1; i <= ARGC; i++) {
|
||||
Var arg = ARG(i);
|
||||
// If it's already a string don't allocate a new string instead use it.
|
||||
if (IS_OBJ(arg) && AS_OBJ(arg)->type == OBJ_STRING) {
|
||||
if (IS_OBJ_TYPE(arg, OBJ_STRING)) {
|
||||
str = (String*)AS_OBJ(arg);
|
||||
} else {
|
||||
str = toString(vm, arg);
|
||||
@ -814,8 +815,8 @@ Var varModulo(PKVM* vm, Var v1, Var v2) {
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
if (IS_OBJ(v1) && AS_OBJ(v1)->type == OBJ_STRING) {
|
||||
String* str = (String*)AS_OBJ(v1);
|
||||
if (IS_OBJ_TYPE(v1, OBJ_STRING)) {
|
||||
//const String* str = (const String*)AS_OBJ(v1);
|
||||
TODO; // "fmt" % v2.
|
||||
}
|
||||
|
||||
@ -888,9 +889,10 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
||||
case OBJ_MAP:
|
||||
{
|
||||
TODO; // Not sure should I allow this(below).
|
||||
//Var value = mapGet((Map*)obj, VAR_OBJ(&attrib->_super));
|
||||
//Var value = mapGet((Map*)obj, VAR_OBJ(attrib));
|
||||
//if (IS_UNDEF(value)) {
|
||||
// vm->fiber->error = stringFormat(vm, "Key (\"@\") not exists.", attrib);
|
||||
// vm->fiber->error = stringFormat(vm, "Key (\"@\") not exists.",
|
||||
// attrib);
|
||||
// return VAR_NULL;
|
||||
//}
|
||||
//return value;
|
||||
@ -910,7 +912,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
||||
} else {
|
||||
newList(vm, 0);
|
||||
}
|
||||
return VAR_OBJ(&list->_super);
|
||||
return VAR_OBJ(list);
|
||||
}
|
||||
|
||||
ERR_NO_ATTRIB();
|
||||
@ -1102,7 +1104,8 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
|
||||
|
||||
void varsetSubscript(PKVM* vm, Var on, Var key, Var value) {
|
||||
if (!IS_OBJ(on)) {
|
||||
vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.", varTypeName(on));
|
||||
vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.",
|
||||
varTypeName(on));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1125,7 +1128,8 @@ void varsetSubscript(PKVM* vm, Var on, Var key, Var value) {
|
||||
case OBJ_MAP:
|
||||
{
|
||||
if (IS_OBJ(key) && !isObjectHashable(AS_OBJ(key)->type)) {
|
||||
vm->fiber->error = stringFormat(vm, "$ type is not hashable.", varTypeName(key));
|
||||
vm->fiber->error = stringFormat(vm, "$ type is not hashable.",
|
||||
varTypeName(key));
|
||||
} else {
|
||||
mapSet(vm, (Map*)obj, key, value);
|
||||
}
|
||||
|
11
src/core.h
11
src/core.h
@ -14,17 +14,17 @@ void initializeCore(PKVM* vm);
|
||||
|
||||
// Find the builtin function name and returns it's index in the builtins array
|
||||
// if not found returns -1.
|
||||
int findBuiltinFunction(PKVM* vm, const char* name, uint32_t length);
|
||||
int findBuiltinFunction(const PKVM* vm, const char* name, uint32_t length);
|
||||
|
||||
// Returns the builtin function at index [index].
|
||||
Function* getBuiltinFunction(PKVM* vm, int index);
|
||||
Function* getBuiltinFunction(const PKVM* vm, int index);
|
||||
|
||||
// Returns the builtin function's name at index [index].
|
||||
const char* getBuiltinFunctionName(PKVM* vm, int index);
|
||||
const char* getBuiltinFunctionName(const PKVM* vm, int index);
|
||||
|
||||
// Return the core library with the [name] if exists in the core libs,
|
||||
// otherwise returns NULL.
|
||||
Script* getCoreLib(PKVM* vm, String* name);
|
||||
Script* getCoreLib(const PKVM* vm, String* name);
|
||||
|
||||
/*****************************************************************************/
|
||||
/* OPERATORS */
|
||||
@ -42,7 +42,8 @@ bool varLesser(Var v1, Var v2); // Returns v1 < v2.
|
||||
// Returns the attribute named [attrib] on the variable [on].
|
||||
Var varGetAttrib(PKVM* vm, Var on, String* attrib);
|
||||
|
||||
// Set the attribute named [attrib] on the variable [on] with the given [value].
|
||||
// Set the attribute named [attrib] on the variable [on] with the given
|
||||
// [value].
|
||||
void varSetAttrib(PKVM* vm, Var on, String* name, Var value);
|
||||
|
||||
// Returns the subscript value (ie. on[key]).
|
||||
|
@ -70,7 +70,7 @@ static void _dumpValue(PKVM* vm, Var value, bool recursive) {
|
||||
bool first = true;
|
||||
for (uint32_t i = 0; i < map->capacity; i++) {
|
||||
if (IS_UNDEF(map->entries[i].key)) continue;
|
||||
if (!first) printf(", "); first = false;
|
||||
if (!first) { printf(", "); first = false; }
|
||||
|
||||
_dumpValue(vm, map->entries[i].key, true);
|
||||
printf(":");
|
||||
|
@ -63,7 +63,7 @@ extern "C" {
|
||||
// }
|
||||
//
|
||||
#define PK_DOC(func, doc) \
|
||||
static char __pkdoc__##func[] = doc; void func(PKVM* vm)
|
||||
/* TODO: static char __pkdoc__##func[] = doc;*/ void func(PKVM* vm)
|
||||
|
||||
/*****************************************************************************/
|
||||
/* POCKETLANG TYPES */
|
||||
@ -179,7 +179,7 @@ PK_PUBLIC void pkFreeVM(PKVM* vm);
|
||||
PK_PUBLIC void pkSetUserData(PKVM* vm, void* user_data);
|
||||
|
||||
// Returns the associated user data.
|
||||
PK_PUBLIC void* pkGetUserData(PKVM* vm);
|
||||
PK_PUBLIC void* pkGetUserData(const PKVM* vm);
|
||||
|
||||
// Create a new handle for the [value]. This is usefull to keep the [value]
|
||||
// alive once it aquired from the stack. Do not use the [value] once creating
|
||||
@ -189,7 +189,7 @@ PK_PUBLIC PkHandle* pkNewHandle(PKVM* vm, PkVar value);
|
||||
|
||||
// Return the PkVar pointer in the handle, the returned pointer will be valid
|
||||
// till the handle is released.
|
||||
PK_PUBLIC PkVar pkGetHandleValue(PkHandle* handle);
|
||||
PK_PUBLIC PkVar pkGetHandleValue(const PkHandle* handle);
|
||||
|
||||
// Release the handle and allow it's value to be garbage collected. Always call
|
||||
// this for every handles before freeing the VM.
|
||||
@ -251,17 +251,17 @@ PK_PUBLIC void pkSetRuntimeError(PKVM* vm, const char* message);
|
||||
|
||||
// Return the type of the [value] this will help to get the type of the
|
||||
// variable that was extracted from pkGetArg() earlier.
|
||||
PK_PUBLIC PkVarType pkGetValueType(PkVar value);
|
||||
PK_PUBLIC PkVarType pkGetValueType(const PkVar value);
|
||||
|
||||
// Return the current functions argument count. This is needed for functions
|
||||
// registered with -1 argument count (which means variadic arguments).
|
||||
PK_PUBLIC int pkGetArgc(PKVM* vm);
|
||||
PK_PUBLIC int pkGetArgc(const PKVM* vm);
|
||||
|
||||
// Return the [arg] th argument as a PkVar. This pointer will only be
|
||||
// valid till the current function ends, because it points to the var at the
|
||||
// stack and it'll popped when the current call frame ended. Use handlers to
|
||||
// keep the var alive even after that.
|
||||
PK_PUBLIC PkVar pkGetArg(PKVM* vm, int arg);
|
||||
PK_PUBLIC PkVar pkGetArg(const PKVM* vm, int arg);
|
||||
|
||||
// The below functions are used to extract the function arguments from the
|
||||
// stack as a type. They will first validate the argument's type and set a
|
||||
@ -296,7 +296,7 @@ PK_PUBLIC PkHandle* pkNewStringLength(PKVM* vm, const char* value, size_t len);
|
||||
PK_PUBLIC PkHandle* pkNewList(PKVM* vm);
|
||||
PK_PUBLIC PkHandle* pkNewMap(PKVM* vm);
|
||||
|
||||
PK_PUBLIC const char* pkStringGetData(PkVar value);
|
||||
PK_PUBLIC const char* pkStringGetData(const PkVar value);
|
||||
|
||||
|
||||
// TODO:
|
||||
|
@ -120,4 +120,4 @@ int utf8_encodeValue(int value, uint8_t* bytes);
|
||||
// value.
|
||||
int utf8_decodeBytes(uint8_t* bytes, int* value);
|
||||
|
||||
#endif // UTF8_H
|
||||
#endif // UTF8_H
|
||||
|
103
src/var.c
103
src/var.c
@ -9,21 +9,21 @@
|
||||
#include "utils.h"
|
||||
#include "vm.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
/* PUBLIC API */
|
||||
/*****************************************************************************/
|
||||
/*****************************************************************************/
|
||||
/* PUBLIC API */
|
||||
/*****************************************************************************/
|
||||
|
||||
PkVarType pkGetValueType(PkVar value) {
|
||||
PkVarType pkGetValueType(const PkVar value) {
|
||||
__ASSERT(value != NULL, "Given value was NULL.");
|
||||
|
||||
if (IS_NULL(*(Var*)(value))) return PK_NULL;
|
||||
if (IS_BOOL(*(Var*)(value))) return PK_BOOL;
|
||||
if (IS_NUM(*(Var*)(value))) return PK_NUMBER;
|
||||
if (IS_NULL(*(const Var*)(value))) return PK_NULL;
|
||||
if (IS_BOOL(*(const Var*)(value))) return PK_BOOL;
|
||||
if (IS_NUM(*(const Var*)(value))) return PK_NUMBER;
|
||||
|
||||
__ASSERT(IS_OBJ(*(Var*)(value)),
|
||||
__ASSERT(IS_OBJ(*(const Var*)(value)),
|
||||
"Invalid var pointer. Might be a dangling pointer");
|
||||
|
||||
Object* obj = AS_OBJ(*(Var*)(value));
|
||||
const Object* obj = AS_OBJ(*(const Var*)(value));
|
||||
switch (obj->type) {
|
||||
case OBJ_STRING: return PK_STRING;
|
||||
case OBJ_LIST: return PK_LIST;
|
||||
@ -40,26 +40,24 @@ PkVarType pkGetValueType(PkVar value) {
|
||||
}
|
||||
|
||||
PkHandle* pkNewString(PKVM* vm, const char* value) {
|
||||
return vmNewHandle(vm, VAR_OBJ(&newString(vm, value)->_super));
|
||||
return vmNewHandle(vm, VAR_OBJ(newString(vm, value)));
|
||||
}
|
||||
|
||||
PkHandle* pkNewStringLength(PKVM* vm, const char* value, size_t len) {
|
||||
return vmNewHandle(vm, VAR_OBJ(&newStringLength(vm, value,
|
||||
(uint32_t)len)->_super));
|
||||
return vmNewHandle(vm, VAR_OBJ(newStringLength(vm, value, (uint32_t)len)));
|
||||
}
|
||||
|
||||
PkHandle* pkNewList(PKVM* vm) {
|
||||
return vmNewHandle(vm, VAR_OBJ(&newList(vm, MIN_CAPACITY)->_super));
|
||||
return vmNewHandle(vm, VAR_OBJ(newList(vm, MIN_CAPACITY)));
|
||||
}
|
||||
|
||||
PkHandle* pkNewMap(PKVM* vm) {
|
||||
return vmNewHandle(vm, VAR_OBJ(&newMap(vm)->_super));
|
||||
return vmNewHandle(vm, VAR_OBJ(newMap(vm)));
|
||||
}
|
||||
|
||||
const char* pkStringGetData(PkVar value) {
|
||||
Var str = (*(Var*)value);
|
||||
__ASSERT(IS_OBJ(str) && AS_OBJ(str)->type == OBJ_STRING,
|
||||
"Value should be of type string.");
|
||||
const char* pkStringGetData(const PkVar value) {
|
||||
const Var str = (*(const Var*)value);
|
||||
__ASSERT(IS_OBJ_TYPE(str, OBJ_STRING), "Value should be of type string.");
|
||||
return ((String*)AS_OBJ(str))->data;
|
||||
}
|
||||
|
||||
@ -75,9 +73,9 @@ const char* pkStringGetData(PkVar value) {
|
||||
// but take more memory.
|
||||
#define MAP_LOAD_PERCENT 75
|
||||
|
||||
// The factor a collection would grow by when it's exceeds the current capacity.
|
||||
// The new capacity will be calculated by multiplying it's old capacity by the
|
||||
// GROW_FACTOR.
|
||||
// The factor a collection would grow by when it's exceeds the current
|
||||
// capacity. The new capacity will be calculated by multiplying it's old
|
||||
// capacity by the GROW_FACTOR.
|
||||
#define GROW_FACTOR 2
|
||||
|
||||
void varInitObject(Object* self, PKVM* vm, ObjectType type) {
|
||||
@ -161,31 +159,31 @@ static void blackenObject(Object* obj, PKVM* vm) {
|
||||
|
||||
case OBJ_SCRIPT:
|
||||
{
|
||||
Script* script = (Script*)obj;
|
||||
Script* scr = (Script*)obj;
|
||||
vm->bytes_allocated += sizeof(Script);
|
||||
|
||||
grayObject(vm, &script->path->_super);
|
||||
grayObject(vm, &script->moudle->_super);
|
||||
grayObject(vm, &scr->path->_super);
|
||||
grayObject(vm, &scr->moudle->_super);
|
||||
|
||||
grayVarBuffer(vm, &script->globals);
|
||||
vm->bytes_allocated += sizeof(Var) * script->globals.capacity;
|
||||
grayVarBuffer(vm, &scr->globals);
|
||||
vm->bytes_allocated += sizeof(Var) * scr->globals.capacity;
|
||||
|
||||
// Integer buffer have no gray call.
|
||||
vm->bytes_allocated += sizeof(uint32_t) * script->global_names.capacity;
|
||||
vm->bytes_allocated += sizeof(uint32_t) * scr->global_names.capacity;
|
||||
|
||||
grayVarBuffer(vm, &script->literals);
|
||||
vm->bytes_allocated += sizeof(Var) * script->literals.capacity;
|
||||
grayVarBuffer(vm, &scr->literals);
|
||||
vm->bytes_allocated += sizeof(Var) * scr->literals.capacity;
|
||||
|
||||
grayFunctionBuffer(vm, &script->functions);
|
||||
vm->bytes_allocated += sizeof(Function*) * script->functions.capacity;
|
||||
grayFunctionBuffer(vm, &scr->functions);
|
||||
vm->bytes_allocated += sizeof(Function*) * scr->functions.capacity;
|
||||
|
||||
// Integer buffer have no gray call.
|
||||
vm->bytes_allocated += sizeof(uint32_t) * script->function_names.capacity;
|
||||
vm->bytes_allocated += sizeof(uint32_t) * scr->function_names.capacity;
|
||||
|
||||
grayStringBuffer(vm, &script->names);
|
||||
vm->bytes_allocated += sizeof(String*) * script->names.capacity;
|
||||
grayStringBuffer(vm, &scr->names);
|
||||
vm->bytes_allocated += sizeof(String*) * scr->names.capacity;
|
||||
|
||||
grayObject(vm, &script->body->_super);
|
||||
grayObject(vm, &scr->body->_super);
|
||||
} break;
|
||||
|
||||
case OBJ_FUNC:
|
||||
@ -217,7 +215,7 @@ static void blackenObject(Object* obj, PKVM* vm) {
|
||||
|
||||
// Blacken call frames.
|
||||
for (int i = 0; i < fiber->frame_count; i++) {
|
||||
grayObject(vm, &fiber->frames[i].fn->_super);
|
||||
grayObject(vm, (Object*)&fiber->frames[i].fn->_super);
|
||||
grayObject(vm, &fiber->frames[i].fn->owner->_super);
|
||||
}
|
||||
vm->bytes_allocated += sizeof(CallFrame) * fiber->frame_capacity;
|
||||
@ -793,11 +791,11 @@ bool isObjectHashable(ObjectType type) {
|
||||
struct OuterSequence {
|
||||
struct OuterSequence* outer;
|
||||
// If false it'll be map. If we have multiple sequence we should use an enum
|
||||
// here but we only ever support list and map as builtin sequence (thus bool).
|
||||
// here but we only ever support list and map as builtin sequence (so bool).
|
||||
bool is_list;
|
||||
union {
|
||||
List* list;
|
||||
Map* map;
|
||||
const List* list;
|
||||
const Map* map;
|
||||
};
|
||||
};
|
||||
typedef struct OuterSequence OuterSequence;
|
||||
@ -821,12 +819,12 @@ static void toStringInternal(PKVM* vm, Var v, ByteBuffer* buff,
|
||||
|
||||
} else if (IS_OBJ(v)) {
|
||||
|
||||
Object* obj = AS_OBJ(v);
|
||||
const Object* obj = AS_OBJ(v);
|
||||
switch (obj->type) {
|
||||
|
||||
case OBJ_STRING:
|
||||
{
|
||||
String* str = (String*)obj;
|
||||
const String* str = (const String*)obj;
|
||||
if (outer == NULL) {
|
||||
ByteBufferAddString(buff, vm, str->data, str->length);
|
||||
return;
|
||||
@ -841,7 +839,7 @@ static void toStringInternal(PKVM* vm, Var v, ByteBuffer* buff,
|
||||
|
||||
case OBJ_LIST:
|
||||
{
|
||||
List* list = (List*)obj;
|
||||
const List* list = (const List*)obj;
|
||||
if (list->elements.count == 0) {
|
||||
ByteBufferAddString(buff, vm, "[]", 2);
|
||||
return;
|
||||
@ -872,7 +870,7 @@ static void toStringInternal(PKVM* vm, Var v, ByteBuffer* buff,
|
||||
|
||||
case OBJ_MAP:
|
||||
{
|
||||
Map* map = (Map*)obj;
|
||||
const Map* map = (const Map*)obj;
|
||||
if (map->entries == NULL) {
|
||||
ByteBufferAddString(buff, vm, "{}", 2);
|
||||
return;
|
||||
@ -898,7 +896,7 @@ static void toStringInternal(PKVM* vm, Var v, ByteBuffer* buff,
|
||||
do {
|
||||
|
||||
// Get the next valid key index.
|
||||
bool _done = false; //< To break from inner loop if we don't have anymore keys.
|
||||
bool _done = false;
|
||||
while (IS_UNDEF(map->entries[i].key)) {
|
||||
if (++i >= map->capacity) {
|
||||
_done = true;
|
||||
@ -916,18 +914,20 @@ static void toStringInternal(PKVM* vm, Var v, ByteBuffer* buff,
|
||||
toStringInternal(vm, map->entries[i].value, buff, &seq_map);
|
||||
i++;
|
||||
} while (i < map->capacity);
|
||||
|
||||
byteBufferWrite(buff, vm, '}');
|
||||
return;
|
||||
}
|
||||
|
||||
case OBJ_RANGE:
|
||||
{
|
||||
Range* range = (Range*)obj;
|
||||
const Range* range = (const Range*)obj;
|
||||
|
||||
char buff_from[STR_NUM_BUFF_SIZE];
|
||||
int len_from = snprintf(buff_from, sizeof(buff_from), "%f", range->from);
|
||||
const int len_from = snprintf(buff_from, sizeof(buff_from), "%f",
|
||||
range->from);
|
||||
char buff_to[STR_NUM_BUFF_SIZE];
|
||||
int len_to = snprintf(buff_to, sizeof(buff_to), "%f", range->to);
|
||||
const int len_to = snprintf(buff_to, sizeof(buff_to), "%f", range->to);
|
||||
ByteBufferAddString(buff, vm, "[Range:", 7);
|
||||
ByteBufferAddString(buff, vm, buff_from, len_from);
|
||||
ByteBufferAddString(buff, vm, "..", 2);
|
||||
@ -937,10 +937,11 @@ static void toStringInternal(PKVM* vm, Var v, ByteBuffer* buff,
|
||||
}
|
||||
|
||||
case OBJ_SCRIPT: {
|
||||
Script* scr = ((Script*)obj);
|
||||
const Script* scr = (const Script*)obj;
|
||||
ByteBufferAddString(buff, vm, "[Module:", 8);
|
||||
if (scr->moudle != NULL) {
|
||||
ByteBufferAddString(buff, vm, scr->moudle->data, scr->moudle->length);
|
||||
ByteBufferAddString(buff, vm, scr->moudle->data,
|
||||
scr->moudle->length);
|
||||
} else {
|
||||
byteBufferWrite(buff, vm, '"');
|
||||
ByteBufferAddString(buff, vm, scr->path->data, scr->path->length);
|
||||
@ -951,9 +952,9 @@ static void toStringInternal(PKVM* vm, Var v, ByteBuffer* buff,
|
||||
}
|
||||
|
||||
case OBJ_FUNC: {
|
||||
Function* func = (Function*)obj;
|
||||
const Function* fn = (const Function*)obj;
|
||||
ByteBufferAddString(buff, vm, "[Func:", 6);
|
||||
ByteBufferAddString(buff, vm, func->name, (uint32_t)strlen(func->name));
|
||||
ByteBufferAddString(buff, vm, fn->name, (uint32_t)strlen(fn->name));
|
||||
byteBufferWrite(buff, vm, ']');
|
||||
return;
|
||||
}
|
||||
|
18
src/var.h
18
src/var.h
@ -113,7 +113,8 @@
|
||||
#define VAR_BOOL(value) ((value)? VAR_TRUE : VAR_FALSE)
|
||||
#define VAR_INT(value) (_MASK_INTEGER | (uint32_t)(int32_t)(value))
|
||||
#define VAR_NUM(value) (doubleToVar(value))
|
||||
#define VAR_OBJ(value) ((Var)(_MASK_OBJECT | (uint64_t)(uintptr_t)(value)))
|
||||
#define VAR_OBJ(value) /* [value] is an instance of Object */ \
|
||||
((Var)(_MASK_OBJECT | (uint64_t)(uintptr_t)(&value->_super)))
|
||||
|
||||
// Const casting.
|
||||
#define ADD_CONST(value) ((value) | _MASK_CONST)
|
||||
@ -129,6 +130,7 @@
|
||||
#define IS_INT(value) ((value & _MASK_INTEGER) == _MASK_INTEGER)
|
||||
#define IS_NUM(value) ((value & _MASK_QNAN) != _MASK_QNAN)
|
||||
#define IS_OBJ(value) ((value & _MASK_OBJECT) == _MASK_OBJECT)
|
||||
#define IS_OBJ_TYPE(obj, obj_type) IS_OBJ(obj) && AS_OBJ(obj)->type == obj_type
|
||||
|
||||
// Decode types.
|
||||
#define AS_BOOL(value) ((value) == VAR_TRUE)
|
||||
@ -294,9 +296,9 @@ struct Function {
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t* ip; //< Pointer to the next instruction byte code.
|
||||
Function* fn; //< Function of the frame.
|
||||
Var* rbp; //< Stack base pointer. (%rbp)
|
||||
const uint8_t* ip; //< Pointer to the next instruction byte code.
|
||||
const Function* fn; //< Function of the frame.
|
||||
Var* rbp; //< Stack base pointer. (%rbp)
|
||||
} CallFrame;
|
||||
|
||||
struct Fiber {
|
||||
@ -338,7 +340,9 @@ struct Fiber {
|
||||
String* error;
|
||||
};
|
||||
|
||||
// Methods ////////////////////////////////////////////////////////////////////
|
||||
/*****************************************************************************/
|
||||
/* METHODS */
|
||||
/*****************************************************************************/
|
||||
|
||||
// Initialize the object with it's default value.
|
||||
void varInitObject(Object* self, PKVM* vm, ObjectType type);
|
||||
@ -423,7 +427,9 @@ Var mapRemoveKey(PKVM* vm, Map* self, Var key);
|
||||
// Release all the object owned by the [self] including itself.
|
||||
void freeObject(PKVM* vm, Object* self);
|
||||
|
||||
// Utility functions //////////////////////////////////////////////////////////
|
||||
/*****************************************************************************/
|
||||
/* UTILITY FUNCTIONS */
|
||||
/*****************************************************************************/
|
||||
|
||||
// Returns the type name of the PkVarType enum value.
|
||||
const char* getPkVarTypeName(PkVarType type);
|
||||
|
121
src/vm.c
121
src/vm.c
@ -97,7 +97,7 @@ void pkFreeVM(PKVM* self) {
|
||||
DEALLOCATE(self, self);
|
||||
}
|
||||
|
||||
void* pkGetUserData(PKVM* vm) {
|
||||
void* pkGetUserData(const PKVM* vm) {
|
||||
return vm->config.user_data;
|
||||
}
|
||||
|
||||
@ -109,7 +109,7 @@ PkHandle* pkNewHandle(PKVM* vm, PkVar value) {
|
||||
return vmNewHandle(vm, *((Var*)value));
|
||||
}
|
||||
|
||||
PkVar pkGetHandleValue(PkHandle* handle) {
|
||||
PkVar pkGetHandleValue(const PkHandle* handle) {
|
||||
return (PkVar)&handle->value;
|
||||
}
|
||||
|
||||
@ -164,7 +164,8 @@ void vmPushTempRef(PKVM* self, Object* obj) {
|
||||
}
|
||||
|
||||
void vmPopTempRef(PKVM* self) {
|
||||
ASSERT(self->temp_reference_count > 0, "Temporary reference is empty to pop.");
|
||||
ASSERT(self->temp_reference_count > 0,
|
||||
"Temporary reference is empty to pop.");
|
||||
self->temp_reference_count--;
|
||||
}
|
||||
|
||||
@ -189,8 +190,8 @@ void vmCollectGarbage(PKVM* self) {
|
||||
}
|
||||
|
||||
// Mark the handles.
|
||||
for (PkHandle* handle = self->handles; handle != NULL; handle = handle->next) {
|
||||
grayValue(self, handle->value);
|
||||
for (PkHandle* h = self->handles; h != NULL; h = h->next) {
|
||||
grayValue(self, h->value);
|
||||
}
|
||||
|
||||
// Garbage collection triggered at the middle of a compilation.
|
||||
@ -253,7 +254,7 @@ static void* defaultRealloc(void* memory, size_t new_size, void* user_data) {
|
||||
}
|
||||
|
||||
static inline Script* getScript(PKVM* vm, String* path) {
|
||||
Var scr = mapGet(vm->scripts, VAR_OBJ(&path->_super));
|
||||
Var scr = mapGet(vm->scripts, VAR_OBJ(path));
|
||||
if (IS_UNDEF(scr)) return NULL;
|
||||
ASSERT(AS_OBJ(scr)->type == OBJ_SCRIPT, OOPS);
|
||||
return (Script*)AS_OBJ(scr);
|
||||
@ -261,8 +262,8 @@ static inline Script* getScript(PKVM* vm, String* path) {
|
||||
|
||||
// If failed to resolve it'll return false. Parameter [result] should be points
|
||||
// to the string which is the path that has to be resolved and once it resolved
|
||||
// the provided result's string's on_done() will be called and, it's string will
|
||||
// be updated with the new resolved path string.
|
||||
// the provided result's string's on_done() will be called and, it's string
|
||||
// will be updated with the new resolved path string.
|
||||
static inline bool resolveScriptPath(PKVM* vm, pkStringPtr* path_string) {
|
||||
if (vm->config.resolve_path_fn == NULL) return true;
|
||||
|
||||
@ -274,7 +275,7 @@ static inline bool resolveScriptPath(PKVM* vm, pkStringPtr* path_string) {
|
||||
// fiber == NULL => vm haven't started yet and it's a root script.
|
||||
resolved = vm->config.resolve_path_fn(vm, NULL, path);
|
||||
} else {
|
||||
Function* fn = fiber->frames[fiber->frame_count - 1].fn;
|
||||
const Function* fn = fiber->frames[fiber->frame_count - 1].fn;
|
||||
resolved = vm->config.resolve_path_fn(vm, fn->owner->path->data, path);
|
||||
}
|
||||
|
||||
@ -292,10 +293,10 @@ static inline Var importScript(PKVM* vm, String* path_name) {
|
||||
|
||||
// Check in the core libs.
|
||||
Script* scr = getCoreLib(vm, path_name);
|
||||
if (scr != NULL) return VAR_OBJ(&scr->_super);
|
||||
if (scr != NULL) return VAR_OBJ(scr);
|
||||
|
||||
// Check in the scripts cache.
|
||||
Var entry = mapGet(vm->scripts, VAR_OBJ(&path_name->_super));
|
||||
Var entry = mapGet(vm->scripts, VAR_OBJ(path_name));
|
||||
if (!IS_UNDEF(entry)) {
|
||||
ASSERT(AS_OBJ(entry)->type == OBJ_SCRIPT, OOPS);
|
||||
return entry;
|
||||
@ -325,20 +326,20 @@ static inline void growStack(PKVM* vm, int size) {
|
||||
// If we reached here that means the stack is moved by the reallocation and
|
||||
// we have to update all the pointers that pointing to the old stack slots.
|
||||
|
||||
/*
|
||||
' '
|
||||
' ' ' '
|
||||
' ' | | <new_rsp
|
||||
old_rsp> | | | |
|
||||
| | .----> | value | <new_ptr
|
||||
| | | | |
|
||||
old_ptr> | value | ------' |________| <new_rbp
|
||||
| | ^ new stack
|
||||
old_rbp> |________| | height
|
||||
old stack
|
||||
|
||||
new_ptr = new_rbp + height
|
||||
= fiber->stack + ( old_ptr - old_rbp ) */
|
||||
//
|
||||
// ' '
|
||||
// ' ' ' '
|
||||
// ' ' | | <new_rsp
|
||||
// old_rsp> | | | |
|
||||
// | | .----> | value | <new_ptr
|
||||
// | | | | |
|
||||
// old_ptr> | value | ------' |________| <new_rbp
|
||||
// | | ^ new stack
|
||||
// old_rbp> |________| | height
|
||||
// old stack
|
||||
//
|
||||
// new_ptr = new_rbp + height
|
||||
// = fiber->stack + ( old_ptr - old_rbp )
|
||||
#define MAP_PTR(old_ptr) (fiber->stack + ((old_ptr) - old_rbp))
|
||||
|
||||
// Update the stack top pointer and the return pointer.
|
||||
@ -352,7 +353,7 @@ static inline void growStack(PKVM* vm, int size) {
|
||||
}
|
||||
}
|
||||
|
||||
static inline void pushCallFrame(PKVM* vm, Function* fn) {
|
||||
static inline void pushCallFrame(PKVM* vm, const Function* fn) {
|
||||
ASSERT(!fn->is_native, "Native function shouldn't use call frames.");
|
||||
|
||||
/* Grow the stack frame if needed. */
|
||||
@ -389,7 +390,7 @@ void vmReportError(PKVM* vm) {
|
||||
vm->config.error_fn(vm, PK_ERROR_RUNTIME, NULL, -1, fiber->error->data);
|
||||
for (int i = fiber->frame_count - 1; i >= 0; i--) {
|
||||
CallFrame* frame = &fiber->frames[i];
|
||||
Function* fn = frame->fn;
|
||||
const Function* fn = frame->fn;
|
||||
ASSERT(!fn->is_native, OOPS);
|
||||
int line = fn->fn->oplines.data[frame->ip - fn->fn->opcodes.data - 1];
|
||||
vm->config.error_fn(vm, PK_ERROR_STACKTRACE, fn->owner->path->data, line,
|
||||
@ -410,8 +411,7 @@ static inline PkInterpretResult interpretSource(PKVM* vm, pkStringPtr source,
|
||||
if (scr == NULL) {
|
||||
scr = newScript(vm, path_name);
|
||||
vmPushTempRef(vm, &scr->_super); // scr.
|
||||
mapSet(vm, vm->scripts, VAR_OBJ(&path_name->_super),
|
||||
VAR_OBJ(&scr->_super));
|
||||
mapSet(vm, vm->scripts, VAR_OBJ(path_name), VAR_OBJ(scr));
|
||||
vmPopTempRef(vm); // scr.
|
||||
}
|
||||
vmPopTempRef(vm); // path_name.
|
||||
@ -474,7 +474,7 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
_script->initialized = true;
|
||||
|
||||
// Reference to the instruction pointer in the call frame.
|
||||
register uint8_t** ip;
|
||||
register const uint8_t** ip;
|
||||
#define IP (*ip) // Convinent macro to the instruction pointer.
|
||||
|
||||
register Var* rbp; //< Stack base pointer register.
|
||||
@ -550,7 +550,7 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
dumpStackFrame(vm); \
|
||||
} while (false)
|
||||
#else
|
||||
#define DEBUG_CALL_STACK() ((void*)0)
|
||||
#define DEBUG_CALL_STACK() NO_OP
|
||||
#endif
|
||||
|
||||
#define SWITCH() Opcode instruction; switch (instruction = (Opcode)READ_BYTE())
|
||||
@ -599,14 +599,14 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
OPCODE(PUSH_LIST):
|
||||
{
|
||||
List* list = newList(vm, (uint32_t)READ_SHORT());
|
||||
PUSH(VAR_OBJ(&list->_super));
|
||||
PUSH(VAR_OBJ(list));
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
OPCODE(PUSH_MAP):
|
||||
{
|
||||
Map* map = newMap(vm);
|
||||
PUSH(VAR_OBJ(&map->_super));
|
||||
PUSH(VAR_OBJ(map));
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -614,9 +614,9 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
{
|
||||
Var elem = PEEK(-1); // Don't pop yet, we need the reference for gc.
|
||||
Var list = PEEK(-2);
|
||||
ASSERT(IS_OBJ(list) && AS_OBJ(list)->type == OBJ_LIST, OOPS);
|
||||
ASSERT(IS_OBJ_TYPE(list, OBJ_LIST), OOPS);
|
||||
varBufferWrite(&((List*)AS_OBJ(list))->elements, vm, elem);
|
||||
POP(); // elem
|
||||
DROP(); // elem
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -626,15 +626,16 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
Var key = PEEK(-2); // Don't pop yet, we need the reference for gc.
|
||||
Var on = PEEK(-3);
|
||||
|
||||
ASSERT(IS_OBJ(on) && AS_OBJ(on)->type == OBJ_MAP, OOPS);
|
||||
ASSERT(IS_OBJ_TYPE(on, OBJ_MAP), OOPS);
|
||||
|
||||
if (IS_OBJ(key) && !isObjectHashable(AS_OBJ(key)->type)) {
|
||||
RUNTIME_ERROR(stringFormat(vm, "$ type is not hashable.", varTypeName(key)));
|
||||
RUNTIME_ERROR(stringFormat(vm, "$ type is not hashable.",
|
||||
varTypeName(key)));
|
||||
}
|
||||
mapSet(vm, (Map*)AS_OBJ(on), key, value);
|
||||
|
||||
POP(); // value
|
||||
POP(); // key
|
||||
DROP(); // value
|
||||
DROP(); // key
|
||||
|
||||
DISPATCH();
|
||||
}
|
||||
@ -702,7 +703,7 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
uint16_t index = READ_SHORT();
|
||||
ASSERT(index < script->functions.count, OOPS);
|
||||
Function* fn = script->functions.data[index];
|
||||
PUSH(VAR_OBJ(&fn->_super));
|
||||
PUSH(VAR_OBJ(fn));
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -711,7 +712,7 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
uint16_t index = READ_SHORT();
|
||||
ASSERT_INDEX(index, vm->builtins_count);
|
||||
Function* fn = vm->builtins[index].fn;
|
||||
PUSH(VAR_OBJ(&fn->_super));
|
||||
PUSH(VAR_OBJ(fn));
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -725,7 +726,7 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
Var script = importScript(vm, name);
|
||||
|
||||
// TODO: implement fiber bsed execution.
|
||||
//ASSERT(IS_OBJ(script) && AS_OBJ(script)->type == OBJ_SCRIPT, OOPS);
|
||||
//ASSERT(IS_OBJ_TYPE(script, OBJ_SCRIPT), OOPS);
|
||||
//Script* scr = (Script*)AS_OBJ(script);
|
||||
//if (!scr->initialized) vmRunScript(vm, scr);
|
||||
|
||||
@ -736,11 +737,11 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
|
||||
OPCODE(CALL):
|
||||
{
|
||||
uint16_t argc = READ_SHORT();
|
||||
const uint16_t argc = READ_SHORT();
|
||||
Var* callable = vm->fiber->sp - argc - 1;
|
||||
|
||||
if (IS_OBJ(*callable) && AS_OBJ(*callable)->type == OBJ_FUNC) {
|
||||
Function* fn = (Function*)AS_OBJ(*callable);
|
||||
if (IS_OBJ_TYPE(*callable, OBJ_FUNC)) {
|
||||
const Function* fn = (const Function*)AS_OBJ(*callable);
|
||||
|
||||
// -1 argument means multiple number of args.
|
||||
if (fn->arity != -1 && fn->arity != argc) {
|
||||
@ -946,7 +947,7 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
Var on = PEEK(-1); // Don't pop yet, we need the reference for gc.
|
||||
String* name = script->names.data[READ_SHORT()];
|
||||
Var value = varGetAttrib(vm, on, name);
|
||||
POP(); // on
|
||||
DROP(); // on
|
||||
PUSH(value);
|
||||
|
||||
CHECK_ERROR();
|
||||
@ -969,8 +970,8 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
String* name = script->names.data[READ_SHORT()];
|
||||
varSetAttrib(vm, on, name, value);
|
||||
|
||||
POP(); // value
|
||||
POP(); // on
|
||||
DROP(); // value
|
||||
DROP(); // on
|
||||
PUSH(value);
|
||||
|
||||
CHECK_ERROR();
|
||||
@ -982,8 +983,8 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
Var key = PEEK(-1); // Don't pop yet, we need the reference for gc.
|
||||
Var on = PEEK(-2); // Don't pop yet, we need the reference for gc.
|
||||
Var value = varGetSubscript(vm, on, key);
|
||||
POP(); // key
|
||||
POP(); // on
|
||||
DROP(); // key
|
||||
DROP(); // on
|
||||
PUSH(value);
|
||||
|
||||
CHECK_ERROR();
|
||||
@ -1005,9 +1006,9 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
Var key = PEEK(-2); // Don't pop yet, we need the reference for gc.
|
||||
Var on = PEEK(-3); // Don't pop yet, we need the reference for gc.
|
||||
varsetSubscript(vm, on, key, value);
|
||||
POP(); // value
|
||||
POP(); // key
|
||||
POP(); // on
|
||||
DROP(); // value
|
||||
DROP(); // key
|
||||
DROP(); // on
|
||||
PUSH(value);
|
||||
|
||||
CHECK_ERROR();
|
||||
@ -1042,7 +1043,7 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
// Don't pop yet, we need the reference for gc.
|
||||
Var r = PEEK(-1), l = PEEK(-2);
|
||||
Var value = varAdd(vm, l, r);
|
||||
POP(); POP(); // r, l
|
||||
DROP(); DROP(); // r, l
|
||||
PUSH(value);
|
||||
|
||||
CHECK_ERROR();
|
||||
@ -1054,7 +1055,7 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
// Don't pop yet, we need the reference for gc.
|
||||
Var r = PEEK(-1), l = PEEK(-2);
|
||||
Var value = varSubtract(vm, l, r);
|
||||
POP(); POP(); // r, l
|
||||
DROP(); DROP(); // r, l
|
||||
PUSH(value);
|
||||
|
||||
CHECK_ERROR();
|
||||
@ -1066,7 +1067,7 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
// Don't pop yet, we need the reference for gc.
|
||||
Var r = PEEK(-1), l = PEEK(-2);
|
||||
Var value = varMultiply(vm, l, r);
|
||||
POP(); POP(); // r, l
|
||||
DROP(); DROP(); // r, l
|
||||
PUSH(value);
|
||||
|
||||
CHECK_ERROR();
|
||||
@ -1078,7 +1079,7 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
// Don't pop yet, we need the reference for gc.
|
||||
Var r = PEEK(-1), l = PEEK(-2);
|
||||
Var value = varDivide(vm, l, r);
|
||||
POP(); POP(); // r, l
|
||||
DROP(); DROP(); // r, l
|
||||
PUSH(value);
|
||||
|
||||
CHECK_ERROR();
|
||||
@ -1090,7 +1091,7 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
// Don't pop yet, we need the reference for gc.
|
||||
Var r = PEEK(-1), l = PEEK(-2);
|
||||
Var value = varModulo(vm, l, r);
|
||||
POP(); POP(); // r, l
|
||||
DROP(); DROP(); // r, l
|
||||
PUSH(value);
|
||||
|
||||
CHECK_ERROR();
|
||||
@ -1171,8 +1172,8 @@ PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
||||
if (!IS_NUM(from) || !IS_NUM(to)) {
|
||||
RUNTIME_ERROR(newString(vm, "Range arguments must be number."));
|
||||
}
|
||||
POP(); // to
|
||||
POP(); // from
|
||||
DROP(); // to
|
||||
DROP(); // from
|
||||
PUSH(VAR_OBJ(newRange(vm, AS_NUM(from), AS_NUM(to))));
|
||||
DISPATCH();
|
||||
}
|
||||
|
2
src/vm.h
2
src/vm.h
@ -138,7 +138,7 @@ PkHandle* vmNewHandle(PKVM* self, Var value);
|
||||
// them to the working set (the gray_list). Pop the top object from the
|
||||
// working set add all of it's referenced objects to the working set and mark
|
||||
// it black (try-color marking) We'll keep doing this till the working set
|
||||
// become empty, and at this point any object which isn't marked is a garbage.
|
||||
// become empty, at this point any object which isn't marked is a garbage.
|
||||
//
|
||||
// Every single heap allocated objects will be in the VM's link list. Those
|
||||
// objects which are reachable have marked (ie. is_marked = true) once the
|
||||
|
Loading…
Reference in New Issue
Block a user