Hitting this problem couple of times, wonder if there's a good way to avoid it?
In example code below, a parser checks the current symbol and the next one. The parser had to be var object, as it maintains and updates the current index for the parsed string, and to get the next symbol, you had to pass that parser inside of the lambda function.
type Parser* = object
text_ref*: ref string
i*: int
...
proc parse_xy(pr: var Parser): string =
pr.collect(proc (c: char): bool =
not (c == 'x' and pr.get(1) == 'y') # <= error, pr can't be captured
)
var pr = Parser.init("some text xy another text")
echo pr.parse_xy
Error
play.nim(38, 23) Error: 'pr' is of type <var Parser> which cannot be captured as it would violate memory safety, declared here: play.nim(36, 15); using '-d:nimNoLentIterators' helps in some cases
Full code, play
import std/[options, sugar]
type Parser* = object
text_ref*: ref string
i*: int
proc init*(_: type[Parser], text: string, i = 0): Parser =
var text_ref = string.new
text_ref[] = text
Parser(text_ref: text_ref, i: i)
proc text*(pr: Parser): string =
pr.text_ref[]
proc is_finished*(pr: Parser): bool =
pr.i > pr.text.high
proc get*(pr: Parser, shift = 0): Option[char] =
let i = pr.i + shift
if i >= 0 and i < pr.text.len: pr.text[i].some else: char.none
proc inc*(pr: var Parser) =
if pr.i < pr.text.len: pr.i.inc
proc collect*(pr: var Parser, fn: (char) -> bool): string =
while not pr.is_finished:
let c = pr.get.get
if not fn(c): break
result.add c
pr.inc
proc `==`*[T](a: Option[T], b: T): bool = a.is_some and a.get == b
proc `==`*[T](a: T, b: Option[T]): bool = b.is_some and b.get == a
proc parse_xy(pr: var Parser): string =
pr.collect(proc (c: char): bool =
not (c == 'x' and pr.get(1) == 'y') # <= error, pr can't be captured
)
var pr = Parser.init("some text xy another text")
echo pr.parse_xy
)
Or if you know it's memory safe to do so
nim
)
Like so
import std/[options, sugar]
type Parser* = ref object
text_ref*: ref string
i*: int
proc init*(_: type[Parser], text: string, i = 0): Parser =
var text_ref = string.new
text_ref[] = text
Parser(text_ref: text_ref, i: i)
proc text*(pr: Parser): string =
pr.text_ref[]
proc is_finished*(pr: Parser): bool =
pr.i > pr.text.high
proc get*(pr: Parser, shift = 0): Option[char] =
let i = pr.i + shift
if i >= 0 and i < pr.text.len: pr.text[i].some else: char.none
proc inc*(pr: Parser) =
if pr.i < pr.text.len: pr.i.inc
proc collect*(pr: Parser, fn: (char) -> bool): string =
while not pr.is_finished:
let c = pr.get.get
if not fn(c): break
result.add c
pr.inc
proc `==`*[T](a: Option[T], b: T): bool = a.is_some and a.get == b
proc `==`*[T](a: T, b: Option[T]): bool = b.is_some and b.get == a
proc parse_xy(pr: Parser): string =
pr.collect(proc (c: char): bool =
not (c == 'x' and pr.get(1) == 'y') # <= error, pr can't be captured
)
var pr = Parser.init("some text xy another text")
echo pr.parse_xy
But frankly, just throw it all away and use a parser generator instead, you're terrible at it.
all of your code is terrible.
hold my beer. all my recent nim code looks like php
hold my beer. all my recent nim code looks like php
Yet I bet it doesn't try to emulate simple for loops with worse lambda constructions. ;-)