diff options
author | bptato <nincsnevem662@gmail.com> | 2022-07-22 19:52:31 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2022-07-22 19:52:31 +0200 |
commit | 6f7bcc54ab03bc31be309352c73fd8d8153f9c91 (patch) | |
tree | dc4fd8a80ccc8a5a8f7c5a567fcbf80c3e66eccb /src/js | |
parent | c69a8ab7576e2053afc5dfcee5c7152a07c31230 (diff) | |
download | chawan-6f7bcc54ab03bc31be309352c73fd8d8153f9c91.tar.gz |
Add search function
Uses libregexp from QuickJS. Incremental search is quite hacky for now, but overall it seems to work OK.
Diffstat (limited to 'src/js')
-rw-r--r-- | src/js/javascript.nim | 14 | ||||
-rw-r--r-- | src/js/regex.nim | 105 |
2 files changed, 117 insertions, 2 deletions
diff --git a/src/js/javascript.nim b/src/js/javascript.nim index 2518f252..95e37110 100644 --- a/src/js/javascript.nim +++ b/src/js/javascript.nim @@ -35,6 +35,9 @@ proc newJSContext*(rt: JSRuntime): JSContext = opaque.err = "" JS_SetContextOpaque(result, opaque) +proc newJSContextRaw*(rt: JSRuntime): JSContext = + result = JS_NewContextRaw(rt) + func getJSObject*(ctx: JSContext, v: JSValue): JSObject = result.ctx = ctx result.val = v @@ -94,14 +97,21 @@ proc setFunctionProperty*(obj: JSObject, name: string, fun: JSCFunction) = # return fun(JSObject(ctx: ctx, qjs: obj), invoc) #), cstring(name), 1)) -proc free*(ctx: JSContext) = +proc free*(ctx: var JSContext) = + eprint "free" + let opaque = ctx.getOpaque() + if opaque != nil: + dealloc(opaque) JS_FreeContext(ctx) + ctx = nil -proc free*(rt: JSRuntime) = +proc free*(rt: var JSRuntime) = JS_FreeRuntime(rt) + rt = nil proc free*(obj: JSObject) = JS_FreeValue(obj.ctx, obj.val) + #TODO maybe? obj.val = JS_NULL proc eval*(ctx: JSContext, s: string, file: string, eval_flags: int): JSObject = result.ctx = ctx diff --git a/src/js/regex.nim b/src/js/regex.nim new file mode 100644 index 00000000..a1bd7d35 --- /dev/null +++ b/src/js/regex.nim @@ -0,0 +1,105 @@ +# Interface for QuickJS libregexp. + +import options + +import bindings/libregexp +import bindings/quickjs +import js/javascript + +export + LRE_FLAG_GLOBAL, + LRE_FLAG_IGNORECASE, + LRE_FLAG_MULTILINE, + LRE_FLAG_DOTALL, + LRE_FLAG_UTF16, + LRE_FLAG_STICKY + +type + Regex* = object + bytecode*: ptr uint8 + + RegexResult* = object + success*: bool + captures*: seq[tuple[s, e: int]] # start, end + +var dummyRuntime = newJSRuntime() +var dummyContext = dummyRuntime.newJSContextRaw() + +proc `=destroy`(regex: var Regex) = + if regex.bytecode != nil: + dummyRuntime.js_free_rt(regex.bytecode) + regex.bytecode = nil + +proc compileRegex*(buf: string, flags: int): Option[Regex] = + var regex: Regex + var len: cint + var error_msg_size = 64 + var error_msg = cast[cstring](alloc0(error_msg_size)) + let bytecode = lre_compile(addr len, error_msg, cint(error_msg_size), cstring(buf), csize_t(buf.len), cint(flags), dummyContext) + if error_msg != nil: + #TODO error handling? + dealloc(error_msg) + error_msg = nil + if bytecode == nil: + return none(Regex) # Failed to compile. + regex.bytecode = bytecode + return some(regex) + +proc compileSearchRegex*(str: string): Option[Regex] = + # Parse any applicable flags in regex/<flags>. The last forward slash is + # dropped when <flags> is empty, and interpreted as a character when the + # flags are is invalid. + + var i = str.high + var flagsi = -1 + while i >= 0: + case str[i] + of '/': + if i > 0 and str[i - 1] == '\\': break # escaped + flagsi = i + break + of 'i', 'm', 's': discard + else: break # invalid flag + dec i + + var flags = LRE_FLAG_GLOBAL # for easy backwards matching + + if flagsi == -1: + return compileRegex(str, flags) + + for i in flagsi..str.high: + case str[i] + of '/': discard + of 'i': flags = flags or LRE_FLAG_IGNORECASE + of 'm': flags = flags or LRE_FLAG_MULTILINE + of 's': flags = flags or LRE_FLAG_DOTALL + else: assert false + return compileRegex(str.substr(0, flagsi - 1), flags) + +proc exec*(regex: Regex, str: string, start = 0): RegexResult = + assert 0 <= start and start <= str.len + let cstr = cstring(str) + let captureCount = lre_get_capture_count(cast[ptr uint8](regex.bytecode)) + var capture: ptr ptr uint8 = nil + if captureCount > 0: + capture = cast[ptr ptr uint8](alloc0(sizeof(ptr uint8) * captureCount * 2)) + let ret = lre_exec(capture, regex.bytecode, + cast[ptr uint8](cstr), cint(start), + cint(str.len), cint(0), dummyContext) + result.success = ret == 1 #TODO error handling? (-1) + if result.success: + var i = 0 + let cstrAddress = cast[int](cstr) + while i < captureCount * sizeof(ptr uint8): + let startPointerAddress = cast[int](capture) + i + i += sizeof(ptr uint8) + let endPointerAddress = cast[int](capture) + i + i += sizeof(ptr uint8) + let startPointer = cast[ptr ptr uint8](startPointerAddress) + let endPointer = cast[ptr ptr uint8](endPointerAddress) + let startAddress = cast[int](startPointer[]) + let endAddress = cast[int](endPointer[]) + let s = startAddress - cstrAddress + let e = endAddress - cstrAddress + result.captures.add((s, e)) + dealloc(capture) |