#include "addonGlobals.h" #include "AddonGrammar.h" AddonGrammar::AddonGrammar(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { grammarCode = info[0].As().Utf8Value(); if (info.Length() > 1 && info[1].IsObject()) { Napi::Object options = info[1].As(); if (options.Has("addonExports")) { addonExportsRef = Napi::Persistent(options.Get("addonExports").As()); hasAddonExportsRef = true; } if (options.Has("rootRuleName")) { rootRuleName = options.Get("rootRuleName").As().Utf8Value(); } } auto parsed_grammar = llama_grammar_init_impl(nullptr, grammarCode.c_str(), rootRuleName.c_str(), false, nullptr, 0, nullptr, 0); // will be empty if there are parse errors if (parsed_grammar == nullptr) { Napi::Error::New(info.Env(), "Failed to parse grammar").ThrowAsJavaScriptException(); return; } llama_grammar_free_impl(parsed_grammar); } AddonGrammar::~AddonGrammar() { if (hasAddonExportsRef) { addonExportsRef.Unref(); hasAddonExportsRef = false; } } Napi::Value AddonGrammar::isTextCompatible(const Napi::CallbackInfo& info) { const std::string testText = info[0].As().Utf8Value(); auto parsed_grammar = llama_grammar_init_impl(nullptr, grammarCode.c_str(), rootRuleName.c_str(), false, nullptr, 0, nullptr, 0); // will be empty if there are parse errors if (parsed_grammar == nullptr) { Napi::Error::New(info.Env(), "Failed to parse grammar").ThrowAsJavaScriptException(); return Napi::Boolean::New(info.Env(), false); } const auto cpts = unicode_cpts_from_utf8(testText); llama_grammar_stacks & stacks_cur = llama_grammar_get_stacks(parsed_grammar); for (const auto & cpt : cpts) { try { llama_grammar_accept(parsed_grammar, cpt); } catch (const std::exception & e) { llama_grammar_free_impl(parsed_grammar); return Napi::Boolean::New(info.Env(), false); } catch (...) { llama_grammar_free_impl(parsed_grammar); return Napi::Boolean::New(info.Env(), false); } if (stacks_cur.empty()) { // no stacks means that the grammar failed to match at this point llama_grammar_free_impl(parsed_grammar); return Napi::Boolean::New(info.Env(), false); } } for (const auto & stack : stacks_cur) { if (stack.empty()) { // an empty stack means that the grammar has been completed llama_grammar_free_impl(parsed_grammar); return Napi::Boolean::New(info.Env(), true); } } llama_grammar_free_impl(parsed_grammar); return Napi::Boolean::New(info.Env(), false); } void AddonGrammar::init(Napi::Object exports) { exports.Set( "AddonGrammar", DefineClass( exports.Env(), "AddonGrammar", { InstanceMethod("isTextCompatible", &AddonGrammar::isTextCompatible), } ) ); }