Home

Published

- 8 min read

Fan's Standard Library (03/07/24)

img of Fan's Standard Library (03/07/24)

Type declarations are in include/lib.hpp. The filesystem’s structure maps to C++ namespaces and the Fan class definitions in lang/.

Introduction

Fan’s purpose is to be a fun, performant, and capable language with a portable embeddable runtime. But what language is capable without an extensive standard library? For the past few months I’ve been slowly building out Fan’s standard library, but regardless of how many functions I define or APIs I design, there’s no way I’ll be able to develop and extensive standard library on my own. That’s why I’m starting this document. This is a high-level map of what standard library modules are being planned, developed, or not considered.

But first, a little guide to how Fan recognizes and resolves modules. If you plan on developing a module for the standard library, it’s something you must keep in mind.

Fan’s Module Resolution

A basic import in Fan looks like so:

   import "std/fs" for Fs, Path

Use the import keyword to specify the path to a Fan file with classes or variables you’d like to import. Specify the module’s path. Modules whose path starts with “std/” pull from the standard library. This is stored at the $FAN_LIB environment variable. Fan will only read files that end in the .fan extension. Leave the file extension off your module path. Use for to specify which variables and/or classes you’d like to import followed by a comma-seperated list of said targets.

In my development environment for Fan, I have the $FAN_LIB variable set to my local copy of the standard library: lang/. If I were to request the Fs class from a script in that folder, the location of my std/fs module would be: lang/fs.fan. Since lang/ is set as my Fan’s standard library, the std/ prefix simply maps to lang/, regardless of where I’m running Fan from.

Modules

fs

Module fs stores all filesystem related APIs for Fan. This includes the File class, which represents all files, and the Path class, which represents filesystem paths.

class Fs

   class Fs {
    foreign static cwd()

    /**
    * @returns Num|null
    */
    static write(path, text) {
        if (path is String && text is String) {
            var file = File.open(path, "w+")
            var bytesLen = text.count
            file.write(text)
            file.close()
            return bytesLen
        }

        return null
    }

    static append(path, text) {
        if (path is String && text is String) {
            var file = File.open(path, "a")
            var len = text.count
            file.write(text)
            file.close()

            return len

        }
        return null
    }

    /// Returns String or null
    static read(path) {
        if (path is String) {
            var target = Path.from(path)
            if (!target.exists()) {
                return null
            }

            var file = File.open(path, "r")
            var body = file.read()
            file.close()

            return body
        }

        return null
    }

    /**
    * Removes file at given path. Will not delete directories
    */
    foreign static remove(path)

    /**
    * Recursively lists all files and directories in the provided path
    */
    foreign static listAllRecursive(path)

    foreign static listAll(path)

    foreign static isDirectory(path)

    foreign static mkdir(path)
}

class File

   foreign class File {
    construct open(path, mode) {
    }

    foreign write(text)
    foreign read()
    foreign close()
}

class Path

   
class Path {

    construct from(file) {
        _filepath = file
    }

    exists() {
        return Path.exists(_filepath)
    }



    /// Returns a bool indicating if the Path is to a directory or not.
    isDirectory() {
        return Fs.isDirectory(_filepath)
    }

    toString() {
        return _filepath
    }

    abs(path) {
        return Path.canonical(path)
    }

    abs() {
        return Path.canonical(_filepath)
    }

    replaceExt(str) {
        var newFP = _filepath.trimEnd(Path.ext(_filepath))
        return newFP + str
    }

    /// join concatenates the entries (String or List of String) to the existing path
    /// with the system's PATH_SEPARATOR. Returns the complete string on success or null on failure.

    static join(entries) {
     var buffer = ""

     for (entry in entries) {
        buffer = buffer + Path.separator() + entry
     }

     var i = 0
     var newBuffer = ""
     while (i < buffer.count) {
        var char = buffer[i]
        var s = Path.separator()
        if (i+1 <= buffer.count) {
            if (char != s || (char == s && buffer[i+1] != s)) {
                    newBuffer = newBuffer + char
            }
        }

        i = i + 1
     }

     return Path.from(newBuffer)
    }

