Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-mcp

Model Context Protocol server for Pudu language documentation and compiler tools

0.1.1Apache-2.01

InstallClose

SourceFile.pudu

Pudu52 lines1.6 KB

GitHub ↗
1/** @Domain.Code.SourceFile.Module — maps module names to source paths */2module PuduLangMcp.Domain.Code.SourceFile34import Std.Char as Char5import Std.Text as Text67/// The file extension of Pudu source.8const EXTENSION: Str = ".pudu"910/// The name a `module` declaration gives, when the source begins with a valid one.11export fn moduleNameOf(source: Str) -> Option[Str] {12  for raw in source.split("\n") {13    let line = raw.trim()14    let comment = line.startsWith("//") || (line.startsWith("/*") && line.endsWith("*/"))15    if !line.isEmpty() && !comment {16      if !line.startsWith("module ") { return None }17      let name = line.drop(7).trim()18      return if isModuleName(name) { Some(name) } else { None }19    }20  }21  None22}2324/// The path under a source root that a module's file must have.25export fn fileOf(moduleName: Str) -> Str {26  moduleName.replace(".", "/") + EXTENSION27}2829/// The module a source-root-relative file declares by its path, when the path is a module path.30export fn moduleOfFile(relative: Str) -> Option[Str] {31  if !relative.endsWith(EXTENSION) { return None }32  let name = Text.stripSuffix(relative, EXTENSION).replace("/", ".")33  if isModuleName(name) { Some(name) } else { None }34}3536/// Whether text is a dotted module name.37export fn isModuleName(name: Str) -> Bool {38  if name.isEmpty() { return false }39  for segment in name.split(".") {40    if !isSegment(segment) { return false }41  }42  true43}4445/// Whether text is one segment of a module name.46fn isSegment(segment: Str) -> Bool {47  match Text.uncons(segment) {48    case None => false49    case Some((first, rest)) => Char.isLetter(first) && Text.allChars(rest, |c: Char| Char.isAlphanumeric(c) || c == '_')50  }51}52