OverviewFilesCommitsIssuesDocsReleasesPeers

ollama_semantic_function_calling / ollamatools-new / keet / node_modules / hyperschema / lib / codegen.js

504 lines · javascript ref:
const gen = require('generate-object-property')
const s = require('generate-string')

module.exports = function generateSchema (hyperschema, { esm = false, filename } = {}) {
  const structs = []
  const structsByName = new Map()
  const deferred = []
  const dedup = new Map()
  const externalStructs = new Map()

  for (let i = 0; i < hyperschema.schema.length; i++) {
    const fqn = hyperschema.typesByPosition.get(i)
    const type = hyperschema.resolve(fqn)
    if (type.isStruct) {
      structs.push(type)
    }
    if (type.isArray) {
      structs.push(type)
    }
    if (type.isEnum) {
      structs.push(type)
    }
    if (type.isExternal) {
      structs.push(type)
    }
  }

  for (let i = 0; i < structs.length; i++) {
    const struct = structs[i]
    const id = 'encoding' + i
    const result = { encoder: null, id }
    structsByName.set(struct.fqn, result)
  }
  for (let i = 0; i < structs.length; i++) {
    const struct = structs[i]
    const result = structsByName.get(struct.fqn)

    if (struct.isExternal) {
      if (!filename) throw new Error('Must provide filename if using external types')
      const req = struct.require(filename)
      let v = externalStructs.get(req)
      if (!v) {
        v = 'external' + externalStructs.size
        externalStructs.set(req, v)
      }
      result.encoder = `const ${result.id} = ${gen(v, struct.external)}\n`
      continue
    }

    result.encoder = struct.isEnum
      ? generateEnum(result.id, struct)
      : generateStruct(result.id, struct, deferred)
  }

  let str = ''

  str += '// This file is autogenerated by the hyperschema compiler\n'
  str += `// Schema Version: ${hyperschema.version}\n`
  str += '/* eslint-disable camelcase */\n'
  str += '/* eslint-disable quotes */\n'
  str += '\n'
  str += `const VERSION = ${hyperschema.version}\n`

  if (esm) {
    str += 'import { c } from \'hyperschema/runtime\'\n'
  } else {
    str += 'const { c } = require(\'hyperschema/runtime\')\n'
  }
  for (const [req, v] of externalStructs) {
    if (esm) {
      str += `import * as ${v} from ${s(req)}\n`
    } else {
      str += `const ${v} = require(${s(req)})\n`
    }
  }

  str += '\n'
  str += '// eslint-disable-next-line no-unused-vars\n'
  str += 'let version = VERSION\n'
  str += '\n'

  for (let i = 0; i < structs.length; i++) {
    const struct = structs[i]
    const { encoder } = structsByName.get(struct.fqn)
    str += encoder
    str += '\n'
  }

  if (deferred.length) {
    str += deferred.join('')
    str += '\n'
  }

  str += 'function setVersion (v) {\n'
  str += '  version = v\n'
  str += '}\n'
  str += '\n'

  str += 'function encode (name, value, v = VERSION) {\n'
  str += '  version = v\n'
  str += '  return c.encode(getEncoding(name), value)\n'
  str += '}\n'
  str += '\n'

  str += 'function decode (name, buffer, v = VERSION) {\n'
  str += '  version = v\n'
  str += '  return c.decode(getEncoding(name), buffer)\n'
  str += '}\n'
  str += '\n'

  str += 'function getEnum (name) {\n'
  str += '  switch (name) {\n'
  for (let i = 0; i < structs.length; i++) {
    const struct = structs[i]
    if (struct.enum) str += `    case ${s(struct.fqn)}: return ${structsByName.get(struct.fqn).id}_enum\n`
  }
  str += '    default: throw new Error(\'Enum not found \' + name)\n'
  str += '  }\n'
  str += '}\n'

  str += '\n'

  str += 'function getEncoding (name) {\n'
  str += '  switch (name) {\n'
  for (let i = 0; i < structs.length; i++) {
    const struct = structs[i]
    str += `    case ${s(struct.fqn)}: return ${structsByName.get(struct.fqn).id}\n`
  }
  str += '    default: throw new Error(\'Encoder not found \' + name)\n'
  str += '  }\n'
  str += '}\n'

  str += '\n'

  str += 'function getStruct (name, v = VERSION) {\n'
  str += '  const enc = getEncoding(name)\n'
  str += '  return {\n'
  str += '    preencode (state, m) {\n'
  str += '      version = v\n'
  str += '      enc.preencode(state, m)\n'
  str += '    },\n'
  str += '    encode (state, m) {\n'
  str += '      version = v\n'
  str += '      enc.encode(state, m)\n'
  str += '    },\n'
  str += '    decode (state) {\n'
  str += '      version = v\n'
  str += '      return enc.decode(state)\n'
  str += '    }\n'
  str += '  }\n'
  str += '}\n'
  str += '\n'
  str += 'const resolveStruct = getStruct // compat\n'
  str += '\n'

  if (esm) {
    str += 'export { resolveStruct, getStruct, getEnum, getEncoding, encode, decode, setVersion, version }\n'
  } else {
    str += 'module.exports = { resolveStruct, getStruct, getEnum, getEncoding, encode, decode, setVersion, version }\n'
  }

  return str

  function generateEnum (id, struct) {
    let str = ''

    str += `const ${id}_enum = {\n`

    for (let i = 0; i < struct.enum.length; i++) {
      const e = struct.enum[i]
      const value = struct.strings ? s(e.key) : i + struct.offset
      str += `  ${gen.property(e.key)}: ${value}${i < struct.enum.length - 1 ? ',' : ''}\n`
    }

    str += '}\n\n'

    const preencode = generateEncode({ preencode: true })
    const encode = generateEncode()
    const decode = generateDecode()

    str += `// ${struct.fqn} enum\n`
    str += `const ${id} = {\n`
    str += '  preencode (state, m) {\n'
    str += `${preencode}`
    str += '  },\n'
    str += '  encode (state, m) {\n'
    str += `${encode}`
    str += '  },\n'
    str += '  decode (state) {\n'
    str += `${decode}`
    str += '  }\n'
    str += '}\n'

    return str

    function generateEncode ({ preencode = false } = {}) {
      const max = struct.enum.length + struct.offset - 1
      const encode = preencode ? 'preencode' : 'encode'

      if (preencode && max <= 0xfc) {
        return `    state.end++ // max enum is ${max} so always one byte\n`
      }

      if (!struct.strings) {
        let str = ''
        str += `    if (m > ${max}) throw new Error('Unknown enum')\n`
        str += `    c.uint.${encode}(state, m)\n`
        return str
      }

      let str = '    switch (m) {\n'
      for (let i = 0; i < struct.enum.length; i++) {
        str += `      case ${s(struct.enum[i].key)}:\n`
        str += `        c.uint.${encode}(state, ${i + struct.offset})\n`
        str += '        break\n'
      }
      str += '      default: throw new Error(\'Unknown enum\')\n'
      str += '    }\n'
      return str
    }

    function generateDecode () {
      if (!struct.strings) return '    return c.uint.decode(state)\n'

      let str = '    switch (c.uint.decode(state)) {\n'
      for (let i = 0; i < struct.enum.length; i++) {
        str += `      case ${i + struct.offset}: return ${s(struct.enum[i].key)}\n`
      }
      str += '      default: return null\n'
      str += '    }\n'
      return str
    }
  }

  function getGeneratedTypeEncoder (type) {
    if (type.isPrimitive) {
      return gen('c', type.name)
    }

    if (type.isAlias) {
      return getGeneratedTypeEncoder(type.type)
    }

    return structsByName.get(type.fqn).id
  }

  function dedupConst (name, def, deferred = false) {
    if (dedup.has(def)) return `const ${name} = ${dedup.get(def).name}\n`
    dedup.set(def, { deferred, name })
    return `const ${name} = ${def}\n`
  }

  function getGeneratedFieldEncoder (field) {
    const typeStr = getGeneratedTypeEncoder(field.type)

    if (field.array) {
      if (field.framed) {
        return `c.array(c.frame(${typeStr}))`
      }
      return `c.array(${typeStr})`
    }

    if (field.framed) {
      return `c.frame(${typeStr})`
    }

    return typeStr
  }

  function generateStruct (id, struct, deferred) {
    const fieldTypes = new Map()

    let str = ''
    let enc = ''

    if (struct.isArray) {
      enc = getGeneratedTypeEncoder(struct.type)
    } else {
      for (let i = 0; i < struct.fields.length; i++) {
        const field = struct.fields[i]
        if (!field.framed && !field.array) continue

        const fieldId = `${id}_${i}`
        fieldTypes.set(field.name, fieldId)

        const fieldEnc = getGeneratedFieldEncoder(field)
        const isDeferred = !!dedup.get(fieldEnc)?.deferred

        // if recursive, defer the field type
        if (structsByName.get(field.type.fqn)?.encoder === null || isDeferred) {
          let d = ''
          d += `// ${struct.fqn}.${field.name}, deferred due to recusive use\n`
          d += dedupConst(fieldId, fieldEnc, true)
          deferred.push(d)
        } else {
          str += `// ${struct.fqn}.${field.name}\n`
          str += dedupConst(fieldId, fieldEnc, false)
        }
      }

      if (str) {
        str += '\n'
      }

      const preencode = generateEncode({ preencode: true })
      const encode = generateEncode()
      const decode = generateDecode()

      enc += '{\n'
      enc += '  preencode (state, m) {\n'
      enc += `${preencode.trimRight()}\n`
      enc += '  },\n'
      enc += '  encode (state, m) {\n'
      enc += `${encode.trimRight()}\n`
      enc += '  },\n'
      enc += '  decode (state) {\n'
      enc += `${decode.trimRight()}\n`
      enc += '  }\n'
      enc += '}'
    }

    str += `// ${struct.fqn}\n`

    if (struct.isArray) {
      if (struct.framed) {
        str += dedupConst(id, `c.array(c.frame(${enc}))`)
      } else {
        str += dedupConst(id, `c.array(${enc})`)
      }
    } else {
      str += dedupConst(id, enc)
    }

    return str

    function generateEncode ({ preencode = false } = {}) {
      const fn = preencode ? 'preencode' : 'encode'
      let str = ''
      let maxFlag = 0
      let fastFlags = false

      const flags = []
      for (let i = 0; i < struct.optionals.length; i++) {
        const field = struct.optionals[i]
        maxFlag = Math.max(maxFlag, field.flag)
        if (field.external) continue
        flags.push('(' + vInlinePrefix(field.version, gen('m', field.name)) + ' ? ' + field.flag + ' : 0)')
      }

      if (flags.length) {
        if (maxFlag < 128 && preencode) {
          fastFlags = true
        } else if (flags.length === 1) {
          str += `    const flags = ${flags[0].slice(1, -1)}\n\n`
        } else {
          str += `    const flags =\n      ${flags.join(' |\n      ')}\n\n`
        }
      }

      let bitfield = false
      let optional = false
      for (let i = 0; i < struct.fields.length; i++) {
        const field = struct.fields[i]
        if (field.external) continue

        if (i === struct.flagsPosition) {
          if (fastFlags) {
            str += `    state.end++ // max flag is ${maxFlag} so always one byte`
          } else {
            str += `    c.uint.${fn}(state, flags)`
          }
          bitfield = true
        }
        if (field.type.bool) continue

        if (bitfield === true) {
          str += '\n'
          bitfield = false
        }
        if (!field.required && !optional) {
          str += '\n'
          optional = true
        }

        let prefix = '   '
        if (!field.required) {
          prefix += ` ${vPrefix(field.version, gen('m', field.name))}`
        }

        str += `${prefix} ${getFieldEncoder(field)}.${fn}(state, ${gen('m', field.name)})`
        if (i !== struct.fields.length - 1) {
          let onlyBools = true
          for (let j = i + 1; j < struct.fields.length; j++) {
            const field = struct.fields[j]
            if (!field) break
            if (!field.type.bool) {
              onlyBools = false
              break
            }
          }
          if (!onlyBools || !bitfield) str += '\n'
        }
      }
      return str
    }

    function generateDecode () {
      let str = ''
      let seen = 0

      const pre = new Map()
      for (let i = 0; i < struct.fields.length; i++) {
        const field = struct.fields[i]
        if (!field.required || field.type.bool || i === struct.flagsPosition) break
        seen++
        if (field.external) continue
        pre.set(field, 'r' + i)
        str += `    const r${i} = ${getFieldEncoder(field)}.decode(state)\n`
      }

      if (seen === struct.flagsPosition) {
        if (alwaysOptional(struct)) {
          str += '    const flags = c.uint.decode(state)\n\n'
        } else {
          str += '    const flags = state.start < state.end ? c.uint.decode(state) : 0\n\n'
        }
      } else if (pre.size) {
        str += '\n'
      }

      str += '    return {\n'

      for (let i = 0; i < struct.fields.length; i++) {
        const field = struct.fields[i]
        const end = i < struct.fields.length - 1 ? ',\n' : '\n'
        const flag = vInlinePrefix(field.version, `(flags & ${field.flag}) !== 0`)
        const prop = gen.property(field.name)

        if (field.external) {
          str += `      ${prop}: ${field.type.default}${end}`
          continue
        }

        if (pre.has(field)) {
          str += `      ${prop}: ${pre.get(field)}${end}`
          continue
        }

        if (field.type.bool) {
          str += `      ${prop}: ${flag}${end}`
          continue
        }
        if (field.required) {
          str += `      ${prop}: ${getFieldEncoder(field)}.decode(state)${end}`
          continue
        }

        str += `      ${prop}: ${flag} ? ${getFieldEncoder(field)}.decode(state) : ${field.type.default}${end}`
      }

      str += '    }'

      return str
    }

    function vPrefix (v, condition) {
      if (v === 1) return condition ? `if (${condition})` : ''
      return condition ? `if (version >= ${v} && ${condition})` : `if (version >= ${v}) `
    }

    function vInlinePrefix (v, condition) {
      if (v === 1) return condition || ''
      return condition ? `(version >= ${v} && ${condition})` : `version >= ${v}`
    }

    function getFieldEncoder (field) {
      if (fieldTypes.has(field.name)) return fieldTypes.get(field.name)
      return getEncoder(field.type)
    }

    function getEncoder (type) {
      if (type.isAlias) return getEncoder(type.type)
      if (type.isPrimitive) return `c.${type.name}`
      return structsByName.get(type.fqn).id
    }
  }
}

function alwaysOptional (struct) {
  for (let i = 0; i < struct.fields.length; i++) {
    const f = struct.fields[i]
    if (f.required) continue

    for (let j = 0; j < i; j++) {
      const p = struct.fields[j]
      if (p.version !== f.version) return false
    }

    return true
  }

  return false
}