diff --git a/doc/pic/juliaset.png b/doc/pic/juliaset.png new file mode 100644 index 0000000..4ff8b2a Binary files /dev/null and b/doc/pic/juliaset.png differ diff --git a/module/json.cpp b/module/json.cpp new file mode 100644 index 0000000..3771206 --- /dev/null +++ b/module/json.cpp @@ -0,0 +1,349 @@ +#include "../src/nasal.h" +#include "../src/nasal_type.h" +#include "../src/nasal_gc.h" + +#include +#include +#include +#include + +namespace nasal { + +enum class token_type { + tok_eof, + tok_lbrace, + tok_rbrace, + tok_lbrkt, + tok_rbrkt, + tok_comma, + tok_colon, + tok_str, + tok_num, + tok_id, + tok_bool +}; + +std::string get_content(token_type type) { + switch(type) { + case token_type::tok_eof: return "eof"; + case token_type::tok_lbrace: return "`{`"; + case token_type::tok_rbrace: return "`}`"; + case token_type::tok_lbrkt: return "`[`"; + case token_type::tok_rbrkt: return "`]`"; + case token_type::tok_comma: return "`,`"; + case token_type::tok_colon: return "`:`"; + case token_type::tok_str: return "string"; + case token_type::tok_num: return "number"; + case token_type::tok_id: return "identifier"; + case token_type::tok_bool: return "boolean"; + } +} + +struct token { + token_type type; + std::string content; +}; + +class json { +private: + usize parse_error = 0; + std::string text = ""; + usize line = 1; + usize ptr = 0; + token this_token; + +private: + std::string var_generate(var&); + std::string vector_generate(nas_vec&); + std::string hash_generate(nas_hash&); + std::string map_generate(nas_map&); + +private: + bool is_num(char c) { + return std::isdigit(c); + } + bool is_id(char c) { + return std::isalpha(c) || c=='_'; + } + bool check(char c) { + return c=='{' || c=='}' || c=='[' || c==']' || + c==':' || c==',' || c=='"' || c=='\'' || + is_num(c) || is_id(c); + } + void next(); + void match(token_type); + var vector_member(gc*); + var vector_object_generate(gc*); + void hash_member(nas_hash&, gc*); + var hash_object_generate(gc*); + +public: + std::string stringify(var&); + var parse(const std::string&, gc*); + bool has_error() const { return parse_error; } +}; + +std::string json::var_generate(var& value) { + switch(value.type) { + case vm_type::vm_num: { + std::stringstream out; + out << value.num(); + return out.str(); + } + case vm_type::vm_str: return "\"" + value.str() + "\""; + case vm_type::vm_vec: return vector_generate(value.vec()); + case vm_type::vm_hash: return hash_generate(value.hash()); + default: break; + } + return "\"undefined\""; +} + +std::string json::vector_generate(nas_vec& vect) { + std::string out = "["; + for(auto& i : vect.elems) { + out += var_generate(i) + ","; + } + if (out.back()==',') { + out.pop_back(); + } + out += "]"; + return out; +} + +std::string json::hash_generate(nas_hash& hash) { + std::string out = "{"; + for(auto& i : hash.elems) { + out += "\"" + i.first + "\":"; + out += var_generate(i.second) + ","; + } + if (out.back()==',') { + out.pop_back(); + } + out += "}"; + return out; +} + +std::string json::map_generate(nas_map& nmap) { + std::string out = "{"; + for(auto& i : nmap.mapper) { + out += "\"" + i.first + "\":"; + out += var_generate(*i.second) + ","; + } + if (out.back()==',') { + out.pop_back(); + } + out += "}"; + return out; +} + +std::string json::stringify(var& object) { + if (object.is_vec()) { + return vector_generate(object.vec()); + } else if (object.is_hash()) { + return hash_generate(object.hash()); + } else if (object.is_map()) { + return map_generate(object.map()); + } + return "[]"; +} + +void json::next() { + while(ptr=text.length()) { + this_token = {token_type::tok_eof, "eof"}; + return; + } + auto c = text[ptr]; + switch(c) { + case '{': this_token = {token_type::tok_lbrace, "{"}; ++ptr; return; + case '}': this_token = {token_type::tok_rbrace, "}"}; ++ptr; return; + case '[': this_token = {token_type::tok_lbrkt, "["}; ++ptr; return; + case ']': this_token = {token_type::tok_rbrkt, "]"}; ++ptr; return; + case ',': this_token = {token_type::tok_comma, ","}; ++ptr; return; + case ':': this_token = {token_type::tok_colon, ":"}; ++ptr; return; + default: break; + } + if (is_num(c)) { + auto temp = std::string(1, c); + ++ptr; + while(ptrnewstr(this_token.content); + next(); + } else if (this_token.type==token_type::tok_num) { + result = var::num(str2num(this_token.content.c_str())); + next(); + } + return result; +} + +var json::vector_object_generate(gc* ngc) { + auto vect_object = ngc->alloc(vm_type::vm_vec); + auto& vec = vect_object.vec().elems; + ngc->temp = vect_object; + match(token_type::tok_lbrkt); + vec.push_back(vector_member(ngc)); + while(this_token.type==token_type::tok_comma) { + match(token_type::tok_comma); + vec.push_back(vector_member(ngc)); + } + match(token_type::tok_rbrkt); + ngc->temp = nil; + return vect_object; +} + +void json::hash_member(nas_hash& hash, gc* ngc) { + const auto name = this_token.content; + if (this_token.type==token_type::tok_rbrace) { + return; + } + if (this_token.type==token_type::tok_str) { + match(token_type::tok_str); + } else { + match(token_type::tok_id); + } + match(token_type::tok_colon); + if (this_token.type==token_type::tok_lbrace) { + hash.elems.insert({name, hash_object_generate(ngc)}); + } else if (this_token.type==token_type::tok_lbrkt) { + hash.elems.insert({name, vector_object_generate(ngc)}); + } else if (this_token.type==token_type::tok_str || + this_token.type==token_type::tok_bool) { + hash.elems.insert({name, ngc->newstr(this_token.content)}); + next(); + } else if (this_token.type==token_type::tok_num) { + hash.elems.insert({name, var::num(str2num(this_token.content.c_str()))}); + next(); + } +} + +var json::hash_object_generate(gc* ngc) { + auto hash_object = ngc->alloc(vm_type::vm_hash); + ngc->temp = hash_object; // cause problem + match(token_type::tok_lbrace); + hash_member(hash_object.hash(), ngc); + while(this_token.type==token_type::tok_comma) { + match(token_type::tok_comma); + hash_member(hash_object.hash(), ngc); + } + match(token_type::tok_rbrace); + ngc->temp = nil; + return hash_object; +} + +var json::parse(const std::string& input, gc* ngc) { + usize parse_error = 0; + usize line = 1; + usize ptr = 0; + this_token = {token_type::tok_eof, ""}; + + if (input.empty()) { + std::cerr << "json::parse: empty string.\n"; + ++parse_error; + return nil; + } + text = input; + next(); + if (this_token.type==token_type::tok_lbrkt) { + return vector_object_generate(ngc); + } else { + return hash_object_generate(ngc); + } + return nil; +} + +var stringify(var* args, usize size, gc* ngc) { + auto object = args[0]; + if (!object.is_vec() && !object.is_hash() && !object.is_map()) { + return nas_err("json::stringify", "must use hashmap or vector"); + } + return ngc->newstr(json().stringify(object)); +} + +var parse(var* args, usize size, gc* ngc) { + auto input = args[0]; + if (!input.is_str()) { + return nas_err("json::parse", "must use string"); + } + json instance; + auto result = instance.parse(input.str(), ngc); + if (instance.has_error()) { + return nas_err("json::parse", "parse error"); + } + return result; +} + +module_func_info func_tbl[] = { + {"stringify", stringify}, + {"parse", parse}, + {nullptr, nullptr} +}; + +extern "C" module_func_info* get() { + return func_tbl; +} + +} \ No newline at end of file diff --git a/module/libjson.nas b/module/libjson.nas new file mode 100644 index 0000000..4eb91ba --- /dev/null +++ b/module/libjson.nas @@ -0,0 +1,17 @@ +# module json +# 2023/11/27 ValKmjolnir + +use std.dylib; + +var _dynamic_lib = dylib.dlopen("libjson."~(os.platform()=="windows"?"dll":"so")); +var _stringify = _dynamic_lib.stringify; +var _parse = _dynamic_lib.parse; +var _call = dylib.limitcall(1); + +var stringify = func(object) { + return _call(_stringify, object); +} + +var parse = func(input_string) { + return _call(_parse, input_string); +} \ No newline at end of file