├── .github └── workflows │ └── docgen.yml ├── .gitignore ├── LICENSE ├── README.md ├── examples ├── basic.nim └── texts.txt ├── nimkov.nim ├── nimkov.nimble └── nimkov ├── constants.nim ├── generator.nim ├── typedefs.nim ├── utils.nim └── validators.nim /.github/workflows/docgen.yml: -------------------------------------------------------------------------------- 1 | name: Docgen 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | docs: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | with: 14 | persist-credentials: false 15 | fetch-depth: 0 16 | - uses: jiro4989/setup-nim-action@v1 17 | with: 18 | nim-version: 'stable' 19 | - name: Run documentaion generation 20 | run: nimble genDoc 21 | - name: Push 22 | uses: s0/git-publish-subdir-action@develop 23 | env: 24 | REPO: self 25 | BRANCH: docs 26 | FOLDER: docs 27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 28 | MESSAGE: "generating docs for commit: {sha}" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | nimcache/ 2 | nimblecache/ 3 | htmldocs/ 4 | .exe -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 bit0r1n - Sergey Khomich 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nimkov 2 | Nim library, that can help you to generate string, based on Markov chains (Markov text generator) 3 | 4 | ## Installation 5 | 6 | `nimble install nimkov` 7 | 8 | ## Example 9 | ```nim 10 | import nimkov/generator 11 | 12 | let markov = newMarkov(@["hello world", "world of chains", "world for me"]) 13 | 14 | echo markov.generate() 15 | ``` 16 | -------------------------------------------------------------------------------- /examples/basic.nim: -------------------------------------------------------------------------------- 1 | import strutils 2 | import ../nimkov/[generator, typedefs, validators] 3 | 4 | var file = open("texts.txt") 5 | let phrases = file.readAll().split("\n") 6 | file.close() 7 | 8 | let markov = newMarkov(phrases, kind = mgtWeighted) 9 | 10 | try: 11 | echo markov.generate( 12 | validator = symbolsCount(50, 200), 13 | attempts = 5000 14 | ) 15 | except CatchableError as e: 16 | echo e.name -------------------------------------------------------------------------------- /examples/texts.txt: -------------------------------------------------------------------------------- 1 | Сегодня звезды встали таким образом, что ты можешь заняться бодимодификацией. Однако, есть риск выебать собаку. Поэтому Двач советует тебе дрочить чужой хуй. 2 | спасибо двач 3 | Суп. Постоянно даю своему свежевысранному говну имя. При том нормальное не говняное. Типа Евгений или Виталий. Вытерев свой сракотан и помыв руки долго его не смываю, веду беседу. По типу парейдолии питаюсь в его складах разглядеть очертания человеческого лица. Если нахожу что- то подобное, то поливаю эту часть говна водичкой. Так оно начинает немного шевелиться и будто мимика появляется. Иногда говно оказывается интересным собеседником, а иногда откровенным говном. Но каждый раз, когда я его смываю, у меня сдавливает в груди и становится печально на душе. Будто сына своего родного сливаю. Свою кровинушку. Частичку моей души. Как- то так аноны. Как- то так. 4 | Когда мне было 15 лет, и я ходил срать бятя всё время как-бы невзначай крутился возле толчка, и всё спрашивал, что ты там затих, почему тебя не слышно? первый раз я не ответил, так он начал ломиться в дверь, и орать, что ты там молчишь, что с тобой? начал материться, и говорить, что вообще дверь с петель снимет, алсо, батя ругался, если я сру и не смываю, причём не просто вконце срания, а непосредственно после вылезания какашки, мотивировал это тем, что воняет, и сам потом мне говорил: вот я какну и смываю, и ты так делай! однажды я срать сел, и слышу, батя где-то у двери встал в отдалении, ну я жопу вытер, и на пол накарачики присел, а там щель очень широкая снизу у двери, ну я в щель и смотрю, а там батя на карачиках сидит и в щель смотрит, и мне говорит: ты чё? ебанутый? чё ты там делаешь? батя кстати всё время какие-то травы пьёт, чтобы срать часто, срёт по 5 раз в день, а потом говорит, что жопу жжёт, и ещё пердит он. пиздец короче! реальная история. я не тролль 5 | Игорь, ты? Помнишь меня? Я твой одноклассник. Я узнал тебя по твоим шизоидным словам и высерам. А помнишь, как мы всем классом нассали тебе в кружку в третьем классе, на сладкоежке? Ты ещё выпил, облизнулся и попросил добавки. А потом тебя пришёл забирать твой отец, тот самый дворник, который на Вернадского изнасиловал собаку и получил условный срок за то, что украл плавленный сырок в магазине. Он зашёл в класс, все стали смеяться, а ты обосрался под себя от стыда, а потом сказал, что всю жизнь будешь ненавидеть дворником, но в 9м классе, когда ты пошёл на рейд, чтобы их отпиздить, то они пустили тебя по кругу, после чего тебе наложили на анус восемь швов. Как поживаешь, Игорян? 6 | Когда хочеться себя там, чу-чуть пожалеть, "ой, я там такой несчастный, блять, я там так, всё, такая жизнь", ты блять живёшь, нахуй, во-первых в 21-ом веке.Ты, сука блять, туда бы попал, в 40-вые то годы, на этот пиздец, ты бы там сука быстро либо бы понял по чём фунт изюма и как хорошо жить и как хорошо каждый день очередной прожить, нахуй, как это хорошо вообще ощущать эту жизнь и что тебя не убили сегодня или там не здох, не придавило жив здоров и более менее, вот.Расхляпились, нахуй, расчервились, блять, измельчались, ахуеть как," а всё ой как мне хочеться жалко всё блять" Себя, сука, вспоминай себя кто бы ты нибыл или как кто когда хочеться сильно пожалеть через что прошли предки.Начинается вот это соплежуйство ёбаное блять "Ой я вот тут не так, а у меня не это, а я вот так, а я вот хорошо выгляжу? Как мне подкачаться, блять?" Ты, блять, не об этом думаешь. Ты пойди наруби дров, нахуй, почисть собственный, двор, подъезд, блять, прибери наклонись лишний раз к земле, сука блять, поклонись в труде, свой двор преобразуй, а не только, блять, на тачках разъежяй, да думай какую очередную тёлку соблазнить или что обо мне тёлки подумают или что, блять, как нахуй.Подыши этими просторами, блять, и будь поглащён в эту природу, и всегда помни шо ты сейчас живёшь это одна из граней вариаций из существа, и есть такие экстримы шо там прыжок с парашута, блять, или какие-то с домов прыжки это, блять, нихуя, ты будешь то же самое делать по горам скакать или там, блять, по болотам по топям ещё под обстерлом артилерийским неприменным ни шагу назад, а вперёд либо победить, либо сдохнуть, нахуй, вот это понимаешь а то начинается, блять, соплежуйство вот это вот. 7 | Здарова, Лех, нихуя себе как тесен мир, живу заебись, вспоминаю наш дом-интернат для умственно отсталых с улыбкой и теплом на душе. Помню это чувство беззаботности. Как ходили в мужскую баню с тобой зарабатывать на сладости. Я все время меньше зарабатывал, а ты хвастался что успеваешь больше раз отсосать. А на самом деле обманывал меня и давал в жопу. А помнишь как Любовь Васильевна нашла у тебя в шкафчике дохлых котят, а ты сказал что они мои и я просто дал их тебе, чтобы меня не спалили. К слову, я жалею что так стыдился своим отцом. Ведь я был единственный из группы у кого были родители. Недавно видел Сашку Абросимова, с которым вы вечно бегали ночью в подвал, а потом ты приходил с одышкой и странно пукал всю ночь. Он дворником сейчас работает на уралмаше. Хотел подойти, но испугался формы. Почему-то сразу вспомнилось как мой отец возил нас на рыбалку. Ты не помнишь что там происходило? Потому что у меня от этой воды со странным запахом всегда пропадала память, а у тебя был белый понос, помнишь? Ты еще грешил на то что много мороженого ешь? Эх, годы золотые. 8 | Анон. Так получилось, что я случайно выебал жидкий бетон и у меня теперь отваливается хуй. Ебал минут 5 его с хлюпаньем, периодически восстанавливая двумя пальцами в вязкой массе "сочную дырочку которая так и просит в себя кукан". Кончил в бетон, подправил мастерком чтоб не заметили и пошел в душ. И тут каааааак защипало бля. Конкретно головка. Я чуть не заорал. Но весь хер и залупа были в сером цементе и я через силу и боль но с мылом все оттер. И вот, у меня уже второй день головка болит, гноится так, что можно по утру "выдавить" пол чайной ложки гноя и пленок. Анон, выручай, я что, подхватил спид от бетона? Как быстро пройдет? К врачу бежать или само пройдет за недельку? 9 | Доброго утречка, мои зайчишки-шалунишки! Опять сонненькие, сладенькие котятки? Ну ка, давайте маленькие, просыпайтесь, мордочки умывульки, лапками потягульки-потягульки! 10 | Одну лапку подняли, другу лапку подняли - полетели, полетели, полетели, наааа головку сели. Какие вы у меня умнички, медвежатки ласковые, пингвинчики прямо ня, люблю вас всех, нежненькие мои, любименькие, лапками няшите игриво, лентюльки мои пушистенькие, вставать не хотите, глазки сонные, ушки маленькие, мордочкой кривите, а я вас обниму так нежненько, на коленочку посажу и к животику нежненько прижму, чтобы вы бурундчуки махонькие не мёрзли, мурлыкой фыркали приятно так, тёпленько в ладошку и хвостиками хитрили, как котятки смешнявые, ня воть :3 11 | Я мог дать больше или меньше инфы, но моё любимое число 16, поэтому вот так. 12 | С очень большой вероятностью ты прочтёшь этот текст жопой или отмахнёшься. 13 | С очень большой вероятностью в глубине души ты понимаешь, что уже умер и перегорел, что не способен ничего особенно хотеть и просто создал тред от безысходной тоски. 14 | С очень большой вероятностью ты забросишь свой тред через пару дней, а то и сразу после того, как обосрёшься сегодня утром. 15 | Почти наверняка ты обычная зачатая слякотью пидорашка без огня в сердце, и когда ты проснёшься по-настоящему, 2Г уже станет реальностью, а обоссанный матрас – твоим смертным одром; твой мир, исчезая, распадаясь звоном в ушах, серыми мушками и удушливой тяжестью в груди, успеет сверкнуть упущенным счастьем; и ты поймёшь, что теперь сделал бы всё, чтобы дотянуться, тебя захлестнёт непередаваемое среди живых раскаяние – но тут опустится тьма, навсегда избавив тебя от стыда и боли неудачно прожитых лет. 16 | Я пишу только потому, что сам часто (включая и этот момент) предаю свои идеалы – а значит, не вправе тебя презирать или ставить клеймо. Иначе мне пришлось бы поставить его и на себя тоже. 17 | игорь, ты случаем не из этих, которые масюнь любят? - Конечно, нет. Я бы никогда не подумал о том, чтобы раздеть маснонно и облизать её худенькое тело, покусывая шею и целуя её очаровательные маленькие сосочки. Только бессердечный монстр может подумать о её милом масюньем ротике и язычке, охватывающих толстый член. скользкий от её слюны и двигающийся в её рту, пока не наступит эрекция столь обильная, что её маленькое горло вряд ли сможет проглотить столько. Мысль о густой сперме, переполняющей ее рот, стекающей с её щечек на плоскую грудь, её тонких ручках зачерпывающих сё и слизывающих с кончиков пальцев просто ужасна. Вы просто сборише больных извращенцев, думающих о том, чтобы раздвинуть сё стройные бедра, направив член на сё чистую, узкую, девственную шёлку. и войти в неё так глубоко, что только стон сорвётся с её губ, липких от спермы, пока её маленькое тело дрожит от того, что се невинность порвали одним быстрым движением. Мне отвратительно думать о том, что вы еще больше возбуждаетесь, налегая на нее, слушая сё ускоренное дыхание, ее масюньи стоны и придыхания, пока вы ускоряете движения, сё сладкое дыхание, теплое и влажное, в ваше лицо, и ее плоскую грудь, блестяшую от капель свежего пота, быстро вздымаюшуюся и падаюшую, чтобы встретиться с вашей. Это на самом деле омерзительно, как вы ошупываете всё её тело, пока насилуете её, чувствуя её сосочки, твердеющие под вашим языком, пока вы лижете ее грудь, её шею и её юдмышки, чувствуете вкус её кожи и пота, пока она дрожит от стимуляции, и доводите её до оргазма, стушая сё мягкий крик первого оргазма, пока ваш член находится глубоко внутри нее, дико пульсируя, выплескивая огромн е количество горячей спермы. рвушейся вперёд и вытекающей впервые из ее только что осквер- ненной вагины, заполняя её матку, только чтобы вылиться из нее с противным хлюпаньем. -------------------------------------------------------------------------------- /nimkov.nim: -------------------------------------------------------------------------------- 1 | ## Welcome to nimkov Documentation! 2 | ## 3 | ## Reference 4 | ## ========= 5 | ## - `validators` Includes simple validators, such as "return text, that satisfy my limit of words or symbols" 6 | ## 7 | ## - `generator` Includes Markov object and it functionally, such as preparing Markov generator to work, adding or removing all phrases and generating text 8 | ## 9 | ## - `typedefs` Includes all types, that used in this library 10 | ## 11 | ## Example of usage 12 | ## ================ 13 | ## .. code-block:: nim 14 | ## import nimkov/generator 15 | ## 16 | ## let markov = newMarkov() 17 | ## 18 | ## markov.add("hello world") 19 | ## markov.add("world of chains") 20 | ## markov.add("world for me") 21 | ## # or 22 | ## markov.add(@["hello world", "world of chains", "world for me"]) 23 | ## 24 | ## echo markov.generate() 25 | ## 26 | ## ---- 27 | ## 28 | 29 | import ./nimkov/[ 30 | validators, typedefs, generator 31 | ] 32 | 33 | export validators, typedefs, generator 34 | -------------------------------------------------------------------------------- /nimkov.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "1.2.3" 4 | author = "bit0r1n" 5 | description = "Text generator, based on Markov Chains (Markov text generator)" 6 | license = "MIT" 7 | 8 | # Dependencies 9 | 10 | requires "nim >= 0.20.0" 11 | 12 | 13 | task genDoc, "Generates the documentation for nimkov": 14 | rmDir("docs") 15 | exec("nim doc2 --outdir=docs --project --index:on --git.url:https://github.com/bit0r1n/nimkov --git.commit:master nimkov.nim") 16 | exec("nim buildindex -o:docs/theindex.html docs/") 17 | 18 | writeFile("docs/CNAME", "nimkov.bitor.in") 19 | writeFile("docs/index.html", """ 20 | 21 | 22 | 23 | 24 | 25 | 26 |

