mirror of
https://github.com/CPunch/Cosmo.git
synced 2025-09-26 21:30:16 +00:00
Compare commits
49 Commits
main
...
c945c56482
Author | SHA1 | Date | |
---|---|---|---|
c945c56482 | |||
89d443d767 | |||
54a959438b | |||
355842989b | |||
45f36e6e87 | |||
819e76b711 | |||
f116efa02c | |||
465f4d5e4a | |||
3efee51224 | |||
2836de090b | |||
8e278e3a7d | |||
c8cae03604 | |||
5d805e258b | |||
8df4cc65e3 | |||
7279623e24 | |||
517b0b9532 | |||
1df2e212cb | |||
![]() |
84ec5d2aee | ||
6859ec98ad | |||
057716e0d4 | |||
b9e9dedac6 | |||
1813bbeb1b | |||
471589d379 | |||
70f931df18 | |||
c13db54d7d | |||
da85d640ce | |||
6bc4ec6b04 | |||
f92ffcecbd | |||
66d77bc54b | |||
afac75753f | |||
92b2db9678 | |||
![]() |
b30616bb3c | ||
![]() |
9e6c6038f1 | ||
![]() |
1200e2d512 | ||
2050359d2f | |||
47051575cb | |||
7c7a2ed8d9 | |||
d1ea5c9703 | |||
7c6c075c2a | |||
14539057aa | |||
461e1d0c15 | |||
2e07715a7d | |||
bc43eaaa75 | |||
43a278e12d | |||
![]() |
44f1674b09 | ||
![]() |
27aedd2969 | ||
![]() |
7caa696aa2 | ||
![]() |
2e395065f8 | ||
![]() |
5b8dc30bb8 |
26
.clang-format
Normal file
26
.clang-format
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: Mozilla
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: Right
|
||||
AlignConsecutiveMacros: AcrossEmptyLinesAndComments
|
||||
AlignConsecutiveAssignments: None
|
||||
AlignConsecutiveBitFields: None
|
||||
AlignConsecutiveDeclarations: None
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowShortEnumsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AlwaysBreakAfterReturnType: None
|
||||
BreakBeforeBraces: Mozilla
|
||||
IndentWidth: 4
|
||||
ColumnLimit: 100
|
||||
IncludeBlocks: Regroup
|
||||
IndentPPDirectives: AfterHash
|
||||
...
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
*.o
|
||||
bin
|
||||
.vscode
|
||||
CMakeFiles
|
22
CMakeLists.txt
Normal file
22
CMakeLists.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(cosmo VERSION 0.1.0 LANGUAGES C)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED True)
|
||||
set(CMAKE_DISABLE_SOURCE_CHANGES ON CACHE BOOL "Prevent writing files to CMAKE_SOURCE_DIR under configure")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY bin)
|
||||
|
||||
set (CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -Wall")
|
||||
set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fsanitize=address")
|
||||
set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -Wall")
|
||||
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT cosmo)
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
file(GLOB sources CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/src/*.c)
|
||||
add_executable(${PROJECT_NAME} main.c)
|
||||
target_sources(${PROJECT_NAME} PRIVATE ${sources})
|
||||
target_link_libraries(${PROJECT_NAME} m)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/src)
|
||||
target_compile_features(${PROJECT_NAME} PRIVATE c_std_99)
|
10
Makefile
10
Makefile
@@ -1,8 +1,8 @@
|
||||
# make clean && make && ./bin/cosmo
|
||||
|
||||
CC=clang
|
||||
CFLAGS=-fPIE -Wall -O3 -std=c11
|
||||
LDFLAGS=-lm #-fsanitize=address
|
||||
CFLAGS=-fPIE -Wall -O0 -Isrc -std=c99 -g
|
||||
LDFLAGS=-lm -fsanitize=address
|
||||
OUT=bin/cosmo
|
||||
|
||||
CHDR=\
|
||||
@@ -19,6 +19,8 @@ CHDR=\
|
||||
src/cvm.h\
|
||||
src/cobj.h\
|
||||
src/cbaselib.h\
|
||||
src/cdump.h\
|
||||
src/cundump.h\
|
||||
|
||||
CSRC=\
|
||||
src/cchunk.c\
|
||||
@@ -33,7 +35,9 @@ CSRC=\
|
||||
src/cvm.c\
|
||||
src/cobj.c\
|
||||
src/cbaselib.c\
|
||||
src/main.c\
|
||||
src/cdump.c\
|
||||
src/cundump.c\
|
||||
main.c\
|
||||
|
||||
COBJ=$(CSRC:.c=.o)
|
||||
|
||||
|
16
README.md
16
README.md
@@ -1,33 +1,35 @@
|
||||
# Cosmo
|
||||
[](https://ci.appveyor.com/project/CPunch/Cosmo)
|
||||
|
||||
Cosmo is a portable scripting language loosely based off of Lua. Cosmo easily allows the user to extend the language through the use of Proto objects, which describe the behavior of Objects. For example the following is a simple Vector Proto which describes behavior for a Vector-like object.
|
||||
|
||||
```lua
|
||||
proto Vector
|
||||
function __init(self)
|
||||
func __init(self)
|
||||
self.vector = []
|
||||
self.x = 0
|
||||
end
|
||||
|
||||
function __index(self, key)
|
||||
func __index(self, key)
|
||||
return self.vector[key]
|
||||
end
|
||||
|
||||
function push(self, val)
|
||||
func push(self, val)
|
||||
self.vector[self.x++] = val
|
||||
end
|
||||
|
||||
function pop(self)
|
||||
func pop(self)
|
||||
return self.vector[--self.x]
|
||||
end
|
||||
end
|
||||
|
||||
var vector = Vector()
|
||||
let vector = Vector()
|
||||
|
||||
for (var i = 0; i < 4; i++) do
|
||||
for (let i = 0; i < 4; i++) do
|
||||
vector:push(i)
|
||||
end
|
||||
|
||||
for (var i = 0; i < 4; i++) do
|
||||
for (let i = 0; i < 4; i++) do
|
||||
print(vector:pop() .. " : " .. vector[i])
|
||||
end
|
||||
```
|
||||
|
33
appveyor.yml
Normal file
33
appveyor.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
version: 'cosmo-0.1.{build}'
|
||||
|
||||
# we compile every commit under all branches
|
||||
#branch:
|
||||
# only:
|
||||
# - main
|
||||
|
||||
# only run CI if we changed actual code
|
||||
only_commits:
|
||||
files:
|
||||
- src/
|
||||
- main.c
|
||||
- Makefile
|
||||
- CMakeLists.txt
|
||||
- appveyor.yml
|
||||
|
||||
# images we're using
|
||||
image:
|
||||
- Ubuntu2004
|
||||
|
||||
platform:
|
||||
- x64
|
||||
|
||||
install:
|
||||
- sudo apt install clang cmake -y
|
||||
|
||||
build_script:
|
||||
- make && ./bin/cosmo examples/testsuite.cosmo examples/getters_setters.cosmo
|
||||
|
||||
artifacts:
|
||||
- path: bin
|
||||
name: ubuntu20_04-bin-x64
|
||||
type: zip
|
@@ -46,8 +46,8 @@ There are two main types of for loops, the traditional c-style for loops, and th
|
||||
The c-style for loops starts with the `for` keyword, followed by '(' and an initializer, a conditional expression, and an iterator statement each separated by a ';', followed by ')' then the `do` keyword. The loop body is ended by the matching `end` keyword. Like so:
|
||||
|
||||
```
|
||||
var total = 0
|
||||
for (var i = 0; i < 10; i++) do
|
||||
let total = 0
|
||||
for (let i = 0; i < 10; i++) do
|
||||
total = total + i
|
||||
end
|
||||
print(total)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# Introduction
|
||||
|
||||
Cosmo is a lightweight embeddable scripting language written in C11. Cosmo has comparable syntax to Lua 5.1, so if you are familiar with that syntax, learning Cosmo should be trivial. Cosmo has eccentric support for object-oriented programming, procedural programming, and functional programming. To see some examples that highlight the syntax, please see the `examples/` directory.
|
||||
Cosmo is a lightweight embeddable scripting language written in C99. Cosmo has comparable syntax to Lua 5.1, so if you are familiar with that syntax, learning Cosmo should be trivial. To see some examples that highlight the syntax, please see the `examples/` directory.
|
||||
|
||||
As Cosmo is an embeddable scripting language, it is designed to be extended by the host program (from here on referenced as 'host'.) Cosmo provides extensive C API for the host to set up the Cosmo VM, modify state, add custom Proto objects, define custom globals and more.
|
||||
|
||||
|
@@ -6,17 +6,17 @@ For example, the following is a proto description for a Range Iterator Object, m
|
||||
|
||||
```
|
||||
proto Range
|
||||
function __init(self, x)
|
||||
func __init(self, x)
|
||||
self.max = x
|
||||
end
|
||||
|
||||
// __iter expects an iterable object to be returned (an object with __next defined)
|
||||
function __iter(self)
|
||||
func __iter(self)
|
||||
self.i = 0
|
||||
return self
|
||||
end
|
||||
|
||||
function __next(self)
|
||||
func __next(self)
|
||||
if self.i >= self.max then
|
||||
return nil // exit iterator loop
|
||||
end
|
||||
@@ -50,7 +50,7 @@ When an object is called using the `()` operator, `__init` is called and a new O
|
||||
Objects hold fields, these fields can be grabbed using the '.' operator. Conversely, fields can also be set using the '.' and '=' operators. For example:
|
||||
|
||||
```
|
||||
var object = {
|
||||
let object = {
|
||||
field = "Hello world"
|
||||
}
|
||||
|
||||
@@ -63,17 +63,17 @@ Objects have two main ways of being declared, first was just shown in the above
|
||||
|
||||
```
|
||||
proto Test
|
||||
function __init(self)
|
||||
func __init(self)
|
||||
// __init is required for an object to be instantiated, the 'self' passed is the
|
||||
// newly allocated object with it's proto already set
|
||||
end
|
||||
|
||||
function print(self)
|
||||
func print(self)
|
||||
print(self)
|
||||
end
|
||||
end
|
||||
|
||||
var objTest = Test()
|
||||
let objTest = Test()
|
||||
|
||||
// the ':' operator is used to invoke a method. if the '.' operator is used instead, the
|
||||
// raw closure will be given meaning the 'self' parameter won't be populated
|
||||
@@ -94,6 +94,7 @@ that are called on special operators.
|
||||
| __init | `(<object>, ...)` | Newly crafted object is passed, called on instantiation |
|
||||
| __newindex | `(<object>, key, newValue)` | Called on new index using the '[] = ' operator |
|
||||
| __index | `(<object>, key)` -> `value` | Called on index using the '[]' operator |
|
||||
| __equal | `(<object>, <object>)` -> `<boolean>` | Called on equality fail if both protos have the same `__equal` metamethod defined |
|
||||
| __tostring | `(<object>)` -> `<string>` | Called when tostring() is called on an object |
|
||||
| __tonumber | `(<object>)` -> `<number>` | Called when tonumber() is called on an object |
|
||||
| __count | `(<object>)` -> `<number>` | Called when object is used with the '#' count operator |
|
||||
|
@@ -17,8 +17,8 @@
|
||||
| -------- | ---------------------------- | -------------------------------------- |
|
||||
| `!` | "Not" logical operator, flips the logical polarity. | `print(!true)` -> `false` |
|
||||
| `#` | "Count" calls '__count' metamethod on objects or gives the count of entries in tables | `print(#[1,2,3])` -> `3`, `print(#{__count = function(self) return self.x end, x = 1337})` -> `1337` |
|
||||
| `++` | Increment operator. | `var i = 0 print(++i .. ", " .. i++ .. ", " .. i)` -> `1, 1, 2` |
|
||||
| `--` | Decrement operator. | `var i = 0 print(--i .. ", " .. i-- .. ", " .. i)` -> `-1, -1, -2` |
|
||||
| `++` | Increment operator. | `let i = 0 print(++i .. ", " .. i++ .. ", " .. i)` -> `1, 1, 2` |
|
||||
| `--` | Decrement operator. | `let i = 0 print(--i .. ", " .. i-- .. ", " .. i)` -> `-1, -1, -2` |
|
||||
| `( ... )` | Call operator. Arguments should be separated using `,`. | `print("Hello", " ", "world!")` -> `Hello world!` |
|
||||
> -> means 'outputs'
|
||||
|
||||
|
65
docs/stdlib.md
Normal file
65
docs/stdlib.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Standard Library
|
||||
|
||||
Cosmo comes with a standard library which is broken into separate modules where each can be selectively loaded using the C API.
|
||||
|
||||
## Base Library
|
||||
|
||||
Includes misc. functions. The "junk drawer" of the standard library. Without these functions however, Cosmo would be severely limited in functionality.
|
||||
|
||||
| Name | Type | Behavior | Example |
|
||||
| ------------ | ------------------------------------------------ | ----------------------------------------------------------- | ---------------- |
|
||||
| print | `(...)` | Writes primitives to stdout, if a `<ref>` is passed, tostring() is invoked before outputting | `print("Hello world!")` |
|
||||
| type | `(<ANY>)` -> `<string>` | Returns the passed arguments datatype as a string | `type(1)` -> `"<number>"` |
|
||||
| tonumber | `(<ANY>)` -> `<number>` | Converts the datatype to a `<number>`, if a `<ref>` is passed `__tonumber` metamethod is invoked | `tonumber("12")` -> `12` |
|
||||
| tostring | `(<ANY>)` -> `<string>` | Converts the datatype to a `<string>`, if a `<ref>` is passed `__tostring` metamethod is invoked | `tostring(12)` -> `"12"` |
|
||||
| error | `(<string>)` | Throws an error with the passed `<string>` | `error("error!")` |
|
||||
| pcall | `(<callable>)` -> `<bool>, <error> or <ANY>` | Tries a protected call on the passed function, if an error is thrown, `<bool>` will be false and the 2nd result will be the error message | `pcall(error("Hello world!"))` -> `false, "Hello world!"` |
|
||||
| assert | `(<bool>, <string>)` | If the passed `<bool>` is false, an error is thrown, optionally uses custom error message | `assert(1 == 1, "Error Message!")` |
|
||||
| loadstring | `(<string>)` -> `<boolean>, <function> or <error>` | If the `<string>` compiled successfully, 1st result will be true and the 2nd result will be the newly compiled function. If there was a compiler/lexer error, the 1st result will be false and the 2nd result will be the error | `loadstring("print(\"hi\")")()` |
|
||||
> -> means 'returns'
|
||||
|
||||
## String Library
|
||||
|
||||
Includes functions and methods to manipulate strings. When this library is loaded all <string> objects have their proto's set to the string.* object. Enabling
|
||||
you to invoke the API directly on <string> objects without using `string.*`, eg. `"Hello":len()` is the same as `string.len("Hello")`.
|
||||
|
||||
| Name | Type | Behavior | Example |
|
||||
| ------------ | ------------------------------------------------ | ----------------------------------------------------------- | ---------------- |
|
||||
| string.len | `(<string>)` -> `<number>` | Returns the length of the passed string | `"hi":len()` -> `2` |
|
||||
| string.sub | `(str<string>, start<number>[, length<number>])` -> `<string>` | Makes a substring of `str` starting at `start` with length of `length or str:len() - start` | `"Hello World":sub(6)` -> `"World"` |
|
||||
| string.find | `(str<string>, find<string>[, start<number>])` -> `index<number>` | Searches for first occurrence of `find` in `str` starting at `start or 0`. Returns index or -1 if failed | `"Hello world":find("wo")` -> `6` |
|
||||
| string.split | `(str<string>, splitter<string>)` -> `[<string>, ...]` | Splits `str` into substrings using `splitter` as the splitter. Returns an array (table) starting at 0 of the strings. | `"Hello world":split(" ")` -> `["Hello", "world"]` |
|
||||
| string.byte | `(str<string>)` -> `<number>` | Returns the byte of the first character in the string. | `"A":byte()` -> `65` |
|
||||
| string.char | `(byte<number>)` -> `<string>` | Returns a 1 character string of the byte passed. | `string.char(65)` -> `"A"` |
|
||||
| string.rep | `(str<string>, times<number>)` -> `<string>` | Repeats the string and returns the newly allocated string | `("A" .. "B"):rep(2)` -> "ABAB" |
|
||||
> -> means 'returns'
|
||||
|
||||
## Math Library
|
||||
|
||||
Includes functions to do some common algebraic operations.
|
||||
|
||||
| Name | Type | Behavior | Example |
|
||||
| ------------ | ------------------------------------------------ | --------------------------------------------------- | ------------------------ |
|
||||
| math.abs | `(X<number>)` -> `<number>` | Returns the absolute value of X | `math.abs(-2)` -> `2` |
|
||||
| math.floor | `(X<number>)` -> `<number>` | Rounds down to the nearest integer | `math.floor(5.3)` -> `5` |
|
||||
| math.ceil | `(X<number>)` -> `<number>` | Rounds up to the nearest integer | `math.ceil(5.3)` -> `6` |
|
||||
| math.rad | `(Deg<number>)` -> `<number>` | Converts degrees to radians | `math.rad(180)` -> `3.14159` |
|
||||
| math.deg | `(Rad<number>)` -> `<number>` | Converts radians to degrees | `math.deg(3.14159)` -> `180` |
|
||||
| math.sin | `(Rad<number>)` -> `<number>` | Returns the sine of radian `Rad` | `math.sin(math.rad(90))` -> `1` |
|
||||
| math.cos | `(Rad<number>)` -> `<number>` | Returns the cosine of radian `Rad` | `math.cos(math.rad(180))` -> `-1` |
|
||||
| math.tan | `(Rad<number>)` -> `<number>` | Returns the tangent of radian `Rad` | `math.tan(math.rad(45))` -> `1` |
|
||||
| math.asin | `(sin<number>)` -> `<number>` | Returns the arc sine of radian `Rad` | `math.deg(math.asin(1))` -> `90` |
|
||||
| math.acos | `(cos<number>)` -> `<number>` | Returns the arc cosine of radian `Rad` | `math.deg(math.acos(-1))` -> `180` |
|
||||
| math.atan | `(tan<number>)` -> `<number>` | Returns the arc tangent of radian `Rad` | `math.deg(math.atan(1))` -> `45` |
|
||||
> -> means 'returns'
|
||||
|
||||
## OS Library
|
||||
|
||||
Includes functions that interact with the operating system.
|
||||
|
||||
| Name | Type | Behavior | Example |
|
||||
| ------------ | ------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------ |
|
||||
| os.read | `(path<string>)` -> `<string>` or `<nil>` | Returns a file's contents or nil if it doesn't exist/an error occurred | `os.read("path")` -> `Hello, World!`|
|
||||
| os.time | `()` -> `<number>` | Returns the system time in Epoch format | `os.time()` -> `1.61691e+09` |
|
||||
| os.system | `(cmd<string>)` -> `<number>` | Runs a system command as if it were a terminal and returns the exit code | `os.system("mkdir test")` -> `0` |
|
||||
> -> means 'returns'
|
@@ -17,5 +17,5 @@ There are two main types of datatypes in Cosmo, primitives and references. Primi
|
||||
| String | A string of characters | `"ABC"`, `"\x41\x42\x43"`, `"\b1000001\b1000010\b1000011"` |
|
||||
| Object | A stateful data structure. See `objects.md`. | `{x = 3}`, `proto Test end` |
|
||||
| Table | A generic data structure. | `[1,2,3]`, `[1 = "hello", "two" = "world"]` |
|
||||
| Function | A callable routine. | `function() print("Hello world!") end` |
|
||||
| func | A callable routine. | `function() print("Hello world!") end` |
|
||||
> There are some other reference datatypes that are used internally, however these will remain undocumented until they are accessible by the user
|
@@ -1,8 +1,8 @@
|
||||
// just testing continues and breaks
|
||||
|
||||
for (var x = 0; x < 700; x++) do
|
||||
for (var i = 0; true; i++) do
|
||||
var str = i .. "." .. x
|
||||
for (let x = 0; x < 700; x++) do
|
||||
for (let i = 0; true; i++) do
|
||||
let str = i .. "." .. x
|
||||
if (i == 998) then
|
||||
print(i .. " reached")
|
||||
break // exits the loop
|
||||
@@ -13,9 +13,9 @@ for (var x = 0; x < 700; x++) do
|
||||
end
|
||||
|
||||
// same example as the for loop but done manually using a while loop
|
||||
var i = 0
|
||||
let i = 0
|
||||
while true do
|
||||
var str = i .. "." .. x
|
||||
let str = i .. "." .. x
|
||||
if (i++ == 998) then
|
||||
print("done")
|
||||
break
|
||||
|
40
examples/compare.cosmo
Normal file
40
examples/compare.cosmo
Normal file
@@ -0,0 +1,40 @@
|
||||
let strtable = []
|
||||
let strLen = 4 // length of all strings to generate
|
||||
let AByte = "A":byte() // grabs the ascii value of 'A'
|
||||
|
||||
proto stringBuilder
|
||||
func __init(self, length)
|
||||
self.len = length
|
||||
end
|
||||
|
||||
// we are the iterator object lol
|
||||
func __iter(self)
|
||||
self.x = 0
|
||||
return self
|
||||
end
|
||||
|
||||
func __next(self)
|
||||
let x = self.x++
|
||||
|
||||
// if we've generated all the possible strings, return nil ending the loop
|
||||
if x >= 26 ^ self.len then
|
||||
return nil
|
||||
end
|
||||
|
||||
// generate the string
|
||||
let str = ""
|
||||
for (let i = 0; i < self.len; i++) do
|
||||
str = string.char(AByte + (x % 26)) .. str
|
||||
|
||||
x = math.floor(x / 26)
|
||||
end
|
||||
|
||||
return str
|
||||
end
|
||||
end
|
||||
|
||||
// generate a bunch of strings & populate the table
|
||||
print("generating " .. 26 ^ strLen .. " strings...")
|
||||
for str in stringBuilder(strLen) do
|
||||
strtable[str] = true
|
||||
end
|
@@ -1,4 +1,4 @@
|
||||
local function fib(num)
|
||||
local func fib(num)
|
||||
if num <= 1 then
|
||||
return num
|
||||
else
|
||||
@@ -6,6 +6,6 @@ local function fib(num)
|
||||
end
|
||||
end
|
||||
|
||||
for (var i = 1; i < 40; i++) do
|
||||
for (let i = 1; i < 40; i++) do
|
||||
print("The fib number of " .. i .. " is " .. fib(i))
|
||||
end
|
@@ -1,4 +1,4 @@
|
||||
var object = {
|
||||
let object = {
|
||||
__setter = [
|
||||
"field1" = function(self, val)
|
||||
print("setter for field1 called!")
|
||||
|
@@ -1,22 +1,22 @@
|
||||
proto Vector
|
||||
function __init(self)
|
||||
func __init(self)
|
||||
self.vector = []
|
||||
self.x = 0
|
||||
end
|
||||
|
||||
function push(self, val)
|
||||
func push(self, val)
|
||||
self.vector[self.x++] = val
|
||||
end
|
||||
|
||||
function pop(self)
|
||||
func pop(self)
|
||||
return self.vector[--self.x]
|
||||
end
|
||||
|
||||
function __index(self, key)
|
||||
func __index(self, key)
|
||||
return self.vector[key]
|
||||
end
|
||||
|
||||
function __iter(self)
|
||||
func __iter(self)
|
||||
// you don't *have* to make a new object, i just wanted to show off anonymous functions
|
||||
return {__next = (function(self)
|
||||
return self.vector[self.iterIndex++]
|
||||
@@ -27,9 +27,9 @@ proto Vector
|
||||
end
|
||||
end
|
||||
|
||||
var vector = Vector()
|
||||
let vector = Vector()
|
||||
|
||||
for (var i = 0; i < 100000; i++) do
|
||||
for (let i = 0; i < 100000; i++) do
|
||||
vector:push(i)
|
||||
end
|
||||
|
||||
|
@@ -1,14 +1,14 @@
|
||||
proto Range
|
||||
function __init(self, x)
|
||||
func __init(self, x)
|
||||
self.max = x
|
||||
end
|
||||
|
||||
function __iter(self)
|
||||
func __iter(self)
|
||||
self.i = 0
|
||||
return self
|
||||
end
|
||||
|
||||
function __next(self)
|
||||
func __next(self)
|
||||
if self.i >= self.max then
|
||||
return nil // exit iterator loop
|
||||
end
|
||||
|
@@ -1,15 +1,15 @@
|
||||
proto Test
|
||||
function __init(self, x)
|
||||
func __init(self, x)
|
||||
self.x = x
|
||||
end
|
||||
|
||||
function print(self)
|
||||
func print(self)
|
||||
print(self.x)
|
||||
end
|
||||
end
|
||||
|
||||
// stressing the GC
|
||||
for (var i = 0; ; i++) do
|
||||
var x = Test("Hello world " .. i)
|
||||
for (let i = 0; i < 100000; i++) do
|
||||
let x = Test("Hello world " .. i)
|
||||
x:print()
|
||||
end
|
@@ -1,7 +1,7 @@
|
||||
var words = "hello world! this is a sentence with words separated by space":split(" ")
|
||||
let words = "hello world! this is a sentence with words separated by space":split(" ")
|
||||
|
||||
var str = ""
|
||||
for (var i = 0; i < #words; i++) do
|
||||
let str = ""
|
||||
for (let i = 0; i < #words; i++) do
|
||||
str = str .. words[i]
|
||||
print(words[i])
|
||||
end
|
||||
|
@@ -1,10 +1,10 @@
|
||||
// crafts a dummy proto
|
||||
proto test
|
||||
function __init(self) end
|
||||
func __init(self) end
|
||||
end
|
||||
|
||||
// instance of test
|
||||
var obj = test()
|
||||
let obj = test()
|
||||
|
||||
test.__index = function(self, key)
|
||||
print("__index called!")
|
||||
|
45
examples/testsuite.cosmo
Normal file
45
examples/testsuite.cosmo
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
This script tests cosmo and makes sure everything still runs correctly. Pretty minimal for now
|
||||
*/
|
||||
|
||||
print("starting Testsuite...")
|
||||
|
||||
// tests the string.* library
|
||||
|
||||
assert("Hello world!":sub(6) == "world!", "string.sub() failed!")
|
||||
assert("A":rep(6) == "AAAAAA", "string.rep() failed!")
|
||||
|
||||
// tests some basic PEMDAS arithmetic
|
||||
|
||||
assert(2 * (2 + 6) == 16, "PEMDAS check #1 failed!")
|
||||
assert(2 / 5 + 3 / 5 == 1, "PEMDAS check #2 failed!")
|
||||
|
||||
// iterator test
|
||||
|
||||
proto Range
|
||||
func __init(self, x)
|
||||
self.max = x
|
||||
end
|
||||
|
||||
func __iter(self)
|
||||
self.i = 0
|
||||
return self
|
||||
end
|
||||
|
||||
func __next(self)
|
||||
if self.i >= self.max then
|
||||
return nil // exit iterator loop
|
||||
end
|
||||
|
||||
return self.i++
|
||||
end
|
||||
end
|
||||
|
||||
let total = 0
|
||||
for i in Range(100) do
|
||||
total = total + i
|
||||
end
|
||||
|
||||
assert(total == 4950, "Iterator check failed!")
|
||||
|
||||
print("Testsuite passed!")
|
@@ -1,27 +1,27 @@
|
||||
proto test
|
||||
function __init(self, x)
|
||||
func __init(self, x)
|
||||
self:setArg(x)
|
||||
end
|
||||
|
||||
function __tostring(self)
|
||||
var total = 1
|
||||
func __tostring(self)
|
||||
let total = 1
|
||||
|
||||
for (var i = self.x; i > 0; i = i - 1) do
|
||||
for (let i = self.x; i > 0; i = i - 1) do
|
||||
total = total * i;
|
||||
end
|
||||
|
||||
return "The factorial of " .. self.x .. " is " .. total
|
||||
end
|
||||
|
||||
function setArg(self, x)
|
||||
func setArg(self, x)
|
||||
self.x = x
|
||||
end
|
||||
end
|
||||
|
||||
var t = test(1)
|
||||
let t = test(1)
|
||||
|
||||
for (var x = 1; x < 1000; x = x + 1) do
|
||||
for (var i = 1; i < 100; i = i + 1) do
|
||||
for (let x = 1; x < 1000; x = x + 1) do
|
||||
for (let i = 1; i < 100; i = i + 1) do
|
||||
t:setArg(i)
|
||||
|
||||
print(t)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
// adds all args passed (expects numbers)
|
||||
function add(start, ...args)
|
||||
func add(start, ...args)
|
||||
// starting at `start`, add up all numbers passed
|
||||
local total = start
|
||||
for val in args do
|
||||
|
@@ -31,7 +31,9 @@ int cosmoB_input(CState *state, int nargs, CValue *args) {
|
||||
return 1; // 1 return value
|
||||
}
|
||||
|
||||
static void interpret(CState *state, const char *script, const char *mod) {
|
||||
static bool interpret(CState *state, const char *script, const char *mod) {
|
||||
bool ret;
|
||||
|
||||
// cosmoV_compileString pushes the result onto the stack (COBJ_ERROR or COBJ_CLOSURE)
|
||||
if (cosmoV_compileString(state, script, mod)) {
|
||||
COSMOVMRESULT res = cosmoV_call(state, 0, 0); // 0 args being passed, 0 results expected
|
||||
@@ -43,7 +45,9 @@ static void interpret(CState *state, const char *script, const char *mod) {
|
||||
cosmoV_printError(state, state->error);
|
||||
}
|
||||
|
||||
ret = state->panic;
|
||||
state->panic = false; // so our repl isn't broken
|
||||
return !ret;
|
||||
}
|
||||
|
||||
static void repl() {
|
||||
@@ -110,7 +114,8 @@ static char *readFile(const char* path) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void runFile(const char* fileName) {
|
||||
static bool runFile(const char* fileName) {
|
||||
bool ret;
|
||||
char* script = readFile(fileName);
|
||||
CState *state = cosmoV_newState();
|
||||
cosmoB_loadLibrary(state);
|
||||
@@ -122,10 +127,11 @@ static void runFile(const char* fileName) {
|
||||
|
||||
cosmoV_register(state, 1);
|
||||
|
||||
interpret(state, script, fileName);
|
||||
ret = interpret(state, script, fileName);
|
||||
|
||||
cosmoV_freeState(state);
|
||||
free(script);
|
||||
return ret; // let the caller know if the script failed
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
@@ -133,7 +139,9 @@ int main(int argc, const char *argv[]) {
|
||||
repl();
|
||||
} else if (argc >= 2) { // they passed a file (or more lol)
|
||||
for (int i = 1; i < argc; i++) {
|
||||
runFile(argv[i]);
|
||||
if (!runFile(argv[i])) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
491
src/cbaselib.c
491
src/cbaselib.c
@@ -1,15 +1,17 @@
|
||||
#include "cbaselib.h"
|
||||
#include "cvm.h"
|
||||
#include "cvalue.h"
|
||||
#include "cobj.h"
|
||||
|
||||
#include "cmem.h"
|
||||
#include "cobj.h"
|
||||
#include "cvalue.h"
|
||||
#include "cvm.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
// ================================================================ [BASELIB] ================================================================
|
||||
// ================================================================ [BASELIB]
|
||||
|
||||
int cosmoB_print(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_print(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
for (int i = 0; i < nargs; i++) {
|
||||
if (IS_REF(args[i])) { // if its a CObj*, generate the CObjString
|
||||
CObjString *str = cosmoV_toString(state, args[i]);
|
||||
@@ -23,25 +25,31 @@ int cosmoB_print(CState *state, int nargs, CValue *args) {
|
||||
return 0; // print doesn't return any args
|
||||
}
|
||||
|
||||
int cosmoB_assert(CState *state, int nargs, CValue *args) {
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "assert() expected 1 argument, got %d!", nargs);
|
||||
int cosmoB_assert(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs < 1 || nargs > 2) {
|
||||
cosmoV_error(state, "assert() expected 1 or 2 arguments, got %d!", nargs);
|
||||
return 0; // nothing pushed onto the stack to return
|
||||
}
|
||||
|
||||
if (!IS_BOOLEAN(args[0])) {
|
||||
if (!IS_BOOLEAN(args[0]) || (nargs == 2 && !IS_STRING(args[1]))) {
|
||||
if (nargs == 2) {
|
||||
cosmoV_typeError(state, "assert()", "<boolean>, <string>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
} else {
|
||||
cosmoV_typeError(state, "assert()", "<boolean>", "%s", cosmoV_typeStr(args[0]));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!cosmoV_readBoolean(args[0])) { // expression passed was false, error!
|
||||
cosmoV_error(state, "assert() failed!");
|
||||
} // else do nothing :)
|
||||
if (!cosmoV_readBoolean(args[0])) // expression passed was false, error!
|
||||
cosmoV_error(state, "%s", nargs == 2 ? cosmoV_readCString(args[1]) : "assert() failed!");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cosmoB_type(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_type(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "type() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@@ -52,7 +60,8 @@ int cosmoB_type(CState *state, int nargs, CValue *args) {
|
||||
return 1; // 1 return value, the type string :D
|
||||
}
|
||||
|
||||
int cosmoB_pcall(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_pcall(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs < 1) {
|
||||
cosmoV_error(state, "pcall() expected at least 1 argument!");
|
||||
return 0;
|
||||
@@ -72,7 +81,8 @@ int cosmoB_pcall(CState *state, int nargs, CValue *args) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
int cosmoB_tonumber(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_tonumber(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "tonumber() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@@ -82,7 +92,8 @@ int cosmoB_tonumber(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_tostring(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_tostring(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "tostring() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@@ -92,8 +103,9 @@ int cosmoB_tostring(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_loadstring(CState *state, int nargs, CValue *args) {
|
||||
if (nargs < 1) {
|
||||
int cosmoB_loadstring(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "loadstring() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
@@ -110,26 +122,30 @@ int cosmoB_loadstring(CState *state, int nargs, CValue *args) {
|
||||
return 2; // <boolean>, <closure> or <error>
|
||||
}
|
||||
|
||||
void cosmoB_loadLibrary(CState *state) {
|
||||
const char *identifiers[] = {
|
||||
"print",
|
||||
"assert",
|
||||
"type",
|
||||
"pcall",
|
||||
"tonumber",
|
||||
"tostring",
|
||||
"loadstring"
|
||||
};
|
||||
int cosmoB_error(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "error() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
CosmoCFunction baseLib[] = {
|
||||
cosmoB_print,
|
||||
cosmoB_assert,
|
||||
cosmoB_type,
|
||||
cosmoB_pcall,
|
||||
cosmoB_tonumber,
|
||||
cosmoB_tostring,
|
||||
cosmoB_loadstring
|
||||
};
|
||||
if (!IS_STRING(args[0])) {
|
||||
cosmoV_typeError(state, "error()", "<string>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_error(state, "%s", cosmoV_readCString(args[0]));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cosmoB_loadLibrary(CState *state)
|
||||
{
|
||||
const char *identifiers[] = {"print", "assert", "type", "pcall",
|
||||
"tonumber", "tostring", "loadstring", "error"};
|
||||
|
||||
CosmoCFunction baseLib[] = {cosmoB_print, cosmoB_assert, cosmoB_type, cosmoB_pcall,
|
||||
cosmoB_tonumber, cosmoB_tostring, cosmoB_loadstring, cosmoB_error};
|
||||
|
||||
int i;
|
||||
for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) {
|
||||
@@ -146,9 +162,10 @@ void cosmoB_loadLibrary(CState *state) {
|
||||
cosmoB_loadMathLib(state);
|
||||
}
|
||||
|
||||
// ================================================================ [OBJECT.*] ================================================================
|
||||
// ================================================================ [OBJECT.*]
|
||||
|
||||
int cosmoB_osetProto(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_osetProto(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs == 2) {
|
||||
CObj *obj = cosmoV_readRef(args[0]); // object to set proto too
|
||||
CObjObject *proto = cosmoV_readObject(args[1]);
|
||||
@@ -161,7 +178,8 @@ int cosmoB_osetProto(CState *state, int nargs, CValue *args) {
|
||||
return 0; // nothing
|
||||
}
|
||||
|
||||
int cosmoB_ogetProto(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_ogetProto(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "Expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@@ -172,14 +190,16 @@ int cosmoB_ogetProto(CState *state, int nargs, CValue *args) {
|
||||
return 1; // 1 result
|
||||
}
|
||||
|
||||
int cosmoB_oisChild(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_oisChild(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
cosmoV_error(state, "object.ischild() expected 2 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_REF(args[0]) || !IS_OBJECT(args[1])) {
|
||||
cosmoV_typeError(state, "object.ischild()", "<reference obj>, <object>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
cosmoV_typeError(state, "object.ischild()", "<reference obj>, <object>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -191,14 +211,11 @@ int cosmoB_oisChild(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
COSMO_API void cosmoB_loadObjLib(CState *state) {
|
||||
const char *identifiers[] = {
|
||||
"ischild"
|
||||
};
|
||||
COSMO_API void cosmoB_loadObjLib(CState *state)
|
||||
{
|
||||
const char *identifiers[] = {"ischild"};
|
||||
|
||||
CosmoCFunction objLib[] = {
|
||||
cosmoB_oisChild
|
||||
};
|
||||
CosmoCFunction objLib[] = {cosmoB_oisChild};
|
||||
|
||||
// make object library object
|
||||
cosmoV_pushString(state, "object");
|
||||
@@ -235,10 +252,11 @@ COSMO_API void cosmoB_loadObjLib(CState *state) {
|
||||
cosmoV_register(state, 1);
|
||||
}
|
||||
|
||||
// ================================================================ [OS.*] ================================================================
|
||||
// ================================================================ [OS.*]
|
||||
|
||||
// os.read()
|
||||
int cosmoB_osRead(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_osRead(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "os.read() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@@ -282,7 +300,8 @@ int cosmoB_osRead(CState *state, int nargs, CValue *args) {
|
||||
}
|
||||
|
||||
// os.time()
|
||||
int cosmoB_osTime(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_osTime(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
struct timeval time;
|
||||
if (nargs > 0) {
|
||||
cosmoV_error(state, "os.time() expected no arguments, got %d!", nargs);
|
||||
@@ -294,16 +313,29 @@ int cosmoB_osTime(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
COSMO_API void cosmoB_loadOSLib(CState *state) {
|
||||
const char *identifiers[] = {
|
||||
"read",
|
||||
"time"
|
||||
};
|
||||
// os.system()
|
||||
int cosmoB_osSystem(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "os.system() expects 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
CosmoCFunction osLib[] = {
|
||||
cosmoB_osRead,
|
||||
cosmoB_osTime
|
||||
};
|
||||
if (!IS_STRING(args[0])) {
|
||||
cosmoV_typeError(state, "os.system()", "<string>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// run the command and return the exit code
|
||||
cosmoV_pushNumber(state, system(cosmoV_readCString(args[0])));
|
||||
return 1;
|
||||
}
|
||||
|
||||
COSMO_API void cosmoB_loadOSLib(CState *state)
|
||||
{
|
||||
const char *identifiers[] = {"read", "time", "system"};
|
||||
|
||||
CosmoCFunction osLib[] = {cosmoB_osRead, cosmoB_osTime, cosmoB_osSystem};
|
||||
|
||||
cosmoV_pushString(state, "os");
|
||||
|
||||
@@ -317,13 +349,15 @@ COSMO_API void cosmoB_loadOSLib(CState *state) {
|
||||
cosmoV_register(state, 1); // register the os.* object to the global table
|
||||
}
|
||||
|
||||
// ================================================================ [STRING.*] ================================================================
|
||||
// ================================================================ [STRING.*]
|
||||
|
||||
// string.sub
|
||||
int cosmoB_sSub(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_sSub(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs == 2) {
|
||||
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) {
|
||||
cosmoV_typeError(state, "string.sub()", "<string>, <number>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
cosmoV_typeError(state, "string.sub()", "<string>, <number>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -332,14 +366,17 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) {
|
||||
|
||||
// make sure we stay within memory
|
||||
if (indx < 0 || indx >= str->length) {
|
||||
cosmoV_error(state, "string.sub() expected index to be 0-%d, got %d!", str->length - 1, indx);
|
||||
cosmoV_error(state, "string.sub() expected index to be 0-%d, got %d!", str->length - 1,
|
||||
indx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushLString(state, str->str + ((int)indx), str->length - ((int)indx));
|
||||
} else if (nargs == 3) {
|
||||
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1]) || !IS_NUMBER(args[2])) {
|
||||
cosmoV_typeError(state, "string.sub()", "<string>, <number>, <number>", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2]));
|
||||
cosmoV_typeError(state, "string.sub()", "<string>, <number>, <number>", "%s, %s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
|
||||
cosmoV_typeStr(args[2]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -349,7 +386,9 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) {
|
||||
|
||||
// make sure we stay within memory
|
||||
if (indx + length < 0 || indx + length >= str->length || indx < 0 || indx >= str->length) {
|
||||
cosmoV_error(state, "string.sub() expected subbed string goes out of bounds, max length is %d!", str->length);
|
||||
cosmoV_error(
|
||||
state, "string.sub() expected subbed string goes out of bounds, max length is %d!",
|
||||
str->length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -363,10 +402,12 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) {
|
||||
}
|
||||
|
||||
// string.find
|
||||
int cosmoB_sFind(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_sFind(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs == 2) {
|
||||
if (!IS_STRING(args[0]) || !IS_STRING(args[1])) {
|
||||
cosmoV_typeError(state, "string.find()", "<string>, <string>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
cosmoV_typeError(state, "string.find()", "<string>, <string>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -375,15 +416,19 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args) {
|
||||
|
||||
char *indx = strstr(str->str, ptrn->str);
|
||||
|
||||
// failed, we have nothing to return
|
||||
if (indx == NULL)
|
||||
return 0;
|
||||
// failed, return the error index -1
|
||||
if (indx == NULL) {
|
||||
cosmoV_pushNumber(state, -1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// success! push the index
|
||||
cosmoV_pushNumber(state, indx - str->str);
|
||||
} else if (nargs == 3) {
|
||||
if (!IS_STRING(args[0]) || !IS_STRING(args[1]) || !IS_NUMBER(args[2])) {
|
||||
cosmoV_typeError(state, "string.find()", "<string>, <string>, <number>", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2]));
|
||||
cosmoV_typeError(state, "string.find()", "<string>, <string>, <number>", "%s, %s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
|
||||
cosmoV_typeStr(args[2]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -393,9 +438,11 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args) {
|
||||
|
||||
char *indx = strstr(str->str + startIndx, ptrn->str);
|
||||
|
||||
// failed, we have nothing to return
|
||||
if (indx == NULL)
|
||||
return 0;
|
||||
// failed, return the error index -1
|
||||
if (indx == NULL) {
|
||||
cosmoV_pushNumber(state, -1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// success! push the index
|
||||
cosmoV_pushNumber(state, indx - str->str);
|
||||
@@ -408,14 +455,16 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args) {
|
||||
}
|
||||
|
||||
// string.split
|
||||
int cosmoB_sSplit(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_sSplit(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
cosmoV_error(state, "string.split() expected 2 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_STRING(args[0]) || !IS_STRING(args[1])) {
|
||||
cosmoV_typeError(state, "string.split()", "<string>, <string>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
cosmoV_typeError(state, "string.split()", "<string>, <string>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -431,7 +480,8 @@ int cosmoB_sSplit(CState *state, int nargs, CValue *args) {
|
||||
nIndx = strstr(indx, ptrn->str);
|
||||
|
||||
cosmoV_pushNumber(state, nEntries++);
|
||||
cosmoV_pushLString(state, indx, nIndx == NULL ? str->length - (indx - str->str) : nIndx - indx);
|
||||
cosmoV_pushLString(state, indx,
|
||||
nIndx == NULL ? str->length - (indx - str->str) : nIndx - indx);
|
||||
|
||||
indx = nIndx + ptrn->length;
|
||||
} while (nIndx != NULL);
|
||||
@@ -442,7 +492,8 @@ int cosmoB_sSplit(CState *state, int nargs, CValue *args) {
|
||||
}
|
||||
|
||||
// string.byte
|
||||
int cosmoB_sByte(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_sByte(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "string.byte() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@@ -456,8 +507,8 @@ int cosmoB_sByte(CState *state, int nargs, CValue *args) {
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
|
||||
if (str->length < 1) {
|
||||
// the length of the string is less than 1, in the future I might throw an error for this, but
|
||||
// for now im going to copy lua and just return a nil
|
||||
// the length of the string is less than 1, in the future I might throw an error for this,
|
||||
// but for now im going to copy lua and just return a nil
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -467,7 +518,8 @@ int cosmoB_sByte(CState *state, int nargs, CValue *args) {
|
||||
}
|
||||
|
||||
// string.char
|
||||
int cosmoB_sChar(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_sChar(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "string.char() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@@ -478,7 +530,8 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// small side effect of truncating the number, but ignoring the decimal instead of throwing an error is the better option imo
|
||||
// small side effect of truncating the number, but ignoring the decimal instead of throwing an
|
||||
// error is the better option imo
|
||||
int num = (int)cosmoV_readNumber(args[0]);
|
||||
char c = num;
|
||||
|
||||
@@ -487,27 +540,72 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// basically, treat the c value on the stack as an """"array"""" with a length of 1
|
||||
// basically, treat the character value on the C stack as an """"array"""" with a length of 1
|
||||
cosmoV_pushLString(state, &c, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void cosmoB_loadStrLib(CState *state) {
|
||||
const char *identifiers[] = {
|
||||
"sub",
|
||||
"find",
|
||||
"split",
|
||||
"byte",
|
||||
"char"
|
||||
};
|
||||
int cosmoB_sLen(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs < 1) {
|
||||
cosmoV_error(state, "string.len() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
CosmoCFunction strLib[] = {
|
||||
cosmoB_sSub,
|
||||
cosmoB_sFind,
|
||||
cosmoB_sSplit,
|
||||
cosmoB_sByte,
|
||||
cosmoB_sChar
|
||||
};
|
||||
if (!IS_STRING(args[0])) {
|
||||
cosmoV_typeError(state, "string.len", "<string>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, strlen(cosmoV_readCString(args[0])));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_sRep(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
cosmoV_error(state, "string.rep() expected 2 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// expects <string>, <number>
|
||||
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) {
|
||||
cosmoV_typeError(state, "string.rep", "<string>, <number>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
int times = (int)cosmoV_readNumber(args[1]);
|
||||
|
||||
if (times <= 0) {
|
||||
cosmoV_error(state, "Expected times to be > 0, got %d!", times);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// allocated the new buffer for the string
|
||||
size_t length = str->length * times;
|
||||
char *newStr = cosmoM_xmalloc(state, length + 1); // + 1 for the NULL terminator
|
||||
|
||||
// copy the string over the new buffer
|
||||
for (int i = 0; i < times; i++)
|
||||
memcpy(&newStr[i * str->length], str->str, str->length);
|
||||
|
||||
// write the NULL terminator
|
||||
newStr[length] = '\0';
|
||||
|
||||
// finally, push the resulting string onto the stack
|
||||
cosmoV_pushRef(state, (CObj *)cosmoO_takeString(state, newStr, length));
|
||||
return 1;
|
||||
}
|
||||
|
||||
void cosmoB_loadStrLib(CState *state)
|
||||
{
|
||||
const char *identifiers[] = {"sub", "find", "split", "byte", "char", "len", "rep"};
|
||||
|
||||
CosmoCFunction strLib[] = {cosmoB_sSub, cosmoB_sFind, cosmoB_sSplit, cosmoB_sByte,
|
||||
cosmoB_sChar, cosmoB_sLen, cosmoB_sRep};
|
||||
|
||||
// make string library object
|
||||
cosmoV_pushString(state, "string");
|
||||
@@ -526,10 +624,11 @@ void cosmoB_loadStrLib(CState *state) {
|
||||
cosmoV_register(state, 1);
|
||||
}
|
||||
|
||||
// ================================================================ [MATH] ================================================================
|
||||
// ================================================================ [MATH]
|
||||
|
||||
// math.abs
|
||||
int cosmoB_mAbs(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_mAbs(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.abs() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@@ -545,7 +644,8 @@ int cosmoB_mAbs(CState *state, int nargs, CValue *args) {
|
||||
}
|
||||
|
||||
// math.floor
|
||||
int cosmoB_mFloor(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_mFloor(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.floor() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@@ -561,7 +661,8 @@ int cosmoB_mFloor(CState *state, int nargs, CValue *args) {
|
||||
}
|
||||
|
||||
// math.ceil
|
||||
int cosmoB_mCeil(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_mCeil(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.ceil() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@@ -584,18 +685,144 @@ int cosmoB_mCeil(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void cosmoB_loadMathLib(CState *state) {
|
||||
const char *identifiers[] = {
|
||||
"abs",
|
||||
"floor",
|
||||
"ceil"
|
||||
};
|
||||
int cosmoB_mSin(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.sin() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
CosmoCFunction mathLib[] = {
|
||||
cosmoB_mAbs,
|
||||
cosmoB_mFloor,
|
||||
cosmoB_mCeil
|
||||
};
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.sin", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, sin(cosmoV_readNumber(args[0])));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_mCos(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.cos() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.cos", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, cos(cosmoV_readNumber(args[0])));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_mTan(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.tan() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.tan", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, tan(cosmoV_readNumber(args[0])));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_mASin(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.asin() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.asin", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, asin(cosmoV_readNumber(args[0])));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_mACos(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.acos() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.acos", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, acos(cosmoV_readNumber(args[0])));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_mATan(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.atan() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.atan", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, atan(cosmoV_readNumber(args[0])));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_mRad(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.rad() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.rad", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// convert the degree to radians
|
||||
cosmoV_pushNumber(state, cosmoV_readNumber(args[0]) * (acos(-1) / 180));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_mDeg(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.deg() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.deg", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// convert the degree to radians
|
||||
cosmoV_pushNumber(state, cosmoV_readNumber(args[0]) * (180 / acos(-1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
void cosmoB_loadMathLib(CState *state)
|
||||
{
|
||||
const char *identifiers[] = {"abs", "floor", "ceil", "sin", "cos", "tan",
|
||||
"asin", "acos", "atan", "rad", "deg"};
|
||||
|
||||
CosmoCFunction mathLib[] = {cosmoB_mAbs, cosmoB_mFloor, cosmoB_mCeil, cosmoB_mSin,
|
||||
cosmoB_mCos, cosmoB_mTan, cosmoB_mASin, cosmoB_mACos,
|
||||
cosmoB_mATan, cosmoB_mRad, cosmoB_mDeg};
|
||||
|
||||
// make math library object
|
||||
cosmoV_pushString(state, "math");
|
||||
@@ -605,29 +832,36 @@ void cosmoB_loadMathLib(CState *state) {
|
||||
cosmoV_pushCFunction(state, mathLib[i]);
|
||||
}
|
||||
|
||||
cosmoV_pushString(state, "pi");
|
||||
cosmoV_pushNumber(state, acos(-1));
|
||||
i++;
|
||||
|
||||
// make the object and register it as a global to the state
|
||||
cosmoV_makeObject(state, i);
|
||||
cosmoV_register(state, 1);
|
||||
}
|
||||
|
||||
// ================================================================ [VM.*] ================================================================
|
||||
// ================================================================ [VM.*]
|
||||
|
||||
// vm.__getter["globals"]
|
||||
int cosmoB_vgetGlobal(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_vgetGlobal(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
// this function doesn't need to check anything, just return the global table
|
||||
cosmoV_pushRef(state, (CObj *)state->globals);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// vm.__setter["globals"]
|
||||
int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
cosmoV_error(state, "Expected 2 argumenst, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_TABLE(args[1])) {
|
||||
cosmoV_typeError(state, "vm.__setter[\"globals\"]", "<object>, <table>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
cosmoV_typeError(state, "vm.__setter[\"globals\"]", "<object>, <table>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -637,14 +871,16 @@ int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cosmoB_vindexBProto(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_vindexBProto(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
cosmoV_error(state, "Expected 2 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[1])) {
|
||||
cosmoV_typeError(state, "baseProtos.__index", "<object>, <number>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
cosmoV_typeError(state, "baseProtos.__index", "<object>, <number>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -663,14 +899,17 @@ int cosmoB_vindexBProto(CState *state, int nargs, CValue *args) {
|
||||
return 1; // 1 value pushed, 1 value returned
|
||||
}
|
||||
|
||||
int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 3) {
|
||||
cosmoV_error(state, "Expected 3 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[1]) || !IS_OBJECT(args[2])) {
|
||||
cosmoV_typeError(state, "baseProtos.__newindex", "<object>, <number>, <object>", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2]));
|
||||
cosmoV_typeError(state, "baseProtos.__newindex", "<object>, <number>, <object>",
|
||||
"%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
|
||||
cosmoV_typeStr(args[2]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -687,7 +926,8 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args) {
|
||||
}
|
||||
|
||||
// vm.collect()
|
||||
int cosmoB_vcollect(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_vcollect(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
// first, unfreeze the state (we start frozen on entry to any C Function)
|
||||
cosmoM_unfreezeGC(state);
|
||||
|
||||
@@ -701,7 +941,8 @@ int cosmoB_vcollect(CState *state, int nargs, CValue *args) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cosmoB_loadVM(CState *state) {
|
||||
void cosmoB_loadVM(CState *state)
|
||||
{
|
||||
// make vm.* object
|
||||
cosmoV_pushString(state, "vm");
|
||||
|
||||
|
@@ -19,6 +19,7 @@ COSMO_API void cosmoB_loadObjLib(CState *state);
|
||||
|
||||
/* loads the os library, including:
|
||||
- os.read()
|
||||
- os.system()
|
||||
- os.time()
|
||||
*/
|
||||
COSMO_API void cosmoB_loadOSLib(CState *state);
|
||||
@@ -29,8 +30,10 @@ COSMO_API void cosmoB_loadOSLib(CState *state);
|
||||
- string.split & <string>:split()
|
||||
- string.byte & <string>:byte()
|
||||
- string.char & <string>:char()
|
||||
- string.rep & <string>:rep()
|
||||
|
||||
The base proto object for strings is also set, allowing you to invoke the string.* api through string objects, eg.
|
||||
The base proto object for strings is also set, allowing you to invoke the string.* api through
|
||||
string objects, eg.
|
||||
`"hello world":split(" ")` is equivalent to `string.split("hello world", " ")`
|
||||
*/
|
||||
COSMO_API void cosmoB_loadStrLib(CState *state);
|
||||
@@ -39,6 +42,7 @@ COSMO_API void cosmoB_loadStrLib(CState *state);
|
||||
- math.abs
|
||||
- math.floor
|
||||
- math.ceil
|
||||
- math.tan
|
||||
*/
|
||||
COSMO_API void cosmoB_loadMathLib(CState *state);
|
||||
|
||||
|
31
src/cchunk.c
31
src/cchunk.c
@@ -1,15 +1,19 @@
|
||||
#include "cmem.h"
|
||||
#include "cchunk.h"
|
||||
|
||||
#include "cmem.h"
|
||||
#include "cobj.h"
|
||||
#include "cvalue.h"
|
||||
#include "cvm.h"
|
||||
|
||||
CChunk *newChunk(CState* state, size_t startCapacity) {
|
||||
CChunk *newChunk(CState *state, size_t startCapacity)
|
||||
{
|
||||
CChunk *chunk = cosmoM_xmalloc(state, sizeof(CChunk));
|
||||
initChunk(state, chunk, startCapacity);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
void initChunk(CState* state, CChunk *chunk, size_t startCapacity) {
|
||||
void initChunk(CState *state, CChunk *chunk, size_t startCapacity)
|
||||
{
|
||||
chunk->capacity = startCapacity;
|
||||
chunk->lineCapacity = startCapacity;
|
||||
chunk->count = 0;
|
||||
@@ -20,7 +24,8 @@ void initChunk(CState* state, CChunk *chunk, size_t startCapacity) {
|
||||
initValArray(state, &chunk->constants, ARRAY_START);
|
||||
}
|
||||
|
||||
void cleanChunk(CState* state, CChunk *chunk) {
|
||||
void cleanChunk(CState *state, CChunk *chunk)
|
||||
{
|
||||
// first, free the chunk buffer
|
||||
cosmoM_freearray(state, INSTRUCTION, chunk->buf, chunk->capacity);
|
||||
// then the line info
|
||||
@@ -29,16 +34,18 @@ void cleanChunk(CState* state, CChunk *chunk) {
|
||||
cleanValArray(state, &chunk->constants);
|
||||
}
|
||||
|
||||
void freeChunk(CState* state, CChunk *chunk) {
|
||||
void freeChunk(CState *state, CChunk *chunk)
|
||||
{
|
||||
cleanChunk(state, chunk);
|
||||
// now, free the wrapper struct
|
||||
cosmoM_free(state, CChunk, chunk);
|
||||
}
|
||||
|
||||
int addConstant(CState* state, CChunk *chunk, CValue value) {
|
||||
int addConstant(CState *state, CChunk *chunk, CValue value)
|
||||
{
|
||||
// before adding the constant, check if we already have it
|
||||
for (size_t i = 0; i < chunk->constants.count; i++) {
|
||||
if (cosmoV_equal(value, chunk->constants.values[i]))
|
||||
if (cosmoV_equal(state, value, chunk->constants.values[i]))
|
||||
return i; // we already have a matching constant!
|
||||
}
|
||||
|
||||
@@ -48,9 +55,10 @@ int addConstant(CState* state, CChunk *chunk, CValue value) {
|
||||
return chunk->constants.count - 1; // return the index of the new constants
|
||||
}
|
||||
|
||||
// ================================================================ [WRITE TO CHUNK] ================================================================
|
||||
// ================================================================ [WRITE TO CHUNK]
|
||||
|
||||
void writeu8Chunk(CState* state, CChunk *chunk, INSTRUCTION i, int line) {
|
||||
void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line)
|
||||
{
|
||||
// does the buffer need to be reallocated?
|
||||
cosmoM_growarray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity);
|
||||
cosmoM_growarray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity);
|
||||
@@ -60,9 +68,10 @@ void writeu8Chunk(CState* state, CChunk *chunk, INSTRUCTION i, int line) {
|
||||
chunk->buf[chunk->count++] = i;
|
||||
}
|
||||
|
||||
void writeu16Chunk(CState* state, CChunk *chunk, uint16_t i, int line) {
|
||||
void writeu16Chunk(CState *state, CChunk *chunk, uint16_t i, int line)
|
||||
{
|
||||
static const int sz = sizeof(uint16_t) / sizeof(INSTRUCTION);
|
||||
INSTRUCTION *buffer = (INSTRUCTION *)(&i);
|
||||
int sz = sizeof(uint16_t) / sizeof(INSTRUCTION);
|
||||
|
||||
for (int i = 0; i < sz; i++) {
|
||||
writeu8Chunk(state, chunk, buffer[i], line);
|
||||
|
16
src/cchunk.h
16
src/cchunk.h
@@ -1,21 +1,19 @@
|
||||
#ifndef CCHUNK_H
|
||||
#define CCHUNK_H
|
||||
|
||||
#include "cosmo.h"
|
||||
|
||||
#include "coperators.h"
|
||||
#include "cosmo.h"
|
||||
#include "cvalue.h"
|
||||
|
||||
typedef struct CValueArray CValueArray;
|
||||
|
||||
typedef struct CChunk {
|
||||
struct CChunk
|
||||
{
|
||||
size_t capacity; // the amount of space we've allocated for
|
||||
size_t count; // the space we're currently using
|
||||
INSTRUCTION *buf; // whole chunk
|
||||
CValueArray constants; // holds constants
|
||||
size_t lineCapacity;
|
||||
int *lineInfo;
|
||||
} CChunk;
|
||||
};
|
||||
|
||||
CChunk *newChunk(CState *state, size_t startCapacity);
|
||||
void initChunk(CState *state, CChunk *chunk, size_t startCapacity);
|
||||
@@ -28,11 +26,13 @@ void writeu8Chunk(CState* state, CChunk *chunk, INSTRUCTION i, int line);
|
||||
void writeu16Chunk(CState *state, CChunk *chunk, uint16_t i, int line);
|
||||
|
||||
// read from chunk
|
||||
static inline INSTRUCTION readu8Chunk(CChunk *chunk, int offset) {
|
||||
static inline INSTRUCTION readu8Chunk(CChunk *chunk, int offset)
|
||||
{
|
||||
return chunk->buf[offset];
|
||||
}
|
||||
|
||||
static inline uint16_t readu16Chunk(CChunk *chunk, int offset) {
|
||||
static inline uint16_t readu16Chunk(CChunk *chunk, int offset)
|
||||
{
|
||||
return *((uint16_t *)(&chunk->buf[offset]));
|
||||
}
|
||||
|
||||
|
53
src/cdebug.c
53
src/cdebug.c
@@ -1,49 +1,62 @@
|
||||
#include "cdebug.h"
|
||||
#include "cvalue.h"
|
||||
#include "cobj.h"
|
||||
|
||||
void printIndent(int indent) {
|
||||
#include "cobj.h"
|
||||
#include "cvalue.h"
|
||||
|
||||
void printIndent(int indent)
|
||||
{
|
||||
for (int i = 0; i < indent; i++)
|
||||
printf("\t");
|
||||
}
|
||||
|
||||
int simpleInstruction(const char *name, int offset) {
|
||||
int simpleInstruction(const char *name, int offset)
|
||||
{
|
||||
printf("%s", name);
|
||||
return offset + 1; // consume opcode
|
||||
}
|
||||
|
||||
int u8OperandInstruction(const char *name, CChunk *chunk, int offset) {
|
||||
int u8OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||
{
|
||||
printf("%-16s [%03d]", name, readu8Chunk(chunk, offset + 1));
|
||||
return offset + 2;
|
||||
}
|
||||
|
||||
int u16OperandInstruction(const char *name, CChunk *chunk, int offset) {
|
||||
int u16OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||
{
|
||||
printf("%-16s [%05d]", name, readu16Chunk(chunk, offset + 1));
|
||||
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION));
|
||||
}
|
||||
|
||||
int JumpInstruction(const char *name, CChunk *chunk, int offset, int dir) {
|
||||
int JumpInstruction(const char *name, CChunk *chunk, int offset, int dir)
|
||||
{
|
||||
int jmp = ((int)readu16Chunk(chunk, offset + 1)) * dir;
|
||||
printf("%-16s [%05d] - jumps to %04d", name, jmp, offset + 3 + jmp);
|
||||
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION));
|
||||
}
|
||||
|
||||
int u8u8OperandInstruction(const char *name, CChunk *chunk, int offset) {
|
||||
printf("%-16s [%03d] [%03d]", name, readu8Chunk(chunk, offset + 1), readu8Chunk(chunk, offset + 2));
|
||||
int u8u8OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||
{
|
||||
printf("%-16s [%03d] [%03d]", name, readu8Chunk(chunk, offset + 1),
|
||||
readu8Chunk(chunk, offset + 2));
|
||||
return offset + 3; // op + u8 + u8
|
||||
}
|
||||
|
||||
int u8u16OperandInstruction(const char *name, CChunk *chunk, int offset) {
|
||||
printf("%-16s [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1), readu16Chunk(chunk, offset + 2));
|
||||
int u8u16OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||
{
|
||||
printf("%-16s [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1),
|
||||
readu16Chunk(chunk, offset + 2));
|
||||
return offset + 4; // op + u8 + u16
|
||||
}
|
||||
|
||||
int u8u8u16OperandInstruction(const char *name, CChunk *chunk, int offset) {
|
||||
printf("%-16s [%03d] [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1), readu8Chunk(chunk, offset + 2), readu16Chunk(chunk, offset + 3));
|
||||
int u8u8u16OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||
{
|
||||
printf("%-16s [%03d] [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1),
|
||||
readu8Chunk(chunk, offset + 2), readu16Chunk(chunk, offset + 3));
|
||||
return offset + 5; // op + u8 + u8 + u16
|
||||
}
|
||||
|
||||
int constInstruction(const char *name, CChunk *chunk, int offset) {
|
||||
int constInstruction(const char *name, CChunk *chunk, int offset)
|
||||
{
|
||||
int index = readu16Chunk(chunk, offset + 1);
|
||||
printf("%-16s [%05d] - ", name, index);
|
||||
CValue val = chunk->constants.values[index];
|
||||
@@ -55,7 +68,8 @@ int constInstruction(const char *name, CChunk *chunk, int offset) {
|
||||
|
||||
// public methods in the cdebug.h header
|
||||
|
||||
void disasmChunk(CChunk *chunk, const char *name, int indent) {
|
||||
void disasmChunk(CChunk *chunk, const char *name, int indent)
|
||||
{
|
||||
printIndent(indent);
|
||||
printf("===[[ disasm for %s ]]===\n", name);
|
||||
|
||||
@@ -65,7 +79,8 @@ void disasmChunk(CChunk *chunk, const char *name, int indent) {
|
||||
}
|
||||
}
|
||||
|
||||
int disasmInstr(CChunk *chunk, int offset, int indent) {
|
||||
int disasmInstr(CChunk *chunk, int offset, int indent)
|
||||
{
|
||||
printIndent(indent);
|
||||
printf("%04d ", offset);
|
||||
|
||||
@@ -124,7 +139,8 @@ int disasmInstr(CChunk *chunk, int offset, int indent) {
|
||||
}
|
||||
|
||||
// print the chunk
|
||||
disasmChunk(&cobjFunc->chunk, cobjFunc->name == NULL ? UNNAMEDCHUNK : cobjFunc->name->str, indent+1);
|
||||
disasmChunk(&cobjFunc->chunk, cobjFunc->name == NULL ? UNNAMEDCHUNK : cobjFunc->name->str,
|
||||
indent + 1);
|
||||
return offset;
|
||||
}
|
||||
case OP_CLOSE:
|
||||
@@ -161,6 +177,8 @@ int disasmInstr(CChunk *chunk, int offset, int indent) {
|
||||
return simpleInstruction("OP_DIV", offset);
|
||||
case OP_MOD:
|
||||
return simpleInstruction("OP_MOD", offset);
|
||||
case OP_POW:
|
||||
return simpleInstruction("OP_POW", offset);
|
||||
case OP_TRUE:
|
||||
return simpleInstruction("OP_TRUE", offset);
|
||||
case OP_FALSE:
|
||||
@@ -202,6 +220,5 @@ int disasmInstr(CChunk *chunk, int offset, int indent) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
181
src/cdump.c
Normal file
181
src/cdump.c
Normal file
@@ -0,0 +1,181 @@
|
||||
#include "cdump.h"
|
||||
|
||||
#include "cdebug.h"
|
||||
#include "cmem.h"
|
||||
#include "cobj.h"
|
||||
#include "cvalue.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CState *state;
|
||||
const void *userData;
|
||||
cosmo_Writer writer;
|
||||
int writerStatus;
|
||||
} DumpState;
|
||||
|
||||
static void writeCValue(DumpState *dstate, CValue val);
|
||||
|
||||
static void initDumpState(CState *state, DumpState *dstate, cosmo_Writer writer,
|
||||
const void *userData)
|
||||
{
|
||||
dstate->state = state;
|
||||
dstate->userData = userData;
|
||||
dstate->writer = writer;
|
||||
dstate->writerStatus = 0;
|
||||
}
|
||||
|
||||
static void writeBlock(DumpState *dstate, const void *data, size_t size)
|
||||
{
|
||||
if (dstate->writerStatus == 0) {
|
||||
dstate->writerStatus = dstate->writer(dstate->state, data, size, dstate->userData);
|
||||
}
|
||||
}
|
||||
|
||||
static void writeu8(DumpState *dstate, uint8_t d)
|
||||
{
|
||||
writeBlock(dstate, &d, sizeof(uint8_t));
|
||||
}
|
||||
|
||||
static void writeu32(DumpState *dstate, uint32_t d)
|
||||
{
|
||||
writeBlock(dstate, &d, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
static void writeSize(DumpState *dstate, size_t d)
|
||||
{
|
||||
writeBlock(dstate, &d, sizeof(size_t));
|
||||
}
|
||||
|
||||
static void writeVector(DumpState *dstate, const void *data, size_t size, size_t count)
|
||||
{
|
||||
writeSize(dstate, count);
|
||||
writeBlock(dstate, data, size * count);
|
||||
}
|
||||
|
||||
static void writeHeader(DumpState *dstate)
|
||||
{
|
||||
writeBlock(dstate, COSMO_MAGIC, COSMO_MAGIC_LEN);
|
||||
|
||||
/* after the magic, we write some platform information */
|
||||
writeu8(dstate, cosmoD_isBigEndian());
|
||||
writeu8(dstate, sizeof(cosmo_Number));
|
||||
writeu8(dstate, sizeof(size_t));
|
||||
writeu8(dstate, sizeof(int));
|
||||
}
|
||||
|
||||
static void writeCObjString(DumpState *dstate, CObjString *obj)
|
||||
{
|
||||
if (obj == NULL) { /* this is in case cobjfunction's name or module strings are null */
|
||||
writeu32(dstate, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* write string length */
|
||||
writeu32(dstate, obj->length);
|
||||
|
||||
/* write string data */
|
||||
writeBlock(dstate, obj->str, obj->length);
|
||||
}
|
||||
|
||||
static void writeCObjFunction(DumpState *dstate, CObjFunction *obj)
|
||||
{
|
||||
writeCObjString(dstate, obj->name);
|
||||
writeCObjString(dstate, obj->module);
|
||||
|
||||
writeu32(dstate, obj->args);
|
||||
writeu32(dstate, obj->upvals);
|
||||
writeu8(dstate, obj->variadic);
|
||||
|
||||
/* write chunk info */
|
||||
writeVector(dstate, obj->chunk.buf, sizeof(uint8_t), obj->chunk.count);
|
||||
|
||||
/* write line info */
|
||||
writeVector(dstate, obj->chunk.lineInfo, sizeof(int), obj->chunk.count);
|
||||
|
||||
/* write constants */
|
||||
writeSize(dstate, obj->chunk.constants.count);
|
||||
for (int i = 0; i < obj->chunk.constants.count; i++) {
|
||||
writeCValue(dstate, obj->chunk.constants.values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void writeCObj(DumpState *dstate, CObj *obj)
|
||||
{
|
||||
/*
|
||||
we can kind of cheat here since our parser only emits a few very limited CObjs...
|
||||
CChunks will only ever have the following CObj's in their constant table:
|
||||
- COBJ_STRING
|
||||
- COBJ_FUNCTION
|
||||
|
||||
the rest of the objects are created during runtime. yay!
|
||||
*/
|
||||
CObjType t = cosmoO_readType(obj);
|
||||
|
||||
/* write cobj type */
|
||||
writeu8(dstate, t);
|
||||
|
||||
/* write object payload/body */
|
||||
switch (t) {
|
||||
case COBJ_STRING:
|
||||
writeCObjString(dstate, (CObjString *)obj);
|
||||
break;
|
||||
case COBJ_FUNCTION:
|
||||
writeCObjFunction(dstate, (CObjFunction *)obj);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define WRITE_VAR(dstate, type, expression) \
|
||||
{ \
|
||||
type _tmp = expression; \
|
||||
writeBlock(dstate, &_tmp, sizeof(_tmp)); \
|
||||
break; \
|
||||
}
|
||||
|
||||
static void writeCValue(DumpState *dstate, CValue val)
|
||||
{
|
||||
CosmoType t = GET_TYPE(val);
|
||||
|
||||
/* write value type */
|
||||
writeu8(dstate, t);
|
||||
|
||||
/* write value payload/body */
|
||||
switch (t) {
|
||||
case COSMO_TNUMBER:
|
||||
WRITE_VAR(dstate, cosmo_Number, cosmoV_readNumber(val))
|
||||
case COSMO_TBOOLEAN:
|
||||
WRITE_VAR(dstate, bool, cosmoV_readBoolean(val))
|
||||
case COSMO_TREF:
|
||||
writeCObj(dstate, cosmoV_readRef(val));
|
||||
break;
|
||||
case COSMO_TNIL: /* fallthrough, no body */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#undef WRITE_VAR
|
||||
|
||||
bool cosmoD_isBigEndian()
|
||||
{
|
||||
union
|
||||
{
|
||||
uint32_t i;
|
||||
uint8_t c[4];
|
||||
} _indxint = {0xDEADB33F};
|
||||
|
||||
return _indxint.c[0] == 0xDE;
|
||||
}
|
||||
|
||||
int cosmoD_dump(CState *state, CObjFunction *func, cosmo_Writer writer, const void *userData)
|
||||
{
|
||||
DumpState dstate;
|
||||
initDumpState(state, &dstate, writer, userData);
|
||||
|
||||
writeHeader(&dstate);
|
||||
writeCObjFunction(&dstate, func);
|
||||
|
||||
return dstate.writerStatus;
|
||||
}
|
17
src/cdump.h
Normal file
17
src/cdump.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef COSMO_DUMP_H
|
||||
#define COSMO_DUMP_H
|
||||
|
||||
#include "cobj.h"
|
||||
#include "cosmo.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define COSMO_MAGIC "COS\x12"
|
||||
#define COSMO_MAGIC_LEN 4
|
||||
|
||||
bool cosmoD_isBigEndian();
|
||||
|
||||
/* returns non-zero on error */
|
||||
int cosmoD_dump(CState *state, CObjFunction *func, cosmo_Writer writer, const void *userData);
|
||||
|
||||
#endif
|
182
src/clex.c
182
src/clex.c
@@ -1,4 +1,5 @@
|
||||
#include "clex.h"
|
||||
|
||||
#include "cmem.h"
|
||||
|
||||
#include <string.h>
|
||||
@@ -13,7 +14,7 @@ CReservedWord reservedWords[] = {
|
||||
{ TOKEN_END, "end", 3},
|
||||
{ TOKEN_FALSE, "false", 5},
|
||||
{ TOKEN_FOR, "for", 3},
|
||||
{TOKEN_FUNCTION, "function", 8},
|
||||
{ TOKEN_FUNC, "func", 4},
|
||||
{ TOKEN_IF, "if", 2},
|
||||
{ TOKEN_IN, "in", 2},
|
||||
{ TOKEN_LOCAL, "local", 5},
|
||||
@@ -24,49 +25,57 @@ CReservedWord reservedWords[] = {
|
||||
{ TOKEN_RETURN, "return", 6},
|
||||
{ TOKEN_THEN, "then", 4},
|
||||
{ TOKEN_TRUE, "true", 4},
|
||||
{TOKEN_VAR, "var", 3},
|
||||
{ TOKEN_LET, "let", 3},
|
||||
{ TOKEN_WHILE, "while", 5}
|
||||
};
|
||||
|
||||
// returns true if current token is a heap allocated buffer
|
||||
static bool isBuffer(CLexState *state) {
|
||||
static bool isBuffer(CLexState *state)
|
||||
{
|
||||
return state->buffer != NULL;
|
||||
}
|
||||
|
||||
// marks the current token as heap allocated & allocates the buffer
|
||||
static void makeBuffer(CLexState *state) {
|
||||
state->buffer = cosmoM_xmalloc(state->cstate, sizeof(char) * 32); // start with a 32 character long buffer
|
||||
static void makeBuffer(CLexState *state)
|
||||
{
|
||||
state->buffer =
|
||||
cosmoM_xmalloc(state->cstate, sizeof(char) * 32); // start with a 32 character long buffer
|
||||
state->bufCount = 0;
|
||||
state->bufCap = 32;
|
||||
}
|
||||
|
||||
static void resetBuffer(CLexState *state) {
|
||||
static void resetBuffer(CLexState *state)
|
||||
{
|
||||
state->buffer = NULL;
|
||||
state->bufCount = 0;
|
||||
state->bufCap = 0;
|
||||
}
|
||||
|
||||
// cancels the token heap buffer and frees it
|
||||
static void freeBuffer(CLexState *state) {
|
||||
static void freeBuffer(CLexState *state)
|
||||
{
|
||||
cosmoM_freearray(state->cstate, char, state->buffer, state->bufCap);
|
||||
|
||||
resetBuffer(state);
|
||||
}
|
||||
|
||||
// adds character to buffer
|
||||
static void appendBuffer(CLexState *state, char c) {
|
||||
static void appendBuffer(CLexState *state, char c)
|
||||
{
|
||||
cosmoM_growarray(state->cstate, char, state->buffer, state->bufCount, state->bufCap);
|
||||
|
||||
state->buffer[state->bufCount++] = c;
|
||||
}
|
||||
|
||||
// saves the current character to the buffer, grows the buffer as needed
|
||||
static void saveBuffer(CLexState *state) {
|
||||
static void saveBuffer(CLexState *state)
|
||||
{
|
||||
appendBuffer(state, *state->currentChar);
|
||||
}
|
||||
|
||||
// resets the lex state buffer & returns the allocated buffer as a null terminated string
|
||||
static char *cutBuffer(CLexState *state, int *length) {
|
||||
static char *cutBuffer(CLexState *state, int *length)
|
||||
{
|
||||
// append the null terminator
|
||||
appendBuffer(state, '\0');
|
||||
|
||||
@@ -84,7 +93,8 @@ static char *cutBuffer(CLexState *state, int *length) {
|
||||
return cosmoM_reallocate(state->cstate, buf, cap, count);
|
||||
}
|
||||
|
||||
static CToken makeToken(CLexState *state, CTokenType type) {
|
||||
static CToken makeToken(CLexState *state, CTokenType type)
|
||||
{
|
||||
CToken token;
|
||||
token.type = type;
|
||||
token.line = state->line;
|
||||
@@ -101,7 +111,8 @@ static CToken makeToken(CLexState *state, CTokenType type) {
|
||||
return token;
|
||||
}
|
||||
|
||||
static CToken makeError(CLexState *state, const char *msg) {
|
||||
static CToken makeError(CLexState *state, const char *msg)
|
||||
{
|
||||
CToken token;
|
||||
token.type = TOKEN_ERROR;
|
||||
token.start = (char *)msg;
|
||||
@@ -114,19 +125,23 @@ static CToken makeError(CLexState *state, const char *msg) {
|
||||
return token;
|
||||
}
|
||||
|
||||
static inline bool isEnd(CLexState *state) {
|
||||
static inline bool isEnd(CLexState *state)
|
||||
{
|
||||
return *state->currentChar == '\0';
|
||||
}
|
||||
|
||||
static inline bool isNumerical(char c) {
|
||||
static inline bool isNumerical(char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
static bool isAlpha(char c) {
|
||||
static bool isAlpha(char c)
|
||||
{
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; // identifiers can have '_'
|
||||
}
|
||||
|
||||
static bool match(CLexState *state, char expected) {
|
||||
static bool match(CLexState *state, char expected)
|
||||
{
|
||||
if (isEnd(state) || *state->currentChar != expected)
|
||||
return false;
|
||||
|
||||
@@ -135,35 +150,41 @@ static bool match(CLexState *state, char expected) {
|
||||
return true;
|
||||
}
|
||||
|
||||
char peek(CLexState *state) {
|
||||
char peek(CLexState *state)
|
||||
{
|
||||
return *state->currentChar;
|
||||
}
|
||||
|
||||
static char peekNext(CLexState *state) {
|
||||
static char peekNext(CLexState *state)
|
||||
{
|
||||
if (isEnd(state))
|
||||
return '\0';
|
||||
|
||||
return state->currentChar[1];
|
||||
}
|
||||
|
||||
char next(CLexState *state) {
|
||||
char next(CLexState *state)
|
||||
{
|
||||
if (isEnd(state))
|
||||
return '\0'; // return a null terminator
|
||||
state->currentChar++;
|
||||
return state->currentChar[-1];
|
||||
}
|
||||
|
||||
bool isHex(char c) {
|
||||
bool isHex(char c)
|
||||
{
|
||||
return isNumerical(c) || ('A' <= c && 'F' >= c) || ('a' <= c && 'f' >= c);
|
||||
}
|
||||
|
||||
CTokenType identifierType(CLexState *state) {
|
||||
CTokenType identifierType(CLexState *state)
|
||||
{
|
||||
int length = state->currentChar - state->startChar;
|
||||
|
||||
// check against reserved word list
|
||||
for (size_t i = 0; i < sizeof(reservedWords) / sizeof(CReservedWord); i++) {
|
||||
// it matches the reserved word
|
||||
if (reservedWords[i].len == length && memcmp(state->startChar, reservedWords[i].word, length) == 0)
|
||||
if (reservedWords[i].len == length &&
|
||||
memcmp(state->startChar, reservedWords[i].word, length) == 0)
|
||||
return reservedWords[i].type;
|
||||
}
|
||||
|
||||
@@ -171,7 +192,8 @@ CTokenType identifierType(CLexState *state) {
|
||||
return TOKEN_IDENTIFIER;
|
||||
}
|
||||
|
||||
void skipWhitespace(CLexState *state) {
|
||||
void skipWhitespace(CLexState *state)
|
||||
{
|
||||
while (true) {
|
||||
char c = peek(state);
|
||||
switch (c) {
|
||||
@@ -184,14 +206,19 @@ void skipWhitespace(CLexState *state) {
|
||||
break;
|
||||
case '/': // consume comments
|
||||
if (peekNext(state) == '/') {
|
||||
// skip to next line (also let \n be consumed on the next iteration to properly handle that)
|
||||
while (!isEnd(state) && peek(state) != '\n') // if it's not a newline or the end of the source
|
||||
// skip to next line (also let \n be consumed on the next iteration to properly
|
||||
// handle that)
|
||||
while (!isEnd(state) &&
|
||||
peek(state) != '\n') // if it's not a newline or the end of the source
|
||||
next(state);
|
||||
|
||||
// keep consuming whitespace
|
||||
break;
|
||||
} else if (peekNext(state) == '*') { // multiline comments
|
||||
while (!isEnd(state) && !(peek(state) == '*' && peekNext(state) == '/')) // if it's the end of the comment or the end of the source
|
||||
while (!isEnd(state) &&
|
||||
!(peek(state) == '*' &&
|
||||
peekNext(state) ==
|
||||
'/')) // if it's the end of the comment or the end of the source
|
||||
next(state);
|
||||
|
||||
// consume the '*/'
|
||||
@@ -208,7 +235,8 @@ void skipWhitespace(CLexState *state) {
|
||||
}
|
||||
}
|
||||
|
||||
CToken parseString(CLexState *state) {
|
||||
CToken parseString(CLexState *state)
|
||||
{
|
||||
makeBuffer(state); // buffer mode
|
||||
while (peek(state) != '"' && !isEnd(state)) {
|
||||
switch (peek(state)) {
|
||||
@@ -218,10 +246,19 @@ CToken parseString(CLexState *state) {
|
||||
next(state); // consume the '\' character
|
||||
|
||||
switch (peek(state)) {
|
||||
case 'r': case 'n': appendBuffer(state, '\n'); break;
|
||||
case 't': appendBuffer(state, '\t'); break;
|
||||
case '\\': appendBuffer(state, '\\'); break;
|
||||
case '"': appendBuffer(state, '"'); break;
|
||||
case 'r':
|
||||
case 'n':
|
||||
appendBuffer(state, '\n');
|
||||
break;
|
||||
case 't':
|
||||
appendBuffer(state, '\t');
|
||||
break;
|
||||
case '\\':
|
||||
appendBuffer(state, '\\');
|
||||
break;
|
||||
case '"':
|
||||
appendBuffer(state, '"');
|
||||
break;
|
||||
case 'x': // hexadecimal character encoding
|
||||
next(state); // skip 'x'
|
||||
|
||||
@@ -282,7 +319,8 @@ CToken parseString(CLexState *state) {
|
||||
break;
|
||||
}
|
||||
|
||||
return makeError(state, "Unknown special character!"); // TODO: maybe a more descriptive error?
|
||||
return makeError(
|
||||
state, "Unknown special character!"); // TODO: maybe a more descriptive error?
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,7 +341,8 @@ CToken parseString(CLexState *state) {
|
||||
return makeToken(state, TOKEN_STRING);
|
||||
}
|
||||
|
||||
CToken parseNumber(CLexState *state) {
|
||||
CToken parseNumber(CLexState *state)
|
||||
{
|
||||
switch (peek(state)) {
|
||||
case 'x': // hexadecimal number
|
||||
next(state);
|
||||
@@ -325,7 +364,6 @@ CToken parseNumber(CLexState *state) {
|
||||
// if it is a number, fall through and parse normally
|
||||
}
|
||||
|
||||
|
||||
// consume number
|
||||
while (isNumerical(peek(state))) {
|
||||
next(state);
|
||||
@@ -342,7 +380,8 @@ CToken parseNumber(CLexState *state) {
|
||||
return makeToken(state, TOKEN_NUMBER);
|
||||
}
|
||||
|
||||
CToken parseIdentifier(CLexState *state) {
|
||||
CToken parseIdentifier(CLexState *state)
|
||||
{
|
||||
// read literal
|
||||
while ((isAlpha(peek(state)) || isNumerical(peek(state))) && !isEnd(state))
|
||||
next(state);
|
||||
@@ -350,7 +389,8 @@ CToken parseIdentifier(CLexState *state) {
|
||||
return makeToken(state, identifierType(state)); // is it a reserved word?
|
||||
}
|
||||
|
||||
CLexState *cosmoL_newLexState(CState *cstate, const char *source) {
|
||||
CLexState *cosmoL_newLexState(CState *cstate, const char *source)
|
||||
{
|
||||
CLexState *state = cosmoM_xmalloc(cstate, sizeof(CLexState));
|
||||
state->startChar = (char *)source;
|
||||
state->currentChar = (char *)source;
|
||||
@@ -364,11 +404,13 @@ CLexState *cosmoL_newLexState(CState *cstate, const char *source) {
|
||||
return state;
|
||||
}
|
||||
|
||||
void cosmoL_freeLexState(CState *state, CLexState *lstate) {
|
||||
void cosmoL_freeLexState(CState *state, CLexState *lstate)
|
||||
{
|
||||
cosmoM_free(state, CLexState, lstate);
|
||||
}
|
||||
|
||||
CToken cosmoL_scanToken(CLexState *state) {
|
||||
CToken cosmoL_scanToken(CLexState *state)
|
||||
{
|
||||
skipWhitespace(state);
|
||||
|
||||
state->startChar = state->currentChar;
|
||||
@@ -380,37 +422,59 @@ CToken cosmoL_scanToken(CLexState *state) {
|
||||
|
||||
switch (c) {
|
||||
// single character tokens
|
||||
case '(': return makeToken(state, TOKEN_LEFT_PAREN);
|
||||
case ')': return makeToken(state, TOKEN_RIGHT_PAREN);
|
||||
case '{': return makeToken(state, TOKEN_LEFT_BRACE);
|
||||
case '}': return makeToken(state, TOKEN_RIGHT_BRACE);
|
||||
case '[': return makeToken(state, TOKEN_LEFT_BRACKET);
|
||||
case ']': return makeToken(state, TOKEN_RIGHT_BRACKET);
|
||||
case ';': return makeToken(state, TOKEN_EOS);
|
||||
case ',': return makeToken(state, TOKEN_COMMA);
|
||||
case ':': return makeToken(state, TOKEN_COLON);
|
||||
case '*': return makeToken(state, TOKEN_STAR);
|
||||
case '%': return makeToken(state, TOKEN_PERCENT);
|
||||
case '^': return makeToken(state, TOKEN_CARROT);
|
||||
case '#': return makeToken(state, TOKEN_POUND);
|
||||
case '/': return makeToken(state, TOKEN_SLASH);
|
||||
case '(':
|
||||
return makeToken(state, TOKEN_LEFT_PAREN);
|
||||
case ')':
|
||||
return makeToken(state, TOKEN_RIGHT_PAREN);
|
||||
case '{':
|
||||
return makeToken(state, TOKEN_LEFT_BRACE);
|
||||
case '}':
|
||||
return makeToken(state, TOKEN_RIGHT_BRACE);
|
||||
case '[':
|
||||
return makeToken(state, TOKEN_LEFT_BRACKET);
|
||||
case ']':
|
||||
return makeToken(state, TOKEN_RIGHT_BRACKET);
|
||||
case ';':
|
||||
return makeToken(state, TOKEN_EOS);
|
||||
case ',':
|
||||
return makeToken(state, TOKEN_COMMA);
|
||||
case ':':
|
||||
return makeToken(state, TOKEN_COLON);
|
||||
case '*':
|
||||
return makeToken(state, TOKEN_STAR);
|
||||
case '%':
|
||||
return makeToken(state, TOKEN_PERCENT);
|
||||
case '^':
|
||||
return makeToken(state, TOKEN_CARROT);
|
||||
case '#':
|
||||
return makeToken(state, TOKEN_POUND);
|
||||
case '/':
|
||||
return makeToken(state, TOKEN_SLASH);
|
||||
// two character tokens
|
||||
case '+':
|
||||
return match(state, '+') ? makeToken(state, TOKEN_PLUS_PLUS) : makeToken(state, TOKEN_PLUS);
|
||||
case '-':
|
||||
return match(state, '-') ? makeToken(state, TOKEN_MINUS_MINUS) : makeToken(state, TOKEN_MINUS);
|
||||
return match(state, '-') ? makeToken(state, TOKEN_MINUS_MINUS)
|
||||
: makeToken(state, TOKEN_MINUS);
|
||||
case '.':
|
||||
return match(state, '.') ? (match(state, '.') ? makeToken(state, TOKEN_DOT_DOT_DOT) : makeToken(state, TOKEN_DOT_DOT)) : makeToken(state, TOKEN_DOT);
|
||||
return match(state, '.') ? (match(state, '.') ? makeToken(state, TOKEN_DOT_DOT_DOT)
|
||||
: makeToken(state, TOKEN_DOT_DOT))
|
||||
: makeToken(state, TOKEN_DOT);
|
||||
case '!':
|
||||
return match(state, '=') ? makeToken(state, TOKEN_BANG_EQUAL) : makeToken(state, TOKEN_BANG);
|
||||
return match(state, '=') ? makeToken(state, TOKEN_BANG_EQUAL)
|
||||
: makeToken(state, TOKEN_BANG);
|
||||
case '=':
|
||||
return match(state, '=') ? makeToken(state, TOKEN_EQUAL_EQUAL) : makeToken(state, TOKEN_EQUAL);
|
||||
return match(state, '=') ? makeToken(state, TOKEN_EQUAL_EQUAL)
|
||||
: makeToken(state, TOKEN_EQUAL);
|
||||
case '>':
|
||||
return match(state, '=') ? makeToken(state, TOKEN_GREATER_EQUAL) : makeToken(state, TOKEN_GREATER);
|
||||
return match(state, '=') ? makeToken(state, TOKEN_GREATER_EQUAL)
|
||||
: makeToken(state, TOKEN_GREATER);
|
||||
case '<':
|
||||
return match(state, '=') ? makeToken(state, TOKEN_LESS_EQUAL) : makeToken(state, TOKEN_LESS);
|
||||
return match(state, '=') ? makeToken(state, TOKEN_LESS_EQUAL)
|
||||
: makeToken(state, TOKEN_LESS);
|
||||
// literals
|
||||
case '"': return parseString(state);
|
||||
case '"':
|
||||
return parseString(state);
|
||||
default:
|
||||
if (isNumerical(c))
|
||||
return parseNumber(state);
|
||||
|
19
src/clex.h
19
src/clex.h
@@ -3,7 +3,8 @@
|
||||
|
||||
#include "cosmo.h"
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
// single character tokens
|
||||
TOKEN_LEFT_PAREN,
|
||||
TOKEN_RIGHT_PAREN,
|
||||
@@ -56,7 +57,7 @@ typedef enum {
|
||||
TOKEN_ELSEIF,
|
||||
TOKEN_END,
|
||||
TOKEN_FOR,
|
||||
TOKEN_FUNCTION,
|
||||
TOKEN_FUNC,
|
||||
TOKEN_PROTO,
|
||||
TOKEN_IF,
|
||||
TOKEN_IN,
|
||||
@@ -65,30 +66,34 @@ typedef enum {
|
||||
TOKEN_OR,
|
||||
TOKEN_RETURN,
|
||||
TOKEN_THEN,
|
||||
TOKEN_VAR,
|
||||
TOKEN_LET,
|
||||
TOKEN_WHILE,
|
||||
|
||||
TOKEN_ERROR,
|
||||
TOKEN_EOF
|
||||
} CTokenType;
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
CTokenType type;
|
||||
const char *word;
|
||||
int len;
|
||||
} CReservedWord;
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
CTokenType type;
|
||||
char *start;
|
||||
int length;
|
||||
int line;
|
||||
} CToken;
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
char *currentChar;
|
||||
char *startChar;
|
||||
char *buffer; // if non-NULL & bufCount > 0, token->start & token->length will be set to buffer & bufCount respectively
|
||||
char *buffer; // if non-NULL & bufCount > 0, token->start & token->length will be set to buffer
|
||||
// & bufCount respectively
|
||||
size_t bufCount;
|
||||
size_t bufCap;
|
||||
int line; // current line
|
||||
|
77
src/cmem.c
77
src/cmem.c
@@ -1,13 +1,15 @@
|
||||
#include "cmem.h"
|
||||
#include "cstate.h"
|
||||
#include "cvalue.h"
|
||||
#include "ctable.h"
|
||||
#include "cparse.h"
|
||||
#include "cobj.h"
|
||||
|
||||
#include "cbaselib.h"
|
||||
#include "cobj.h"
|
||||
#include "cparse.h"
|
||||
#include "cstate.h"
|
||||
#include "ctable.h"
|
||||
#include "cvalue.h"
|
||||
|
||||
// realloc wrapper
|
||||
void *cosmoM_reallocate(CState* state, void *buf, size_t oldSize, size_t newSize) {
|
||||
void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize)
|
||||
{
|
||||
state->allocatedBytes += newSize - oldSize;
|
||||
|
||||
if (newSize == 0) { // it needs to be freed
|
||||
@@ -39,7 +41,8 @@ void *cosmoM_reallocate(CState* state, void *buf, size_t oldSize, size_t newSize
|
||||
return newBuf;
|
||||
}
|
||||
|
||||
COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed) {
|
||||
COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed)
|
||||
{
|
||||
if (!(cosmoM_isFrozen(state)) && state->allocatedBytes + needed > state->nextGC) {
|
||||
cosmoM_collectGarbage(state); // cya lol
|
||||
return true;
|
||||
@@ -51,7 +54,8 @@ COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed) {
|
||||
void markObject(CState *state, CObj *obj);
|
||||
void markValue(CState *state, CValue val);
|
||||
|
||||
void markTable(CState *state, CTable *tbl) {
|
||||
void markTable(CState *state, CTable *tbl)
|
||||
{
|
||||
if (tbl->table == NULL) // table is still being initialized
|
||||
return;
|
||||
|
||||
@@ -64,14 +68,17 @@ void markTable(CState *state, CTable *tbl) {
|
||||
}
|
||||
|
||||
// frees white members from the table
|
||||
void tableRemoveWhite(CState *state, CTable *tbl) {
|
||||
void tableRemoveWhite(CState *state, CTable *tbl)
|
||||
{
|
||||
if (tbl->table == NULL) // table is still being initialized
|
||||
return;
|
||||
|
||||
int cap = tbl->capacityMask + 1;
|
||||
for (int i = 0; i < cap; i++) {
|
||||
CTableEntry *entry = &tbl->table[i];
|
||||
if (IS_REF(entry->key) && !(cosmoV_readRef(entry->key))->isMarked) { // if the key is a object and it's white (unmarked), remove it from the table
|
||||
if (IS_REF(entry->key) &&
|
||||
!(cosmoV_readRef(entry->key))->isMarked) { // if the key is a object and it's white
|
||||
// (unmarked), remove it from the table
|
||||
cosmoT_remove(state, tbl, entry->key);
|
||||
}
|
||||
}
|
||||
@@ -79,14 +86,17 @@ void tableRemoveWhite(CState *state, CTable *tbl) {
|
||||
cosmoT_checkShrink(state, tbl); // recovers the memory we're no longer using
|
||||
}
|
||||
|
||||
void markArray(CState *state, CValueArray *array) {
|
||||
void markArray(CState *state, CValueArray *array)
|
||||
{
|
||||
for (size_t i = 0; i < array->count; i++) {
|
||||
markValue(state, array->values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// mark all references associated with the object
|
||||
void blackenObject(CState *state, CObj *obj) {
|
||||
// black = keep, white = discard
|
||||
void blackenObject(CState *state, CObj *obj)
|
||||
{
|
||||
markObject(state, (CObj *)obj->proto);
|
||||
switch (obj->type) {
|
||||
case COBJ_STRING:
|
||||
@@ -151,7 +161,8 @@ void blackenObject(CState *state, CObj *obj) {
|
||||
}
|
||||
}
|
||||
|
||||
void markObject(CState *state, CObj *obj) {
|
||||
void markObject(CState *state, CObj *obj)
|
||||
{
|
||||
if (obj == NULL || obj->isMarked) // skip if NULL or already marked
|
||||
return;
|
||||
|
||||
@@ -168,25 +179,29 @@ void markObject(CState *state, CObj *obj) {
|
||||
return;
|
||||
|
||||
// we can use cosmoM_growarray because we lock the GC when we entered in cosmoM_collectGarbage
|
||||
cosmoM_growarray(state, CObj*, state->grayStack.array, state->grayStack.count, state->grayStack.capacity);
|
||||
cosmoM_growarray(state, CObj *, state->grayStack.array, state->grayStack.count,
|
||||
state->grayStack.capacity);
|
||||
|
||||
state->grayStack.array[state->grayStack.count++] = obj;
|
||||
}
|
||||
|
||||
void markValue(CState *state, CValue val) {
|
||||
void markValue(CState *state, CValue val)
|
||||
{
|
||||
if (IS_REF(val))
|
||||
markObject(state, cosmoV_readRef(val));
|
||||
}
|
||||
|
||||
// trace our gray references
|
||||
void traceGrays(CState *state) {
|
||||
void traceGrays(CState *state)
|
||||
{
|
||||
while (state->grayStack.count > 0) {
|
||||
CObj *obj = state->grayStack.array[--state->grayStack.count];
|
||||
blackenObject(state, obj);
|
||||
}
|
||||
}
|
||||
|
||||
void sweep(CState *state) {
|
||||
void sweep(CState *state)
|
||||
{
|
||||
CObj *prev = NULL;
|
||||
CObj *object = state->objects;
|
||||
while (object != NULL) {
|
||||
@@ -209,7 +224,8 @@ void sweep(CState *state) {
|
||||
}
|
||||
}
|
||||
|
||||
void markUserRoots(CState *state) {
|
||||
void markUserRoots(CState *state)
|
||||
{
|
||||
CObj *root = state->userRoots;
|
||||
|
||||
// traverse userRoots and mark all the object
|
||||
@@ -219,7 +235,8 @@ void markUserRoots(CState *state) {
|
||||
}
|
||||
}
|
||||
|
||||
void markRoots(CState *state) {
|
||||
void markRoots(CState *state)
|
||||
{
|
||||
// mark all values on the stack
|
||||
for (StkPtr value = state->stack; value < state->top; value++) {
|
||||
markValue(state, *value);
|
||||
@@ -253,7 +270,8 @@ void markRoots(CState *state) {
|
||||
traceGrays(state);
|
||||
}
|
||||
|
||||
COSMO_API void cosmoM_collectGarbage(CState *state) {
|
||||
COSMO_API void cosmoM_collectGarbage(CState *state)
|
||||
{
|
||||
#ifdef GC_DEBUG
|
||||
printf("-- GC start\n");
|
||||
size_t start = state->allocatedBytes;
|
||||
@@ -262,26 +280,32 @@ COSMO_API void cosmoM_collectGarbage(CState *state) {
|
||||
|
||||
markRoots(state);
|
||||
|
||||
tableRemoveWhite(state, &state->strings); // make sure we aren't referencing any strings that are about to be freed
|
||||
tableRemoveWhite(
|
||||
state,
|
||||
&state->strings); // make sure we aren't referencing any strings that are about to be freed
|
||||
// now finally, free all the unmarked objects
|
||||
sweep(state);
|
||||
|
||||
// set our next GC event
|
||||
cosmoM_updateThreshhold(state);
|
||||
|
||||
state->freezeGC--; // we don't want to use cosmoM_unfreezeGC because that might trigger a GC event (if GC_STRESS is defined)
|
||||
state->freezeGC--; // we don't want to use cosmoM_unfreezeGC because that might trigger a GC
|
||||
// event (if GC_STRESS is defined)
|
||||
#ifdef GC_DEBUG
|
||||
printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection scheduled at %ld bytes\n",
|
||||
printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection "
|
||||
"scheduled at %ld bytes\n",
|
||||
start - state->allocatedBytes, start, state->allocatedBytes, state->nextGC);
|
||||
getchar(); // pauses execution
|
||||
#endif
|
||||
}
|
||||
|
||||
COSMO_API void cosmoM_updateThreshhold(CState *state) {
|
||||
COSMO_API void cosmoM_updateThreshhold(CState *state)
|
||||
{
|
||||
state->nextGC = state->allocatedBytes * HEAP_GROW_FACTOR;
|
||||
}
|
||||
|
||||
COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot) {
|
||||
COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot)
|
||||
{
|
||||
// first, check and make sure this root doesn't already exist in the list
|
||||
CObj *root = state->userRoots;
|
||||
while (root != NULL) {
|
||||
@@ -296,7 +320,8 @@ COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot) {
|
||||
state->userRoots = newRoot;
|
||||
}
|
||||
|
||||
COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot) {
|
||||
COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot)
|
||||
{
|
||||
CObj *prev = NULL;
|
||||
CObj *root = state->userRoots;
|
||||
|
||||
|
19
src/cmem.h
19
src/cmem.h
@@ -2,7 +2,6 @@
|
||||
#define CMEME_C // meme lol
|
||||
|
||||
#include "cosmo.h"
|
||||
|
||||
#include "cstate.h"
|
||||
|
||||
// #define GC_STRESS
|
||||
@@ -14,7 +13,8 @@
|
||||
|
||||
#ifdef GC_DEBUG
|
||||
# define cosmoM_freearray(state, type, buf, capacity) \
|
||||
printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, __LINE__); \
|
||||
printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, \
|
||||
__LINE__); \
|
||||
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
|
||||
#else
|
||||
# define cosmoM_freearray(state, type, buf, capacity) \
|
||||
@@ -33,12 +33,10 @@
|
||||
printf("freeing %p [size %lu] at %s:%d\n", x, sizeof(type), __FILE__, __LINE__); \
|
||||
cosmoM_reallocate(state, x, sizeof(type), 0)
|
||||
#else
|
||||
#define cosmoM_free(state, type, x) \
|
||||
cosmoM_reallocate(state, x, sizeof(type), 0)
|
||||
# define cosmoM_free(state, type, x) cosmoM_reallocate(state, x, sizeof(type), 0)
|
||||
#endif
|
||||
|
||||
#define cosmoM_isFrozen(state) \
|
||||
(state->freezeGC > 0)
|
||||
#define cosmoM_isFrozen(state) (state->freezeGC > 0)
|
||||
|
||||
// if debugging, print the locations of when the state is frozen/unfrozen
|
||||
#ifdef GC_DEBUG
|
||||
@@ -53,8 +51,7 @@
|
||||
#else
|
||||
|
||||
// freeze's the garbage collector until cosmoM_unfreezeGC is called
|
||||
#define cosmoM_freezeGC(state) \
|
||||
state->freezeGC++
|
||||
# define cosmoM_freezeGC(state) state->freezeGC++
|
||||
|
||||
// unfreeze's the garbage collector and tries to run a garbage collection cycle
|
||||
# define cosmoM_unfreezeGC(state) \
|
||||
@@ -64,7 +61,8 @@
|
||||
#endif
|
||||
|
||||
COSMO_API void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize);
|
||||
COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed); // returns true if GC event was triggered
|
||||
COSMO_API bool cosmoM_checkGarbage(CState *state,
|
||||
size_t needed); // returns true if GC event was triggered
|
||||
COSMO_API void cosmoM_collectGarbage(CState *state);
|
||||
COSMO_API void cosmoM_updateThreshhold(CState *state);
|
||||
|
||||
@@ -75,7 +73,8 @@ COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot);
|
||||
COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot);
|
||||
|
||||
// wrapper for cosmoM_reallocate so we can track our memory usage (it's also safer :P)
|
||||
static inline void *cosmoM_xmalloc(CState *state, size_t sz) {
|
||||
static inline void *cosmoM_xmalloc(CState *state, size_t sz)
|
||||
{
|
||||
return cosmoM_reallocate(state, NULL, 0, sz);
|
||||
}
|
||||
|
||||
|
277
src/cobj.c
277
src/cobj.c
@@ -1,14 +1,18 @@
|
||||
#include "cobj.h"
|
||||
|
||||
#include "clex.h"
|
||||
#include "cmem.h"
|
||||
#include "cstate.h"
|
||||
#include "ctable.h"
|
||||
#include "cobj.h"
|
||||
#include "cmem.h"
|
||||
#include "cvm.h"
|
||||
#include "clex.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// we don't actually hash the whole string :eyes:
|
||||
uint32_t hashString(const char *str, size_t sz) {
|
||||
uint32_t hashString(const char *str, size_t sz)
|
||||
{
|
||||
uint32_t hash = sz;
|
||||
size_t step = (sz >> 5) + 1;
|
||||
|
||||
@@ -18,7 +22,8 @@ uint32_t hashString(const char *str, size_t sz) {
|
||||
return hash;
|
||||
}
|
||||
|
||||
CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type) {
|
||||
CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type)
|
||||
{
|
||||
CObj *obj = (CObj *)cosmoM_xmalloc(state, sz);
|
||||
obj->type = type;
|
||||
obj->isMarked = false;
|
||||
@@ -34,7 +39,8 @@ CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
void cosmoO_free(CState *state, CObj* obj) {
|
||||
void cosmoO_free(CState *state, CObj *obj)
|
||||
{
|
||||
#ifdef GC_DEBUG
|
||||
printf("freeing %p [", obj);
|
||||
printObject(obj);
|
||||
@@ -89,28 +95,97 @@ void cosmoO_free(CState *state, CObj* obj) {
|
||||
cosmoM_free(state, CObjClosure, closure);
|
||||
break;
|
||||
}
|
||||
case COBJ_MAX: { /* stubbed, should never happen */ }
|
||||
case COBJ_MAX:
|
||||
default: { /* stubbed, should never happen */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool cosmoO_equal(CObj* obj1, CObj* obj2) {
|
||||
bool cosmoO_equal(CState *state, CObj *obj1, CObj *obj2)
|
||||
{
|
||||
CObjObject *proto1, *proto2;
|
||||
CValue eq1, eq2;
|
||||
|
||||
if (obj1 == obj2) // its the same reference, this compares strings for us since they're interned
|
||||
// anyways :)
|
||||
return true;
|
||||
|
||||
// its not the same type, maybe both <ref>'s have the same '__equal' metamethod in their protos?
|
||||
if (obj1->type != obj2->type)
|
||||
return false;
|
||||
goto _eqFail;
|
||||
|
||||
switch (obj1->type) {
|
||||
case COBJ_STRING:
|
||||
return obj1 == obj2; // compare pointers because we already intern all strings :)
|
||||
case COBJ_STRING: {
|
||||
/*
|
||||
we already compared the pointers at the top of the function, this prevents the `__equal`
|
||||
metamethod from being checked. If you plan on using `__equal` with strings just remove
|
||||
this case!
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
case COBJ_CFUNCTION: {
|
||||
CObjCFunction *cfunc1 = (CObjCFunction *)obj1;
|
||||
CObjCFunction *cfunc2 = (CObjCFunction *)obj2;
|
||||
return cfunc1->cfunc == cfunc2->cfunc;
|
||||
if (cfunc1->cfunc == cfunc2->cfunc)
|
||||
return true;
|
||||
goto _eqFail;
|
||||
}
|
||||
case COBJ_METHOD: {
|
||||
CObjMethod *method1 = (CObjMethod *)obj1;
|
||||
CObjMethod *method2 = (CObjMethod *)obj2;
|
||||
if (cosmoV_equal(state, method1->func, method2->func))
|
||||
return true;
|
||||
goto _eqFail;
|
||||
}
|
||||
case COBJ_CLOSURE: {
|
||||
CObjClosure *closure1 = (CObjClosure *)obj1;
|
||||
CObjClosure *closure2 = (CObjClosure *)obj2;
|
||||
// we just compare the function pointer
|
||||
if (closure1->function == closure2->function)
|
||||
return true;
|
||||
goto _eqFail;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
goto _eqFail;
|
||||
}
|
||||
|
||||
CObjObject *cosmoO_newObject(CState *state) {
|
||||
_eqFail:
|
||||
// this is pretty expensive (bad lookup caching helps a lot), but it only all gets run if both
|
||||
// objects have protos & both have the `__equal` metamethod defined so... it should stay light
|
||||
// for the majority of cases
|
||||
if ((proto1 = cosmoO_grabProto(obj1)) != NULL &&
|
||||
(proto2 = cosmoO_grabProto(obj2)) != NULL && // make sure both protos exist
|
||||
cosmoO_getIString(
|
||||
state, proto1, ISTRING_EQUAL,
|
||||
&eq1) && // grab the `__equal` metamethod from the first proto, if fail abort
|
||||
cosmoO_getIString(
|
||||
state, proto2, ISTRING_EQUAL,
|
||||
&eq2) && // grab the `__equal` metamethod from the second proto, if fail abort
|
||||
cosmoV_equal(state, eq1, eq2)) { // compare the two `__equal` metamethods
|
||||
|
||||
// now finally, call the `__equal` metamethod (<object>, <object>)
|
||||
cosmoV_pushValue(state, eq1);
|
||||
cosmoV_pushRef(state, obj1);
|
||||
cosmoV_pushRef(state, obj2);
|
||||
if (cosmoV_call(state, 2, 1) != COSMOVM_OK)
|
||||
return false;
|
||||
|
||||
// check return value and make sure it's a boolean
|
||||
if (!IS_BOOLEAN(*cosmoV_getTop(state, 0))) {
|
||||
cosmoV_error(state, "__equal expected to return <boolean>, got %s!",
|
||||
cosmoV_typeStr(*cosmoV_pop(state)));
|
||||
return false;
|
||||
}
|
||||
|
||||
// return the result
|
||||
return cosmoV_readBoolean(*cosmoV_pop(state));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CObjObject *cosmoO_newObject(CState *state)
|
||||
{
|
||||
CObjObject *obj = (CObjObject *)cosmoO_allocateBase(state, sizeof(CObjObject), COBJ_OBJECT);
|
||||
obj->istringFlags = 0;
|
||||
obj->userP = NULL; // reserved for C API
|
||||
@@ -123,7 +198,8 @@ CObjObject *cosmoO_newObject(CState *state) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
CObjTable *cosmoO_newTable(CState *state) {
|
||||
CObjTable *cosmoO_newTable(CState *state)
|
||||
{
|
||||
CObjTable *obj = (CObjTable *)cosmoO_allocateBase(state, sizeof(CObjTable), COBJ_TABLE);
|
||||
|
||||
// init the table (might cause a GC event)
|
||||
@@ -134,8 +210,10 @@ CObjTable *cosmoO_newTable(CState *state) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
CObjFunction *cosmoO_newFunction(CState *state) {
|
||||
CObjFunction *func = (CObjFunction*)cosmoO_allocateBase(state, sizeof(CObjFunction), COBJ_FUNCTION);
|
||||
CObjFunction *cosmoO_newFunction(CState *state)
|
||||
{
|
||||
CObjFunction *func =
|
||||
(CObjFunction *)cosmoO_allocateBase(state, sizeof(CObjFunction), COBJ_FUNCTION);
|
||||
func->args = 0;
|
||||
func->upvals = 0;
|
||||
func->variadic = false;
|
||||
@@ -146,13 +224,16 @@ CObjFunction *cosmoO_newFunction(CState *state) {
|
||||
return func;
|
||||
}
|
||||
|
||||
CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func) {
|
||||
CObjCFunction *cfunc = (CObjCFunction*)cosmoO_allocateBase(state, sizeof(CObjCFunction), COBJ_CFUNCTION);
|
||||
CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func)
|
||||
{
|
||||
CObjCFunction *cfunc =
|
||||
(CObjCFunction *)cosmoO_allocateBase(state, sizeof(CObjCFunction), COBJ_CFUNCTION);
|
||||
cfunc->cfunc = func;
|
||||
return cfunc;
|
||||
}
|
||||
|
||||
CObjError *cosmoO_newError(CState *state, CValue err) {
|
||||
CObjError *cosmoO_newError(CState *state, CValue err)
|
||||
{
|
||||
CObjError *cerror = (CObjError *)cosmoO_allocateBase(state, sizeof(CObjError), COBJ_ERROR);
|
||||
cerror->err = err;
|
||||
cerror->frameCount = state->frameCount;
|
||||
@@ -168,14 +249,16 @@ CObjError *cosmoO_newError(CState *state, CValue err) {
|
||||
return cerror;
|
||||
}
|
||||
|
||||
CObjMethod *cosmoO_newMethod(CState *state, CValue func, CObj *obj) {
|
||||
CObjMethod *cosmoO_newMethod(CState *state, CValue func, CObj *obj)
|
||||
{
|
||||
CObjMethod *method = (CObjMethod *)cosmoO_allocateBase(state, sizeof(CObjMethod), COBJ_METHOD);
|
||||
method->func = func;
|
||||
method->obj = obj;
|
||||
return method;
|
||||
}
|
||||
|
||||
CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func) {
|
||||
CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func)
|
||||
{
|
||||
// initialize array of pointers
|
||||
CObjUpval **upvalues = cosmoM_xmalloc(state, sizeof(CObjUpval *) * func->upvals);
|
||||
|
||||
@@ -183,7 +266,8 @@ CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func) {
|
||||
upvalues[i] = NULL;
|
||||
}
|
||||
|
||||
CObjClosure *closure = (CObjClosure*)cosmoO_allocateBase(state, sizeof(CObjClosure), COBJ_CLOSURE);
|
||||
CObjClosure *closure =
|
||||
(CObjClosure *)cosmoO_allocateBase(state, sizeof(CObjClosure), COBJ_CLOSURE);
|
||||
closure->function = func;
|
||||
closure->upvalues = upvalues;
|
||||
closure->upvalueCount = func->upvals;
|
||||
@@ -191,7 +275,8 @@ CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func) {
|
||||
return closure;
|
||||
}
|
||||
|
||||
CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val) {
|
||||
CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val)
|
||||
{
|
||||
CObjUpval *upval = (CObjUpval *)cosmoO_allocateBase(state, sizeof(CObjUpval), COBJ_UPVALUE);
|
||||
upval->val = val;
|
||||
upval->closed = cosmoV_newNil();
|
||||
@@ -200,7 +285,8 @@ CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val) {
|
||||
return upval;
|
||||
}
|
||||
|
||||
CObjString *cosmoO_copyString(CState *state, const char *str, size_t length) {
|
||||
CObjString *cosmoO_copyString(CState *state, const char *str, size_t length)
|
||||
{
|
||||
uint32_t hash = hashString(str, length);
|
||||
CObjString *lookup = cosmoT_lookupString(&state->strings, str, length, hash);
|
||||
|
||||
@@ -215,29 +301,34 @@ CObjString *cosmoO_copyString(CState *state, const char *str, size_t length) {
|
||||
return cosmoO_allocateString(state, buf, length, hash);
|
||||
}
|
||||
|
||||
// length shouldn't include the null terminator! str should be a null terminated string! (char array should also have been allocated using cosmoM_xmalloc!)
|
||||
CObjString *cosmoO_takeString(CState *state, char *str, size_t length) {
|
||||
// length shouldn't include the null terminator! str should be a null terminated string! (char array
|
||||
// should also have been allocated using cosmoM_xmalloc!)
|
||||
CObjString *cosmoO_takeString(CState *state, char *str, size_t length)
|
||||
{
|
||||
uint32_t hash = hashString(str, length);
|
||||
|
||||
CObjString *lookup = cosmoT_lookupString(&state->strings, str, length, hash);
|
||||
|
||||
// have we already interned this string?
|
||||
if (lookup != NULL) {
|
||||
cosmoM_freearray(state, char, str, length + 1); // free our passed character array, it's unneeded!
|
||||
cosmoM_freearray(state, char, str,
|
||||
length + 1); // free our passed character array, it's unneeded!
|
||||
return lookup;
|
||||
}
|
||||
|
||||
return cosmoO_allocateString(state, str, length, hash);
|
||||
}
|
||||
|
||||
CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uint32_t hash) {
|
||||
CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uint32_t hash)
|
||||
{
|
||||
CObjString *strObj = (CObjString *)cosmoO_allocateBase(state, sizeof(CObjString), COBJ_STRING);
|
||||
strObj->isIString = false;
|
||||
strObj->str = (char *)str;
|
||||
strObj->length = sz;
|
||||
strObj->hash = hash;
|
||||
|
||||
// we push & pop the string so our GC can find it (we don't use freezeGC/unfreezeGC because we *want* a GC event to happen)
|
||||
// we push & pop the string so our GC can find it (we don't use freezeGC/unfreezeGC because we
|
||||
// *want* a GC event to happen)
|
||||
cosmoV_pushRef(state, (CObj *)strObj);
|
||||
cosmoT_insert(state, &state->strings, cosmoV_newRef((CObj *)strObj));
|
||||
cosmoV_pop(state);
|
||||
@@ -245,7 +336,8 @@ CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uin
|
||||
return strObj;
|
||||
}
|
||||
|
||||
CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args) {
|
||||
CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args)
|
||||
{
|
||||
StkPtr start = state->top;
|
||||
const char *end;
|
||||
char c;
|
||||
@@ -290,12 +382,14 @@ CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args)
|
||||
}
|
||||
|
||||
cosmoV_pushString(state, format); // push the rest of the string
|
||||
cosmoV_concat(state, state->top - start); // use cosmoV_concat to concat all the strings on the stack
|
||||
cosmoV_concat(state,
|
||||
state->top - start); // use cosmoV_concat to concat all the strings on the stack
|
||||
return cosmoV_readString(*start); // start should be state->top - 1
|
||||
}
|
||||
|
||||
// walks the protos of obj and checks for proto
|
||||
bool cosmoO_isDescendant(CObj *obj, CObjObject *proto) {
|
||||
bool cosmoO_isDescendant(CObj *obj, CObjObject *proto)
|
||||
{
|
||||
CObjObject *curr = obj->proto;
|
||||
|
||||
while (curr != NULL) {
|
||||
@@ -310,9 +404,12 @@ bool cosmoO_isDescendant(CObj *obj, CObjObject *proto) {
|
||||
}
|
||||
|
||||
// returns false if error thrown
|
||||
bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj) {
|
||||
if (!cosmoT_get(&proto->tbl, key, val)) { // if the field doesn't exist in the object, check the proto
|
||||
if (cosmoO_getIString(state, proto, ISTRING_GETTER, val) && IS_TABLE(*val) && cosmoT_get(&cosmoV_readTable(*val)->tbl, key, val)) {
|
||||
bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj)
|
||||
{
|
||||
if (!cosmoT_get(state, &proto->tbl, key,
|
||||
val)) { // if the field doesn't exist in the object, check the proto
|
||||
if (cosmoO_getIString(state, proto, ISTRING_GETTER, val) && IS_TABLE(*val) &&
|
||||
cosmoT_get(state, &cosmoV_readTable(*val)->tbl, key, val)) {
|
||||
cosmoV_pushValue(state, *val); // push function
|
||||
cosmoV_pushRef(state, (CObj *)obj); // push object
|
||||
if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call the function with the 1 argument
|
||||
@@ -321,7 +418,8 @@ bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *v
|
||||
return true;
|
||||
}
|
||||
|
||||
if (proto->_obj.proto != NULL && cosmoO_getRawObject(state, proto->_obj.proto, key, val, obj))
|
||||
if (proto->_obj.proto != NULL &&
|
||||
cosmoO_getRawObject(state, proto->_obj.proto, key, val, obj))
|
||||
return true;
|
||||
|
||||
*val = cosmoV_newNil();
|
||||
@@ -331,7 +429,8 @@ bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *v
|
||||
return true;
|
||||
}
|
||||
|
||||
void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue val, CObj *obj) {
|
||||
void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue val, CObj *obj)
|
||||
{
|
||||
CValue ret;
|
||||
|
||||
// if the object is locked, throw an error
|
||||
@@ -341,7 +440,8 @@ void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue va
|
||||
}
|
||||
|
||||
// check for __setters
|
||||
if (cosmoO_getIString(state, proto, ISTRING_SETTER, &ret) && IS_TABLE(ret) && cosmoT_get(&cosmoV_readTable(ret)->tbl, key, &ret)) {
|
||||
if (cosmoO_getIString(state, proto, ISTRING_SETTER, &ret) && IS_TABLE(ret) &&
|
||||
cosmoT_get(state, &cosmoV_readTable(ret)->tbl, key, &ret)) {
|
||||
cosmoV_pushValue(state, ret); // push function
|
||||
cosmoV_pushRef(state, (CObj *)obj); // push object
|
||||
cosmoV_pushValue(state, val); // push new value
|
||||
@@ -361,43 +461,52 @@ void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue va
|
||||
}
|
||||
}
|
||||
|
||||
void cosmoO_setUserP(CObjObject *object, void *p) {
|
||||
void cosmoO_setUserP(CObjObject *object, void *p)
|
||||
{
|
||||
object->userP = p;
|
||||
}
|
||||
|
||||
void *cosmoO_getUserP(CObjObject *object) {
|
||||
void *cosmoO_getUserP(CObjObject *object)
|
||||
{
|
||||
return object->userP;
|
||||
}
|
||||
|
||||
void cosmoO_setUserI(CObjObject *object, int i) {
|
||||
void cosmoO_setUserI(CObjObject *object, int i)
|
||||
{
|
||||
object->userI = i;
|
||||
}
|
||||
|
||||
int cosmoO_getUserI(CObjObject *object) {
|
||||
int cosmoO_getUserI(CObjObject *object)
|
||||
{
|
||||
return object->userI;
|
||||
}
|
||||
|
||||
void cosmoO_setUserT(CObjObject *object, int t) {
|
||||
void cosmoO_setUserT(CObjObject *object, int t)
|
||||
{
|
||||
object->userT = t;
|
||||
}
|
||||
|
||||
int cosmoO_getUserT(CObjObject *object) {
|
||||
int cosmoO_getUserT(CObjObject *object)
|
||||
{
|
||||
return object->userT;
|
||||
}
|
||||
|
||||
void cosmoO_lock(CObjObject *object) {
|
||||
void cosmoO_lock(CObjObject *object)
|
||||
{
|
||||
object->isLocked = true;
|
||||
}
|
||||
|
||||
void cosmoO_unlock(CObjObject *object) {
|
||||
void cosmoO_unlock(CObjObject *object)
|
||||
{
|
||||
object->isLocked = false;
|
||||
}
|
||||
|
||||
bool rawgetIString(CState *state, CObjObject *object, int flag, CValue *val) {
|
||||
bool rawgetIString(CState *state, CObjObject *object, int flag, CValue *val)
|
||||
{
|
||||
if (readFlag(object->istringFlags, flag))
|
||||
return false; // it's been cached as bad
|
||||
|
||||
if (!cosmoT_get(&object->tbl, cosmoV_newRef(state->iStrings[flag]), val)) {
|
||||
if (!cosmoT_get(state, &object->tbl, cosmoV_newRef(state->iStrings[flag]), val)) {
|
||||
// mark it bad!
|
||||
setFlagOn(object->istringFlags, flag);
|
||||
return false;
|
||||
@@ -406,7 +515,8 @@ bool rawgetIString(CState *state, CObjObject *object, int flag, CValue *val) {
|
||||
return true; // :)
|
||||
}
|
||||
|
||||
bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val) {
|
||||
bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val)
|
||||
{
|
||||
CObjObject *obj = object;
|
||||
|
||||
do {
|
||||
@@ -417,7 +527,8 @@ bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val)
|
||||
return false; // obj->proto was false, the istring doesn't exist in this object chain
|
||||
}
|
||||
|
||||
bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val) {
|
||||
bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val)
|
||||
{
|
||||
if (cosmoO_getIString(state, object, ISTRING_INDEX, val)) {
|
||||
cosmoV_pushValue(state, *val); // push function
|
||||
cosmoV_pushRef(state, (CObj *)object); // push object
|
||||
@@ -433,7 +544,8 @@ bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *v
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val) {
|
||||
bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val)
|
||||
{
|
||||
CValue ret; // return value for cosmoO_getIString
|
||||
|
||||
if (cosmoO_getIString(state, object, ISTRING_NEWINDEX, &ret)) {
|
||||
@@ -449,7 +561,8 @@ bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue
|
||||
return false;
|
||||
}
|
||||
|
||||
CObjString *cosmoO_toString(CState *state, CObj *obj) {
|
||||
CObjString *cosmoO_toString(CState *state, CObj *obj)
|
||||
{
|
||||
CObjObject *protoObject = cosmoO_grabProto(obj);
|
||||
CValue res;
|
||||
|
||||
@@ -463,7 +576,8 @@ CObjString *cosmoO_toString(CState *state, CObj *obj) {
|
||||
// make sure the __tostring function returned a string
|
||||
StkPtr ret = cosmoV_getTop(state, 0);
|
||||
if (!IS_STRING(*ret)) {
|
||||
cosmoV_error(state, "__tostring expected to return <string>, got %s!", cosmoV_typeStr(*ret));
|
||||
cosmoV_error(state, "__tostring expected to return <string>, got %s!",
|
||||
cosmoV_typeStr(*ret));
|
||||
return cosmoO_copyString(state, "<err>", 5);
|
||||
}
|
||||
|
||||
@@ -482,12 +596,14 @@ CObjString *cosmoO_toString(CState *state, CObj *obj) {
|
||||
}
|
||||
case COBJ_FUNCTION: {
|
||||
CObjFunction *func = (CObjFunction *)obj;
|
||||
return func->name != NULL ? func->name : cosmoO_copyString(state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK));
|
||||
return func->name != NULL ? func->name
|
||||
: cosmoO_copyString(state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK));
|
||||
}
|
||||
case COBJ_CFUNCTION: {
|
||||
CObjCFunction *cfunc = (CObjCFunction *)obj;
|
||||
char buf[64];
|
||||
int sz = sprintf(buf, "<c function> %p", (void*)cfunc->cfunc) + 1; // +1 for the null character
|
||||
int sz =
|
||||
sprintf(buf, "<c function> %p", (void *)cfunc->cfunc) + 1; // +1 for the null character
|
||||
return cosmoO_copyString(state, buf, sz);
|
||||
}
|
||||
case COBJ_OBJECT: {
|
||||
@@ -512,7 +628,8 @@ CObjString *cosmoO_toString(CState *state, CObj *obj) {
|
||||
}
|
||||
}
|
||||
|
||||
cosmo_Number cosmoO_toNumber(CState *state, CObj *obj) {
|
||||
cosmo_Number cosmoO_toNumber(CState *state, CObj *obj)
|
||||
{
|
||||
CObjObject *proto = cosmoO_grabProto(obj);
|
||||
CValue res;
|
||||
|
||||
@@ -524,7 +641,8 @@ cosmo_Number cosmoO_toNumber(CState *state, CObj *obj) {
|
||||
|
||||
StkPtr temp = cosmoV_getTop(state, 0);
|
||||
if (!IS_NUMBER(*temp)) {
|
||||
cosmoV_error(state, "__tonumber expected to return <number>, got %s!", cosmoV_typeStr(*temp));
|
||||
cosmoV_error(state, "__tonumber expected to return <number>, got %s!",
|
||||
cosmoV_typeStr(*temp));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -543,19 +661,22 @@ cosmo_Number cosmoO_toNumber(CState *state, CObj *obj) {
|
||||
}
|
||||
}
|
||||
|
||||
int cosmoO_count(CState *state, CObj *obj) {
|
||||
int cosmoO_count(CState *state, CObj *obj)
|
||||
{
|
||||
CObjObject *proto = cosmoO_grabProto(obj);
|
||||
CValue res;
|
||||
|
||||
if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_COUNT, &res)) {
|
||||
cosmoV_pushValue(state, res);
|
||||
cosmoV_pushRef(state, (CObj *)obj);
|
||||
if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call res, we expect 1 return value of type <number>
|
||||
if (cosmoV_call(state, 1, 1) !=
|
||||
COSMOVM_OK) // call res, we expect 1 return value of type <number>
|
||||
return 0;
|
||||
|
||||
StkPtr ret = cosmoV_getTop(state, 0);
|
||||
if (!IS_NUMBER(*ret)) {
|
||||
cosmoV_error(state, "__count expected to return <number>, got %s!", cosmoV_typeStr(*ret));
|
||||
cosmoV_error(state, "__count expected to return <number>, got %s!",
|
||||
cosmoV_typeStr(*ret));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -579,11 +700,12 @@ int cosmoO_count(CState *state, CObj *obj) {
|
||||
}
|
||||
}
|
||||
|
||||
void printObject(CObj *o) {
|
||||
void printObject(CObj *o)
|
||||
{
|
||||
switch (o->type) {
|
||||
case COBJ_STRING: {
|
||||
CObjString *objStr = (CObjString *)o;
|
||||
printf("%.*s", objStr->length, objStr->str);
|
||||
printf("<string> \"%.*s\"", objStr->length, objStr->str);
|
||||
break;
|
||||
}
|
||||
case COBJ_OBJECT: {
|
||||
@@ -637,16 +759,25 @@ void printObject(CObj *o) {
|
||||
}
|
||||
}
|
||||
|
||||
const char *cosmoO_typeStr(CObj* obj) {
|
||||
const char *cosmoO_typeStr(CObj *obj)
|
||||
{
|
||||
switch (obj->type) {
|
||||
case COBJ_STRING: return "<string>";
|
||||
case COBJ_OBJECT: return "<object>";
|
||||
case COBJ_TABLE: return "<table>";
|
||||
case COBJ_FUNCTION: return "<function>";
|
||||
case COBJ_CFUNCTION: return "<c function>";
|
||||
case COBJ_METHOD: return "<method>";
|
||||
case COBJ_CLOSURE: return "<closure>";
|
||||
case COBJ_UPVALUE: return "<upvalue>";
|
||||
case COBJ_STRING:
|
||||
return "<string>";
|
||||
case COBJ_OBJECT:
|
||||
return "<object>";
|
||||
case COBJ_TABLE:
|
||||
return "<table>";
|
||||
case COBJ_FUNCTION:
|
||||
return "<function>";
|
||||
case COBJ_CFUNCTION:
|
||||
return "<c function>";
|
||||
case COBJ_METHOD:
|
||||
return "<method>";
|
||||
case COBJ_CLOSURE:
|
||||
return "<closure>";
|
||||
case COBJ_UPVALUE:
|
||||
return "<upvalue>";
|
||||
|
||||
default:
|
||||
return "<unkn obj>"; // TODO: maybe panic? could be a malformed object :eyes:
|
||||
|
94
src/cobj.h
94
src/cobj.h
@@ -3,7 +3,8 @@
|
||||
|
||||
#include "cosmo.h"
|
||||
|
||||
typedef enum CObjType {
|
||||
typedef enum CObjType
|
||||
{
|
||||
COBJ_STRING,
|
||||
COBJ_OBJECT,
|
||||
COBJ_TABLE,
|
||||
@@ -17,14 +18,10 @@ typedef enum CObjType {
|
||||
COBJ_MAX
|
||||
} CObjType;
|
||||
|
||||
#include "cstate.h"
|
||||
#include "cchunk.h"
|
||||
#include "cvalue.h"
|
||||
#include "cstate.h"
|
||||
#include "ctable.h"
|
||||
|
||||
typedef struct CState CState;
|
||||
typedef struct CCallFrame CCallFrame;
|
||||
typedef uint32_t cosmo_Flag;
|
||||
#include "cvalue.h"
|
||||
|
||||
#define CommonHeader CObj _obj
|
||||
#define readFlag(x, flag) (x & (1u << flag))
|
||||
@@ -32,49 +29,57 @@ typedef uint32_t cosmo_Flag;
|
||||
|
||||
typedef int (*CosmoCFunction)(CState *state, int argCount, CValue *args);
|
||||
|
||||
typedef struct CObj {
|
||||
struct CObj
|
||||
{
|
||||
struct CObj *next;
|
||||
struct CObj *nextRoot; // for the root linked list
|
||||
struct CObjObject *proto; // protoobject, describes the behavior of the object
|
||||
CObjType type;
|
||||
bool isMarked; // for the GC
|
||||
} CObj;
|
||||
};
|
||||
|
||||
typedef struct CObjString {
|
||||
struct CObjString
|
||||
{
|
||||
CommonHeader; // "is a" CObj
|
||||
char *str; // NULL termincated string
|
||||
char *str; // NULL terminated string
|
||||
uint32_t hash; // for hashtable lookup
|
||||
int length;
|
||||
bool isIString;
|
||||
} CObjString;
|
||||
};
|
||||
|
||||
typedef struct CObjError {
|
||||
struct CObjError
|
||||
{
|
||||
CommonHeader; // "is a" CObj
|
||||
CValue err; // error string
|
||||
CCallFrame *frames;
|
||||
int frameCount;
|
||||
int line; // reserved for parser errors
|
||||
bool parserError; // if true, cosmoV_printError will format the error to the lexer
|
||||
} CObjError;
|
||||
};
|
||||
|
||||
typedef struct CObjObject {
|
||||
struct CObjObject
|
||||
{
|
||||
CommonHeader; // "is a" CObj
|
||||
CTable tbl;
|
||||
cosmo_Flag istringFlags; // enables us to have a much faster lookup for reserved IStrings (like __init, __index, etc.)
|
||||
union { // userdata (NULL by default)
|
||||
cosmo_Flag istringFlags; // enables us to have a much faster lookup for reserved IStrings (like
|
||||
// __init, __index, etc.)
|
||||
union
|
||||
{ // userdata (NULL by default)
|
||||
void *userP;
|
||||
int userI;
|
||||
};
|
||||
int userT; // user-defined type (for describing the userdata pointer/integer)
|
||||
bool isLocked;
|
||||
} CObjObject;
|
||||
};
|
||||
|
||||
typedef struct CObjTable { // table, a wrapper for CTable
|
||||
struct CObjTable
|
||||
{ // table, a wrapper for CTable
|
||||
CommonHeader; // "is a" CObj
|
||||
CTable tbl;
|
||||
} CObjTable;
|
||||
};
|
||||
|
||||
typedef struct CObjFunction {
|
||||
struct CObjFunction
|
||||
{
|
||||
CommonHeader; // "is a" CObj
|
||||
CChunk chunk;
|
||||
CObjString *name;
|
||||
@@ -82,32 +87,36 @@ typedef struct CObjFunction {
|
||||
int args;
|
||||
int upvals;
|
||||
bool variadic;
|
||||
} CObjFunction;
|
||||
};
|
||||
|
||||
typedef struct CObjCFunction {
|
||||
struct CObjCFunction
|
||||
{
|
||||
CommonHeader; // "is a" CObj
|
||||
CosmoCFunction cfunc;
|
||||
} CObjCFunction;
|
||||
};
|
||||
|
||||
typedef struct CObjClosure {
|
||||
struct CObjClosure
|
||||
{
|
||||
CommonHeader; // "is a" CObj
|
||||
CObjFunction *function;
|
||||
CObjUpval **upvalues;
|
||||
int upvalueCount;
|
||||
} CObjClosure;
|
||||
};
|
||||
|
||||
typedef struct CObjMethod {
|
||||
struct CObjMethod
|
||||
{
|
||||
CommonHeader; // "is a " CObj
|
||||
CValue func;
|
||||
CObj *obj; // obj this method is bound too
|
||||
} CObjMethod;
|
||||
};
|
||||
|
||||
typedef struct CObjUpval {
|
||||
struct CObjUpval
|
||||
{
|
||||
CommonHeader; // "is a" CObj
|
||||
CValue closed;
|
||||
CValue *val;
|
||||
struct CObjUpval *next;
|
||||
} CObjUpval;
|
||||
};
|
||||
|
||||
#undef CommonHeader
|
||||
|
||||
@@ -120,6 +129,7 @@ typedef struct CObjUpval {
|
||||
#define IS_CLOSURE(x) isObjType(x, COBJ_CLOSURE)
|
||||
|
||||
#define cosmoV_readString(x) ((CObjString *)cosmoV_readRef(x))
|
||||
#define cosmoV_readCString(x) (((CObjString *)cosmoV_readRef(x))->str)
|
||||
#define cosmoV_readObject(x) ((CObjObject *)cosmoV_readRef(x))
|
||||
#define cosmoV_readTable(x) ((CObjTable *)cosmoV_readRef(x))
|
||||
#define cosmoV_readFunction(x) ((CObjFunction *)cosmoV_readRef(x))
|
||||
@@ -128,18 +138,21 @@ typedef struct CObjUpval {
|
||||
#define cosmoV_readClosure(x) ((CObjClosure *)cosmoV_readRef(x))
|
||||
|
||||
#define cosmoO_readCString(x) ((CObjString *)x)->str
|
||||
#define cosmoO_readType(x) ((CObj *)x)->type
|
||||
|
||||
static inline bool isObjType(CValue val, CObjType type) {
|
||||
static inline bool isObjType(CValue val, CObjType type)
|
||||
{
|
||||
return IS_REF(val) && cosmoV_readRef(val)->type == type;
|
||||
}
|
||||
|
||||
// just protects against macro expansion
|
||||
static inline bool IS_CALLABLE(CValue val) {
|
||||
static inline bool IS_CALLABLE(CValue val)
|
||||
{
|
||||
return IS_CLOSURE(val) || IS_CFUNCTION(val) || IS_METHOD(val);
|
||||
}
|
||||
|
||||
void cosmoO_free(CState *state, CObj *obj);
|
||||
bool cosmoO_equal(CObj* obj1, CObj* obj2);
|
||||
bool cosmoO_equal(CState *state, CObj *obj1, CObj *obj2);
|
||||
|
||||
// walks the protos of obj and checks for proto
|
||||
bool cosmoO_isDescendant(CObj *obj, CObjObject *proto);
|
||||
@@ -154,7 +167,8 @@ CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func);
|
||||
CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val);
|
||||
|
||||
// grabs the base proto of the CObj* (if CObj is a CObjObject, that is returned)
|
||||
static inline CObjObject *cosmoO_grabProto(CObj *obj) {
|
||||
static inline CObjObject *cosmoO_grabProto(CObj *obj)
|
||||
{
|
||||
return obj->type == COBJ_OBJECT ? (CObjObject *)obj : obj->proto;
|
||||
}
|
||||
|
||||
@@ -163,11 +177,13 @@ void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue va
|
||||
bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val);
|
||||
bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val);
|
||||
|
||||
// sets the user-defined pointer, if a user-define integer is already defined it will be over written
|
||||
// sets the user-defined pointer, if a user-define integer is already defined it will be over
|
||||
// written
|
||||
void cosmoO_setUserP(CObjObject *object, void *p);
|
||||
// gets the user-defined pointer
|
||||
void *cosmoO_getUserP(CObjObject *object);
|
||||
// sets the user-defined integer, if a user-define pointer is already defined it will be over written
|
||||
// sets the user-defined integer, if a user-define pointer is already defined it will be over
|
||||
// written
|
||||
void cosmoO_setUserI(CObjObject *object, int i);
|
||||
// gets the user-defined integer
|
||||
int cosmoO_getUserI(CObjObject *object);
|
||||
@@ -183,10 +199,12 @@ void cosmoO_unlock(CObjObject *object);
|
||||
// internal string
|
||||
bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val);
|
||||
|
||||
// copies the *str buffer to the heap and returns a CObjString struct which is also on the heap (length should not include the null terminator)
|
||||
// copies the *str buffer to the heap and returns a CObjString struct which is also on the heap
|
||||
// (length should not include the null terminator)
|
||||
CObjString *cosmoO_copyString(CState *state, const char *str, size_t length);
|
||||
|
||||
// length shouldn't include the null terminator! str should be a null terminated string! (char array should also have been allocated using cosmoM_xmalloc!)
|
||||
// length shouldn't include the null terminator! str should be a null terminated string! (char array
|
||||
// should also have been allocated using cosmoM_xmalloc!)
|
||||
CObjString *cosmoO_takeString(CState *state, char *str, size_t length);
|
||||
|
||||
// allocates a CObjStruct pointing directly to *str
|
||||
|
@@ -5,7 +5,8 @@
|
||||
|
||||
// instructions
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
// STACK/STATE MANIPULATION
|
||||
OP_LOADCONST, // pushes const[uint8_t] to the stack
|
||||
OP_SETGLOBAL, // pops and sets global[const[uint16_t]]
|
||||
|
25
src/cosmo.h
25
src/cosmo.h
@@ -1,18 +1,18 @@
|
||||
#ifndef COSMOMAIN_H
|
||||
#define COSMOMAIN_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
SAFE_STACK:
|
||||
if undefined, the stack will not be checked for stack overflows. This may improve performance, however
|
||||
this will produce undefined behavior as you reach the stack limit (and may cause a seg fault!). It is recommended to keep this enabled.
|
||||
if undefined, the stack will not be checked for stack overflows. This may improve
|
||||
performance, however this will produce undefined behavior as you reach the stack limit (and may
|
||||
cause a seg fault!). It is recommended to keep this enabled.
|
||||
*/
|
||||
#define SAFE_STACK
|
||||
// #define NAN_BOXXED
|
||||
@@ -20,26 +20,34 @@
|
||||
// forward declare *most* stuff so our headers are cleaner
|
||||
typedef struct CState CState;
|
||||
typedef struct CChunk CChunk;
|
||||
typedef struct CCallFrame CCallFrame;
|
||||
|
||||
#ifdef NAN_BOXXED
|
||||
typedef union CValue CValue;
|
||||
#else
|
||||
typedef struct CValue CValue;
|
||||
#endif
|
||||
|
||||
typedef struct CValueArray CValueArray;
|
||||
typedef uint32_t cosmo_Flag;
|
||||
|
||||
// objs
|
||||
typedef struct CObj CObj;
|
||||
typedef struct CObjObject CObjObject;
|
||||
typedef struct CObjString CObjString;
|
||||
typedef struct CObjUpval CObjUpval;
|
||||
typedef struct CObjFunction CObjFunction;
|
||||
typedef struct CObjCFunction CObjCFunction;
|
||||
typedef struct CObjMethod CObjMethod;
|
||||
typedef struct CObjError CObjError;
|
||||
typedef struct CObjObject CObjObject;
|
||||
typedef struct CObjTable CObjTable;
|
||||
typedef struct CObjClosure CObjClosure;
|
||||
|
||||
typedef uint8_t INSTRUCTION;
|
||||
|
||||
typedef int (*cosmo_Reader)(CState *state, void *data, size_t size, const void *ud);
|
||||
typedef int (*cosmo_Writer)(CState *state, const void *data, size_t size, const void *ud);
|
||||
|
||||
#define COSMOMAX_UPVALS 80
|
||||
#define FRAME_MAX 64
|
||||
#define STACK_MAX (256 * FRAME_MAX)
|
||||
@@ -48,7 +56,6 @@ typedef uint8_t INSTRUCTION;
|
||||
#define UNNAMEDCHUNK "_main"
|
||||
#define COSMOASSERT(x) assert(x)
|
||||
|
||||
#define CERROR(err) \
|
||||
printf("%s : %s\n", "[ERROR]", err)
|
||||
#define CERROR(err) printf("%s : %s\n", "[ERROR]", err)
|
||||
|
||||
#endif
|
||||
|
570
src/cparse.c
570
src/cparse.c
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,11 @@
|
||||
#ifndef CPARSE_H
|
||||
#define CPARSE_H
|
||||
|
||||
#include "cosmo.h"
|
||||
#include "clex.h"
|
||||
#include "cosmo.h"
|
||||
|
||||
// compiles source into CChunk, if NULL is returned, a syntaxical error has occurred and pushed onto the stack
|
||||
// compiles source into CChunk, if NULL is returned, a syntaxical error has occurred and pushed onto
|
||||
// the stack
|
||||
CObjFunction *cosmoP_compileString(CState *state, const char *source, const char *module);
|
||||
|
||||
#endif
|
||||
|
20
src/cstate.c
20
src/cstate.c
@@ -1,12 +1,14 @@
|
||||
#include "cstate.h"
|
||||
|
||||
#include "cchunk.h"
|
||||
#include "cmem.h"
|
||||
#include "cobj.h"
|
||||
#include "cvm.h"
|
||||
#include "cmem.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
CState *cosmoV_newState() {
|
||||
CState *cosmoV_newState()
|
||||
{
|
||||
// we use C's malloc because we don't want to trigger a GC with an invalid state
|
||||
CState *state = malloc(sizeof(CState));
|
||||
|
||||
@@ -50,6 +52,7 @@ CState *cosmoV_newState() {
|
||||
state->iStrings[ISTRING_TOSTRING] = cosmoO_copyString(state, "__tostring", 10);
|
||||
state->iStrings[ISTRING_TONUMBER] = cosmoO_copyString(state, "__tonumber", 10);
|
||||
state->iStrings[ISTRING_INDEX] = cosmoO_copyString(state, "__index", 7);
|
||||
state->iStrings[ISTRING_EQUAL] = cosmoO_copyString(state, "__equal", 7);
|
||||
state->iStrings[ISTRING_NEWINDEX] = cosmoO_copyString(state, "__newindex", 10);
|
||||
state->iStrings[ISTRING_COUNT] = cosmoO_copyString(state, "__count", 7);
|
||||
|
||||
@@ -72,7 +75,8 @@ CState *cosmoV_newState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
void cosmoV_freeState(CState *state) {
|
||||
void cosmoV_freeState(CState *state)
|
||||
{
|
||||
#ifdef GC_DEBUG
|
||||
printf("state %p is being free'd!\n", state);
|
||||
#endif
|
||||
@@ -99,15 +103,16 @@ void cosmoV_freeState(CState *state) {
|
||||
// TODO: yeah idk, it looks like im missing 520 bytes somewhere? i'll look into it later
|
||||
/*#ifdef GC_DEBUG
|
||||
if (state->allocatedBytes != sizeof(CState)) {
|
||||
printf("state->allocatedBytes doesn't match expected value (%lu), got %lu!", sizeof(CState), state->allocatedBytes);
|
||||
exit(0);
|
||||
printf("state->allocatedBytes doesn't match expected value (%lu), got %lu!",
|
||||
sizeof(CState), state->allocatedBytes); exit(0);
|
||||
}
|
||||
#endif*/
|
||||
free(state);
|
||||
}
|
||||
|
||||
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value
|
||||
void cosmoV_register(CState *state, int pairs) {
|
||||
void cosmoV_register(CState *state, int pairs)
|
||||
{
|
||||
for (int i = 0; i < pairs; i++) {
|
||||
StkPtr key = cosmoV_getTop(state, 1);
|
||||
StkPtr val = cosmoV_getTop(state, 0);
|
||||
@@ -119,7 +124,8 @@ void cosmoV_register(CState *state, int pairs) {
|
||||
}
|
||||
}
|
||||
|
||||
void cosmoV_printStack(CState *state) {
|
||||
void cosmoV_printStack(CState *state)
|
||||
{
|
||||
printf("==== [[ stack dump ]] ====\n");
|
||||
for (CValue *top = state->top - 1; top >= state->stack; top--) {
|
||||
printf("%d: ", (int)(top - state->stack));
|
||||
|
32
src/cstate.h
32
src/cstate.h
@@ -1,21 +1,24 @@
|
||||
#ifndef CSTATE_H
|
||||
#define CSTATE_H
|
||||
|
||||
#include "cosmo.h"
|
||||
#include "cobj.h"
|
||||
#include "cvalue.h"
|
||||
#include "cosmo.h"
|
||||
#include "ctable.h"
|
||||
#include "cvalue.h"
|
||||
|
||||
typedef struct CCallFrame {
|
||||
struct CCallFrame
|
||||
{
|
||||
CObjClosure *closure;
|
||||
INSTRUCTION *pc;
|
||||
CValue *base;
|
||||
} CCallFrame;
|
||||
};
|
||||
|
||||
typedef enum IStringEnum {
|
||||
typedef enum IStringEnum
|
||||
{
|
||||
ISTRING_INIT, // __init
|
||||
ISTRING_TOSTRING, // __tostring
|
||||
ISTRING_TONUMBER, // __tonumber
|
||||
ISTRING_EQUAL, // __equals
|
||||
ISTRING_INDEX, // __index
|
||||
ISTRING_NEWINDEX, // __newindex
|
||||
ISTRING_COUNT, // __count
|
||||
@@ -24,24 +27,29 @@ typedef enum IStringEnum {
|
||||
ISTRING_ITER, // __iter
|
||||
ISTRING_NEXT, // __next
|
||||
ISTRING_RESERVED, // __reserved
|
||||
ISTRING_MAX
|
||||
ISTRING_MAX // if this becomes greater than 33, we are out of space in cosmo_Flag. you'll have
|
||||
// to change that to uint64_t
|
||||
} IStringEnum;
|
||||
|
||||
typedef struct ArrayCObj {
|
||||
typedef struct ArrayCObj
|
||||
{
|
||||
CObj **array;
|
||||
int count;
|
||||
int capacity;
|
||||
} ArrayCObj;
|
||||
|
||||
typedef struct CState {
|
||||
struct CState
|
||||
{
|
||||
bool panic;
|
||||
int freezeGC; // when > 0, GC events will be ignored (for internal use)
|
||||
int frameCount;
|
||||
|
||||
CObjError *error; // NULL, unless panic is true
|
||||
CObj *objects; // tracks all of our allocated objects
|
||||
CObj *userRoots; // user definable roots, this holds CObjs that should be considered "roots", lets the VM know you are holding a reference to a CObj in your code
|
||||
ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but *have been* found
|
||||
CObj *userRoots; // user definable roots, this holds CObjs that should be considered "roots",
|
||||
// lets the VM know you are holding a reference to a CObj in your code
|
||||
ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but
|
||||
// *have been* found
|
||||
size_t allocatedBytes;
|
||||
size_t nextGC; // when allocatedBytes reaches this threshhold, trigger a GC event
|
||||
|
||||
@@ -51,10 +59,10 @@ typedef struct CState {
|
||||
|
||||
CValue *top; // top of the stack
|
||||
CObjObject *protoObjects[COBJ_MAX]; // proto object for each COBJ type [NULL = no default proto]
|
||||
CObjString *iStrings[ISTRING_MAX]; // strings used internally by the VM, eg. __init, __index & friends
|
||||
CObjString *iStrings[ISTRING_MAX]; // strings used internally by the VM, eg. __init, __index
|
||||
CCallFrame callFrame[FRAME_MAX]; // call frames
|
||||
CValue stack[STACK_MAX]; // stack
|
||||
} CState;
|
||||
};
|
||||
|
||||
COSMO_API CState *cosmoV_newState();
|
||||
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value
|
||||
|
97
src/ctable.c
97
src/ctable.c
@@ -1,7 +1,8 @@
|
||||
#include "ctable.h"
|
||||
|
||||
#include "cmem.h"
|
||||
#include "cvalue.h"
|
||||
#include "cobj.h"
|
||||
#include "cvalue.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@@ -10,12 +11,15 @@
|
||||
#define MIN_TABLE_CAPACITY ARRAY_START
|
||||
|
||||
// bit-twiddling hacks, gets the next power of 2
|
||||
unsigned int nextPow2(unsigned int x) {
|
||||
if (x <= ARRAY_START - 1) return ARRAY_START; // sanity check
|
||||
unsigned int nextPow2(unsigned int x)
|
||||
{
|
||||
if (x <= ARRAY_START - 1)
|
||||
return ARRAY_START; // sanity check
|
||||
x--;
|
||||
|
||||
int power = 2;
|
||||
while (x >>= 1) power <<= 1;
|
||||
while (x >>= 1)
|
||||
power <<= 1;
|
||||
|
||||
if (power < ARRAY_START)
|
||||
return ARRAY_START;
|
||||
@@ -23,7 +27,8 @@ unsigned int nextPow2(unsigned int x) {
|
||||
return power;
|
||||
}
|
||||
|
||||
void cosmoT_initTable(CState *state, CTable *tbl, int startCap) {
|
||||
void cosmoT_initTable(CState *state, CTable *tbl, int startCap)
|
||||
{
|
||||
startCap = startCap != 0 ? startCap : ARRAY_START; // sanity check :P
|
||||
|
||||
tbl->capacityMask = startCap - 1;
|
||||
@@ -39,7 +44,8 @@ void cosmoT_initTable(CState *state, CTable *tbl, int startCap) {
|
||||
}
|
||||
}
|
||||
|
||||
void cosmoT_addTable(CState *state, CTable *from, CTable *to) {
|
||||
void cosmoT_addTable(CState *state, CTable *from, CTable *to)
|
||||
{
|
||||
int cap = from->capacityMask + 1;
|
||||
for (int i = 0; i < cap; i++) {
|
||||
CTableEntry *entry = &from->table[i];
|
||||
@@ -51,11 +57,13 @@ void cosmoT_addTable(CState *state, CTable *from, CTable *to) {
|
||||
}
|
||||
}
|
||||
|
||||
void cosmoT_clearTable(CState *state, CTable *tbl) {
|
||||
void cosmoT_clearTable(CState *state, CTable *tbl)
|
||||
{
|
||||
cosmoM_freearray(state, CTableEntry, tbl->table, (tbl->capacityMask + 1));
|
||||
}
|
||||
|
||||
uint32_t getObjectHash(CObj *obj) {
|
||||
uint32_t getObjectHash(CObj *obj)
|
||||
{
|
||||
switch (obj->type) {
|
||||
case COBJ_STRING:
|
||||
return ((CObjString *)obj)->hash;
|
||||
@@ -64,7 +72,8 @@ uint32_t getObjectHash(CObj *obj) {
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t getValueHash(CValue *val) {
|
||||
uint32_t getValueHash(CValue *val)
|
||||
{
|
||||
switch (GET_TYPE(*val)) {
|
||||
case COSMO_TREF:
|
||||
return getObjectHash(cosmoV_readRef(*val));
|
||||
@@ -76,7 +85,8 @@ uint32_t getValueHash(CValue *val) {
|
||||
return 0;
|
||||
|
||||
memcpy(buf, &num, sizeof(buf));
|
||||
for (size_t i = 0; i < sizeof(cosmo_Number)/sizeof(uint32_t); i++) buf[0] += buf[i];
|
||||
for (size_t i = 0; i < sizeof(cosmo_Number) / sizeof(uint32_t); i++)
|
||||
buf[0] += buf[i];
|
||||
return buf[0];
|
||||
}
|
||||
// TODO: add support for other types
|
||||
@@ -86,9 +96,11 @@ uint32_t getValueHash(CValue *val) {
|
||||
}
|
||||
|
||||
// mask should always be (capacity - 1)
|
||||
static CTableEntry *findEntry(CTableEntry *entries, int mask, CValue key) {
|
||||
static CTableEntry *findEntry(CState *state, CTableEntry *entries, int mask, CValue key)
|
||||
{
|
||||
uint32_t hash = getValueHash(&key);
|
||||
uint32_t indx = hash & mask; // since we know the capacity will *always* be a power of 2, we can use bitwise & to perform a MUCH faster mod operation
|
||||
uint32_t indx = hash & mask; // since we know the capacity will *always* be a power of 2, we can
|
||||
// use bitwise & to perform a MUCH faster mod operation
|
||||
CTableEntry *tomb = NULL;
|
||||
|
||||
// keep looking for an open slot in the entries array
|
||||
@@ -104,7 +116,7 @@ static CTableEntry *findEntry(CTableEntry *entries, int mask, CValue key) {
|
||||
// its a tombstone!
|
||||
tomb = entry;
|
||||
}
|
||||
} else if (cosmoV_equal(entry->key, key)) {
|
||||
} else if (cosmoV_equal(state, entry->key, key)) {
|
||||
return entry;
|
||||
}
|
||||
|
||||
@@ -112,7 +124,8 @@ static CTableEntry *findEntry(CTableEntry *entries, int mask, CValue key) {
|
||||
}
|
||||
}
|
||||
|
||||
static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrink) {
|
||||
static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrink)
|
||||
{
|
||||
if (canShrink && cosmoT_checkShrink(state, tbl))
|
||||
return;
|
||||
|
||||
@@ -122,7 +135,8 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
|
||||
|
||||
cosmoM_checkGarbage(state, size); // if this allocation would cause a GC, run the GC
|
||||
|
||||
if (tbl->count < cachedCount) // the GC removed some objects from this table and resized it, ignore our resize event!
|
||||
if (tbl->count < cachedCount) // the GC removed some objects from this table and resized it,
|
||||
// ignore our resize event!
|
||||
return;
|
||||
|
||||
CTableEntry *entries = cosmoM_xmalloc(state, size);
|
||||
@@ -142,7 +156,7 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
|
||||
continue; // skip empty keys
|
||||
|
||||
// get new entry location & update the node
|
||||
CTableEntry *newEntry = findEntry(entries, newCapacity - 1, oldEntry->key);
|
||||
CTableEntry *newEntry = findEntry(state, entries, newCapacity - 1, oldEntry->key);
|
||||
newEntry->key = oldEntry->key;
|
||||
newEntry->val = oldEntry->val;
|
||||
newCount++; // inc count
|
||||
@@ -157,10 +171,14 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
|
||||
tbl->tombstones = 0;
|
||||
}
|
||||
|
||||
bool cosmoT_checkShrink(CState *state, CTable *tbl) {
|
||||
bool cosmoT_checkShrink(CState *state, CTable *tbl)
|
||||
{
|
||||
// if count > 8 and active entries < tombstones
|
||||
if (tbl->count > MIN_TABLE_CAPACITY && (tbl->count - tbl->tombstones < tbl->tombstones || tbl->tombstones > 50)) { // TODO: 50 should be a threshhold
|
||||
resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR, false); // shrink based on active entries to the next pow of 2
|
||||
if (tbl->count > MIN_TABLE_CAPACITY &&
|
||||
(tbl->count - tbl->tombstones < tbl->tombstones ||
|
||||
tbl->tombstones > 50)) { // TODO: 50 should be a threshhold
|
||||
resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR,
|
||||
false); // shrink based on active entries to the next pow of 2
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -168,7 +186,8 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl) {
|
||||
}
|
||||
|
||||
// returns a pointer to the allocated value
|
||||
COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) {
|
||||
COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key)
|
||||
{
|
||||
// make sure we have enough space allocated
|
||||
int cap = tbl->capacityMask + 1;
|
||||
if (tbl->count + 1 > (int)(cap * MAX_TABLE_FILL)) {
|
||||
@@ -178,7 +197,8 @@ COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) {
|
||||
}
|
||||
|
||||
// insert into the table
|
||||
CTableEntry *entry = findEntry(tbl->table, tbl->capacityMask, key); // -1 for our capacity mask
|
||||
CTableEntry *entry =
|
||||
findEntry(state, tbl->table, tbl->capacityMask, key); // -1 for our capacity mask
|
||||
|
||||
if (IS_NIL(entry->key)) {
|
||||
if (IS_NIL(entry->val)) // is it empty?
|
||||
@@ -191,43 +211,52 @@ COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) {
|
||||
return &entry->val;
|
||||
}
|
||||
|
||||
bool cosmoT_get(CTable *tbl, CValue key, CValue *val) {
|
||||
bool cosmoT_get(CState *state, CTable *tbl, CValue key, CValue *val)
|
||||
{
|
||||
// sanity check
|
||||
if (tbl->count == 0) {
|
||||
*val = cosmoV_newNil();
|
||||
return false;
|
||||
}
|
||||
|
||||
CTableEntry *entry = findEntry(tbl->table, tbl->capacityMask, key);
|
||||
CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key);
|
||||
*val = entry->val;
|
||||
|
||||
// return if get was successful
|
||||
return !(IS_NIL(entry->key));
|
||||
}
|
||||
|
||||
bool cosmoT_remove(CState* state, CTable *tbl, CValue key) {
|
||||
if (tbl->count == 0) return 0; // sanity check
|
||||
bool cosmoT_remove(CState *state, CTable *tbl, CValue key)
|
||||
{
|
||||
if (tbl->count == 0)
|
||||
return 0; // sanity check
|
||||
|
||||
CTableEntry *entry = findEntry(tbl->table, tbl->capacityMask, key);
|
||||
CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key);
|
||||
if (IS_NIL(entry->key)) // sanity check
|
||||
return false;
|
||||
|
||||
// crafts tombstone
|
||||
entry->key = cosmoV_newNil(); // this has to be nil
|
||||
entry->val = cosmoV_newBoolean(false); // doesn't really matter what this is, as long as it isn't nil
|
||||
entry->val =
|
||||
cosmoV_newBoolean(false); // doesn't really matter what this is, as long as it isn't nil
|
||||
tbl->tombstones++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns the active entry count
|
||||
COSMO_API int cosmoT_count(CTable *tbl) {
|
||||
COSMO_API int cosmoT_count(CTable *tbl)
|
||||
{
|
||||
return tbl->count - tbl->tombstones;
|
||||
}
|
||||
|
||||
CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32_t hash) {
|
||||
if (tbl->count == 0) return 0; // sanity check
|
||||
uint32_t indx = hash & tbl->capacityMask; // since we know the capacity will *always* be a power of 2, we can use bitwise & to perform a MUCH faster mod operation
|
||||
CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32_t hash)
|
||||
{
|
||||
if (tbl->count == 0)
|
||||
return 0; // sanity check
|
||||
uint32_t indx =
|
||||
hash & tbl->capacityMask; // since we know the capacity will *always* be a power of 2, we
|
||||
// can use bitwise & to perform a MUCH faster mod operation
|
||||
|
||||
// keep looking for an open slot in the entries array
|
||||
while (true) {
|
||||
@@ -236,7 +265,8 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32
|
||||
// check if it's an empty slot (meaning we dont have it in the table)
|
||||
if (IS_NIL(entry->key) && IS_NIL(entry->val)) {
|
||||
return NULL;
|
||||
} else if (IS_STRING(entry->key) && cosmoV_readString(entry->key)->length == length && memcmp(cosmoV_readString(entry->key)->str, str, length) == 0) {
|
||||
} else if (IS_STRING(entry->key) && cosmoV_readString(entry->key)->length == length &&
|
||||
memcmp(cosmoV_readString(entry->key)->str, str, length) == 0) {
|
||||
// it's a match!
|
||||
return (CObjString *)cosmoV_readRef(entry->key);
|
||||
}
|
||||
@@ -246,7 +276,8 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32
|
||||
}
|
||||
|
||||
// for debugging purposes
|
||||
void cosmoT_printTable(CTable *tbl, const char *name) {
|
||||
void cosmoT_printTable(CTable *tbl, const char *name)
|
||||
{
|
||||
printf("==== [[%s]] ====\n", name);
|
||||
int cap = tbl->capacityMask + 1;
|
||||
for (int i = 0; i < cap; i++) {
|
||||
|
11
src/ctable.h
11
src/ctable.h
@@ -1,17 +1,20 @@
|
||||
#ifndef CTABLE_H
|
||||
#define CTABLE_H
|
||||
|
||||
/* TODO: rewrite this table implementation. compared to other languages (including python!) this table is verrryyyy slow */
|
||||
/* TODO: rewrite this table implementation. compared to other languages (including python!) this
|
||||
* table is verrryyyy slow */
|
||||
|
||||
#include "cosmo.h"
|
||||
#include "cvalue.h"
|
||||
|
||||
typedef struct CTableEntry {
|
||||
typedef struct CTableEntry
|
||||
{
|
||||
CValue key;
|
||||
CValue val;
|
||||
} CTableEntry;
|
||||
|
||||
typedef struct CTable {
|
||||
typedef struct CTable
|
||||
{
|
||||
int count;
|
||||
int capacityMask; // +1 to get the capacity
|
||||
int tombstones;
|
||||
@@ -26,7 +29,7 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl);
|
||||
|
||||
CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32_t hash);
|
||||
CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key);
|
||||
bool cosmoT_get(CTable *tbl, CValue key, CValue *val);
|
||||
bool cosmoT_get(CState *state, CTable *tbl, CValue key, CValue *val);
|
||||
bool cosmoT_remove(CState *state, CTable *tbl, CValue key);
|
||||
|
||||
void cosmoT_printTable(CTable *tbl, const char *name);
|
||||
|
212
src/cundump.c
Normal file
212
src/cundump.c
Normal file
@@ -0,0 +1,212 @@
|
||||
#include "cundump.h"
|
||||
|
||||
#include "cchunk.h"
|
||||
#include "cdump.h"
|
||||
#include "cmem.h"
|
||||
#include "cvm.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CState *state;
|
||||
const void *userData;
|
||||
cosmo_Reader reader;
|
||||
int readerStatus;
|
||||
} UndumpState;
|
||||
|
||||
static bool readCValue(UndumpState *udstate, CValue *val);
|
||||
|
||||
#define check(e) \
|
||||
if (!e) { \
|
||||
printf("FAILED %d\n", __LINE__); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
static void initUndumpState(CState *state, UndumpState *udstate, cosmo_Reader reader,
|
||||
const void *userData)
|
||||
{
|
||||
udstate->state = state;
|
||||
udstate->userData = userData;
|
||||
udstate->reader = reader;
|
||||
udstate->readerStatus = 0;
|
||||
}
|
||||
|
||||
static bool readBlock(UndumpState *udstate, void *data, size_t size)
|
||||
{
|
||||
if (udstate->readerStatus == 0) {
|
||||
udstate->readerStatus = udstate->reader(udstate->state, data, size, udstate->userData);
|
||||
}
|
||||
|
||||
return udstate->readerStatus == 0;
|
||||
}
|
||||
|
||||
static bool readu8(UndumpState *udstate, uint8_t *d)
|
||||
{
|
||||
return readBlock(udstate, d, sizeof(uint8_t));
|
||||
}
|
||||
|
||||
static bool readu32(UndumpState *udstate, uint32_t *d)
|
||||
{
|
||||
return readBlock(udstate, d, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
static bool readSize(UndumpState *udstate, size_t *d)
|
||||
{
|
||||
return readBlock(udstate, d, sizeof(size_t));
|
||||
}
|
||||
|
||||
static bool readVector(UndumpState *udstate, void **data, size_t size, size_t *count)
|
||||
{
|
||||
check(readSize(udstate, count));
|
||||
*data = cosmoM_xmalloc(udstate->state, (*count) * size);
|
||||
return readBlock(udstate, *data, (*count) * size);
|
||||
}
|
||||
|
||||
#define checku8(udstate, d, tmp) \
|
||||
check(readu8(udstate, &tmp)); \
|
||||
if (d != tmp) { \
|
||||
cosmoV_error(udstate->state, "bad header!"); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
static bool checkHeader(UndumpState *udstate)
|
||||
{
|
||||
char magic[COSMO_MAGIC_LEN];
|
||||
uint8_t tmp;
|
||||
|
||||
/* check header */
|
||||
readBlock(udstate, magic, COSMO_MAGIC_LEN);
|
||||
if (memcmp(magic, COSMO_MAGIC, COSMO_MAGIC_LEN) != 0) {
|
||||
cosmoV_error(udstate->state, "bad header!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* after the magic, we read some platform information */
|
||||
checku8(udstate, cosmoD_isBigEndian(), tmp);
|
||||
checku8(udstate, sizeof(cosmo_Number), tmp);
|
||||
checku8(udstate, sizeof(size_t), tmp);
|
||||
checku8(udstate, sizeof(int), tmp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#undef checku8
|
||||
|
||||
static bool readCObjString(UndumpState *udstate, CObjString **str)
|
||||
{
|
||||
uint32_t size;
|
||||
char *data;
|
||||
|
||||
check(readu32(udstate, (uint32_t *)&size));
|
||||
if (size == 0) { /* empty string */
|
||||
*str = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
data = cosmoM_xmalloc(udstate->state, size + 1);
|
||||
check(readBlock(udstate, (void *)data, size));
|
||||
data[size] = '\0'; /* add NULL-terminator */
|
||||
|
||||
*str = cosmoO_takeString(udstate->state, data, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool readCObjFunction(UndumpState *udstate, CObjFunction **func)
|
||||
{
|
||||
size_t constants;
|
||||
CValue val;
|
||||
|
||||
*func = cosmoO_newFunction(udstate->state);
|
||||
|
||||
check(readCObjString(udstate, &(*func)->name));
|
||||
check(readCObjString(udstate, &(*func)->module));
|
||||
|
||||
check(readu32(udstate, (uint32_t *)&(*func)->args));
|
||||
check(readu32(udstate, (uint32_t *)&(*func)->upvals));
|
||||
check(readu8(udstate, (uint8_t *)&(*func)->variadic));
|
||||
|
||||
/* read chunk info */
|
||||
check(
|
||||
readVector(udstate, (void **)&(*func)->chunk.buf, sizeof(uint8_t), &(*func)->chunk.count));
|
||||
check(
|
||||
readVector(udstate, (void **)&(*func)->chunk.lineInfo, sizeof(int), &(*func)->chunk.count));
|
||||
|
||||
/* read constants */
|
||||
check(readSize(udstate, &constants));
|
||||
for (int i = 0; i < constants; i++) {
|
||||
check(readCValue(udstate, &val));
|
||||
addConstant(udstate->state, &(*func)->chunk, val);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool readCObj(UndumpState *udstate, CObj **obj)
|
||||
{
|
||||
uint8_t type;
|
||||
check(readu8(udstate, &type));
|
||||
|
||||
switch (type) {
|
||||
case COBJ_STRING:
|
||||
return readCObjString(udstate, (CObjString **)obj);
|
||||
case COBJ_FUNCTION:
|
||||
return readCObjFunction(udstate, (CObjFunction **)obj);
|
||||
default:
|
||||
cosmoV_error(udstate->state, "unknown object type!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define READ_VAR(udstate, val, type, creator) \
|
||||
{ \
|
||||
type _tmp; \
|
||||
check(readBlock(udstate, &_tmp, sizeof(type))); \
|
||||
*val = creator(_tmp); \
|
||||
break; \
|
||||
}
|
||||
|
||||
static bool readCValue(UndumpState *udstate, CValue *val)
|
||||
{
|
||||
uint8_t type;
|
||||
check(readu8(udstate, &type));
|
||||
|
||||
switch (type) {
|
||||
case COSMO_TNUMBER:
|
||||
READ_VAR(udstate, val, cosmo_Number, cosmoV_newNumber)
|
||||
case COSMO_TBOOLEAN:
|
||||
READ_VAR(udstate, val, bool, cosmoV_newBoolean)
|
||||
case COSMO_TREF: {
|
||||
CObj *obj;
|
||||
check(readCObj(udstate, (CObj **)&obj));
|
||||
*val = cosmoV_newRef(obj);
|
||||
break;
|
||||
}
|
||||
case COSMO_TNIL:
|
||||
*val = cosmoV_newNil();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int cosmoD_undump(CState *state, cosmo_Reader reader, const void *userData, CObjFunction **func)
|
||||
{
|
||||
UndumpState udstate;
|
||||
initUndumpState(state, &udstate, reader, userData);
|
||||
|
||||
if (!checkHeader(&udstate)) {
|
||||
cosmoV_pushNil(state);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!readCObjFunction(&udstate, func)) {
|
||||
cosmoV_pushNil(state);
|
||||
return 1;
|
||||
}
|
||||
|
||||
cosmoV_pushRef(state, (CObj *)*func);
|
||||
return udstate.readerStatus;
|
||||
}
|
12
src/cundump.h
Normal file
12
src/cundump.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef COSMO_UNDUMP_H
|
||||
#define COSMO_UNDUMP_H
|
||||
|
||||
#include "cobj.h"
|
||||
#include "cosmo.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* returns non-zero on error */
|
||||
int cosmoD_undump(CState *state, cosmo_Reader reader, const void *userData, CObjFunction **func);
|
||||
|
||||
#endif
|
58
src/cvalue.c
58
src/cvalue.c
@@ -1,40 +1,50 @@
|
||||
#include "cosmo.h"
|
||||
#include "cmem.h"
|
||||
#include "cvalue.h"
|
||||
#include "cobj.h"
|
||||
|
||||
void initValArray(CState *state, CValueArray *val, size_t startCapacity) {
|
||||
#include "cmem.h"
|
||||
#include "cobj.h"
|
||||
#include "cosmo.h"
|
||||
|
||||
void initValArray(CState *state, CValueArray *val, size_t startCapacity)
|
||||
{
|
||||
val->count = 0;
|
||||
val->capacity = startCapacity;
|
||||
val->values = NULL;
|
||||
}
|
||||
|
||||
void cleanValArray(CState *state, CValueArray *array) {
|
||||
void cleanValArray(CState *state, CValueArray *array)
|
||||
{
|
||||
cosmoM_freearray(state, CValue, array->values, array->capacity);
|
||||
}
|
||||
|
||||
void appendValArray(CState *state, CValueArray *array, CValue val) {
|
||||
void appendValArray(CState *state, CValueArray *array, CValue val)
|
||||
{
|
||||
cosmoM_growarray(state, CValue, array->values, array->count, array->capacity);
|
||||
|
||||
array->values[array->count++] = val;
|
||||
}
|
||||
|
||||
bool cosmoV_equal(CValue valA, CValue valB) {
|
||||
bool cosmoV_equal(CState *state, CValue valA, CValue valB)
|
||||
{
|
||||
if (GET_TYPE(valA) != GET_TYPE(valB)) // are they the same type?
|
||||
return false;
|
||||
|
||||
// compare
|
||||
switch (GET_TYPE(valA)) {
|
||||
case COSMO_TBOOLEAN: return cosmoV_readBoolean(valA) == cosmoV_readBoolean(valB);
|
||||
case COSMO_TNUMBER: return cosmoV_readNumber(valA) == cosmoV_readNumber(valB);
|
||||
case COSMO_TREF: return cosmoO_equal(cosmoV_readRef(valA), cosmoV_readRef(valB));
|
||||
case COSMO_TNIL: return true;
|
||||
case COSMO_TBOOLEAN:
|
||||
return cosmoV_readBoolean(valA) == cosmoV_readBoolean(valB);
|
||||
case COSMO_TNUMBER:
|
||||
return cosmoV_readNumber(valA) == cosmoV_readNumber(valB);
|
||||
case COSMO_TREF:
|
||||
return cosmoO_equal(state, cosmoV_readRef(valA), cosmoV_readRef(valB));
|
||||
case COSMO_TNIL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CObjString *cosmoV_toString(CState *state, CValue val) {
|
||||
CObjString *cosmoV_toString(CState *state, CValue val)
|
||||
{
|
||||
switch (GET_TYPE(val)) {
|
||||
case COSMO_TNUMBER: {
|
||||
char buf[32];
|
||||
@@ -42,7 +52,8 @@ CObjString *cosmoV_toString(CState *state, CValue val) {
|
||||
return cosmoO_copyString(state, (char *)&buf, size);
|
||||
}
|
||||
case COSMO_TBOOLEAN: {
|
||||
return cosmoV_readBoolean(val) ? cosmoO_copyString(state, "true", 4) : cosmoO_copyString(state, "false", 5);
|
||||
return cosmoV_readBoolean(val) ? cosmoO_copyString(state, "true", 4)
|
||||
: cosmoO_copyString(state, "false", 5);
|
||||
}
|
||||
case COSMO_TREF: {
|
||||
return cosmoO_toString(state, cosmoV_readRef(val));
|
||||
@@ -55,7 +66,8 @@ CObjString *cosmoV_toString(CState *state, CValue val) {
|
||||
}
|
||||
}
|
||||
|
||||
cosmo_Number cosmoV_toNumber(CState *state, CValue val) {
|
||||
cosmo_Number cosmoV_toNumber(CState *state, CValue val)
|
||||
{
|
||||
switch (GET_TYPE(val)) {
|
||||
case COSMO_TNUMBER: {
|
||||
return cosmoV_readNumber(val);
|
||||
@@ -72,19 +84,25 @@ cosmo_Number cosmoV_toNumber(CState *state, CValue val) {
|
||||
}
|
||||
}
|
||||
|
||||
const char *cosmoV_typeStr(CValue val) {
|
||||
const char *cosmoV_typeStr(CValue val)
|
||||
{
|
||||
switch (GET_TYPE(val)) {
|
||||
case COSMO_TNIL: return "<nil>";
|
||||
case COSMO_TBOOLEAN: return "<bool>";
|
||||
case COSMO_TNUMBER: return "<number>";
|
||||
case COSMO_TREF: return cosmoO_typeStr(cosmoV_readRef(val));
|
||||
case COSMO_TNIL:
|
||||
return "<nil>";
|
||||
case COSMO_TBOOLEAN:
|
||||
return "<bool>";
|
||||
case COSMO_TNUMBER:
|
||||
return "<number>";
|
||||
case COSMO_TREF:
|
||||
return cosmoO_typeStr(cosmoV_readRef(val));
|
||||
|
||||
default:
|
||||
return "<unkn val>";
|
||||
}
|
||||
}
|
||||
|
||||
void printValue(CValue val) {
|
||||
void printValue(CValue val)
|
||||
{
|
||||
switch (GET_TYPE(val)) {
|
||||
case COSMO_TNUMBER:
|
||||
printf("%g", cosmoV_readNumber(val));
|
||||
|
32
src/cvalue.h
32
src/cvalue.h
@@ -3,7 +3,8 @@
|
||||
|
||||
#include "cosmo.h"
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
COSMO_TNUMBER, // number has to be 0 because NaN box
|
||||
COSMO_TBOOLEAN,
|
||||
COSMO_TREF,
|
||||
@@ -18,8 +19,8 @@ typedef double cosmo_Number;
|
||||
|
||||
#ifdef NAN_BOXXED
|
||||
/*
|
||||
NaN box, this is great for fitting more in the cpu cache on x86_64 or ARM64 architectures. If you don't know how this works please reference these
|
||||
two articles:
|
||||
NaN box, this is great for fitting more in the cpu cache on x86_64 or ARM64 architectures.
|
||||
If you don't know how this works please reference these two articles:
|
||||
|
||||
https://leonardschuetz.ch/blog/nan-boxing/ and https://piotrduperas.com/posts/nan-boxing/
|
||||
|
||||
@@ -27,10 +28,11 @@ typedef double cosmo_Number;
|
||||
|
||||
TL;DR: we can store payloads in the NaN value in the IEEE 754 standard.
|
||||
*/
|
||||
typedef union CValue {
|
||||
union CValue
|
||||
{
|
||||
uint64_t data;
|
||||
cosmo_Number num;
|
||||
} CValue;
|
||||
};
|
||||
|
||||
# define MASK_TYPE ((uint64_t)0x0007000000000000)
|
||||
# define MASK_PAYLOAD ((uint64_t)0x0000ffffffffffff)
|
||||
@@ -43,7 +45,8 @@ typedef union CValue {
|
||||
# define MASK_QUIETNAN ((uint64_t)0x7ff8000000000000)
|
||||
|
||||
# define GET_TYPE(x) \
|
||||
((((x).data & MASK_QUIETNAN) == MASK_QUIETNAN) ? (((x).data & MASK_TYPE) >> 48) : COSMO_TNUMBER)
|
||||
((((x).data & MASK_QUIETNAN) == MASK_QUIETNAN) ? (((x).data & MASK_TYPE) >> 48) \
|
||||
: COSMO_TNUMBER)
|
||||
|
||||
# define SIG_MASK (MASK_QUIETNAN | MASK_TYPE)
|
||||
# define BOOL_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TBOOLEAN) << 48))
|
||||
@@ -68,14 +71,16 @@ typedef union CValue {
|
||||
/*
|
||||
Tagged union, this is the best platform independent solution
|
||||
*/
|
||||
typedef struct CValue {
|
||||
struct CValue
|
||||
{
|
||||
CosmoType type;
|
||||
union {
|
||||
union
|
||||
{
|
||||
cosmo_Number num;
|
||||
bool b; // boolean
|
||||
CObj *obj;
|
||||
} val;
|
||||
} CValue;
|
||||
};
|
||||
|
||||
# define GET_TYPE(x) ((x).type)
|
||||
|
||||
@@ -103,20 +108,21 @@ typedef struct CValue {
|
||||
|
||||
typedef CValue *StkPtr;
|
||||
|
||||
typedef struct CValueArray {
|
||||
struct CValueArray
|
||||
{
|
||||
size_t capacity;
|
||||
size_t count;
|
||||
CValue *values;
|
||||
} CValueArray;
|
||||
};
|
||||
|
||||
void initValArray(CState *state, CValueArray *val, size_t startCapacity);
|
||||
void cleanValArray(CState *state, CValueArray *array); // cleans array
|
||||
void appendValArray(CState *state, CValueArray *array, CValue val);
|
||||
|
||||
void printValue(CValue val);
|
||||
COSMO_API bool cosmoV_equal(CValue valA, CValue valB);
|
||||
COSMO_API bool cosmoV_equal(CState *state, CValue valA, CValue valB);
|
||||
COSMO_API CObjString *cosmoV_toString(CState *state, CValue val);
|
||||
COSMO_API cosmo_Number cosmoV_toNumber(CState *state, CValue val);
|
||||
COSMO_API const char *cosmoV_typeStr(CValue val); // return constant char array for corresponding type
|
||||
COSMO_API const char *cosmoV_typeStr(CValue val);
|
||||
|
||||
#endif
|
||||
|
296
src/cvm.c
296
src/cvm.c
@@ -1,15 +1,17 @@
|
||||
#include "cvm.h"
|
||||
#include "cstate.h"
|
||||
|
||||
#include "cdebug.h"
|
||||
#include "cmem.h"
|
||||
#include "cparse.h"
|
||||
#include "cstate.h"
|
||||
#include "cundump.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...) {
|
||||
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
cosmoO_pushVFString(state, format, args);
|
||||
@@ -17,7 +19,8 @@ COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...) {
|
||||
}
|
||||
|
||||
// inserts val at state->top - indx - 1, moving everything else up
|
||||
COSMO_API void cosmo_insert(CState *state, int indx, CValue val) {
|
||||
COSMO_API void cosmo_insert(CState *state, int indx, CValue val)
|
||||
{
|
||||
StkPtr tmp = cosmoV_getTop(state, indx);
|
||||
|
||||
// moves everything up
|
||||
@@ -28,7 +31,30 @@ COSMO_API void cosmo_insert(CState *state, int indx, CValue val) {
|
||||
state->top++;
|
||||
}
|
||||
|
||||
COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name) {
|
||||
COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud)
|
||||
{
|
||||
CObjFunction *func;
|
||||
|
||||
if (cosmoD_undump(state, reader, ud, &func)) {
|
||||
// fail recovery
|
||||
state->panic = false;
|
||||
cosmoV_pushRef(state, (CObj *)state->error);
|
||||
return false;
|
||||
};
|
||||
|
||||
#ifdef VM_DEBUG
|
||||
disasmChunk(&func->chunk, func->name ? func->name->str : UNNAMEDCHUNK, 0);
|
||||
#endif
|
||||
|
||||
// push function onto the stack so it doesn't it cleaned up by the GC, at the same stack
|
||||
// location put our closure
|
||||
cosmoV_pushRef(state, (CObj *)func);
|
||||
*(cosmoV_getTop(state, 0)) = cosmoV_newRef(cosmoO_newClosure(state, func));
|
||||
return true;
|
||||
}
|
||||
|
||||
COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name)
|
||||
{
|
||||
CObjFunction *func;
|
||||
|
||||
if ((func = cosmoP_compileString(state, src, name)) != NULL) {
|
||||
@@ -36,19 +62,21 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *
|
||||
#ifdef VM_DEBUG
|
||||
disasmChunk(&func->chunk, func->module->str, 0);
|
||||
#endif
|
||||
// push function onto the stack so it doesn't it cleaned up by the GC, at the same stack location put our closure
|
||||
// push function onto the stack so it doesn't it cleaned up by the GC, at the same stack
|
||||
// location put our closure
|
||||
cosmoV_pushRef(state, (CObj *)func);
|
||||
*(cosmoV_getTop(state, 0)) = cosmoV_newRef(cosmoO_newClosure(state, func));
|
||||
return true;
|
||||
}
|
||||
|
||||
// fail
|
||||
// fail recovery
|
||||
state->panic = false;
|
||||
cosmoV_pushRef(state, (CObj *)state->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
COSMO_API void cosmoV_printError(CState *state, CObjError *err) {
|
||||
COSMO_API void cosmoV_printError(CState *state, CObjError *err)
|
||||
{
|
||||
// print stack trace
|
||||
for (int i = 0; i < err->frameCount; i++) {
|
||||
CCallFrame *frame = &err->frames[i];
|
||||
@@ -57,8 +85,11 @@ COSMO_API void cosmoV_printError(CState *state, CObjError *err) {
|
||||
|
||||
int line = chunk->lineInfo[frame->pc - chunk->buf - 1];
|
||||
|
||||
if (i == err->frameCount - 1 && !err->parserError) // it's the last call frame (and not a parser error), prepare for the objection to be printed
|
||||
fprintf(stderr, "Objection in %.*s on [line %d] in ", function->module->length, function->module->str, line);
|
||||
if (i == err->frameCount - 1 &&
|
||||
!err->parserError) // it's the last call frame (and not a parser error), prepare for the
|
||||
// objection to be printed
|
||||
fprintf(stderr, "Objection in %.*s on [line %d] in ", function->module->length,
|
||||
function->module->str, line);
|
||||
else
|
||||
fprintf(stderr, "[line %d] in ", line);
|
||||
|
||||
@@ -78,11 +109,12 @@ COSMO_API void cosmoV_printError(CState *state, CObjError *err) {
|
||||
}
|
||||
|
||||
/*
|
||||
takes value on top of the stack and wraps an CObjError around it, state->error is set to that value
|
||||
the value on the stack is *expected* to be a string, but not required, so
|
||||
yes, this means you could throw a nil value if you really wanted too..
|
||||
takes value on top of the stack and wraps an CObjError around it, state->error is set to that
|
||||
value the value on the stack is *expected* to be a string, but not required, so yes, this means
|
||||
you could throw a nil value if you really wanted too..
|
||||
*/
|
||||
CObjError* cosmoV_throw(CState *state) {
|
||||
CObjError *cosmoV_throw(CState *state)
|
||||
{
|
||||
StkPtr temp = cosmoV_getTop(state, 0);
|
||||
|
||||
CObjError *error = cosmoO_newError(state, *temp);
|
||||
@@ -93,7 +125,8 @@ CObjError* cosmoV_throw(CState *state) {
|
||||
return error;
|
||||
}
|
||||
|
||||
void cosmoV_error(CState *state, const char *format, ...) {
|
||||
void cosmoV_error(CState *state, const char *format, ...)
|
||||
{
|
||||
if (state->panic)
|
||||
return;
|
||||
|
||||
@@ -110,11 +143,13 @@ void cosmoV_error(CState *state, const char *format, ...) {
|
||||
cosmoV_throw(state);
|
||||
}
|
||||
|
||||
CObjUpval *captureUpvalue(CState *state, CValue *local) {
|
||||
CObjUpval *captureUpvalue(CState *state, CValue *local)
|
||||
{
|
||||
CObjUpval *prev = NULL;
|
||||
CObjUpval *upvalue = state->openUpvalues;
|
||||
|
||||
while (upvalue != NULL && upvalue->val > local) { // while upvalue exists and is higher on the stack than local
|
||||
while (upvalue != NULL &&
|
||||
upvalue->val > local) { // while upvalue exists and is higher on the stack than local
|
||||
prev = upvalue;
|
||||
upvalue = upvalue->next;
|
||||
}
|
||||
@@ -136,8 +171,11 @@ CObjUpval *captureUpvalue(CState *state, CValue *local) {
|
||||
return newUpval;
|
||||
}
|
||||
|
||||
void closeUpvalues(CState *state, CValue *local) {
|
||||
while (state->openUpvalues != NULL && state->openUpvalues->val >= local) { // for every upvalue that points to the local or anything above it
|
||||
void closeUpvalues(CState *state, CValue *local)
|
||||
{
|
||||
while (state->openUpvalues != NULL &&
|
||||
state->openUpvalues->val >=
|
||||
local) { // for every upvalue that points to the local or anything above it
|
||||
CObjUpval *upvalue = state->openUpvalues;
|
||||
upvalue->closed = *upvalue->val;
|
||||
upvalue->val = &upvalue->closed; // upvalue now points to itself :P
|
||||
@@ -145,7 +183,8 @@ void closeUpvalues(CState *state, CValue *local) {
|
||||
}
|
||||
}
|
||||
|
||||
void pushCallFrame(CState *state, CObjClosure *closure, int args) {
|
||||
void pushCallFrame(CState *state, CObjClosure *closure, int args)
|
||||
{
|
||||
#ifdef SAFE_STACK
|
||||
if (state->frameCount >= FRAME_MAX) {
|
||||
cosmoV_error(state, "Callframe overflow!");
|
||||
@@ -159,15 +198,19 @@ void pushCallFrame(CState *state, CObjClosure *closure, int args) {
|
||||
frame->closure = closure;
|
||||
}
|
||||
|
||||
// offset is the offset of the callframe base we set the state->top back too (useful for passing values in the stack as arguments, like methods)
|
||||
void popCallFrame(CState *state, int offset) {
|
||||
closeUpvalues(state, state->callFrame[state->frameCount - 1].base); // close any upvalue still open
|
||||
// offset is the offset of the callframe base we set the state->top back too (useful for passing
|
||||
// values in the stack as arguments, like methods)
|
||||
void popCallFrame(CState *state, int offset)
|
||||
{
|
||||
closeUpvalues(state,
|
||||
state->callFrame[state->frameCount - 1].base); // close any upvalue still open
|
||||
|
||||
state->top = state->callFrame[state->frameCount - 1].base + offset; // resets the stack
|
||||
state->frameCount--;
|
||||
}
|
||||
|
||||
void cosmoV_concat(CState *state, int vals) {
|
||||
void cosmoV_concat(CState *state, int vals)
|
||||
{
|
||||
StkPtr start = state->top - vals;
|
||||
StkPtr end = cosmoV_getTop(state, 0);
|
||||
|
||||
@@ -197,20 +240,19 @@ int cosmoV_execute(CState *state);
|
||||
bool invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset);
|
||||
|
||||
/*
|
||||
calls a native C Function with # args on the stack, nresults are pushed onto the stack upon return.
|
||||
calls a native C Function with # args on the stack, nresults are pushed onto the stack upon
|
||||
return.
|
||||
|
||||
returns:
|
||||
false: state paniced during C Function, error is at state->error
|
||||
true: state->top is moved to base + offset + nresults, with nresults pushed onto the stack from base + offset
|
||||
true: state->top is moved to base + offset + nresults, with nresults pushed onto the stack
|
||||
from base + offset
|
||||
*/
|
||||
static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nresults, int offset) {
|
||||
static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nresults, int offset)
|
||||
{
|
||||
StkPtr savedBase = cosmoV_getTop(state, args);
|
||||
|
||||
// we don't want a GC event during c api because we don't actually trust the user to know how to evade the GC
|
||||
cosmoM_freezeGC(state);
|
||||
int nres = cfunc(state, args, savedBase + 1);
|
||||
cosmoM_unfreezeGC(state);
|
||||
|
||||
|
||||
// caller function wasn't expecting this many return values, cap it
|
||||
if (nres > nresults)
|
||||
@@ -226,7 +268,8 @@ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre
|
||||
return false;
|
||||
|
||||
// push the return value back onto the stack
|
||||
memmove(state->top, results, sizeof(CValue) * nres); // copies the return values to the top of the stack
|
||||
memmove(state->top, results,
|
||||
sizeof(CValue) * nres); // copies the return values to the top of the stack
|
||||
state->top += nres; // and make sure to move state->top to match
|
||||
|
||||
// now, if the caller function expected more return values, push nils onto the stack
|
||||
@@ -237,13 +280,16 @@ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre
|
||||
}
|
||||
|
||||
/*
|
||||
calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon return.
|
||||
calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon
|
||||
return.
|
||||
|
||||
returns:
|
||||
false: state paniced, error is at state->error
|
||||
true: stack->top is moved to base + offset + nresults, with nresults pushed onto the stack from base + offset
|
||||
true: stack->top is moved to base + offset + nresults, with nresults pushed onto the stack
|
||||
from base + offset
|
||||
*/
|
||||
static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, int offset) {
|
||||
static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, int offset)
|
||||
{
|
||||
CObjFunction *func = closure->function;
|
||||
|
||||
// if the function is variadic and theres more args than parameters, push the args into a table
|
||||
@@ -263,7 +309,9 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults,
|
||||
|
||||
pushCallFrame(state, closure, func->args + 1);
|
||||
} else if (args != func->args) { // mismatched args
|
||||
cosmoV_error(state, "Expected %d arguments for %s, got %d!", closure->function->args, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, args);
|
||||
cosmoV_error(state, "Expected %d arguments for %s, got %d!", closure->function->args,
|
||||
closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str,
|
||||
args);
|
||||
return false;
|
||||
} else {
|
||||
// load function into callframe
|
||||
@@ -298,7 +346,8 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool callCValue(CState *state, CValue func, int args, int nresults, int offset) {
|
||||
bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
|
||||
{
|
||||
#ifdef VM_DEBUG
|
||||
printf("\n");
|
||||
printIndent(state->frameCount - 1);
|
||||
@@ -343,7 +392,8 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
|
||||
cosmoV_pushRef(state, (CObj *)newObj);
|
||||
|
||||
// push the nils to fill up the expected return values
|
||||
for (int i = 0; i < nresults - 1; i++) { // -1 since the we already pushed the important value
|
||||
for (int i = 0; i < nresults - 1;
|
||||
i++) { // -1 since the we already pushed the important value
|
||||
cosmoV_pushValue(state, cosmoV_newNil());
|
||||
}
|
||||
}
|
||||
@@ -357,7 +407,8 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool invokeMethod(CState* state, CObj *obj, CValue func, int args, int nresults, int offset) {
|
||||
bool invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset)
|
||||
{
|
||||
// first, set the first argument to the object
|
||||
StkPtr temp = cosmoV_getTop(state, args);
|
||||
*temp = cosmoV_newRef(obj);
|
||||
@@ -365,8 +416,10 @@ bool invokeMethod(CState* state, CObj *obj, CValue func, int args, int nresults,
|
||||
return callCValue(state, func, args + 1, nresults, offset);
|
||||
}
|
||||
|
||||
// wraps cosmoV_call in a protected state, CObjError will be pushed onto the stack if function call failed, else return values are passed
|
||||
COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults) {
|
||||
// wraps cosmoV_call in a protected state, CObjError will be pushed onto the stack if function call
|
||||
// failed, else return values are passed
|
||||
COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults)
|
||||
{
|
||||
StkPtr base = cosmoV_getTop(state, args);
|
||||
|
||||
if (!callCValue(state, *base, args, nresults, 0)) {
|
||||
@@ -388,23 +441,27 @@ COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults) {
|
||||
}
|
||||
|
||||
/*
|
||||
calls a callable object at stack->top - args - 1, passing the # of args to the callable, and ensuring nresults are returned
|
||||
calls a callable object at stack->top - args - 1, passing the # of args to the callable, and
|
||||
ensuring nresults are returned
|
||||
|
||||
returns:
|
||||
COSMOVM_OK: callable object exited normally
|
||||
COSMOVM_RUNTIME_ERR: an error occurred, grab the error from state->error
|
||||
*/
|
||||
COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) {
|
||||
COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults)
|
||||
{
|
||||
StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args
|
||||
|
||||
return callCValue(state, *val, args, nresults, 0) ? COSMOVM_OK : COSMOVM_RUNTIME_ERR;
|
||||
}
|
||||
|
||||
static inline bool isFalsey(StkPtr val) {
|
||||
static inline bool isFalsey(StkPtr val)
|
||||
{
|
||||
return IS_NIL(*val) || (IS_BOOLEAN(*val) && !cosmoV_readBoolean(*val));
|
||||
}
|
||||
|
||||
COSMO_API CObjObject* cosmoV_makeObject(CState *state, int pairs) {
|
||||
COSMO_API CObjObject *cosmoV_makeObject(CState *state, int pairs)
|
||||
{
|
||||
StkPtr key, val;
|
||||
CObjObject *newObj = cosmoO_newObject(state);
|
||||
cosmoV_pushRef(state, (CObj *)newObj); // so our GC doesn't free our new object
|
||||
@@ -424,13 +481,26 @@ COSMO_API CObjObject* cosmoV_makeObject(CState *state, int pairs) {
|
||||
return newObj;
|
||||
}
|
||||
|
||||
COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj) {
|
||||
COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj)
|
||||
{
|
||||
bool replaced = state->protoObjects[objType] != NULL;
|
||||
state->protoObjects[objType] = obj;
|
||||
|
||||
// walk through the object list
|
||||
CObj *curr = state->objects;
|
||||
while (curr != NULL) {
|
||||
// update the proto
|
||||
if (curr->type == objType && curr->proto != NULL) {
|
||||
curr->proto = obj;
|
||||
}
|
||||
curr = curr->next;
|
||||
}
|
||||
|
||||
return replaced;
|
||||
}
|
||||
|
||||
COSMO_API void cosmoV_makeTable(CState *state, int pairs) {
|
||||
COSMO_API void cosmoV_makeTable(CState *state, int pairs)
|
||||
{
|
||||
StkPtr key, val;
|
||||
CObjTable *newObj = cosmoO_newTable(state);
|
||||
cosmoV_pushRef(state, (CObj *)newObj); // so our GC doesn't free our new table
|
||||
@@ -449,13 +519,15 @@ COSMO_API void cosmoV_makeTable(CState *state, int pairs) {
|
||||
cosmoV_pushRef(state, (CObj *)newObj);
|
||||
}
|
||||
|
||||
bool cosmoV_rawget(CState *state, CObj *_obj, CValue key, CValue *val) {
|
||||
bool cosmoV_rawget(CState *state, CObj *_obj, CValue key, CValue *val)
|
||||
{
|
||||
CObjObject *object = cosmoO_grabProto(_obj);
|
||||
|
||||
// no proto to get from
|
||||
if (object == NULL) {
|
||||
CObjString *field = cosmoV_toString(state, key);
|
||||
cosmoV_error(state, "No proto defined! Couldn't get field '%s' from type %s", field->str, cosmoO_typeStr(_obj));
|
||||
cosmoV_error(state, "No proto defined! Couldn't get field '%s' from type %s", field->str,
|
||||
cosmoO_typeStr(_obj));
|
||||
*val = cosmoV_newNil();
|
||||
return false;
|
||||
}
|
||||
@@ -472,13 +544,15 @@ bool cosmoV_rawget(CState *state, CObj *_obj, CValue key, CValue *val) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val) {
|
||||
bool cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val)
|
||||
{
|
||||
CObjObject *object = cosmoO_grabProto(_obj);
|
||||
|
||||
// no proto to set to
|
||||
if (object == NULL) {
|
||||
CObjString *field = cosmoV_toString(state, key);
|
||||
cosmoV_error(state, "No proto defined! Couldn't set field '%s' to type %s", field->str, cosmoO_typeStr(_obj));
|
||||
cosmoV_error(state, "No proto defined! Couldn't set field '%s' to type %s", field->str,
|
||||
cosmoO_typeStr(_obj));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -486,7 +560,8 @@ bool cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val) {
|
||||
return true;
|
||||
}
|
||||
|
||||
COSMO_API bool cosmoV_get(CState *state) {
|
||||
COSMO_API bool cosmoV_get(CState *state)
|
||||
{
|
||||
CValue val;
|
||||
StkPtr obj = cosmoV_getTop(state, 1); // object was pushed first
|
||||
StkPtr key = cosmoV_getTop(state, 0); // then the key
|
||||
@@ -506,7 +581,8 @@ COSMO_API bool cosmoV_get(CState *state) {
|
||||
}
|
||||
|
||||
// yes, this would technically make it possible to set fields of types other than <string>. go crazy
|
||||
COSMO_API bool cosmoV_set(CState *state) {
|
||||
COSMO_API bool cosmoV_set(CState *state)
|
||||
{
|
||||
StkPtr obj = cosmoV_getTop(state, 2); // object was pushed first
|
||||
StkPtr key = cosmoV_getTop(state, 1); // then the key
|
||||
StkPtr val = cosmoV_getTop(state, 0); // and finally the value
|
||||
@@ -524,7 +600,8 @@ COSMO_API bool cosmoV_set(CState *state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val) {
|
||||
COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val)
|
||||
{
|
||||
if (!cosmoV_rawget(state, obj, key, val))
|
||||
return false;
|
||||
|
||||
@@ -540,7 +617,8 @@ COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *va
|
||||
return true;
|
||||
}
|
||||
|
||||
int _tbl__next(CState *state, int nargs, CValue *args) {
|
||||
int _tbl__next(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "Expected 1 parameter, %d received!", nargs);
|
||||
return 0;
|
||||
@@ -558,7 +636,8 @@ int _tbl__next(CState *state, int nargs, CValue *args) {
|
||||
cosmoO_getIString(state, obj, ISTRING_RESERVED, &val);
|
||||
|
||||
if (!IS_TABLE(val)) {
|
||||
return 0; // someone set the __reserved member to something else. this will exit the iterator loop
|
||||
return 0; // someone set the __reserved member to something else. this will exit the
|
||||
// iterator loop
|
||||
}
|
||||
|
||||
CObjTable *table = (CObjTable *)cosmoV_readRef(val);
|
||||
@@ -571,7 +650,8 @@ int _tbl__next(CState *state, int nargs, CValue *args) {
|
||||
} while (IS_NIL(entry->key) && index < cap);
|
||||
cosmoO_setUserI(obj, index); // update the userdata
|
||||
|
||||
if (index < cap && !IS_NIL(entry->key)) { // if the entry is valid, return it's key and value pair
|
||||
if (index < cap &&
|
||||
!IS_NIL(entry->key)) { // if the entry is valid, return it's key and value pair
|
||||
cosmoV_pushValue(state, entry->key);
|
||||
cosmoV_pushValue(state, entry->val);
|
||||
return 2; // we pushed 2 values onto the stack for the return values
|
||||
@@ -587,12 +667,14 @@ int _tbl__next(CState *state, int nargs, CValue *args) {
|
||||
cosmoV_setTop(state, 2); /* pop the 2 values */ \
|
||||
cosmoV_pushValue(state, typeConst(cosmoV_readNumber(*valA) op cosmoV_readNumber(*valB))); \
|
||||
} else { \
|
||||
cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), cosmoV_typeStr(*valB)); \
|
||||
cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), \
|
||||
cosmoV_typeStr(*valB)); \
|
||||
return -1; \
|
||||
} \
|
||||
}
|
||||
|
||||
// returns -1 if panic
|
||||
int cosmoV_execute(CState *state) {
|
||||
int cosmoV_execute(CState *state)
|
||||
{
|
||||
CCallFrame *frame = &state->callFrame[state->frameCount - 1]; // grabs the current frame
|
||||
CValue *constants = frame->closure->function->chunk.constants.values; // cache the pointer :)
|
||||
|
||||
@@ -602,7 +684,8 @@ int cosmoV_execute(CState *state) {
|
||||
while (!state->panic) {
|
||||
#ifdef VM_DEBUG
|
||||
cosmoV_printStack(state);
|
||||
disasmInstr(&frame->closure->function->chunk, frame->pc - frame->closure->function->chunk.buf, state->frameCount - 1);
|
||||
disasmInstr(&frame->closure->function->chunk,
|
||||
frame->pc - frame->closure->function->chunk.buf, state->frameCount - 1);
|
||||
printf("\n");
|
||||
#endif
|
||||
switch (READBYTE()) {
|
||||
@@ -622,7 +705,7 @@ int cosmoV_execute(CState *state) {
|
||||
uint16_t indx = READUINT();
|
||||
CValue ident = constants[indx]; // grabs identifier
|
||||
CValue val; // to hold our value
|
||||
cosmoT_get(&state->globals->tbl, ident, &val);
|
||||
cosmoT_get(state, &state->globals->tbl, ident, &val);
|
||||
cosmoV_pushValue(state, val); // pushes the value to the stack
|
||||
continue;
|
||||
}
|
||||
@@ -725,7 +808,8 @@ int cosmoV_execute(CState *state) {
|
||||
val = cosmoV_getTop(state, i + 1);
|
||||
|
||||
// set key/value pair
|
||||
CValue *newVal = cosmoT_insert(state, &newObj->tbl, cosmoV_newNumber(pairs - i - 1));
|
||||
CValue *newVal =
|
||||
cosmoT_insert(state, &newObj->tbl, cosmoV_newNumber(pairs - i - 1));
|
||||
*newVal = *val;
|
||||
}
|
||||
|
||||
@@ -750,14 +834,16 @@ int cosmoV_execute(CState *state) {
|
||||
|
||||
if (proto != NULL) {
|
||||
// check for __index metamethod
|
||||
if (!cosmoO_indexObject(state, proto, *key, &val)) // if returns false, cosmoV_error was called
|
||||
if (!cosmoO_indexObject(state, proto, *key,
|
||||
&val)) // if returns false, cosmoV_error was called
|
||||
return -1;
|
||||
} else if (obj->type == COBJ_TABLE) {
|
||||
CObjTable *tbl = (CObjTable *)obj;
|
||||
|
||||
cosmoT_get(&tbl->tbl, *key, &val);
|
||||
cosmoT_get(state, &tbl->tbl, *key, &val);
|
||||
} else {
|
||||
cosmoV_error(state, "No proto defined! Couldn't __index from type %s", cosmoV_typeStr(*temp));
|
||||
cosmoV_error(state, "No proto defined! Couldn't __index from type %s",
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -780,7 +866,8 @@ int cosmoV_execute(CState *state) {
|
||||
CObjObject *proto = cosmoO_grabProto(obj);
|
||||
|
||||
if (proto != NULL) {
|
||||
if (!cosmoO_newIndexObject(state, proto, *key, *value)) // if it returns false, cosmoV_error was called
|
||||
if (!cosmoO_newIndexObject(state, proto, *key,
|
||||
*value)) // if it returns false, cosmoV_error was called
|
||||
return -1;
|
||||
} else if (obj->type == COBJ_TABLE) {
|
||||
CObjTable *tbl = (CObjTable *)obj;
|
||||
@@ -788,7 +875,8 @@ int cosmoV_execute(CState *state) {
|
||||
|
||||
*newVal = *value; // set the index
|
||||
} else {
|
||||
cosmoV_error(state, "No proto defined! Couldn't __newindex from type %s", cosmoV_typeStr(*temp));
|
||||
cosmoV_error(state, "No proto defined! Couldn't __newindex from type %s",
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -812,7 +900,8 @@ int cosmoV_execute(CState *state) {
|
||||
return -1;
|
||||
} else {
|
||||
CObjString *field = cosmoV_toString(state, constants[ident]);
|
||||
cosmoV_error(state, "Couldn't set field '%s' on type %s!", field->str, cosmoV_typeStr(*temp));
|
||||
cosmoV_error(state, "Couldn't set field '%s' on type %s!", field->str,
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -831,7 +920,8 @@ int cosmoV_execute(CState *state) {
|
||||
return -1;
|
||||
} else {
|
||||
CObjString *field = cosmoV_toString(state, constants[ident]);
|
||||
cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str, cosmoV_typeStr(*temp));
|
||||
cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str,
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -844,13 +934,15 @@ int cosmoV_execute(CState *state) {
|
||||
StkPtr temp = cosmoV_getTop(state, 0); // that should be the object
|
||||
uint16_t ident = READUINT(); // use for the key
|
||||
|
||||
// this is almost identical to GETOBJECT, however cosmoV_getMethod is used instead of just cosmoV_get
|
||||
// this is almost identical to GETOBJECT, however cosmoV_getMethod is used instead of
|
||||
// just cosmoV_get
|
||||
if (IS_REF(*temp)) {
|
||||
if (!cosmoV_getMethod(state, cosmoV_readRef(*temp), constants[ident], &val))
|
||||
return -1;
|
||||
} else {
|
||||
CObjString *field = cosmoV_toString(state, constants[ident]);
|
||||
cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str, cosmoV_typeStr(*temp));
|
||||
cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str,
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -884,7 +976,8 @@ int cosmoV_execute(CState *state) {
|
||||
StkPtr temp = cosmoV_getTop(state, 0); // should be the object/table
|
||||
|
||||
if (!IS_REF(*temp)) {
|
||||
cosmoV_error(state, "Couldn't iterate over non-iterator type %s!", cosmoV_typeStr(*temp));
|
||||
cosmoV_error(state, "Couldn't iterate over non-iterator type %s!",
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -898,18 +991,23 @@ int cosmoV_execute(CState *state) {
|
||||
cosmoV_pop(state); // pop the object from the stack
|
||||
cosmoV_pushValue(state, val);
|
||||
cosmoV_pushRef(state, (CObj *)obj);
|
||||
if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // we expect 1 return value on the stack, the iterable object
|
||||
if (cosmoV_call(state, 1, 1) !=
|
||||
COSMOVM_OK) // we expect 1 return value on the stack, the iterable object
|
||||
return -1;
|
||||
|
||||
StkPtr iObj = cosmoV_getTop(state, 0);
|
||||
|
||||
if (!IS_OBJECT(*iObj)) {
|
||||
cosmoV_error(state, "Expected iterable object! '__iter' returned %s, expected <object>!", cosmoV_typeStr(*iObj));
|
||||
cosmoV_error(
|
||||
state,
|
||||
"Expected iterable object! '__iter' returned %s, expected <object>!",
|
||||
cosmoV_typeStr(*iObj));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// get __next method and place it at the top of the stack
|
||||
cosmoV_getMethod(state, cosmoV_readRef(*iObj), cosmoV_newRef(state->iStrings[ISTRING_NEXT]), iObj);
|
||||
cosmoV_getMethod(state, cosmoV_readRef(*iObj),
|
||||
cosmoV_newRef(state->iStrings[ISTRING_NEXT]), iObj);
|
||||
} else {
|
||||
cosmoV_error(state, "Expected iterable object! '__iter' not defined!");
|
||||
return -1;
|
||||
@@ -933,7 +1031,8 @@ int cosmoV_execute(CState *state) {
|
||||
cosmoV_setTop(state, 2); // pops the object & the tbl
|
||||
cosmoV_pushRef(state, (CObj *)method); // pushes the method for OP_NEXT
|
||||
} else {
|
||||
cosmoV_error(state, "No proto defined! Couldn't get from type %s", cosmoO_typeStr(obj));
|
||||
cosmoV_error(state, "No proto defined! Couldn't get from type %s",
|
||||
cosmoO_typeStr(obj));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -945,7 +1044,8 @@ int cosmoV_execute(CState *state) {
|
||||
StkPtr temp = cosmoV_getTop(state, 0); // we don't actually pop this off the stack
|
||||
|
||||
if (!IS_METHOD(*temp)) {
|
||||
cosmoV_error(state, "Expected '__next' to be a method, got type %s!", cosmoV_typeStr(*temp));
|
||||
cosmoV_error(state, "Expected '__next' to be a method, got type %s!",
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -953,7 +1053,8 @@ int cosmoV_execute(CState *state) {
|
||||
if (cosmoV_call(state, 0, nresults) != COSMOVM_OK)
|
||||
return -1;
|
||||
|
||||
if (IS_NIL(*(cosmoV_getTop(state, 0)))) { // __next returned a nil, which means to exit the loop
|
||||
if (IS_NIL(*(cosmoV_getTop(
|
||||
state, 0)))) { // __next returned a nil, which means to exit the loop
|
||||
cosmoV_setTop(state, nresults); // pop the return values
|
||||
frame->pc += jump;
|
||||
}
|
||||
@@ -980,9 +1081,11 @@ int cosmoV_execute(CState *state) {
|
||||
StkPtr valB = cosmoV_getTop(state, 0);
|
||||
if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) {
|
||||
cosmoV_setTop(state, 2); /* pop the 2 values */
|
||||
cosmoV_pushValue(state, cosmoV_newNumber(fmod(cosmoV_readNumber(*valA), cosmoV_readNumber(*valB))));
|
||||
cosmoV_pushValue(state, cosmoV_newNumber(fmod(cosmoV_readNumber(*valA),
|
||||
cosmoV_readNumber(*valB))));
|
||||
} else {
|
||||
cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), cosmoV_typeStr(*valB));
|
||||
cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA),
|
||||
cosmoV_typeStr(*valB));
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
@@ -992,9 +1095,11 @@ int cosmoV_execute(CState *state) {
|
||||
StkPtr valB = cosmoV_getTop(state, 0);
|
||||
if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) {
|
||||
cosmoV_setTop(state, 2); /* pop the 2 values */
|
||||
cosmoV_pushValue(state, cosmoV_newNumber(pow(cosmoV_readNumber(*valA), cosmoV_readNumber(*valB))));
|
||||
cosmoV_pushValue(state, cosmoV_newNumber(pow(cosmoV_readNumber(*valA),
|
||||
cosmoV_readNumber(*valB))));
|
||||
} else {
|
||||
cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), cosmoV_typeStr(*valB));
|
||||
cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA),
|
||||
cosmoV_typeStr(*valB));
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
@@ -1108,7 +1213,8 @@ int cosmoV_execute(CState *state) {
|
||||
cosmoV_pushValue(state, val); // pushes old value onto the stack :)
|
||||
|
||||
// call __newindex
|
||||
if (!cosmoO_newIndexObject(state, proto, *key, cosmoV_newNumber(cosmoV_readNumber(val) + inc)))
|
||||
if (!cosmoO_newIndexObject(state, proto, *key,
|
||||
cosmoV_newNumber(cosmoV_readNumber(val) + inc)))
|
||||
return -1;
|
||||
} else
|
||||
return -1; // cosmoO_indexObject failed and threw an error
|
||||
@@ -1126,7 +1232,8 @@ int cosmoV_execute(CState *state) {
|
||||
cosmoV_pushValue(state, *val); // pushes old value onto the stack :)
|
||||
*val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); // sets table index
|
||||
} else {
|
||||
cosmoV_error(state, "No proto defined! Couldn't __index from type %s", cosmoV_typeStr(*temp));
|
||||
cosmoV_error(state, "No proto defined! Couldn't __index from type %s",
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1152,7 +1259,8 @@ int cosmoV_execute(CState *state) {
|
||||
// check that it's a number value
|
||||
if (IS_NUMBER(val)) {
|
||||
cosmoV_pushValue(state, val); // pushes old value onto the stack :)
|
||||
if (!cosmoV_rawset(state, obj, ident, cosmoV_newNumber(cosmoV_readNumber(val) + inc)))
|
||||
if (!cosmoV_rawset(state, obj, ident,
|
||||
cosmoV_newNumber(cosmoV_readNumber(val) + inc)))
|
||||
return -1;
|
||||
} else {
|
||||
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val));
|
||||
@@ -1171,7 +1279,7 @@ int cosmoV_execute(CState *state) {
|
||||
StkPtr valA = cosmoV_pop(state);
|
||||
|
||||
// compare & push
|
||||
cosmoV_pushBoolean(state, cosmoV_equal(*valA, *valB));
|
||||
cosmoV_pushBoolean(state, cosmoV_equal(state, *valA, *valB));
|
||||
continue;
|
||||
}
|
||||
case OP_GREATER: {
|
||||
@@ -1190,9 +1298,15 @@ int cosmoV_execute(CState *state) {
|
||||
NUMBEROP(cosmoV_newBoolean, <=)
|
||||
continue;
|
||||
}
|
||||
case OP_TRUE: cosmoV_pushBoolean(state, true); continue;
|
||||
case OP_FALSE: cosmoV_pushBoolean(state, false); continue;
|
||||
case OP_NIL: cosmoV_pushValue(state, cosmoV_newNil()); continue;
|
||||
case OP_TRUE:
|
||||
cosmoV_pushBoolean(state, true);
|
||||
continue;
|
||||
case OP_FALSE:
|
||||
cosmoV_pushBoolean(state, false);
|
||||
continue;
|
||||
case OP_NIL:
|
||||
cosmoV_pushValue(state, cosmoV_newNil());
|
||||
continue;
|
||||
case OP_RETURN: {
|
||||
uint8_t res = READBYTE();
|
||||
return res;
|
||||
|
75
src/cvm.h
75
src/cvm.h
@@ -1,14 +1,15 @@
|
||||
#ifndef COSMOVM_H
|
||||
#define COSMOVM_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "cosmo.h"
|
||||
#include "cstate.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// #define VM_DEBUG
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
COSMOVM_OK,
|
||||
COSMOVM_RUNTIME_ERR,
|
||||
COSMOVM_BUILDTIME_ERR
|
||||
@@ -28,11 +29,17 @@ COSMO_API CObjError* cosmoV_throw(CState *state);
|
||||
COSMO_API void cosmoV_error(CState *state, const char *format, ...);
|
||||
COSMO_API void cosmo_insert(CState *state, int indx, CValue val);
|
||||
|
||||
// returns true if replacing a previously registered proto object for this type
|
||||
/*
|
||||
Sets the default proto objects for the passed objType. Also walks through the object heap and
|
||||
updates protos for the passed objType if that CObj* has no proto.
|
||||
|
||||
returns true if replacing a previously registered proto object for this type
|
||||
*/
|
||||
COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj);
|
||||
|
||||
/*
|
||||
compiles string into a <closure>, if successful, <closure> will be pushed onto the stack otherwise the <error> will be pushed.
|
||||
compiles string into a <closure>. if successful, <closure> will be pushed onto the stack
|
||||
otherwise the <error> will be pushed.
|
||||
|
||||
returns:
|
||||
false : <error> is at the top of the stack
|
||||
@@ -40,27 +47,45 @@ COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjO
|
||||
*/
|
||||
COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name);
|
||||
|
||||
/*
|
||||
loads a <closure> from a dump. if successful, <closure> will be pushed onto the stack
|
||||
otherwise the <error> will be pushed.
|
||||
|
||||
returns:
|
||||
false : <error> is at the top of the stack
|
||||
true : <closure> is at the top of the stack
|
||||
*/
|
||||
COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud);
|
||||
|
||||
/*
|
||||
expects object to be pushed, then the key.
|
||||
|
||||
if returns false an error was thrown, if returns true the value was pushed onto the stack and the object and key were popped
|
||||
returns false if an error was thrown, returns true if the value was pushed onto the stack and
|
||||
the object and key were popped
|
||||
*/
|
||||
COSMO_API bool cosmoV_get(CState *state);
|
||||
|
||||
/*
|
||||
expects object to be pushed, then the key, and finally the new value.
|
||||
|
||||
if returns false an error was thrown, if returns true the value was set and the object key, and value were popped
|
||||
returns false if an error was thrown, returns true if the value was set and the object key, and
|
||||
value were popped
|
||||
*/
|
||||
COSMO_API bool cosmoV_set(CState *state);
|
||||
|
||||
// wraps the closure into a CObjMethod, so the function is called as an invoked method
|
||||
COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val);
|
||||
|
||||
// clears the stack, callstack and restores the state into a usable state after a calloverflow or
|
||||
// another hard to recover error (keeps the global table intact)
|
||||
COSMO_API bool cosmoV_restore(CState *state);
|
||||
|
||||
// nice to have wrappers
|
||||
|
||||
// pushes raw CValue to the stack
|
||||
static inline void cosmoV_pushValue(CState *state, CValue val) {
|
||||
// pushes a raw CValue to the stack, might throw an error if the stack is overflowed (with the
|
||||
// SAFE_STACK macro on)
|
||||
static inline void cosmoV_pushValue(CState *state, CValue val)
|
||||
{
|
||||
#ifdef SAFE_STACK
|
||||
ptrdiff_t stackSize = state->top - state->stack;
|
||||
|
||||
@@ -82,51 +107,61 @@ static inline void cosmoV_pushValue(CState *state, CValue val) {
|
||||
}
|
||||
|
||||
// sets stack->top to stack->top - indx
|
||||
static inline StkPtr cosmoV_setTop(CState *state, int indx) {
|
||||
static inline StkPtr cosmoV_setTop(CState *state, int indx)
|
||||
{
|
||||
state->top -= indx;
|
||||
return state->top;
|
||||
}
|
||||
|
||||
// returns stack->top - indx - 1
|
||||
static inline StkPtr cosmoV_getTop(CState *state, int indx) {
|
||||
static inline StkPtr cosmoV_getTop(CState *state, int indx)
|
||||
{
|
||||
return &state->top[-(indx + 1)];
|
||||
}
|
||||
|
||||
// pops 1 value off the stack
|
||||
static inline StkPtr cosmoV_pop(CState *state) {
|
||||
// pops 1 value off the stack, returns the popped value
|
||||
static inline StkPtr cosmoV_pop(CState *state)
|
||||
{
|
||||
return cosmoV_setTop(state, 1);
|
||||
}
|
||||
|
||||
// pushes a cosmo_Number to the stack
|
||||
static inline void cosmoV_pushNumber(CState *state, cosmo_Number num) {
|
||||
static inline void cosmoV_pushNumber(CState *state, cosmo_Number num)
|
||||
{
|
||||
cosmoV_pushValue(state, cosmoV_newNumber(num));
|
||||
}
|
||||
|
||||
// pushes a boolean to the stack
|
||||
static inline void cosmoV_pushBoolean(CState *state, bool b) {
|
||||
static inline void cosmoV_pushBoolean(CState *state, bool b)
|
||||
{
|
||||
cosmoV_pushValue(state, cosmoV_newBoolean(b));
|
||||
}
|
||||
|
||||
static inline void cosmoV_pushRef(CState *state, CObj *obj) {
|
||||
static inline void cosmoV_pushRef(CState *state, CObj *obj)
|
||||
{
|
||||
cosmoV_pushValue(state, cosmoV_newRef(obj));
|
||||
}
|
||||
|
||||
// pushes a C Function to the stack
|
||||
static inline void cosmoV_pushCFunction(CState *state, CosmoCFunction func) {
|
||||
static inline void cosmoV_pushCFunction(CState *state, CosmoCFunction func)
|
||||
{
|
||||
cosmoV_pushRef(state, (CObj *)cosmoO_newCFunction(state, func));
|
||||
}
|
||||
|
||||
// len is the length of the string without the NULL terminator
|
||||
static inline void cosmoV_pushLString(CState *state, const char *str, size_t len) {
|
||||
static inline void cosmoV_pushLString(CState *state, const char *str, size_t len)
|
||||
{
|
||||
cosmoV_pushRef(state, (CObj *)cosmoO_copyString(state, str, len));
|
||||
}
|
||||
|
||||
// accepts a null terminated string and copys the buffer to the VM heap
|
||||
static inline void cosmoV_pushString(CState *state, const char *str) {
|
||||
static inline void cosmoV_pushString(CState *state, const char *str)
|
||||
{
|
||||
cosmoV_pushLString(state, str, strlen(str));
|
||||
}
|
||||
|
||||
static inline void cosmoV_pushNil(CState *state) {
|
||||
static inline void cosmoV_pushNil(CState *state)
|
||||
{
|
||||
cosmoV_pushValue(state, cosmoV_newNil());
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user