import std/macros import std/tables import std/options import std/strutils import std/[json, jsonutils] import std/sequtils type GlyphAttribute* = enum # Vowel Attributes outer, slashed, inner, # Cluster/Punctuation Attributes left, center, right, top, middle, bottom, tall, wide, both, # Numeric Attributes circle, dash, vee, hump, dot, GlyphKind* = enum Vowel, Cluster, Punctuation, Syllable, Numeric Glyph* = object spelling*: string kind*: GlyphKind attrs*: set[GlyphAttribute] Replacement* = object original*: string replaced*: string PartOfSpeech* = enum Noun, Pronoun, Verb, Adjective, Adverb, Syntactic V* = enum V_I, V_E, V_A, V_O, V_U Definition* = object brief*: string long*: Option[string] Word* = ref object spelling*: string definitions*: Table[PartOfSpeech, Definition] Extremes* = object i*: Option[string] u*: Option[string] Penta* = ref object spelling*: string name*: string exts*: Extremes elems*: array[V, Word] BiExtremes* = object first*: Extremes second*: Extremes Icosapenta* = ref object spelling*: string name*: string exts*: BiExtremes first*: array[V, Penta] second*: array[V, Penta] Dictionary* = object glyphs*: seq[Glyph] dialects*: Table[string, seq[Replacement]] words*: seq[Word] pentas*: seq[Penta] icosipentas*: seq[Icosapenta] const Vowel2Char*: array[V, char] = [ V_I: 'i', V_E: 'e', V_A: 'a', V_O: 'o', V_U: 'u' ] proc replaceFirst(haystack: string, needle: char, content: char): string = let idx = haystack.find(needle) result = haystack result[idx] = content proc replaceLast(haystack: string, needle: char, content: char): string = let idx = haystack.rfind(needle) result = haystack result[idx] = content macro makeposprocs(): untyped = result = nnkStmtList.newNimNode for pos in PartOfSpeech: let w = ident("w") let defns = ident("definitions") let name = ident(($pos).toLowerAscii) let vpos = pos.newLit result.add quote do: proc `name`(short: string) {.used.} = `w`.`defns`[`vpos`] = Definition(brief: short, long: string.none) proc `name`(short: string, long: string) {.used.} = `w`.`defns`[`vpos`] = Definition(brief: short, long: long.some) template dictionary*(body: untyped) = var dict {.inject.}: Dictionary dict.glyphs = @[] template glyphs(gbody: untyped) = block: proc vowel(spelling: string, attrs: varargs[GlyphAttribute]) = var v: Glyph = Glyph(spelling: spelling, kind: Vowel) for attr in attrs: v.attrs.incl attr dict.glyphs.add v proc cluster(spelling: string, attrs: varargs[GlyphAttribute]) = var c: Glyph = Glyph(spelling: spelling, kind: Cluster) for attr in attrs: c.attrs.incl attr dict.glyphs.add c proc punctuation(spelling: string, attrs: varargs[GlyphAttribute]) = var p: Glyph = Glyph(spelling: spelling, kind: Punctuation) for attr in attrs: p.attrs.incl attr dict.glyphs.add p proc numeric(spelling: string, attrs: varargs[GlyphAttribute]) = var n: Glyph = Glyph(spelling: spelling, kind: Numeric) for attr in attrs: n.attrs.incl attr dict.glyphs.add n proc syllables() = var newglyphs: seq[Glyph] for v in dict.glyphs: if v.kind != Vowel: continue for c in dict.glyphs: if c.kind != Cluster: continue newglyphs.add Glyph( spelling: c.spelling & v.spelling, kind: Syllable, attrs: v.attrs + c.attrs ) dict.glyphs.add newglyphs gbody template dialect(name: string, dbody: untyped) = block: var dial: seq[Replacement] proc replace(orig: string, by: string) = dial.add Replacement(original: orig, replaced: by) dbody dict.dialects[name] = dial template words(wbody: untyped) = block: template word(ortho: string, defns: untyped) = block: var w {.inject.} = Word(spelling: ortho) makeposprocs() defns dict.words.add w template edit_word(theword: Word, defns: untyped) = block: var w {.inject.}: Word = theword makeposprocs() defns template penta(ortho: string, pname: string, defns: untyped) = block: var p {.inject.}: Penta = Penta( spelling: ortho, name: pname, exts: Extremes(i: string.none, u: string.none) ) for v in V: word ortho.replaceFirst(' ', Vowel2Char[v]): p.elems[v] = w proc extremes(i {.inject.}: string, u {.inject.}: string) {.used.} = p.exts.i = some(i) p.exts.u = some(u) template i(defns2: untyped) {.used.} = edit_word p.elems[V_I]: defns2 template e(defns2: untyped) {.used.} = edit_word p.elems[V_E]: defns2 template a(defns2: untyped) {.used.} = edit_word p.elems[V_A]: defns2 template o(defns2: untyped) {.used.} = edit_word p.elems[V_O]: defns2 template u(defns2: untyped) {.used.} = edit_word p.elems[V_U]: defns2 defns dict.pentas.add p template icosipenta(iortho: string, iname: string, idefns: untyped) = block: var i {.inject.}: Icosapenta = Icosapenta( spelling: iortho, name: iname, exts: BiExtremes( first: Extremes(i: string.none, u: string.none), second: Extremes(i: string.none, u: string.none) ) ) for v in V: penta iortho.replaceFirst(' ', Vowel2Char[v]), Vowel2Char[v] & "x " & iname: i.first[v] = p penta iortho.replaceLast(' ', Vowel2Char[v]), "x" & Vowel2Char[v] & " " & iname: i.second[v] = p proc firsts(i {.inject.}: string, u {.inject.}: string) {.used.} = i.exts.first.i = some(i) i.exts.first.u = some(u) proc seconds(i {.inject.}: string, u {.inject.}: string) {.used.} = i.exts.second.i = some(i) i.exts.second.u = some(u) template ix(defns2: untyped) {.used.} = edit_penta i.first[V_I]: defns2 # TODO: rest of the -x and x-, plus write edit_penta template ii(defns2: untyped) {.used.} = edit_word i.first[V_I].elems[V_I]: defns2 # TODO: check that this also edits i.second[V_I].elems[V_I] # TODO: rest of the -- # NOTE: first extremes is exts, second extremes is elems[*].exts wbody body proc toJsonHook(attrs: set[GlyphAttribute]): JsonNode = newJString(attrs.toSeq.join("-")) proc toJsonHook(glyphs: seq[Glyph]): JsonNode = result = newJObject() for glyph in glyphs: let kindname = ($glyph.kind).toLowerAscii if kindname notin result: result[kindname] = newJObject() result[kindname][glyph.spelling] = glyph.attrs.toJson proc toJsonHook(defns: Table[PartOfSpeech, Definition]): JsonNode = result = newJObject() for pos, defn in defns: result[($pos).toLowerAscii] = defn.toJson proc toJsonHook(words: seq[Word]): JsonNode = result = newJObject() for word in words: result[word.spelling] = word.definitions.toJson proc toJsonHook(exts: Extremes): JsonNode = result = newJObject() if exts.i.isSome: result["i"] = newJString(exts.i.get) if exts.u.isSome: result["u"] = newJString(exts.u.get) proc toJson(penta: Penta, dict: Dictionary): JsonNode = result = newJObject() result["extremes"] = penta.exts.toJson result["name"] = newJString(penta.name) # TODO: Consider looking up i/e/a/o/u in dict proc toJson(pentas: seq[Penta], dict: Dictionary): JsonNode = result = newJObject() for penta in pentas: result[penta.spelling] = penta.toJson(dict) proc toJsonHook(dict: Dictionary): JsonNode = result = newJObject() result["glyphs"] = dict.glyphs.toJson result["dialects"] = dict.dialects.toJson result["words"] = dict.words.toJson result["pentas"] = dict.pentas.toJson(dict) proc jsonify*(dict: Dictionary): string = return $(dict.toJson) when isMainModule: dictionary: glyphs: vowel "i", outer vowel "y", both cluster "θ", left, top cluster "∫", left, middle syllables() punctuation "«", left numeric "0", circle dialect "jukashenikan": replace "x", "ç" dialect "gazhenigan": replace "k", "g" words: word "t": noun "thing", "See t - dmPenta for better meaning." # verb "be" penta "n x", "Pronouns": extremes(i="Fully proximal", u="Fully distal") i: pronoun "I/me" echo dict.jsonify