Click this link if this does not redirect you.

27 | 28 | 29 | """) -------------------------------------------------------------------------------- /nimkov/constants.nim: -------------------------------------------------------------------------------- 1 | const 2 | mrkvStart* = "__start" 3 | mrkvEnd* = "__end" -------------------------------------------------------------------------------- /nimkov/generator.nim: -------------------------------------------------------------------------------- 1 | import std/[ tables, strutils, options, random, sequtils ] 2 | import ./[ utils, constants, typedefs, validators ] 3 | 4 | randomize() 5 | 6 | # private fields moment 7 | type MarkovGenerator* = ref object 8 | wordProc: MarkovProcessWordProc 9 | case kind*: MarkovGeneratorModelType 10 | of mgtSimple: 11 | seqModel: Table[string, seq[string]] 12 | of mgtWeighted: 13 | weightModel: Table[string, CountTable[string]] 14 | 15 | proc filterString(str: string): string = 16 | var subResult = newSeq[string]() 17 | 18 | for word in str.split(" "): 19 | if word == mrkvStart or word == mrkvEnd: continue 20 | subResult.add(word) 21 | 22 | result = subResult.join(" ").strip() 23 | 24 | iterator sampleToFrames(sample: string, wordProc: MarkovProcessWordProc): string = 25 | yield mrkvStart 26 | 27 | for word in sample.split(" "): 28 | if word == mrkvStart or word == mrkvEnd: continue 29 | let newWord = wordProc(word) 30 | if newWord.isSome: 31 | yield newWord.get() 32 | 33 | yield mrkvEnd 34 | 35 | proc add*(generator: MarkovGenerator, sample: string) = 36 | ## Adds string to samples. 37 | let samples = toSeq(sampleToFrames(sample, generator.wordProc)) 38 | if samples.len == 2: return 39 | 40 | for i in 0..samples.high: 41 | if i + 1 > samples.high: break 42 | 43 | let currentFrame = samples[i] 44 | let nextFrame = samples[i + 1] 45 | 46 | case generator.kind 47 | of mgtSimple: 48 | if currentFrame notin generator.seqModel: 49 | generator.seqModel[currentFrame] = @[] 50 | generator.seqModel[currentFrame].add(nextFrame) 51 | of mgtWeighted: 52 | if currentFrame notin generator.weightModel: 53 | generator.weightModel[currentFrame] = initCountTable[string]() 54 | generator.weightModel[currentFrame].inc(nextFrame) 55 | proc add*(generator: MarkovGenerator, samples: seq[string]) = 56 | ## Adds seqence of strings to samples. 57 | for sample in samples: 58 | generator.add(sample) 59 | 60 | proc model*(generator: MarkovGenerator): Table[string, CountTable[string]] = 61 | ## Returns model of generator. 62 | case generator.kind 63 | of mgtWeighted: 64 | result = generator.weightModel 65 | of mgtSimple: 66 | for key, value in generator.seqModel: 67 | result[key] = initCountTable[string]() 68 | for frame in value: 69 | result[key][frame] = 1 # always will be 1 since in simple model frames are unique 70 | 71 | proc clear*(generator: MarkovGenerator) = 72 | ## Clears generator. 73 | case generator.kind 74 | of mgtSimple: 75 | generator.seqModel.clear() 76 | of mgtWeighted: 77 | generator.weightModel.clear() 78 | 79 | proc newMarkov*( 80 | samples = newSeq[string](), 81 | wordProc: MarkovProcessWordProc = (proc (word: string): Option[ 82 | string] = some word.unicodeStringToLower()), 83 | kind: MarkovGeneratorModelType = mgtSimple 84 | ): MarkovGenerator = 85 | ## Creates an instance of Markov generator. 86 | result = MarkovGenerator( 87 | kind: kind, 88 | wordProc: wordProc 89 | ) 90 | 91 | for sample in samples: 92 | result.add(sample) 93 | 94 | proc generate*(generator: MarkovGenerator; 95 | attempts: Positive = 1, begin = none string, 96 | validator: MarkovValidator = defaultValidator() 97 | ): string = 98 | ## Generates a string. 99 | case generator.kind 100 | of mgtSimple: 101 | if generator.seqModel.len == 0: 102 | raise MarkovEmptyModelError.newException("Model is empty") 103 | of mgtWeighted: 104 | if generator.weightModel.len == 0: 105 | raise MarkovEmptyModelError.newException("Model is empty") 106 | 107 | var beginStr: string 108 | if begin.isNone: beginStr = mrkvStart 109 | else: 110 | let filtered = filterString(begin.get()) 111 | beginStr = if filtered.len > 0: mrkvStart & " " & filtered else: mrkvStart 112 | 113 | let beginningFrames = beginStr.split(" ") 114 | 115 | for i in 0..attempts: 116 | var attemptResult = beginningFrames 117 | var currentFrame = attemptResult[^1] 118 | 119 | while currentFrame != mrkvEnd: 120 | var nextFrame: string 121 | case generator.kind 122 | of mgtSimple: 123 | if currentFrame notin generator.seqModel: 124 | raise MarkovNotEnoughSamplesError.newException( 125 | "Not enough samples to use \"" & 126 | beginningFrames[1..^1].join(" ") & 127 | "\" as a beginning argument") 128 | 129 | nextFrame = sample(generator.seqModel[currentFrame]) 130 | of mgtWeighted: 131 | if currentFrame notin generator.weightModel: 132 | raise MarkovNotEnoughSamplesError.newException( 133 | "Not enough samples to use \"" & 134 | beginningFrames[1..^1].join(" ") & 135 | "\" as a beginning argument") 136 | 137 | nextFrame = weightedRandom( 138 | generator.weightModel[ 139 | currentFrame].pairs.toSeq) 140 | 141 | attemptResult.add(nextFrame) 142 | currentFrame = nextFrame 143 | 144 | let stringResult = attemptResult[1..^2].join(" ") 145 | 146 | if validator(stringResult) == true: 147 | return stringResult 148 | 149 | raise MarkovOutOfAttemptsError.newException("Out of attempts") 150 | -------------------------------------------------------------------------------- /nimkov/typedefs.nim: -------------------------------------------------------------------------------- 1 | import std/options 2 | 3 | type 4 | MarkovError* = object of CatchableError 5 | MarkovEmptyModelError* = object of MarkovError 6 | ## Throws if trying to generate with empty model 7 | MarkovNotEnoughSamplesError* = object of MarkovError 8 | ## Throws if not found last word of beginning in model 9 | MarkovOutOfAttemptsError* = object of MarkovError 10 | ## Throws if generating is out of attempts 11 | 12 | MarkovValidator* = proc(str: string): bool 13 | ## Validator for generator. If generated text doesn't satisfy 14 | ## the validator - it will try to generate a new string, if the number of attempts allows. 15 | 16 | MarkovGeneratorModelType* = enum 17 | ## Type of model, which will be used in generator. 18 | ## `mgtSimple` - simple model, which will be generated with equal probability, i.e. model will be `Table[string, seq[string]]` 19 | ## `mgtWeighted` - weighted model, which will be generated with weighted probability, i.e. model will be `Table[string, Table[string, int]]` 20 | mgtSimple 21 | mgtWeighted 22 | MarkovGenerateOptions* = object of RootObj 23 | ## Options for generator. 24 | attempts*: Positive 25 | begin*: Option[string] 26 | validator*: MarkovValidator 27 | MarkovProcessWordProc* = proc (word: string): Option[string] 28 | ## Process word, result string will be added to model, if you don't 29 | ## want to add word to model - return `none string`. Recommended to lower case always, which is default -------------------------------------------------------------------------------- /nimkov/utils.nim: -------------------------------------------------------------------------------- 1 | import std/[ unicode, random ] 2 | 3 | proc unicodeStringToLower*(str: string): string = 4 | ## Converts any text to lower case. 5 | result = "" 6 | for s in runes(str): 7 | result.add(s.toLower.toUTF8) 8 | 9 | proc weightedRandom*[T](weights: openArray[tuple[key: T, weight: int]]): T = 10 | var totalWeight = 0 11 | for w in weights: 12 | totalWeight += w.weight 13 | var r = rand(totalWeight) 14 | for w in weights: 15 | r -= w.weight 16 | if r < 0: 17 | return w.key 18 | result = weights[0].key -------------------------------------------------------------------------------- /nimkov/validators.nim: -------------------------------------------------------------------------------- 1 | import std/strutils 2 | from ./typedefs import MarkovValidator 3 | 4 | proc defaultValidator*(): MarkovValidator = (proc (str: string): bool = true) 5 | ## Default validator for text. It always returns "true". 6 | 7 | proc wordsCount*(min = 0, max = int.high): MarkovValidator = 8 | ## Validator, based on words count. Returns true, if count of words satisfy configured limits. 9 | (proc (str: string): bool = str.split(" ").len >= min and str.split(" ").len <= max) 10 | 11 | proc symbolsCount*(min = 0, max = int.high): MarkovValidator = 12 | ## Validator, based on symbols count. Returns true, if count of symbols satisfy configured limits. 13 | (proc (str: string): bool = str.len >= min and str.len <= max) --------------------------------------------------------------------------------