⚡ split type definition from gc.h
This commit is contained in:
parent
8290b7df9f
commit
aab7decd70
|
@ -36,6 +36,7 @@ set(NASAL_OBJECT_SOURCE_FILE
|
||||||
${CMAKE_SOURCE_DIR}/src/nasal_misc.cpp
|
${CMAKE_SOURCE_DIR}/src/nasal_misc.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/nasal_opcode.cpp
|
${CMAKE_SOURCE_DIR}/src/nasal_opcode.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/nasal_parse.cpp
|
${CMAKE_SOURCE_DIR}/src/nasal_parse.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/nasal_type.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/nasal_vm.cpp
|
${CMAKE_SOURCE_DIR}/src/nasal_vm.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/optimizer.cpp
|
${CMAKE_SOURCE_DIR}/src/optimizer.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/symbol_finder.cpp
|
${CMAKE_SOURCE_DIR}/src/symbol_finder.cpp
|
||||||
|
|
8
makefile
8
makefile
|
@ -23,6 +23,7 @@ NASAL_HEADER=\
|
||||||
src/nasal_opcode.h\
|
src/nasal_opcode.h\
|
||||||
src/nasal_parse.h\
|
src/nasal_parse.h\
|
||||||
src/nasal_vm.h\
|
src/nasal_vm.h\
|
||||||
|
src/nasal_type.h\
|
||||||
src/nasal.h\
|
src/nasal.h\
|
||||||
src/optimizer.h\
|
src/optimizer.h\
|
||||||
src/symbol_finder.h\
|
src/symbol_finder.h\
|
||||||
|
@ -57,6 +58,7 @@ NASAL_OBJECT=\
|
||||||
build/unix_lib.o\
|
build/unix_lib.o\
|
||||||
build/dylib_lib.o\
|
build/dylib_lib.o\
|
||||||
build/coroutine.o\
|
build/coroutine.o\
|
||||||
|
build/nasal_type.o\
|
||||||
build/nasal_vm.o\
|
build/nasal_vm.o\
|
||||||
build/nasal_dbg.o\
|
build/nasal_dbg.o\
|
||||||
build/repl.o\
|
build/repl.o\
|
||||||
|
@ -89,7 +91,10 @@ build/repl.o: $(NASAL_HEADER) src/repl.h src/repl.cpp | build
|
||||||
build/nasal_err.o: src/nasal.h src/repl.h src/nasal_err.h src/nasal_err.cpp | build
|
build/nasal_err.o: src/nasal.h src/repl.h src/nasal_err.h src/nasal_err.cpp | build
|
||||||
$(CXX) $(CXXFLAGS) src/nasal_err.cpp -o build/nasal_err.o
|
$(CXX) $(CXXFLAGS) src/nasal_err.cpp -o build/nasal_err.o
|
||||||
|
|
||||||
build/nasal_gc.o: src/nasal.h src/nasal_gc.h src/nasal_gc.cpp | build
|
build/nasal_type.o: src/nasal.h src/nasal_type.h src/nasal_type.cpp | build
|
||||||
|
$(CXX) $(CXXFLAGS) src/nasal_type.cpp -o build/nasal_type.o
|
||||||
|
|
||||||
|
build/nasal_gc.o: src/nasal.h src/nasal_type.h src/nasal_gc.h src/nasal_gc.cpp | build
|
||||||
$(CXX) $(CXXFLAGS) src/nasal_gc.cpp -o build/nasal_gc.o
|
$(CXX) $(CXXFLAGS) src/nasal_gc.cpp -o build/nasal_gc.o
|
||||||
|
|
||||||
build/nasal_import.o: \
|
build/nasal_import.o: \
|
||||||
|
@ -115,6 +120,7 @@ build/nasal_ast.o: \
|
||||||
|
|
||||||
build/nasal_builtin.o: \
|
build/nasal_builtin.o: \
|
||||||
src/nasal.h\
|
src/nasal.h\
|
||||||
|
src/nasal_type.h\
|
||||||
src/nasal_gc.h\
|
src/nasal_gc.h\
|
||||||
src/nasal_builtin.h src/nasal_builtin.cpp | build
|
src/nasal_builtin.h src/nasal_builtin.cpp | build
|
||||||
$(CXX) $(CXXFLAGS) src/nasal_builtin.cpp -o build/nasal_builtin.o
|
$(CXX) $(CXXFLAGS) src/nasal_builtin.cpp -o build/nasal_builtin.o
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "../src/nasal.h"
|
#include "../src/nasal.h"
|
||||||
|
#include "../src/nasal_type.h"
|
||||||
|
#include "../src/nasal_gc.h"
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
namespace fib_module {
|
namespace fib_module {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include "../src/nasal.h"
|
#include "../src/nasal.h"
|
||||||
|
#include "../src/nasal_type.h"
|
||||||
|
#include "../src/nasal_gc.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
dynamic_libs_so = libfib.so libkey.so libnasock.so libmat.so
|
dynamic_libs_so = libfib.so libkey.so libnasock.so libmat.so
|
||||||
dynamic_libs_dll = libfib.dll libkey.dll libnasock.dll libmat.dll
|
dynamic_libs_dll = libfib.dll libkey.dll libnasock.dll libmat.dll
|
||||||
|
|
||||||
used_header = ../src/nasal.h ../src/nasal_gc.h
|
used_header = ../src/nasal.h ../src/nasal_type.h ../src/nasal_gc.h
|
||||||
used_object = ../build/nasal_misc.o ../build/nasal_gc.o
|
used_object = ../build/nasal_misc.o ../build/nasal_type.o ../build/nasal_gc.o
|
||||||
|
|
||||||
STD = c++17
|
STD = c++17
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include "../src/nasal.h"
|
#include "../src/nasal.h"
|
||||||
|
#include "../src/nasal_type.h"
|
||||||
|
#include "../src/nasal_gc.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include "../src/nasal.h"
|
#include "../src/nasal.h"
|
||||||
|
#include "../src/nasal_type.h"
|
||||||
|
#include "../src/nasal_gc.h"
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
|
@ -41,7 +41,7 @@ var builtin_fld(var* local, gc& ngc) {
|
||||||
var str = local[1];
|
var str = local[1];
|
||||||
var startbit = local[2];
|
var startbit = local[2];
|
||||||
var length = local[3];
|
var length = local[3];
|
||||||
if (str.type!=vm_str || str.val.gcobj->unmut) {
|
if (str.type!=vm_str || str.val.gcobj->unmutable) {
|
||||||
return nas_err("fld", "\"str\" must be mutable string");
|
return nas_err("fld", "\"str\" must be mutable string");
|
||||||
}
|
}
|
||||||
if (startbit.type!=vm_num || length.type!=vm_num) {
|
if (startbit.type!=vm_num || length.type!=vm_num) {
|
||||||
|
@ -70,7 +70,7 @@ var builtin_sfld(var* local, gc& ngc) {
|
||||||
var str = local[1];
|
var str = local[1];
|
||||||
var startbit = local[2];
|
var startbit = local[2];
|
||||||
var length = local[3];
|
var length = local[3];
|
||||||
if (str.type!=vm_str || str.val.gcobj->unmut) {
|
if (str.type!=vm_str || str.val.gcobj->unmutable) {
|
||||||
return nas_err("sfld", "\"str\" must be mutable string");
|
return nas_err("sfld", "\"str\" must be mutable string");
|
||||||
}
|
}
|
||||||
if (startbit.type!=vm_num || length.type!=vm_num) {
|
if (startbit.type!=vm_num || length.type!=vm_num) {
|
||||||
|
@ -103,7 +103,7 @@ var builtin_setfld(var* local, gc& ngc) {
|
||||||
var startbit = local[2];
|
var startbit = local[2];
|
||||||
var length = local[3];
|
var length = local[3];
|
||||||
var value = local[4];
|
var value = local[4];
|
||||||
if (str.type!=vm_str || str.val.gcobj->unmut) {
|
if (str.type!=vm_str || str.val.gcobj->unmutable) {
|
||||||
return nas_err("setfld", "\"str\" must be mutable string");
|
return nas_err("setfld", "\"str\" must be mutable string");
|
||||||
}
|
}
|
||||||
if (startbit.type!=vm_num || length.type!=vm_num || value.type!=vm_num) {
|
if (startbit.type!=vm_num || length.type!=vm_num || value.type!=vm_num) {
|
||||||
|
|
|
@ -42,7 +42,7 @@ var builtin_exists(var* local, gc& ngc) {
|
||||||
if (local[1].type!=vm_str) {
|
if (local[1].type!=vm_str) {
|
||||||
return zero;
|
return zero;
|
||||||
}
|
}
|
||||||
return access(local[1].str().c_str(), F_OK)!=-1?one:zero;
|
return access(local[1].str().c_str(), F_OK)!=-1? one:zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_open(var* local, gc& ngc) {
|
var builtin_open(var* local, gc& ngc) {
|
||||||
|
@ -79,7 +79,7 @@ var builtin_read(var* local, gc& ngc) {
|
||||||
if (!fd.objchk(file_type_name)) {
|
if (!fd.objchk(file_type_name)) {
|
||||||
return nas_err("read", "not a valid filehandle");
|
return nas_err("read", "not a valid filehandle");
|
||||||
}
|
}
|
||||||
if (buf.type!=vm_str || buf.val.gcobj->unmut) {
|
if (buf.type!=vm_str || buf.val.gcobj->unmutable) {
|
||||||
return nas_err("read", "\"buf\" must be mutable string");
|
return nas_err("read", "\"buf\" must be mutable string");
|
||||||
}
|
}
|
||||||
if (len.type!=vm_num) {
|
if (len.type!=vm_num) {
|
||||||
|
@ -94,7 +94,7 @@ var builtin_read(var* local, gc& ngc) {
|
||||||
}
|
}
|
||||||
f64 res = fread(buff, 1, len.num(), static_cast<FILE*>(fd.obj().pointer));
|
f64 res = fread(buff, 1, len.num(), static_cast<FILE*>(fd.obj().pointer));
|
||||||
buf.str() = buff;
|
buf.str() = buff;
|
||||||
buf.val.gcobj->unmut = true;
|
buf.val.gcobj->unmutable = true;
|
||||||
delete []buff;
|
delete []buff;
|
||||||
return var::num(res);
|
return var::num(res);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,12 @@
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#include <unistd.h>
|
||||||
|
#else
|
||||||
|
#include <io.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#define F_OK 0 // fuck msc
|
#define F_OK 0 // fuck msc
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include "nasal.h"
|
#include "nasal.h"
|
||||||
|
#include "nasal_type.h"
|
||||||
|
#include "nasal_gc.h"
|
||||||
#include "nasal_err.h"
|
#include "nasal_err.h"
|
||||||
#include "nasal_lexer.h"
|
#include "nasal_lexer.h"
|
||||||
#include "nasal_ast.h"
|
#include "nasal_ast.h"
|
||||||
|
|
|
@ -57,7 +57,7 @@ var builtin_atan2(var* local, gc& ngc) {
|
||||||
|
|
||||||
var builtin_isnan(var* local, gc& ngc) {
|
var builtin_isnan(var* local, gc& ngc) {
|
||||||
var x = local[1];
|
var x = local[1];
|
||||||
return (x.type==vm_num && std::isnan(x.num()))?one:zero;
|
return (x.type==vm_num && std::isnan(x.num()))? one:zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
nasal_builtin_table math_lib_native[] = {
|
nasal_builtin_table math_lib_native[] = {
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
// abbreviation of some useful basic type
|
// abbreviation of some useful basic type
|
||||||
using i32 = std::int32_t;
|
using i32 = std::int32_t;
|
||||||
|
@ -54,8 +53,6 @@ f64 dec2f(const char*);
|
||||||
f64 str2num(const char*);
|
f64 str2num(const char*);
|
||||||
i32 utf8_hdchk(const char);
|
i32 utf8_hdchk(const char);
|
||||||
std::string chrhex(const char);
|
std::string chrhex(const char);
|
||||||
std::string rawstr(const std::string&, const usize maxlen=0);
|
std::string rawstr(const std::string&, const usize maxlen = 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "nasal_gc.h"
|
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "nasal.h"
|
#include "nasal.h"
|
||||||
|
#include "nasal_type.h"
|
||||||
#include "nasal_gc.h"
|
#include "nasal_gc.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning (disable:4566) // i know i'm using utf-8, fuck you
|
#pragma warning (disable:4566) // i know i'm using utf-8, fuck you
|
||||||
#pragma warning (disable:4244)
|
#pragma warning (disable:4244)
|
||||||
|
|
351
src/nasal_gc.cpp
351
src/nasal_gc.cpp
|
@ -2,348 +2,6 @@
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
|
||||||
var nas_vec::get_val(const i32 n) {
|
|
||||||
i32 size = elems.size();
|
|
||||||
if (n<-size || n>=size) {
|
|
||||||
return var::none();
|
|
||||||
}
|
|
||||||
return elems[n>=0? n:n+size];
|
|
||||||
}
|
|
||||||
|
|
||||||
var* nas_vec::get_mem(const i32 n) {
|
|
||||||
i32 size = elems.size();
|
|
||||||
if (n<-size || n>=size) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return &elems[n>=0? n:n+size];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& out, nas_vec& vec) {
|
|
||||||
if (!vec.elems.size() || vec.printed) {
|
|
||||||
out << (vec.elems.size()? "[..]":"[]");
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
vec.printed = true;
|
|
||||||
usize iter = 0, size = vec.elems.size();
|
|
||||||
out << "[";
|
|
||||||
for(auto& i:vec.elems) {
|
|
||||||
out << i << ",]"[(++iter)==size];
|
|
||||||
}
|
|
||||||
vec.printed = false;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
var nas_hash::get_val(const std::string& key) {
|
|
||||||
if (elems.count(key)) {
|
|
||||||
return elems.at(key);
|
|
||||||
} else if (!elems.count("parents")) {
|
|
||||||
return var::none();
|
|
||||||
}
|
|
||||||
var ret = var::none();
|
|
||||||
var val = elems.at("parents");
|
|
||||||
if (val.type!=vm_vec) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
for(auto& i : val.vec().elems) {
|
|
||||||
if (i.type==vm_hash) {
|
|
||||||
ret = i.hash().get_val(key);
|
|
||||||
}
|
|
||||||
if (ret.type!=vm_none) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
var* nas_hash::get_mem(const std::string& key) {
|
|
||||||
if (elems.count(key)) {
|
|
||||||
return &elems.at(key);
|
|
||||||
} else if (!elems.count("parents")) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
var* addr = nullptr;
|
|
||||||
var val = elems.at("parents");
|
|
||||||
if (val.type!=vm_vec) {
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
for(auto& i : val.vec().elems) {
|
|
||||||
if (i.type==vm_hash) {
|
|
||||||
addr = i.hash().get_mem(key);
|
|
||||||
}
|
|
||||||
if (addr) {
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& out, nas_hash& hash) {
|
|
||||||
if (!hash.elems.size() || hash.printed) {
|
|
||||||
out << (hash.elems.size()? "{..}":"{}");
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
hash.printed = true;
|
|
||||||
usize iter = 0, size = hash.elems.size();
|
|
||||||
out << "{";
|
|
||||||
for(auto& i : hash.elems) {
|
|
||||||
out << i.first << ":" << i.second << ",}"[(++iter)==size];
|
|
||||||
}
|
|
||||||
hash.printed = false;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nas_func::clear() {
|
|
||||||
dynamic_parameter_index = -1;
|
|
||||||
local.clear();
|
|
||||||
upval.clear();
|
|
||||||
keys.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void nas_ghost::set(
|
|
||||||
const std::string& ghost_type_name,
|
|
||||||
destructor destructor_pointer,
|
|
||||||
void* ghost_pointer) {
|
|
||||||
type_name = ghost_type_name;
|
|
||||||
destructor_function = destructor_pointer;
|
|
||||||
pointer = ghost_pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nas_ghost::clear() {
|
|
||||||
// do nothing if pointer is null
|
|
||||||
if (!pointer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// do clear pointer if destructor function pointer is null
|
|
||||||
if (!destructor_function) {
|
|
||||||
type_name = "";
|
|
||||||
pointer = nullptr;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// do destruction
|
|
||||||
destructor_function(pointer);
|
|
||||||
type_name = "";
|
|
||||||
pointer = nullptr;
|
|
||||||
destructor_function = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& out, const nas_ghost& ghost) {
|
|
||||||
out << "<object " << ghost.get_ghost_name();
|
|
||||||
out << " at 0x" << std::hex;
|
|
||||||
out << reinterpret_cast<u64>(ghost.pointer) << std::dec << ">";
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nas_co::clear() {
|
|
||||||
if (!ctx.stack) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for(u32 i = 0; i<STACK_DEPTH; ++i) {
|
|
||||||
ctx.stack[i] = var::nil();
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.pc = 0;
|
|
||||||
ctx.localr = nullptr;
|
|
||||||
ctx.memr = nullptr;
|
|
||||||
ctx.canary = ctx.stack+STACK_DEPTH-1;
|
|
||||||
ctx.top = ctx.stack;
|
|
||||||
ctx.funcr = var::nil();
|
|
||||||
ctx.upvalr = var::nil();
|
|
||||||
|
|
||||||
status = status::suspended;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& out, const nas_co& co) {
|
|
||||||
out << "<coroutine at 0x" << std::hex;
|
|
||||||
out << reinterpret_cast<u64>(&co) << std::dec << ">";
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
var nas_map::get_val(const std::string& key) {
|
|
||||||
if (mapper.count(key)) {
|
|
||||||
return *mapper.at(key);
|
|
||||||
}
|
|
||||||
return var::none();
|
|
||||||
}
|
|
||||||
|
|
||||||
var* nas_map::get_mem(const std::string& key) {
|
|
||||||
if (mapper.count(key)) {
|
|
||||||
return mapper.at(key);
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& out, nas_map& mp) {
|
|
||||||
if (!mp.mapper.size() || mp.printed) {
|
|
||||||
out << (mp.mapper.size()? "{..}":"{}");
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
mp.printed = true;
|
|
||||||
usize iter = 0, size = mp.mapper.size();
|
|
||||||
out << "{";
|
|
||||||
for(auto& i : mp.mapper) {
|
|
||||||
out << i.first << ":" << *i.second << ",}"[(++iter)==size];
|
|
||||||
}
|
|
||||||
mp.printed = false;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
nas_val::nas_val(u8 val_type) {
|
|
||||||
mark = gc_status::collected;
|
|
||||||
type = val_type;
|
|
||||||
unmut = 0;
|
|
||||||
switch(val_type) {
|
|
||||||
case vm_str: ptr.str = new std::string; break;
|
|
||||||
case vm_vec: ptr.vec = new nas_vec; break;
|
|
||||||
case vm_hash: ptr.hash = new nas_hash; break;
|
|
||||||
case vm_func: ptr.func = new nas_func; break;
|
|
||||||
case vm_upval: ptr.upval = new nas_upval; break;
|
|
||||||
case vm_obj: ptr.obj = new nas_ghost; break;
|
|
||||||
case vm_co: ptr.co = new nas_co; break;
|
|
||||||
case vm_map: ptr.map = new nas_map; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nas_val::~nas_val() {
|
|
||||||
switch(type) {
|
|
||||||
case vm_str: delete ptr.str; break;
|
|
||||||
case vm_vec: delete ptr.vec; break;
|
|
||||||
case vm_hash: delete ptr.hash; break;
|
|
||||||
case vm_func: delete ptr.func; break;
|
|
||||||
case vm_upval:delete ptr.upval;break;
|
|
||||||
case vm_obj: delete ptr.obj; break;
|
|
||||||
case vm_co: delete ptr.co; break;
|
|
||||||
case vm_map: delete ptr.map; break;
|
|
||||||
}
|
|
||||||
type=vm_nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nas_val::clear() {
|
|
||||||
switch(type) {
|
|
||||||
case vm_str: ptr.str->clear(); break;
|
|
||||||
case vm_vec: ptr.vec->elems.clear(); break;
|
|
||||||
case vm_hash: ptr.hash->elems.clear();break;
|
|
||||||
case vm_func: ptr.func->clear(); break;
|
|
||||||
case vm_upval:ptr.upval->clear(); break;
|
|
||||||
case vm_obj: ptr.obj->clear(); break;
|
|
||||||
case vm_co: ptr.co->clear(); break;
|
|
||||||
case vm_map: ptr.map->clear(); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
f64 var::tonum() {
|
|
||||||
return type!=vm_str? val.num:str2num(str().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string var::tostr() {
|
|
||||||
if (type==vm_str) {
|
|
||||||
return str();
|
|
||||||
} else if (type==vm_num) {
|
|
||||||
std::string tmp=std::to_string(num());
|
|
||||||
tmp.erase(tmp.find_last_not_of('0')+1, std::string::npos);
|
|
||||||
tmp.erase(tmp.find_last_not_of('.')+1, std::string::npos);
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& out, var& ref) {
|
|
||||||
switch(ref.type) {
|
|
||||||
case vm_none: out << "undefined"; break;
|
|
||||||
case vm_nil: out << "nil"; break;
|
|
||||||
case vm_num: out << ref.val.num; break;
|
|
||||||
case vm_str: out << ref.str(); break;
|
|
||||||
case vm_vec: out << ref.vec(); break;
|
|
||||||
case vm_hash: out << ref.hash(); break;
|
|
||||||
case vm_func: out << "func(..) {..}"; break;
|
|
||||||
case vm_obj: out << ref.obj(); break;
|
|
||||||
case vm_co: out << ref.co(); break;
|
|
||||||
case vm_map: out << ref.map(); break;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool var::objchk(const std::string& name) {
|
|
||||||
return type==vm_obj && obj().type_name==name && obj().pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
var var::none() {
|
|
||||||
return {vm_none, static_cast<u32>(0)};
|
|
||||||
}
|
|
||||||
|
|
||||||
var var::nil() {
|
|
||||||
return {vm_nil, static_cast<u32>(0)};
|
|
||||||
}
|
|
||||||
|
|
||||||
var var::ret(u32 pc) {
|
|
||||||
return {vm_ret, pc};
|
|
||||||
}
|
|
||||||
|
|
||||||
var var::cnt(i64 n) {
|
|
||||||
return {vm_cnt, n};
|
|
||||||
}
|
|
||||||
|
|
||||||
var var::num(f64 n) {
|
|
||||||
return {vm_num, n};
|
|
||||||
}
|
|
||||||
|
|
||||||
var var::gcobj(nas_val* p) {
|
|
||||||
return {p->type, p};
|
|
||||||
}
|
|
||||||
|
|
||||||
var var::addr(var* p) {
|
|
||||||
return {vm_addr, p};
|
|
||||||
}
|
|
||||||
|
|
||||||
var* var::addr() {
|
|
||||||
return val.addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 var::ret() {
|
|
||||||
return val.ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
i64& var::cnt() {
|
|
||||||
return val.cnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
f64 var::num() {
|
|
||||||
return val.num;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string& var::str() {
|
|
||||||
return *val.gcobj->ptr.str;
|
|
||||||
}
|
|
||||||
|
|
||||||
nas_vec& var::vec() {
|
|
||||||
return *val.gcobj->ptr.vec;
|
|
||||||
}
|
|
||||||
|
|
||||||
nas_hash& var::hash() {
|
|
||||||
return *val.gcobj->ptr.hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
nas_func& var::func() {
|
|
||||||
return *val.gcobj->ptr.func;
|
|
||||||
}
|
|
||||||
|
|
||||||
nas_upval& var::upval() {
|
|
||||||
return *val.gcobj->ptr.upval;
|
|
||||||
}
|
|
||||||
|
|
||||||
nas_ghost& var::obj() {
|
|
||||||
return *val.gcobj->ptr.obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
nas_co& var::co() {
|
|
||||||
return *val.gcobj->ptr.co;
|
|
||||||
}
|
|
||||||
|
|
||||||
nas_map& var::map() {
|
|
||||||
return *val.gcobj->ptr.map;
|
|
||||||
}
|
|
||||||
|
|
||||||
void gc::do_mark_sweep() {
|
void gc::do_mark_sweep() {
|
||||||
using clk = std::chrono::high_resolution_clock;
|
using clk = std::chrono::high_resolution_clock;
|
||||||
auto begin = clk::now();
|
auto begin = clk::now();
|
||||||
|
@ -556,7 +214,7 @@ void gc::init(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
strs[i] = var::gcobj(new nas_val(vm_str));
|
strs[i] = var::gcobj(new nas_val(vm_str));
|
||||||
strs[i].val.gcobj->unmut = 1;
|
strs[i].val.gcobj->unmutable = 1;
|
||||||
strs[i].str() = constant_strings[i];
|
strs[i].str() = constant_strings[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -568,7 +226,7 @@ void gc::init(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
env_argv[i] = var::gcobj(new nas_val(vm_str));
|
env_argv[i] = var::gcobj(new nas_val(vm_str));
|
||||||
env_argv[i].val.gcobj->unmut = 1;
|
env_argv[i].val.gcobj->unmutable = 1;
|
||||||
env_argv[i].str() = argv[i];
|
env_argv[i].str() = argv[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -724,9 +382,4 @@ void gc::ctxreserve() {
|
||||||
cort = nullptr;
|
cort = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
var nas_err(const std::string& error_function_name, const std::string& info) {
|
|
||||||
std::cerr << "[vm] " << error_function_name << ": " << info << "\n";
|
|
||||||
return var::none();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
277
src/nasal_gc.h
277
src/nasal_gc.h
|
@ -7,288 +7,18 @@
|
||||||
#pragma warning (disable:4102)
|
#pragma warning (disable:4102)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#else
|
|
||||||
#include <io.h>
|
|
||||||
#include <direct.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <unordered_map>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <algorithm>
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include "nasal.h"
|
#include "nasal.h"
|
||||||
|
#include "nasal_type.h"
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
|
||||||
enum vm_type:u8 {
|
|
||||||
/* none-gc object */
|
|
||||||
vm_none = 0,
|
|
||||||
vm_cnt,
|
|
||||||
vm_addr,
|
|
||||||
vm_ret,
|
|
||||||
vm_nil,
|
|
||||||
vm_num,
|
|
||||||
/* gc object */
|
|
||||||
vm_str,
|
|
||||||
vm_vec,
|
|
||||||
vm_hash,
|
|
||||||
vm_func,
|
|
||||||
vm_upval,
|
|
||||||
vm_obj,
|
|
||||||
vm_co,
|
|
||||||
vm_map // for globals and namespaces
|
|
||||||
};
|
|
||||||
|
|
||||||
const u32 gc_type_size = vm_map-vm_str+1;
|
|
||||||
|
|
||||||
struct nas_vec; // vector
|
|
||||||
struct nas_hash; // hashmap(dict)
|
|
||||||
struct nas_func; // function(lambda)
|
|
||||||
struct nas_upval; // upvalue
|
|
||||||
struct nas_ghost; // objects
|
|
||||||
struct nas_co; // coroutine
|
|
||||||
struct nas_map; // mapper
|
|
||||||
struct nas_val; // nas_val includes gc-managed types
|
|
||||||
|
|
||||||
struct var {
|
|
||||||
public:
|
|
||||||
u8 type = vm_none;
|
|
||||||
union {
|
|
||||||
u32 ret;
|
|
||||||
i64 cnt;
|
|
||||||
f64 num;
|
|
||||||
var* addr;
|
|
||||||
nas_val* gcobj;
|
|
||||||
} val;
|
|
||||||
|
|
||||||
private:
|
|
||||||
var(u8 t, u32 pc) {type = t; val.ret = pc;}
|
|
||||||
var(u8 t, i64 ct) {type = t; val.cnt = ct;}
|
|
||||||
var(u8 t, f64 n) {type = t; val.num = n;}
|
|
||||||
var(u8 t, var* p) {type = t; val.addr = p;}
|
|
||||||
var(u8 t, nas_val* p) {type = t; val.gcobj = p;}
|
|
||||||
|
|
||||||
public:
|
|
||||||
var() = default;
|
|
||||||
var(const var&) = default;
|
|
||||||
bool operator==(const var& nr) const {
|
|
||||||
return type==nr.type && val.gcobj==nr.val.gcobj;
|
|
||||||
}
|
|
||||||
bool operator!=(const var& nr) const {
|
|
||||||
return type!=nr.type || val.gcobj!=nr.val.gcobj;
|
|
||||||
}
|
|
||||||
|
|
||||||
// number and string can be translated to each other
|
|
||||||
f64 tonum();
|
|
||||||
std::string tostr();
|
|
||||||
bool objchk(const std::string&);
|
|
||||||
|
|
||||||
// create new var object
|
|
||||||
static var none();
|
|
||||||
static var nil();
|
|
||||||
static var ret(u32);
|
|
||||||
static var cnt(i64);
|
|
||||||
static var num(f64);
|
|
||||||
static var gcobj(nas_val*);
|
|
||||||
static var addr(var*);
|
|
||||||
|
|
||||||
// get content
|
|
||||||
var* addr();
|
|
||||||
u32 ret();
|
|
||||||
i64& cnt();
|
|
||||||
f64 num();
|
|
||||||
std::string& str();
|
|
||||||
nas_vec& vec();
|
|
||||||
nas_hash& hash();
|
|
||||||
nas_func& func();
|
|
||||||
nas_upval& upval();
|
|
||||||
nas_ghost& obj();
|
|
||||||
nas_co& co();
|
|
||||||
nas_map& map();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nas_vec {
|
|
||||||
std::vector<var> elems;
|
|
||||||
|
|
||||||
// mark if this is printed, avoid stackoverflow
|
|
||||||
bool printed;
|
|
||||||
|
|
||||||
nas_vec():printed(false) {}
|
|
||||||
usize size() const {return elems.size();}
|
|
||||||
var get_val(const i32);
|
|
||||||
var* get_mem(const i32);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nas_hash {
|
|
||||||
std::unordered_map<std::string, var> elems;
|
|
||||||
|
|
||||||
// mark if this is printed, avoid stackoverflow
|
|
||||||
bool printed;
|
|
||||||
|
|
||||||
nas_hash(): printed(false) {}
|
|
||||||
usize size() const {return elems.size();}
|
|
||||||
var get_val(const std::string&);
|
|
||||||
var* get_mem(const std::string&);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nas_func {
|
|
||||||
i32 dynamic_parameter_index; // dynamic parameter name index in hash.
|
|
||||||
u32 entry; // pc will set to entry-1 to call this function
|
|
||||||
u32 parameter_size; // used to load default parameters to a new function
|
|
||||||
u32 local_size; // used to expand memory space for local values on stack
|
|
||||||
std::vector<var> local; // local scope with default value(var)
|
|
||||||
std::vector<var> upval; // closure
|
|
||||||
|
|
||||||
// parameter table, u32 begins from 1
|
|
||||||
std::unordered_map<std::string, u32> keys;
|
|
||||||
|
|
||||||
nas_func():
|
|
||||||
dynamic_parameter_index(-1), entry(0),
|
|
||||||
parameter_size(0), local_size(0) {}
|
|
||||||
void clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nas_upval {
|
|
||||||
public:
|
|
||||||
/* on stack, use these variables */
|
|
||||||
bool on_stack;
|
|
||||||
u32 size;
|
|
||||||
var* stk;
|
|
||||||
|
|
||||||
/* not on stack, use this */
|
|
||||||
std::vector<var> elems;
|
|
||||||
|
|
||||||
public:
|
|
||||||
nas_upval(): on_stack(true), size(0), stk(nullptr) {}
|
|
||||||
|
|
||||||
var& operator[](usize n) {
|
|
||||||
return on_stack? stk[n]:elems[n];
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear() {
|
|
||||||
on_stack = true;
|
|
||||||
elems.clear();
|
|
||||||
size = 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nas_ghost {
|
|
||||||
private:
|
|
||||||
using destructor = void (*)(void*);
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::string type_name;
|
|
||||||
destructor destructor_function;
|
|
||||||
void* pointer;
|
|
||||||
|
|
||||||
public:
|
|
||||||
nas_ghost():
|
|
||||||
type_name(""), destructor_function(nullptr), pointer(nullptr) {}
|
|
||||||
~nas_ghost() {clear();}
|
|
||||||
void set(const std::string&, destructor, void*);
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
public:
|
|
||||||
const std::string& get_ghost_name() const {
|
|
||||||
return type_name;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct context {
|
|
||||||
u32 pc = 0;
|
|
||||||
var* localr = nullptr;
|
|
||||||
var* memr = nullptr;
|
|
||||||
var funcr = var::nil();
|
|
||||||
var upvalr = var::nil();
|
|
||||||
var* canary = nullptr;
|
|
||||||
var* stack = nullptr;
|
|
||||||
var* top = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nas_co {
|
|
||||||
enum class status:u32 {
|
|
||||||
suspended,
|
|
||||||
running,
|
|
||||||
dead
|
|
||||||
};
|
|
||||||
|
|
||||||
context ctx;
|
|
||||||
status status;
|
|
||||||
|
|
||||||
nas_co() {
|
|
||||||
ctx.stack = new var[STACK_DEPTH];
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
~nas_co() {
|
|
||||||
delete[] ctx.stack;
|
|
||||||
}
|
|
||||||
void clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nas_map {
|
|
||||||
bool printed = false;
|
|
||||||
std::unordered_map<std::string, var*> mapper;
|
|
||||||
|
|
||||||
nas_map() {}
|
|
||||||
void clear() {
|
|
||||||
mapper.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
var get_val(const std::string&);
|
|
||||||
var* get_mem(const std::string&);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nas_val {
|
|
||||||
enum class gc_status:u8 {
|
|
||||||
uncollected = 0,
|
|
||||||
collected,
|
|
||||||
found
|
|
||||||
};
|
|
||||||
|
|
||||||
gc_status mark;
|
|
||||||
u8 type; // value type
|
|
||||||
u8 unmut; // used to mark if a string is unmutable
|
|
||||||
union {
|
|
||||||
std::string* str;
|
|
||||||
nas_vec* vec;
|
|
||||||
nas_hash* hash;
|
|
||||||
nas_func* func;
|
|
||||||
nas_upval* upval;
|
|
||||||
nas_ghost* obj;
|
|
||||||
nas_co* co;
|
|
||||||
nas_map* map;
|
|
||||||
} ptr;
|
|
||||||
|
|
||||||
nas_val(u8);
|
|
||||||
~nas_val();
|
|
||||||
void clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream&, nas_vec&);
|
|
||||||
std::ostream& operator<<(std::ostream&, nas_hash&);
|
|
||||||
std::ostream& operator<<(std::ostream&, nas_map&);
|
|
||||||
std::ostream& operator<<(std::ostream&, const nas_ghost&);
|
|
||||||
std::ostream& operator<<(std::ostream&, const nas_co&);
|
|
||||||
std::ostream& operator<<(std::ostream&, var&);
|
|
||||||
|
|
||||||
const var zero = var::num(0);
|
|
||||||
const var one = var::num(1);
|
|
||||||
const var nil = var::nil();
|
|
||||||
|
|
||||||
struct gc {
|
struct gc {
|
||||||
/* main context temporary storage */
|
/* main context temporary storage */
|
||||||
context main_context;
|
context main_context;
|
||||||
|
@ -370,7 +100,7 @@ public:
|
||||||
|
|
||||||
var newstr(const char* buff) {
|
var newstr(const char* buff) {
|
||||||
var s = alloc(vm_str);
|
var s = alloc(vm_str);
|
||||||
s.str() = buff;
|
s.str() = std::string(buff);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,9 +111,6 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// use to print error log and return error value
|
|
||||||
var nas_err(const std::string&, const std::string&);
|
|
||||||
|
|
||||||
// module function type
|
// module function type
|
||||||
typedef var (*module_func)(var*, usize, gc*);
|
typedef var (*module_func)(var*, usize, gc*);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,352 @@
|
||||||
|
#include "nasal_type.h"
|
||||||
|
|
||||||
|
namespace nasal {
|
||||||
|
|
||||||
|
var nas_vec::get_val(const i32 n) {
|
||||||
|
i32 size = elems.size();
|
||||||
|
if (n<-size || n>=size) {
|
||||||
|
return var::none();
|
||||||
|
}
|
||||||
|
return elems[n>=0? n:n+size];
|
||||||
|
}
|
||||||
|
|
||||||
|
var* nas_vec::get_mem(const i32 n) {
|
||||||
|
i32 size = elems.size();
|
||||||
|
if (n<-size || n>=size) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return &elems[n>=0? n:n+size];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& out, nas_vec& vec) {
|
||||||
|
if (!vec.elems.size() || vec.printed) {
|
||||||
|
out << (vec.elems.size()? "[..]":"[]");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
vec.printed = true;
|
||||||
|
usize iter = 0, size = vec.elems.size();
|
||||||
|
out << "[";
|
||||||
|
for(auto& i:vec.elems) {
|
||||||
|
out << i << ",]"[(++iter)==size];
|
||||||
|
}
|
||||||
|
vec.printed = false;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nas_hash::get_val(const std::string& key) {
|
||||||
|
if (elems.count(key)) {
|
||||||
|
return elems.at(key);
|
||||||
|
} else if (!elems.count("parents")) {
|
||||||
|
return var::none();
|
||||||
|
}
|
||||||
|
var ret = var::none();
|
||||||
|
var val = elems.at("parents");
|
||||||
|
if (val.type!=vm_vec) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
for(auto& i : val.vec().elems) {
|
||||||
|
if (i.type==vm_hash) {
|
||||||
|
ret = i.hash().get_val(key);
|
||||||
|
}
|
||||||
|
if (ret.type!=vm_none) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
var* nas_hash::get_mem(const std::string& key) {
|
||||||
|
if (elems.count(key)) {
|
||||||
|
return &elems.at(key);
|
||||||
|
} else if (!elems.count("parents")) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
var* addr = nullptr;
|
||||||
|
var val = elems.at("parents");
|
||||||
|
if (val.type!=vm_vec) {
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
for(auto& i : val.vec().elems) {
|
||||||
|
if (i.type==vm_hash) {
|
||||||
|
addr = i.hash().get_mem(key);
|
||||||
|
}
|
||||||
|
if (addr) {
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& out, nas_hash& hash) {
|
||||||
|
if (!hash.elems.size() || hash.printed) {
|
||||||
|
out << (hash.elems.size()? "{..}":"{}");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
hash.printed = true;
|
||||||
|
usize iter = 0, size = hash.elems.size();
|
||||||
|
out << "{";
|
||||||
|
for(auto& i : hash.elems) {
|
||||||
|
out << i.first << ":" << i.second << ",}"[(++iter)==size];
|
||||||
|
}
|
||||||
|
hash.printed = false;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nas_func::clear() {
|
||||||
|
dynamic_parameter_index = -1;
|
||||||
|
local.clear();
|
||||||
|
upval.clear();
|
||||||
|
keys.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void nas_ghost::set(
|
||||||
|
const std::string& ghost_type_name,
|
||||||
|
destructor destructor_pointer,
|
||||||
|
void* ghost_pointer) {
|
||||||
|
type_name = ghost_type_name;
|
||||||
|
destructor_function = destructor_pointer;
|
||||||
|
pointer = ghost_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nas_ghost::clear() {
|
||||||
|
// do nothing if pointer is null
|
||||||
|
if (!pointer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// do clear pointer if destructor function pointer is null
|
||||||
|
if (!destructor_function) {
|
||||||
|
type_name = "";
|
||||||
|
pointer = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// do destruction
|
||||||
|
destructor_function(pointer);
|
||||||
|
type_name = "";
|
||||||
|
pointer = nullptr;
|
||||||
|
destructor_function = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& out, const nas_ghost& ghost) {
|
||||||
|
out << "<object " << ghost.get_ghost_name();
|
||||||
|
out << " at 0x" << std::hex;
|
||||||
|
out << reinterpret_cast<u64>(ghost.pointer) << std::dec << ">";
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nas_co::clear() {
|
||||||
|
if (!ctx.stack) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(u32 i = 0; i<STACK_DEPTH; ++i) {
|
||||||
|
ctx.stack[i] = var::nil();
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.pc = 0;
|
||||||
|
ctx.localr = nullptr;
|
||||||
|
ctx.memr = nullptr;
|
||||||
|
ctx.canary = ctx.stack+STACK_DEPTH-1;
|
||||||
|
ctx.top = ctx.stack;
|
||||||
|
ctx.funcr = var::nil();
|
||||||
|
ctx.upvalr = var::nil();
|
||||||
|
|
||||||
|
status = status::suspended;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& out, const nas_co& co) {
|
||||||
|
out << "<coroutine at 0x" << std::hex;
|
||||||
|
out << reinterpret_cast<u64>(&co) << std::dec << ">";
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nas_map::get_val(const std::string& key) {
|
||||||
|
if (mapper.count(key)) {
|
||||||
|
return *mapper.at(key);
|
||||||
|
}
|
||||||
|
return var::none();
|
||||||
|
}
|
||||||
|
|
||||||
|
var* nas_map::get_mem(const std::string& key) {
|
||||||
|
if (mapper.count(key)) {
|
||||||
|
return mapper.at(key);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& out, nas_map& mp) {
|
||||||
|
if (!mp.mapper.size() || mp.printed) {
|
||||||
|
out << (mp.mapper.size()? "{..}":"{}");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
mp.printed = true;
|
||||||
|
usize iter = 0, size = mp.mapper.size();
|
||||||
|
out << "{";
|
||||||
|
for(auto& i : mp.mapper) {
|
||||||
|
out << i.first << ":" << *i.second << ",}"[(++iter)==size];
|
||||||
|
}
|
||||||
|
mp.printed = false;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
nas_val::nas_val(u8 val_type) {
|
||||||
|
mark = gc_status::collected;
|
||||||
|
type = val_type;
|
||||||
|
unmutable = 0;
|
||||||
|
switch(val_type) {
|
||||||
|
case vm_str: ptr.str = new std::string; break;
|
||||||
|
case vm_vec: ptr.vec = new nas_vec; break;
|
||||||
|
case vm_hash: ptr.hash = new nas_hash; break;
|
||||||
|
case vm_func: ptr.func = new nas_func; break;
|
||||||
|
case vm_upval: ptr.upval = new nas_upval; break;
|
||||||
|
case vm_obj: ptr.obj = new nas_ghost; break;
|
||||||
|
case vm_co: ptr.co = new nas_co; break;
|
||||||
|
case vm_map: ptr.map = new nas_map; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nas_val::~nas_val() {
|
||||||
|
switch(type) {
|
||||||
|
case vm_str: delete ptr.str; break;
|
||||||
|
case vm_vec: delete ptr.vec; break;
|
||||||
|
case vm_hash: delete ptr.hash; break;
|
||||||
|
case vm_func: delete ptr.func; break;
|
||||||
|
case vm_upval:delete ptr.upval;break;
|
||||||
|
case vm_obj: delete ptr.obj; break;
|
||||||
|
case vm_co: delete ptr.co; break;
|
||||||
|
case vm_map: delete ptr.map; break;
|
||||||
|
}
|
||||||
|
type=vm_nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nas_val::clear() {
|
||||||
|
switch(type) {
|
||||||
|
case vm_str: ptr.str->clear(); break;
|
||||||
|
case vm_vec: ptr.vec->elems.clear(); break;
|
||||||
|
case vm_hash: ptr.hash->elems.clear();break;
|
||||||
|
case vm_func: ptr.func->clear(); break;
|
||||||
|
case vm_upval:ptr.upval->clear(); break;
|
||||||
|
case vm_obj: ptr.obj->clear(); break;
|
||||||
|
case vm_co: ptr.co->clear(); break;
|
||||||
|
case vm_map: ptr.map->clear(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f64 var::tonum() {
|
||||||
|
return type!=vm_str? val.num:str2num(str().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string var::tostr() {
|
||||||
|
if (type==vm_str) {
|
||||||
|
return str();
|
||||||
|
} else if (type==vm_num) {
|
||||||
|
std::string tmp=std::to_string(num());
|
||||||
|
tmp.erase(tmp.find_last_not_of('0')+1, std::string::npos);
|
||||||
|
tmp.erase(tmp.find_last_not_of('.')+1, std::string::npos);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& out, var& ref) {
|
||||||
|
switch(ref.type) {
|
||||||
|
case vm_none: out << "undefined"; break;
|
||||||
|
case vm_nil: out << "nil"; break;
|
||||||
|
case vm_num: out << ref.val.num; break;
|
||||||
|
case vm_str: out << ref.str(); break;
|
||||||
|
case vm_vec: out << ref.vec(); break;
|
||||||
|
case vm_hash: out << ref.hash(); break;
|
||||||
|
case vm_func: out << "func(..) {..}"; break;
|
||||||
|
case vm_obj: out << ref.obj(); break;
|
||||||
|
case vm_co: out << ref.co(); break;
|
||||||
|
case vm_map: out << ref.map(); break;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool var::objchk(const std::string& name) {
|
||||||
|
return type==vm_obj && obj().type_name==name && obj().pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
var var::none() {
|
||||||
|
return {vm_none, static_cast<u32>(0)};
|
||||||
|
}
|
||||||
|
|
||||||
|
var var::nil() {
|
||||||
|
return {vm_nil, static_cast<u32>(0)};
|
||||||
|
}
|
||||||
|
|
||||||
|
var var::ret(u32 pc) {
|
||||||
|
return {vm_ret, pc};
|
||||||
|
}
|
||||||
|
|
||||||
|
var var::cnt(i64 n) {
|
||||||
|
return {vm_cnt, n};
|
||||||
|
}
|
||||||
|
|
||||||
|
var var::num(f64 n) {
|
||||||
|
return {vm_num, n};
|
||||||
|
}
|
||||||
|
|
||||||
|
var var::gcobj(nas_val* p) {
|
||||||
|
return {p->type, p};
|
||||||
|
}
|
||||||
|
|
||||||
|
var var::addr(var* p) {
|
||||||
|
return {vm_addr, p};
|
||||||
|
}
|
||||||
|
|
||||||
|
var* var::addr() {
|
||||||
|
return val.addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 var::ret() {
|
||||||
|
return val.ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
i64& var::cnt() {
|
||||||
|
return val.cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
f64 var::num() {
|
||||||
|
return val.num;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string& var::str() {
|
||||||
|
return *val.gcobj->ptr.str;
|
||||||
|
}
|
||||||
|
|
||||||
|
nas_vec& var::vec() {
|
||||||
|
return *val.gcobj->ptr.vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
nas_hash& var::hash() {
|
||||||
|
return *val.gcobj->ptr.hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
nas_func& var::func() {
|
||||||
|
return *val.gcobj->ptr.func;
|
||||||
|
}
|
||||||
|
|
||||||
|
nas_upval& var::upval() {
|
||||||
|
return *val.gcobj->ptr.upval;
|
||||||
|
}
|
||||||
|
|
||||||
|
nas_ghost& var::obj() {
|
||||||
|
return *val.gcobj->ptr.obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
nas_co& var::co() {
|
||||||
|
return *val.gcobj->ptr.co;
|
||||||
|
}
|
||||||
|
|
||||||
|
nas_map& var::map() {
|
||||||
|
return *val.gcobj->ptr.map;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nas_err(const std::string& error_function_name, const std::string& info) {
|
||||||
|
std::cerr << "[vm] " << error_function_name << ": " << info << "\n";
|
||||||
|
return var::none();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,272 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "nasal.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace nasal {
|
||||||
|
|
||||||
|
enum vm_type:u8 {
|
||||||
|
/* none-gc object */
|
||||||
|
vm_none = 0, // error type
|
||||||
|
vm_cnt, // counter for forindex/foreach loop
|
||||||
|
vm_addr, // var* address
|
||||||
|
vm_ret, // return addres(program counter)
|
||||||
|
vm_nil, // nil
|
||||||
|
vm_num, // number
|
||||||
|
/* gc object */
|
||||||
|
vm_str, // string
|
||||||
|
vm_vec, // vector
|
||||||
|
vm_hash, // hashmap(dict)
|
||||||
|
vm_func, // function(lambda)
|
||||||
|
vm_upval, // upvalue
|
||||||
|
vm_obj, // ghost type
|
||||||
|
vm_co, // coroutine
|
||||||
|
vm_map // for globals and namespaces
|
||||||
|
};
|
||||||
|
|
||||||
|
// size of gc object type
|
||||||
|
const u32 gc_type_size = vm_map-vm_str+1;
|
||||||
|
|
||||||
|
// basic types
|
||||||
|
struct nas_vec; // vector
|
||||||
|
struct nas_hash; // hashmap(dict)
|
||||||
|
struct nas_func; // function(lambda)
|
||||||
|
struct nas_upval; // upvalue
|
||||||
|
struct nas_ghost; // objects
|
||||||
|
struct nas_co; // coroutine
|
||||||
|
struct nas_map; // mapper
|
||||||
|
|
||||||
|
// union type
|
||||||
|
struct nas_val; // nas_val includes gc-managed types
|
||||||
|
|
||||||
|
struct var {
|
||||||
|
public:
|
||||||
|
u8 type = vm_none;
|
||||||
|
union {
|
||||||
|
u32 ret;
|
||||||
|
i64 cnt;
|
||||||
|
f64 num;
|
||||||
|
var* addr;
|
||||||
|
nas_val* gcobj;
|
||||||
|
} val;
|
||||||
|
|
||||||
|
private:
|
||||||
|
var(u8 t, u32 pc) {type = t; val.ret = pc;}
|
||||||
|
var(u8 t, i64 ct) {type = t; val.cnt = ct;}
|
||||||
|
var(u8 t, f64 n) {type = t; val.num = n;}
|
||||||
|
var(u8 t, var* p) {type = t; val.addr = p;}
|
||||||
|
var(u8 t, nas_val* p) {type = t; val.gcobj = p;}
|
||||||
|
|
||||||
|
public:
|
||||||
|
var() = default;
|
||||||
|
var(const var&) = default;
|
||||||
|
bool operator==(const var& nr) const {
|
||||||
|
return type==nr.type && val.gcobj==nr.val.gcobj;
|
||||||
|
}
|
||||||
|
bool operator!=(const var& nr) const {
|
||||||
|
return type!=nr.type || val.gcobj!=nr.val.gcobj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// number and string can be translated to each other
|
||||||
|
f64 tonum();
|
||||||
|
std::string tostr();
|
||||||
|
bool objchk(const std::string&);
|
||||||
|
|
||||||
|
// create new var object
|
||||||
|
static var none();
|
||||||
|
static var nil();
|
||||||
|
static var ret(u32);
|
||||||
|
static var cnt(i64);
|
||||||
|
static var num(f64);
|
||||||
|
static var gcobj(nas_val*);
|
||||||
|
static var addr(var*);
|
||||||
|
|
||||||
|
// get value
|
||||||
|
var* addr();
|
||||||
|
u32 ret();
|
||||||
|
i64& cnt();
|
||||||
|
f64 num();
|
||||||
|
std::string& str();
|
||||||
|
nas_vec& vec();
|
||||||
|
nas_hash& hash();
|
||||||
|
nas_func& func();
|
||||||
|
nas_upval& upval();
|
||||||
|
nas_ghost& obj();
|
||||||
|
nas_co& co();
|
||||||
|
nas_map& map();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nas_vec {
|
||||||
|
std::vector<var> elems;
|
||||||
|
|
||||||
|
// mark if this is printed, avoid stackoverflow
|
||||||
|
bool printed;
|
||||||
|
|
||||||
|
nas_vec():printed(false) {}
|
||||||
|
usize size() const {return elems.size();}
|
||||||
|
var get_val(const i32);
|
||||||
|
var* get_mem(const i32);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nas_hash {
|
||||||
|
std::unordered_map<std::string, var> elems;
|
||||||
|
|
||||||
|
// mark if this is printed, avoid stackoverflow
|
||||||
|
bool printed;
|
||||||
|
|
||||||
|
nas_hash(): printed(false) {}
|
||||||
|
usize size() const {return elems.size();}
|
||||||
|
var get_val(const std::string&);
|
||||||
|
var* get_mem(const std::string&);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nas_func {
|
||||||
|
i32 dynamic_parameter_index; // dynamic parameter name index in hash.
|
||||||
|
u32 entry; // pc will set to entry-1 to call this function
|
||||||
|
u32 parameter_size; // used to load default parameters to a new function
|
||||||
|
u32 local_size; // used to expand memory space for local values on stack
|
||||||
|
std::vector<var> local; // local scope with default value(var)
|
||||||
|
std::vector<var> upval; // closure
|
||||||
|
|
||||||
|
// parameter table, u32 begins from 1
|
||||||
|
std::unordered_map<std::string, u32> keys;
|
||||||
|
|
||||||
|
nas_func():
|
||||||
|
dynamic_parameter_index(-1), entry(0),
|
||||||
|
parameter_size(0), local_size(0) {}
|
||||||
|
void clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nas_upval {
|
||||||
|
public:
|
||||||
|
/* on stack, use these variables */
|
||||||
|
bool on_stack;
|
||||||
|
u32 size;
|
||||||
|
var* stack_frame_offset;
|
||||||
|
|
||||||
|
/* not on stack, use this */
|
||||||
|
std::vector<var> elems;
|
||||||
|
|
||||||
|
public:
|
||||||
|
nas_upval(): on_stack(true), size(0), stack_frame_offset(nullptr) {}
|
||||||
|
|
||||||
|
var& operator[](usize n) {
|
||||||
|
return on_stack? stack_frame_offset[n]:elems[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
on_stack = true;
|
||||||
|
elems.clear();
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nas_ghost {
|
||||||
|
private:
|
||||||
|
using destructor = void (*)(void*);
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string type_name;
|
||||||
|
destructor destructor_function;
|
||||||
|
void* pointer;
|
||||||
|
|
||||||
|
public:
|
||||||
|
nas_ghost():
|
||||||
|
type_name(""), destructor_function(nullptr), pointer(nullptr) {}
|
||||||
|
~nas_ghost() {clear();}
|
||||||
|
void set(const std::string&, destructor, void*);
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
public:
|
||||||
|
const std::string& get_ghost_name() const {
|
||||||
|
return type_name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct context {
|
||||||
|
u32 pc = 0;
|
||||||
|
var* localr = nullptr;
|
||||||
|
var* memr = nullptr;
|
||||||
|
var funcr = var::nil();
|
||||||
|
var upvalr = var::nil();
|
||||||
|
var* canary = nullptr;
|
||||||
|
var* stack = nullptr;
|
||||||
|
var* top = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nas_co {
|
||||||
|
enum class status:u32 {
|
||||||
|
suspended,
|
||||||
|
running,
|
||||||
|
dead
|
||||||
|
};
|
||||||
|
|
||||||
|
context ctx;
|
||||||
|
status status;
|
||||||
|
|
||||||
|
nas_co() {
|
||||||
|
ctx.stack = new var[STACK_DEPTH];
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
~nas_co() {
|
||||||
|
delete[] ctx.stack;
|
||||||
|
}
|
||||||
|
void clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nas_map {
|
||||||
|
bool printed = false;
|
||||||
|
std::unordered_map<std::string, var*> mapper;
|
||||||
|
|
||||||
|
nas_map() {}
|
||||||
|
void clear() {
|
||||||
|
mapper.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
var get_val(const std::string&);
|
||||||
|
var* get_mem(const std::string&);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nas_val {
|
||||||
|
enum class gc_status:u8 {
|
||||||
|
uncollected = 0,
|
||||||
|
collected,
|
||||||
|
found
|
||||||
|
};
|
||||||
|
|
||||||
|
gc_status mark;
|
||||||
|
u8 type; // value type
|
||||||
|
u8 unmutable; // used to mark if a string is unmutable
|
||||||
|
union {
|
||||||
|
std::string* str;
|
||||||
|
nas_vec* vec;
|
||||||
|
nas_hash* hash;
|
||||||
|
nas_func* func;
|
||||||
|
nas_upval* upval;
|
||||||
|
nas_ghost* obj;
|
||||||
|
nas_co* co;
|
||||||
|
nas_map* map;
|
||||||
|
} ptr;
|
||||||
|
|
||||||
|
nas_val(u8);
|
||||||
|
~nas_val();
|
||||||
|
void clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream&, nas_vec&);
|
||||||
|
std::ostream& operator<<(std::ostream&, nas_hash&);
|
||||||
|
std::ostream& operator<<(std::ostream&, nas_map&);
|
||||||
|
std::ostream& operator<<(std::ostream&, const nas_ghost&);
|
||||||
|
std::ostream& operator<<(std::ostream&, const nas_co&);
|
||||||
|
std::ostream& operator<<(std::ostream&, var&);
|
||||||
|
|
||||||
|
const var zero = var::num(0);
|
||||||
|
const var one = var::num(1);
|
||||||
|
const var nil = var::nil();
|
||||||
|
|
||||||
|
// use to print error log and return error value
|
||||||
|
var nas_err(const std::string&, const std::string&);
|
||||||
|
|
||||||
|
}
|
|
@ -262,7 +262,7 @@ inline void vm::o_newf() {
|
||||||
// so this size & stk setting has no problem
|
// so this size & stk setting has no problem
|
||||||
var upval = (ctx.upvalr.type==vm_nil)? ngc.alloc(vm_upval):ctx.upvalr;
|
var upval = (ctx.upvalr.type==vm_nil)? ngc.alloc(vm_upval):ctx.upvalr;
|
||||||
upval.upval().size = ctx.funcr.func().local_size;
|
upval.upval().size = ctx.funcr.func().local_size;
|
||||||
upval.upval().stk = ctx.localr;
|
upval.upval().stack_frame_offset = ctx.localr;
|
||||||
func.upval.push_back(upval);
|
func.upval.push_back(upval);
|
||||||
ctx.upvalr = upval;
|
ctx.upvalr = upval;
|
||||||
}
|
}
|
||||||
|
@ -507,7 +507,7 @@ inline void vm::o_neq() {
|
||||||
|
|
||||||
#define op_cmp(type)\
|
#define op_cmp(type)\
|
||||||
--ctx.top;\
|
--ctx.top;\
|
||||||
ctx.top[0] = (ctx.top[0].tonum() type ctx.top[1].tonum())?one:zero;
|
ctx.top[0] = (ctx.top[0].tonum() type ctx.top[1].tonum())? one:zero;
|
||||||
|
|
||||||
inline void vm::o_less() {op_cmp(<);}
|
inline void vm::o_less() {op_cmp(<);}
|
||||||
inline void vm::o_leq() {op_cmp(<=);}
|
inline void vm::o_leq() {op_cmp(<=);}
|
||||||
|
@ -515,7 +515,7 @@ inline void vm::o_grt() {op_cmp(>);}
|
||||||
inline void vm::o_geq() {op_cmp(>=);}
|
inline void vm::o_geq() {op_cmp(>=);}
|
||||||
|
|
||||||
#define op_cmp_const(type)\
|
#define op_cmp_const(type)\
|
||||||
ctx.top[0] = (ctx.top[0].tonum() type cnum[imm[ctx.pc]])?one:zero;
|
ctx.top[0] = (ctx.top[0].tonum() type cnum[imm[ctx.pc]])? one:zero;
|
||||||
|
|
||||||
inline void vm::o_lessc() {op_cmp_const(<);}
|
inline void vm::o_lessc() {op_cmp_const(<);}
|
||||||
inline void vm::o_leqc() {op_cmp_const(<=);}
|
inline void vm::o_leqc() {op_cmp_const(<=);}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
var ppm = func(filename, buffer) {
|
||||||
|
# P3 use ASCII number
|
||||||
|
# P6 use binary character
|
||||||
|
var width = 256;
|
||||||
|
var height = int(size(buffer)/3/width); # ppm use 3 chars for one pixel
|
||||||
|
println("width ", width, ", height ", height);
|
||||||
|
|
||||||
|
var fd = io.open(filename, "wb");
|
||||||
|
io.write(fd, "P6\n"~width~" "~height~"\n255\n");
|
||||||
|
io.write(fd, buffer);
|
||||||
|
io.close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size(arg)<1) {
|
||||||
|
println("need input file and output file");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var content = io.readfile(arg[0], "r");
|
||||||
|
var tail_len = 0;
|
||||||
|
while(math.mod(size(content), 256*3)!=0) {
|
||||||
|
content ~= "A";
|
||||||
|
tail_len += 1;
|
||||||
|
}
|
||||||
|
println("filled ", tail_len);
|
||||||
|
println("size ", size(content));
|
||||||
|
ppm(size(arg)==2? arg[1]:"out.ppm", content);
|
Loading…
Reference in New Issue