Published
- 8 min read
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 Env
for 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 String
s to and from Base64.
class Base64 {
foreign static encode(data)
foreign static decode(data)
}
class Base32
Supported encoding and decoding String
s to and from Base32.
class Base32 {
foreign static encode(data)
foreign static decode(data)
}
class Base16
Supported encoding and decoding String
s 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()
}