OR-1 dataflow CPU sketch
at main 306 lines 9.4 kB view raw
1#include "./parser.h" 2#include "./conversions.h" 3#include "./language.h" 4#include "./logger.h" 5#include "./tree.h" 6 7#include <cstddef> 8#include <napi.h> 9#include <vector> 10 11using namespace Napi; 12using std::vector; 13 14namespace node_tree_sitter { 15 16class CallbackInput final { 17 public: 18 CallbackInput(Function callback, Napi::Value js_buffer_size) { 19 this->callback.Reset(callback, 1); 20 if (js_buffer_size.IsNumber()) { 21 buffer.resize(js_buffer_size.As<Number>().Uint32Value()); 22 } else { 23 buffer.resize(static_cast<uint64_t>(32 * 1024)); 24 } 25 } 26 27 ~CallbackInput() { 28 callback.Reset(); 29 partial_string.Reset(); 30 } 31 32 TSInput Input() { 33 TSInput result; 34 result.payload = static_cast<void *>(this); 35 result.encoding = TSInputEncodingUTF16LE; 36 result.read = Read; 37 return result; 38 } 39 40 private: 41 static String slice(String s, uint32_t offset) { 42 Env env = s.Env(); 43 auto *data = env.GetInstanceData<AddonData>(); 44 return data->string_slice.Call(s, {Number::New(s.Env(), offset)}).As<String>(); 45 } 46 47 static const char * Read(void *payload, uint32_t byte, TSPoint position, uint32_t *bytes_read) { 48 auto *reader = static_cast<CallbackInput *>(payload); 49 Napi::Env env = reader->callback.Env(); 50 51 if (byte != reader->byte_offset) { 52 reader->byte_offset = byte; 53 reader->partial_string.Reset(); 54 } 55 56 *bytes_read = 0; 57 String result; 58 if (!reader->partial_string.IsEmpty()) { 59 result = reader->partial_string.Get("value").As<String>(); 60 } else { 61 Function callback = reader->callback.Value(); 62 Napi::Value result_value = callback({ 63 ByteCountToJS(env, byte), 64 PointToJS(env, position), 65 }); 66 if (env.IsExceptionPending()) { 67 return nullptr; 68 } 69 if (!result_value.IsString()) { 70 return nullptr; 71 } 72 result = result_value.As<String>(); 73 } 74 75 size_t length = 0; 76 size_t utf16_units_read = 0; 77 napi_status status; 78 status = napi_get_value_string_utf16( 79 env, result, nullptr, 0, &length 80 ); 81 if (status != napi_ok) { 82 return nullptr; 83 } 84 status = napi_get_value_string_utf16( 85 env, result, reinterpret_cast<char16_t *>(reader->buffer.data()), reader->buffer.size(), &utf16_units_read 86 ); 87 if (status != napi_ok) { 88 return nullptr; 89 } 90 91 *bytes_read = 2 * utf16_units_read; 92 reader->byte_offset += *bytes_read; 93 94 if (utf16_units_read < length) { 95 if (reader->partial_string.IsEmpty()) { 96 reader->partial_string = Napi::Persistent(Object::New(env)); 97 } 98 reader->partial_string.Set("value", slice(result, utf16_units_read)); 99 } else { 100 reader->partial_string.Reset(); 101 } 102 103 return reinterpret_cast<const char *>(reader->buffer.data()); 104 } 105 106 FunctionReference callback; 107 std::vector<uint16_t> buffer; 108 size_t byte_offset {}; 109 ObjectReference partial_string; 110}; 111 112void Parser::Init(Napi::Env env, Napi::Object exports) { 113 auto *data = env.GetInstanceData<AddonData>(); 114 115 Function ctor = DefineClass(env, "Parser", { 116 InstanceMethod("setLanguage", &Parser::SetLanguage, napi_default_method), 117 InstanceMethod("parse", &Parser::Parse, napi_default_method), 118 InstanceMethod("getIncludedRanges", &Parser::IncludedRanges, napi_default_method), 119 InstanceMethod("getTimeoutMicros", &Parser::TimeoutMicros, napi_default_method), 120 InstanceMethod("setTimeoutMicros", &Parser::SetTimeoutMicros, napi_default_method), 121 InstanceMethod("getLogger", &Parser::GetLogger, napi_default_method), 122 InstanceMethod("setLogger", &Parser::SetLogger, napi_default_method), 123 InstanceMethod("printDotGraphs", &Parser::PrintDotGraphs, napi_default_method), 124 InstanceMethod("reset", &Parser::Reset, napi_default_method), 125 }); 126 127 data->parser_constructor = Napi::Persistent(ctor); 128 exports["Parser"] = ctor; 129 exports["LANGUAGE_VERSION"] = Number::New(env, TREE_SITTER_LANGUAGE_VERSION); 130 131 String s = String::New(env, ""); 132 Napi::Value string_slice_value = s.As<Object>()["slice"]; 133 data->string_slice = Napi::Persistent(string_slice_value.As<Function>()); 134} 135 136Parser::Parser(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Parser>(info), parser_(ts_parser_new()) {} 137 138Parser::~Parser() { 139 ts_parser_print_dot_graphs(parser_, -1); 140 ts_parser_set_logger(parser_, { nullptr, nullptr }); 141 ts_parser_delete(parser_); 142} 143 144namespace { 145 146bool handle_included_ranges(Napi::Env env, TSParser *parser, Napi::Value arg) { 147 uint32_t last_included_range_end = 0; 148 if (arg.IsArray()) { 149 auto js_included_ranges = arg.As<Array>(); 150 vector<TSRange> included_ranges; 151 for (unsigned i = 0; i < js_included_ranges.Length(); i++) { 152 Value range_value = js_included_ranges[i]; 153 if (!range_value.IsObject()) { 154 return false; 155 } 156 auto maybe_range = RangeFromJS(range_value); 157 if (!maybe_range.IsJust()) { 158 return false; 159 } 160 auto range = maybe_range.Unwrap(); 161 if (range.start_byte < last_included_range_end) { 162 throw RangeError::New(env, "Overlapping ranges"); 163 } 164 last_included_range_end = range.end_byte; 165 included_ranges.push_back(range); 166 } 167 ts_parser_set_included_ranges(parser, included_ranges.data(), included_ranges.size()); 168 } else { 169 ts_parser_set_included_ranges(parser, nullptr, 0); 170 } 171 172 return true; 173} 174 175} // namespace 176 177Napi::Value Parser::SetLanguage(const Napi::CallbackInfo &info) { 178 const TSLanguage *language = language_methods::UnwrapLanguage(info[0]); 179 if (language != nullptr) { 180 ts_parser_set_language(parser_, language); 181 return info.This(); 182 } 183 return info.Env().Undefined(); 184} 185 186Napi::Value Parser::Parse(const CallbackInfo &info) { 187 Napi::Env env = info.Env(); 188 189 if (!info[0].IsFunction()) { 190 throw TypeError::New(env, "Input must be a function"); 191 } 192 193 auto callback = info[0].As<Function>(); 194 195 Object js_old_tree; 196 const TSTree *old_tree = nullptr; 197 if (info.Length() > 1 && !info[1].IsNull() && !info[1].IsUndefined() && info[1].IsObject()) { 198 js_old_tree = info[1].As<Object>(); 199 const Tree *tree = Tree::UnwrapTree(js_old_tree); 200 if (tree == nullptr) { 201 throw TypeError::New(env, "Second argument must be a tree"); 202 } 203 old_tree = tree->tree_; 204 } 205 206 Napi::Value buffer_size = env.Null(); 207 if (info.Length() > 2) { 208 buffer_size = info[2]; 209 } 210 211 if (!handle_included_ranges(env, parser_, info[3])) { 212 return env.Undefined(); 213 } 214 215 CallbackInput callback_input(callback, buffer_size); 216 TSTree *tree = ts_parser_parse(parser_, old_tree, callback_input.Input()); 217 Napi::Value result = Tree::NewInstance(env, tree); 218 return result; 219} 220 221Napi::Value Parser::IncludedRanges(const Napi::CallbackInfo &info) { 222 Napi::Env env = info.Env(); 223 uint32_t count; 224 const TSRange *ranges = ts_parser_included_ranges(parser_, &count); 225 226 Napi::Array result = Napi::Array::New(env, count); 227 for (uint32_t i = 0; i < count; i++) { 228 result[i] = RangeToJS(env, ranges[i]); 229 } 230 231 return result; 232} 233 234Napi::Value Parser::TimeoutMicros(const Napi::CallbackInfo &info) { 235 uint64_t timeout_micros = ts_parser_timeout_micros(parser_); 236 return Number::New(info.Env(), static_cast<double>(timeout_micros)); 237} 238 239Napi::Value Parser::SetTimeoutMicros(const Napi::CallbackInfo &info) { 240 uint64_t timeout_micros; 241 if (!info[0].IsNumber()) { 242 throw TypeError::New(info.Env(), "First argument must be a number"); 243 } 244 timeout_micros = info[0].As<Number>().Uint32Value(); 245 ts_parser_set_timeout_micros(parser_, timeout_micros); 246 return info.This(); 247} 248 249Napi::Value Parser::GetLogger(const Napi::CallbackInfo &info) { 250 TSLogger current_logger = ts_parser_logger(parser_); 251 if ((current_logger.payload != nullptr) && current_logger.log == Logger::Log) { 252 auto *logger = static_cast<Logger *>(current_logger.payload); 253 return logger->func.Value(); 254 } 255 return info.Env().Null(); 256} 257 258Napi::Value Parser::SetLogger(const Napi::CallbackInfo &info) { 259 TSLogger current_logger = ts_parser_logger(parser_); 260 261 if (info[0].IsFunction()) { 262 if (current_logger.payload != nullptr) { 263 delete static_cast<Logger *>(current_logger.payload); 264 } 265 ts_parser_set_logger(parser_, Logger::Make(info[0].As<Function>())); 266 } else if (info[0].IsEmpty() || info[0].IsUndefined() || info[0].IsNull() || (info[0].IsBoolean() && !info[0].As<Boolean>())) { 267 if (current_logger.payload != nullptr) { 268 delete static_cast<Logger *>(current_logger.payload); 269 } 270 ts_parser_set_logger(parser_, { nullptr, nullptr }); 271 } else { 272 throw TypeError::New(info.Env(), "Logger callback must either be a function or a falsy value"); 273 } 274 275 return info.This(); 276} 277 278Napi::Value Parser::PrintDotGraphs(const Napi::CallbackInfo &info) { 279 bool should_print = true; 280 int fd = fileno(stderr); 281 282 if (info.Length() > 0) { 283 if (!info[0].IsBoolean()) { 284 throw TypeError::New(info.Env(), "First argument must be a boolean"); 285 } 286 should_print = info[0].As<Boolean>(); 287 } 288 289 if (info.Length() > 1) { 290 if (!info[1].IsNumber()) { 291 throw TypeError::New(info.Env(), "Second argument must be a number"); 292 } 293 fd = info[1].As<Number>().Int32Value(); 294 } 295 296 ts_parser_print_dot_graphs(parser_, should_print ? fd : -1); 297 298 return info.This(); 299} 300 301Napi::Value Parser::Reset(const Napi::CallbackInfo & info) { 302 ts_parser_reset(parser_); 303 return info.This(); 304} 305 306} // namespace node_tree_sitter