UNPKG

@mojir/lits

Version:

Lits is a Lisp dialect implemented in TypeScript

430 lines (326 loc) 10.5 kB
# Lits Lits is a lexically scoped pure functional language with algebraic notation. It combines the power of functional programming with an intuitive, readable syntax. Try it in the [Lits Playground](https://mojir.github.io/lits/). ## Features - **Pure functional language** - Variables cannot be changed, ensuring predictable behavior and easier reasoning about code - **JavaScript interoperability** - JavaScript values and functions can easily be exposed in Lits - **First-class functions** - Functions are treated as values that can be passed to other functions - **Algebraic notation** - All operators can be used as functions, and functions that take two parameters can be used as operators - **Clojure-inspired functions** - Most core functions are inspired by Clojure - **Comprehensive standard library** - Rich set of functions for collections, math, strings, and more - **Structural equality** - Objects are compared by value, not by reference - **Destructuring** - Extract values from complex data structures with ease ## Installation *[Add installation instructions here]* ## Quick Start Here's a simple example to get you started: ``` // Defining a function function square(x) x * x end; // Using the function let result := square(5); // => 25 // Using function as an operator let squares := [1, 2, 3, 4, 5] map square; // => [1, 4, 9, 16, 25] // Using operator as a function let sum := +([1, 2, 3, 4, 5]); // => 15 ``` ## Syntax ### Basic Data Types ``` // Numbers 42 // integer 3.14 // float 0xFFFF // hexadecimal 0b1100 // binary 0o77 // octal -2.3e-2 // scientific notation // Strings "Hello, world!" // Booleans true false // Null null // Arrays [1, 2, 3, 4] // Objects { name := "John", age := 30 } ``` ### Builtin Number Symbols Lits provides a set of predefined mathematical constants that can be used directly in your code: ``` // Mathematical constants PI // => 3.141592653589793 π // => 3.141592653589793 (Unicode alternative) -PI // => -3.141592653589793 -π // => -3.141592653589793 E // => 2.718281828459045 (Euler's number) ε // => 2.718281828459045 (Unicode alternative) -E // => -2.718281828459045 -ε // => -2.718281828459045 PHI // => 1.618033988749895 (Golden ratio) φ // => 1.618033988749895 (Unicode alternative) -PHI // => -1.618033988749895 -φ // => -1.618033988749895 // Epsilon/Delta (smallest representable number) DELTA // => 2.220446049250313e-16 δ // => 2.220446049250313e-16 (Unicode alternative) -DELTA // => -2.220446049250313e-16 -δ // => -2.220446049250313e-16 // Infinity values POSITIVE_INFINITY // => Infinity ∞ // => Infinity (Unicode alternative) NEGATIVE_INFINITY // => -Infinity -// => -Infinity (Unicode alternative) // Integer limits MAX_SAFE_INTEGER // => 9007199254740991 MIN_SAFE_INTEGER // => -9007199254740991 // Floating point limits MAX_VALUE // => 1.7976931348623157e+308 MIN_VALUE // => 5e-324 // Not a Number NaN // => NaN ``` These constants can be used anywhere a number value is expected and help make mathematical code more readable and elegant. ### Variable Binding ``` // Let expression let x := 10; // => 10 // Variables are immutable let x := 20; // Error: x is already defined // But can be shadowed in inner scopes let y := do let x := 20; x end; // => 20, outer x is still 10 ``` ### Functions ``` // Standard function definition function add(a, b) a + b end; // Lambda functions let add := (a, b) -> a + b; // Short form with positional arguments let add := -> $1 + $2; // Single argument short form let cube := x -> x ** 3; let fourth := -> $ ** 4; ``` ### Control Flow ``` // If expression if x > 10 then "large" else "small" end; // => "large" (if x > 10) or "small" (if x <= 10) // Unless expression (reversed if) unless x > 10 then "small" else "large" end; // => "small" (if x <= 10) or "large" (if x > 10) // Switch expression switch x case 0 then "zero" case 1 then "one" case 2 then "two" end; // => "zero" (if x = 0), "one" (if x = 1), "two" (if x = 2), or null (otherwise) // Cond expression cond case val < 5 then "S" case val < 10 then "M" case val < 15 then "L" end ?? "No match"; // => "S" (if val < 5), "M" (if 5 <= val < 10), "L" (if 10 <= val < 15), or "No match" (otherwise) // Try/catch try riskyOperation() catch (error) "Error: " ++ error.message end; // => result of riskyOperation() or error message if an exception occurs ``` ### List Comprehension ``` // Simple for comprehension for each x of [0, 1, 2, 3, 4, 5], let y := x * 3, while even?(y) do y end; // => [0, 6, 12] // Multiple generators for ( each x of [1, 2, 3] each y of [1, 2, 3], when x <= y z of [1, 2, 3] ) [x, y, z] end; // => [[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 2, 1], [1, 2, 2], [1, 2, 3], [1, 3, 1], [1, 3, 2], [1, 3, 3], // [2, 2, 1], [2, 2, 2], [2, 2, 3], [2, 3, 1], [2, 3, 2], [2, 3, 3], // [3, 3, 1], [3, 3, 2], [3, 3, 3]] ``` ### Destructuring ``` // Object destructuring let { name, age } := { name := "John", age := 30 }; // name => "John" // age => 30 // Array destructuring let [nbr1, nbr2] := [1, 2, 3, 4]; // nbr1 => 1 // nbr2 => 2 // Destructuring in function parameters function displayPerson({name, age}) name ++ " is " ++ str(age) ++ " years old" end; displayPerson({ name := "John", age := 30 }); // => "John is 30 years old" ``` ## Operators and Functions ### Function Usage vs Operator Usage All functions that take two parameters can be used as operators: ``` // As a function max(5, 10); // => 10 // As an operator 5 max 10; // => 10 ``` All operators can be used as functions: ``` // As an operator 5 + 3; // => 8 // As a function +(5, 3); // => 8 ``` ### Parameter Order Unlike Clojure, Lits favors subject-first parameter order: ``` // Lits filter([1, 2, 3, 4], odd?); // => [1, 3] // Equivalent Clojure // (filter odd? [1 2 3 4]) ``` This makes operator usage more readable: ``` [1, 2, 3, 4] filter odd?; // => [1, 3] ``` ## Built-in Functions Lits comes with a comprehensive standard library of functions organized into categories: ### Collection Functions `count`, `get`, `get-in`, `contains?`, `assoc`, `assoc-in`, `++`, `not-empty`, `every?`, `not-every?`, `any?`, `not-any?`, `update`, `update-in` ### Array Functions `array`, `range`, `repeat`, `flatten`, `mapcat` ### Sequence Functions `nth`, `push`, `pop`, `unshift`, `shift`, `slice`, `splice`, `reductions`, `reduce`, `reduce-right`, `map`, `filter`, `position`, `index-of`, `last-index-of`, `some`, `reverse`, `first`, `second`, `last`, `rest`, `next`, `take`, `take-last`, `take-while`, `drop`, `drop-last`, `drop-while`, `sort`, `sort-by`, `distinct`, `remove`, `remove-at`, `split-at`, `split-with`, `frequencies`, `group-by`, `partition`, `partition-all`, `partition-by`, `starts-with?`, `ends-with?`, `interleave`, `interpose` ### Math Functions `+`, `-`, `*`, `/`, `mod`, `%`, `quot`, `inc`, `dec`, `√`, `∛`, `**`, `round`, `trunc`, `floor`, `ceil`, `min`, `max`, `abs`, `sign`, `log`, `log2`, `log10`, `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `sinh`, `cosh`, `tanh`, `asinh`, `acosh`, `atanh` ### Functional Programming `apply`, `identity`, `partial`, `comp`, `constantly`, `juxt`, `complement`, `every-pred`, `some-pred`, `fnull` ### Object Functions `dissoc`, `object`, `keys`, `vals`, `entries`, `find`, `merge`, `merge-with`, `zipmap`, `select-keys` ### String Functions `string-repeat`, `str`, `number`, `lower-case`, `upper-case`, `trim`, `trim-left`, `trim-right`, `pad-left`, `pad-right`, `split`, `split-lines`, `template`, `to-char-code`, `from-char-code`, `encode-base64`, `decode-base64`, `encode-uri-component`, `decode-uri-component`, `join`, `capitalize`, `blank?` ### Predicates `boolean?`, `null?`, `number?`, `string?`, `function?`, `integer?`, `array?`, `object?`, `coll?`, `seq?`, `regexp?`, `zero?`, `pos?`, `neg?`, `even?`, `odd?`, `finite?`, `nan?`, `negative-infinity?`, `positive-infinity?`, `false?`, `true?`, `empty?`, `not-empty?` ### Regular Expressions `regexp`, `match`, `replace`, `replace-all` ### Bitwise Operations `<<`, `>>`, `>>>`, `~`, `&`, `bit-and-not`, `|`, `^`, `bit-flip`, `bit-clear`, `bit-set`, `bit-test` ### Assertions `assert`, `assert=`, `assert!=`, `assert-gt`, `assert-lt`, `assert-gte`, `assert-lte`, `assert-true`, `assert-false`, `assert-truthy`, `assert-falsy`, `assert-null`, `assert-throws`, `assert-throws-error`, `assert-not-throws` ## Modules and Exports You can export definitions to make them available to other modules: ``` // Exporting variables export let magic-number := 42; // => 42 // Exporting functions export function square(x) x * x end; ``` ## API Lits provides a JavaScript API for embedding: ```javascript interface Lits { getRuntimeInfo: () => LitsRuntimeInfo run: (program: string, params?: ContextParams & FilePathParams) => unknown context: (programOrAst: string | Ast, params?: ContextParams & FilePathParams) => Context getUndefinedSymbols: (programOrAst: string | Ast, params: ContextParams) => Set<string> tokenize: (program: string, tokenizeParams: FilePathParams & MinifyParams) => TokenStream parse: (tokenStream: TokenStream) => Ast evaluate: (ast: Ast, params: ContextParams) => unknown transformSymbols: (tokenStream: TokenStream, transformer: (symbol: string) => string) => TokenStream untokenize: (tokenStream: TokenStream) => string apply: (fn: LitsFunction, fnParams: unknown[], params: ContextParams) => unknown } ``` ## Examples ### Factorial Function ``` function factorial(n) if n <= 1 then 1 else n * factorial(n - 1) end end; factorial(5); // => 120 ``` ### Fibonacci Sequence ``` function fib(n) if n < 2 then n else fib(n - 1) + fib(n - 2) end end; // Generate the first 10 Fibonacci numbers range(10) map fib; // => [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] ``` ### Working with Collections ``` let people := [ { name := "Alice", age := 25 }, { name := "Bob", age := 30 }, { name := "Charlie", age := 35 }, { name := "Diana", age := 40 } ]; // Get all names people map (p -> p.name); // => ["Alice", "Bob", "Charlie", "Diana"] // Get people older than 30 people filter (p -> p.age > 30); // => [{ name := "Charlie", age := 35 }, { name := "Diana", age := 40 }] // Calculate average age (people map (p -> p.age) reduce +) / count(people); // => 32.5 ``` ## Contributing *[Add contribution guidelines here]* ## License *[Add license information here]*