CtrlK
BlogDocsLog inGet started
Tessl Logo

mcclowes/language-design

Use when designing language features - covers lexer, parser, AST, and interpreter patterns

77

Quality

97%

Does it follow best practices?

Impact

No eval scenarios have been run

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

builtins.mdreferences/

Builtin Function Design

Builtin Registry

type BuiltinFn = (args: Value[], env: Environment) => Value;

interface BuiltinDef {
  name: string;
  arity: number | { min: number; max: number };
  fn: BuiltinFn;
  description: string;
  signature: string;
}

const BUILTINS: Map<string, BuiltinDef> = new Map();

function defineBuiltin(def: BuiltinDef): void {
  BUILTINS.set(def.name, def);
}

Core Builtins

Math

defineBuiltin({
  name: "abs",
  arity: 1,
  fn: ([x]) => Math.abs(asNumber(x)),
  description: "Returns the absolute value",
  signature: "abs(x: Number) -> Number",
});

defineBuiltin({
  name: "min",
  arity: { min: 1, max: Infinity },
  fn: (args) => Math.min(...args.map(asNumber)),
  description: "Returns the minimum value",
  signature: "min(...numbers: Number) -> Number",
});

defineBuiltin({
  name: "max",
  arity: { min: 1, max: Infinity },
  fn: (args) => Math.max(...args.map(asNumber)),
  description: "Returns the maximum value",
  signature: "max(...numbers: Number) -> Number",
});

Lists

defineBuiltin({
  name: "length",
  arity: 1,
  fn: ([list]) => asList(list).length,
  description: "Returns the length of a list",
  signature: "length(list: List) -> Number",
});

defineBuiltin({
  name: "map",
  arity: 2,
  fn: ([list, fn], env) => {
    return asList(list).map((item, index) => {
      return callFunction(fn, [item, index], env);
    });
  },
  description: "Transforms each element",
  signature: "map(list: List, fn: (item, index) -> T) -> List<T>",
});

defineBuiltin({
  name: "filter",
  arity: 2,
  fn: ([list, fn], env) => {
    return asList(list).filter((item, index) => {
      return isTruthy(callFunction(fn, [item, index], env));
    });
  },
  description: "Filters elements by predicate",
  signature: "filter(list: List, fn: (item, index) -> Boolean) -> List",
});

defineBuiltin({
  name: "reduce",
  arity: 3,
  fn: ([list, initial, fn], env) => {
    return asList(list).reduce((acc, item, index) => {
      return callFunction(fn, [acc, item, index], env);
    }, initial);
  },
  description: "Reduces list to a single value",
  signature: "reduce(list: List, initial: T, fn: (acc, item, index) -> T) -> T",
});

IO

defineBuiltin({
  name: "print",
  arity: 1,
  fn: ([value]) => {
    console.log(stringify(value));
    return value; // Return for chaining
  },
  description: "Prints a value and returns it",
  signature: "print(value: Any) -> Any",
});

Type Checking

defineBuiltin({
  name: "type",
  arity: 1,
  fn: ([value]) => {
    if (value === null) return "null";
    if (Array.isArray(value)) return "List";
    if (typeof value === "object") return "Record";
    return capitalize(typeof value);
  },
  description: "Returns the type of a value",
  signature: "type(value: Any) -> String",
});

defineBuiltin({
  name: "isNumber",
  arity: 1,
  fn: ([value]) => typeof value === "number",
  description: "Checks if value is a number",
  signature: "isNumber(value: Any) -> Boolean",
});

Helper Functions

function asNumber(value: Value): number {
  if (typeof value !== "number") {
    throw new TypeError(`Expected Number, got ${typeOf(value)}`);
  }
  return value;
}

function asList(value: Value): Value[] {
  if (!Array.isArray(value)) {
    throw new TypeError(`Expected List, got ${typeOf(value)}`);
  }
  return value;
}

function asFunction(value: Value): FunctionValue {
  if (typeof value !== "function" && !isFunctionValue(value)) {
    throw new TypeError(`Expected Function, got ${typeOf(value)}`);
  }
  return value;
}

function isTruthy(value: Value): boolean {
  if (value === false || value === null || value === undefined) return false;
  if (value === 0) return false;
  if (value === "") return false;
  if (Array.isArray(value) && value.length === 0) return false;
  return true;
}

Variadic Arguments

function checkArity(name: string, args: Value[], arity: number | { min: number; max: number }): void {
  if (typeof arity === "number") {
    if (args.length !== arity) {
      throw new RuntimeError(`${name} expects ${arity} arguments, got ${args.length}`);
    }
  } else {
    if (args.length < arity.min) {
      throw new RuntimeError(`${name} expects at least ${arity.min} arguments, got ${args.length}`);
    }
    if (args.length > arity.max) {
      throw new RuntimeError(`${name} expects at most ${arity.max} arguments, got ${args.length}`);
    }
  }
}

README.md

SKILL.md

tile.json