lisp_interpreter

    Download zip
    Author
    Version
    0.1.2
    License
    Apache-2.0
    Last updated
    10 days ago
    Downloads
    39

    #Lisp Machine - MoonPilot

    A Lisp interpreter implementation in MoonBit, featuring S-expression parsing and evaluation with support for arithmetic operations, variables, functions, and control flow.

    #Features

    • S-expression parsing: Robust tokenization and parsing of Lisp syntax with proper error handling
    • Arithmetic operations: Support for +, -, *, / operations
    • Comparison operations: Support for =, <, >, <=, >= comparisons
    • Variables: Define and use variables with define
    • Functions: Create lambda functions and call them
    • Control flow: Conditional expressions with if
    • Sequential evaluation: Multiple expressions with begin
    • Unicode support: Full support for Unicode characters including emojis

    #Usage

    #Basic Arithmetic

    ///|
    test {
    eval_string("(+ 1 2 3)", content=6)
    eval_string("(* (+ 2 3) (- 8 2))", content=30)
    }

    #Variables and Definitions

    ///|
    test {
    eval_string("(begin (define x 10) (define y 5) (+ x y))", content=15)
    }

    #Conditional Expressions

    ///|
    test "if conditions" {
    eval_string("(if (> 5 2) 42 0)", content=42)
    eval_string("(= 5 5)", content=true)
    eval_string("(< 3 7)", content=true)
    eval_string("(> 5 4)", content=true)
    eval_string("(>= 5 5)", content=true)
    eval_string("(<= 3 7)", content=true)
    }

    #Lambda Functions

    ///|
    test {
    // Basic lambda
    eval_string(
    "(begin (define square (lambda (x) (* x x))) (square 4))",
    content=16,
    )

    // Higher-order functions
    eval_string(
    "(begin (define apply-twice (lambda (f x) (f (f x)))) (define add1 (lambda (x) (+ x 1))) (apply-twice add1 5))",
    content=7,
    )

    // Closures
    eval_string(
    "(begin (define make-adder (lambda (n) (lambda (x) (+ x n)))) (define add5 (make-adder 5)) (add5 10))",
    content=15,
    )
    }

    #Function Definition Shorthand

    ///|
    test {
    // Alternative function definition syntax
    let factorial =
    #| (begin
    #| (define (fact n)
    #| (if (= n 0) 1
    #| (* n (fact (- n 1)))))
    #| (fact 5))
    eval_string(factorial, content=120)
    }

    #S-expression Parsing

    The parser handles various Lisp syntax constructs:

    ///|
    test {
    // Simple expressions
    let simple = @lisp_interpreter.parse_sexp("(hello world)")
    assert_eq(
    simple,
    @lisp_interpreter.Sexp::List([
    @lisp_interpreter.Sexp::Atom("hello"),
    @lisp_interpreter.Sexp::Atom("world"),
    ]),
    )

    // Nested expressions
    let nested = @lisp_interpreter.parse_sexp("(define (square x) (* x x))")
    assert_eq(
    nested,
    @lisp_interpreter.Sexp::List([
    @lisp_interpreter.Sexp::Atom("define"),
    @lisp_interpreter.Sexp::List([
    @lisp_interpreter.Sexp::Atom("square"),
    @lisp_interpreter.Sexp::Atom("x"),
    ]),
    @lisp_interpreter.Sexp::List([
    @lisp_interpreter.Sexp::Atom("*"),
    @lisp_interpreter.Sexp::Atom("x"),
    @lisp_interpreter.Sexp::Atom("x"),
    ]),
    ]),
    )

    // Unicode support
    let unicode = @lisp_interpreter.parse_sexp("(你好 世界 👋🏻)")
    assert_eq(
    unicode,
    @lisp_interpreter.Sexp::List([
    @lisp_interpreter.Sexp::Atom("你好"),
    @lisp_interpreter.Sexp::Atom("世界"),
    @lisp_interpreter.Sexp::Atom("👋🏻"),
    ]),
    )
    }

    #Error Handling

    The interpreter provides comprehensive error handling:

    ///|
    test {
    // Parse errors
    try {
    let _ = @lisp_interpreter.parse_sexp("(hello (world")
    fail("Should have failed with parse error")
    } catch {
    _ => () // Expected parse error
    }

    // Evaluation errors
    try {
    let _ = @lisp_interpreter.evaluate(@lisp_interpreter.parse_sexp("(+ x 1)")) // unbound variable
    fail("Should have failed with unbound variable error")
    } catch {
    _ => () // Expected error for unbound variable
    }
    }

    #Architecture

    The interpreter consists of two main modules:

    1. sexp.mbt: S-expression parsing and tokenization
      • Sexp enum representing atoms and lists
      • @lisp_interpreter.parse_sexp() function for parsing strings into S-expressions
      • tokenize() function for lexical analysis
      • Comprehensive error handling with ParseError types

    2. lisp_interpreter.mbt: Expression evaluation and runtime
      • Value enum representing runtime values (numbers, booleans, symbols, functions)
      • Environment for variable bindings
      • @lisp_interpreter.evaluate() function for expression evaluation
      • Built-in functions and special forms

    #Data Types

    #S-expressions

    ///|
    test {
    // Atoms represent symbols, numbers, and literals
    let _atom = @lisp_interpreter.Sexp::Atom("hello")

    // Lists represent function calls and data structures
    let _list = @lisp_interpreter.Sexp::List([
    @lisp_interpreter.Sexp::Atom("+"),
    @lisp_interpreter.Sexp::Atom("1"),
    @lisp_interpreter.Sexp::Atom("2"),
    ])
    }

    #Runtime Values

    ///|
    test {
    // The interpreter supports various value types:
    let _number = @lisp_interpreter.Value::Number(42)
    let _boolean = @lisp_interpreter.Value::Boolean(true)
    let _symbol = @lisp_interpreter.Value::Symbol("nil")
    // Functions store parameters, body, and closure environment
    let env = @lisp_interpreter.Env::builtin()
    let _func = @lisp_interpreter.Value::Function(
    ["x"],
    @lisp_interpreter.parse_sexp("(* x x)"),
    env,
    )
    }

    #Built-in Operations

    • Arithmetic: +, -, *, /
    • Comparison: =, <, >, <=, >=
    • Special Forms: if, define, lambda, begin

    #License

    Apache-2.0

    ParseError

    pub suberror ParseError {
    UnmatchedParens(String)
    UnexpectedParens(String)
    UnexpectedEOF
    } derive(ToJson,
    Debug
    )

    TODO(upstream): Warning: The type 'ParseError' does not occur in public signature of current package, consider marking it as priv The above message will be misleading because ParseError is actually used in the public signature. Figure out the pub/abstract meaning of suberror This generally makes sense but doesnot make sense for suberror since It is an existential type
    impl Show for ParseError

    ParseError::output

    fn ParseError::output(self : ParseError, logger : &Logger) -> Unit

    ParseError::to_json

    fn ParseError::to_json(ParseError) -> Json

    ParseError::to_string

    fn ParseError::to_string(self : ParseError) -> String

    Env

    impl Show for Env

    Env::builtin

    fn Env::builtin() -> Env

    Env::output

    fn Env::output(self : Env, logger : &Logger) -> Unit

    Env::to_repr

    Env::to_string

    fn Env::to_string(self : Env) -> String

    Sexp

    pub(all) enum Sexp {
    Atom(String)
    List(Array[Sexp])
    } derive(Eq, ToJson,
    Debug
    )

    impl Show for Sexp

    Sexp::equal

    fn Sexp::equal(Sexp, Sexp) -> Bool

    Sexp::not_equal

    fn Sexp::not_equal(x : Sexp, y : Sexp) -> Bool

    Sexp::output

    fn Sexp::output(self : Sexp, logger : &Logger) -> Unit

    Sexp::to_json

    fn Sexp::to_json(Sexp) -> Json

    Sexp::to_repr

    Sexp::to_string

    fn Sexp::to_string(self : Sexp) -> String

    Value

    pub(all) enum Value {
    Number(Int)
    Boolean(Bool)
    Symbol(String)
    Function(Array[String], Sexp, Env)
    BuiltinFunction(String)
    } derive(
    Debug
    )

    impl Show for Value
    impl ToJson for Value

    Value::output

    fn Value::output(self : Value, logger : &Logger) -> Unit

    Value::to_json

    fn Value::to_json(self : Value) -> Json

    Value::to_repr

    Value::to_string

    fn Value::to_string(self : Value) -> String

    eval

    fn eval(s : String) -> Value raise

    evaluate

    fn evaluate(expr : Sexp) -> Value raise

    parse_sexp

    fn parse_sexp(input : String) -> Sexp raise