    foreign static canonical(path)

    foreign static exists(path)

    foreign static separator()

    foreign static ext(path)

    ext() {
            return Path.ext(_filepath)
        }

    foreign static basename(path)

    basename() {
        return Path.basename(_filepath)
    }

    foreign static filename(path)

    filename() {
        return Path.filename(_filepath)
    }

}

os

Module os stores generic operating system calls, including classes like Process for current-process operations, and Envfor modifying and accessing environment variables.

class Process

Class Process supplies methods to access the internal state of your program’s process. It’s PID and PPID. The command line arguments and spawning child processes. All of these actions are managed by Process.

   class Process {

  static args { allArguments.count >= 3 ? allArguments[3..-1] : [] }

  foreign static allArguments
  foreign static cwd
  foreign static pid
  foreign static ppid
  foreign static exec(target, args)
  foreign static exit(code)
}

class Runtime

Class Runtime includes the platform and architecture of the operating system your program’s running on. Static function `typeOf(any) is a reflection interface.

   class Runtime {
  foreign static os
  foreign static arch
  foreign static typeOf(data)
}

class Env

Class Env stores and modifies the processes’ environment variables. It includes a loadDotEnv(String|null) function for handling .env dotfiles in your project and easy array-like getter/setter operands.

Relies upon fs’s File and Path classes.

   class Env {
  // get returns the associated value from 'key'.
  // If the variable is unset, get returns null.
  // @param key: string
  // @return string | null
  foreign static get(key)

  // set associates key=val for the running process.
  // If val is null, set will unset 'key'
  // If an error occurs setting or unsetting 'key', set will
  // throw an error.
  // @param val: string | null
  // @throws
  foreign static set(key, val)

  static [key] {
    return Env.get(key)
  }

  static [key]=(val) {
    return Env.set(key, val)
  }

  static loadDotEnv(path) {
    if (path == null) {
      var cwd = Path.cwd()
      var path = "%(cwd)%(Path.separator()).env"

      if (!Path.exists(path)) {
        return
      }

      var dotenv = File.open(path, "r")
      var envFile = dotenv.read()
      var lines = envFile.split("\n")
      for (line in lines) {
        if (line.contains("=")) {
          var halves = line.split("=")

          this.set(halves[0], halves[1])
        }
      }
      dotenv.close()
    } else {
        var dotenv = File.open(path, "r")
        var envFile = dotenv.read()
        var lines = envFile.split("\n")
        for (line in lines) {
          if (line.contains("=")) {
            var halves = line.split("=")

            this.set(halves[0], halves[1])
          }
        }
      dotenv.close()
    }

  }

}

fmt

Module fmt contains formatting APIs for text.

class Color

Class Color can be used with string interpolation to color the text of ASCII output to the terminal.

Example

   System.print("%(Color.blue())My blue message!%(Color.reset())")

Source Code

   class Color {
    // Regular Colors
    static black() {
         return "\e[0;30m"
    }
    static red() {
         return "\e[0;31m"
    }
    static green() {
         return "\e[0;32m"
    }
    static yellow() {
         return "\e[0;33m"
    }
    static blue() {
         return "\e[0;34m"
    }
    static purple() {
         return "\e[0;35m"
    }
    static cyan() {
         return "\e[0;36m"
    }
    static white() {
         return "\e[0;37m"
    }

    // Bold
    static boldBlack() {
        return "\e[1;30m"
    }
    static boldRed() {
         return "\e[1;31m"
    }
    static boldGreen() {
         return "\e[1;32m"
    }
    static boldYellow() {
         return "\e[1;33m"
    }
    static boldBlue() {
         return "\e[1;34m"
    }
    static boldPurple() {
         return "\e[1;35m"
    }
    static boldCyan() {
         return "\e[1;36m"
    }
    static boldWhite() {
         return "\e[1;37m"
    }

    // Underline
    static underlineBlack() {
         return "\e[4;30m"
    }
    static underlineRed() {
         return "\e[4;31m"
    }
    static underlineGreen() {
         return "\e[4;32m"
    }
    static underlineYellow() {
         return "\e[4;33m"
    }
    static underlineBlue() {
         return "\e[4;34m"
    }
    static underlinePurple() {
         return "\e[4;35m"
    }
    static underlineCyan() {
         return "\e[4;36m"
    }
    static underlineWhite() {
         return "\e[4;37m"
    }

    // Background
    static backgroundBlack() {
         return "\e[40m"
    }
    static backgroundRed() {
         return "\e[41m"
    }
    static backgroundGreen() {
         return "\e[42m"
    }
    static backgroundYellow() {
         return "\e[43m"
    }
    static backgroundBlue() {
         return "\e[44m"
    }
    static backgroundPurple() {
         return "\e[45m"
    }
    static backgroundCyan() {
         return "\e[46m"
    }
    static backgroundWhite() {
         return "\e[47m"
    }

    // High Intensity
    static highIntensityBlack() {
         return "\e[0;90m"
    }
    static highIntensityRed() {
         return "\e[0;91m"
    }
    static highIntensityGreen() {
         return "\e[0;92m"
    }
    static highIntensityYellow() {
         return "\e[0;93m"
    }
    static highIntensityBlue() {
         return "\e[0;94m"
    }
    static highIntensityPurple() {
         return "\e[0;95m"
    }
    static highIntensityCyan() {
         return "\e[0;96m"
    }
    static highIntensityWhite() {
         return "\e[0;97m"
    }

    // Bold High Intensity
    static boldHighIntensityBlack() {
         return "\e[1;90m"
    }
    static boldHighIntensityRed() {
         return "\e[1;91m"
    }
    static boldHighIntensityGreen() {
         return "\e[1;92m"
    }
    static boldHighIntensityYellow() {
         return "\e[1;93m"
    }
    static boldHighIntensityBlue() {
         return "\e[1;94m"
    }
    static boldHighIntensityPurple() {
         return "\e[1;95m"
    }
    static boldHighIntensityCyan() {
         return "\e[1;96m"
    }
    static boldHighIntensityWhite() {
         return "\e[1;97m"
    }

    // High Intensity Backgrounds
    static highIntensityBackgroundBlack() {
         return "\e[0;100m"
    }
    static highIntensityBackgroundRed() {
         return "\e[0;101m"
    }
    static highIntensityBackgroundGreen() {
         return "\e[0;102m"
    }
    static highIntensityBackgroundYellow() {
         return "\e[0;103m"
    }
    static highIntensityBackgroundBlue() {
         return "\e[0;104m"
    }
    static highIntensityBackgroundPurple() {
         return "\e[0;105m"
    }
    static highIntensityBackgroundCyan() {
         return "\e[0;106m"
    }
    static highIntensityBackgroundWhite() {
         return "\e[0;107m"
    }

    // Reset
    static reset() {
        return "\e[0m"
    }
}

encoding

Module encoding contains classes and APIs for decoding and encoding text.

class Base64

Supported encoding and decoding Strings to and from Base64.

   class Base64 {
    foreign static encode(data)

    foreign static decode(data)
}

class Base32

Supported encoding and decoding Strings to and from Base32.

   class Base32 {
    foreign static encode(data)

    foreign static decode(data)
}

class Base16

Supported encoding and decoding Strings to and from Base16.

   class Base16 {
    foreign static encode(data)

    foreign static decode(data)
}

class Markdown

API for Markdown operations

   class Markdown {
    /// Converts CommonMark Markdown to HTML
    foreign static toHTML(buff)
}

net/http

module net/http holds all APIs for the HTTP protocol. It’s very much a work in development, with only basic APIs that are nowhere near polished enough. Work in progress.

class Request

   foreign class Request {
    construct new(url) {}

    foreign send()
}