Nanyx

Source generator

Generate C# source with raw strings and interpolation side by side.

module recordIndexerGenerator

-- Generate C# source for a string indexer over the properties of a record.
-- Raw single-quoted strings keep C# braces literal, and a doubled brace in a
-- double-quoted string is a literal brace next to interpolation.

type Property = (
  name: string
  type: string
  isGettable: bool
  isSettable: bool
)

type PartialRecord = (
  identifier: string
  declaration: string
  namespace?: string
  properties: list(Property)
)

def getterCases: list(Property) -> string = { properties ->
  properties
    \filter { .isGettable }
    \map { p -> '"' ++ p.name ++ '" => ' ++ p.name ++ "," }
    \String.join("\n            ")
}

def setterCases: list(Property) -> string = { properties ->
  properties
    \filter { .isSettable }
    \map { p -> 'case "' ++ p.name ++ '": ' ++ p.name ++ " = ({p.type}) value; break;" }
    \String.join("\n                ")
}

def indexer: PartialRecord -> string = { record ->
  [
    "public object this[string name]"
    '{'
    "    get => name switch"
    '    {'
    "        {getterCases(record.properties)}"
    "        _ => throw new IndexOutOfRangeException(name)"
    '    };'
    "    set"
    '    {'
    "        switch (name)"
    '        {'
    "            {setterCases(record.properties)}"
    "            default: throw new IndexOutOfRangeException(name);"
    '        }'
    '    }'
    '}'
  ] \String.join("\n")
}

export def generateSource: PartialRecord -> string = { record ->
  def body = "partial {record.declaration}\n{{\n{indexer(record)}\n}}"

  match record.namespace
    | #some(ns) -> "using System;\n\nnamespace {ns}\n{{\n{body}\n}}"
    | #none -> "using System;\n\n{body}"
}

@entry
def main = {
  def person = PartialRecord(
    identifier = "Person"
    declaration = "record Person"
    namespace = #some("Demo")
    properties = [
      Property(name = "Name", type = "string", isGettable = true, isSettable = true)
      Property(name = "Age", type = "int", isGettable = true, isSettable = false)
    ]
  )

  println(generateSource(person))
}

This program lives in the repository as examples/source-generator.nyx.