NAME

oh

SYNOPSIS

Command Line Interface:

Evaluate code:
oh e "(print :oh)"
Evaluate code and launch a REPL:
oh e "(setq oh 3)" i
Load files:
oh file.oh file2.oh
Load files and launch a REPL:
oh file1 i file2 i
Load files named i and e:
oh f i f e
Launch a REPL:
oh

Module Interface:

use Oh qw/interpret_string evaluate_element/;

my $result = interpret_string '(+ 1 2 3)';

my $same_result = evaluate_element ['+', 1, 2, 3];

INSTALLATION

cpan Oh

DESCRIPTION

This is a lisp interpreter written in perl trying to get the best of both worlds:

From perl it inherits the portability, unix scripting features and the whole CPAN.

Fron lisp inherits lisp evaluation rules, s-expressions and macros.

I always thought of lisp as a black box with no access to the external world and always thought that perl could be the perfect slave for lisp, being lisp the mind and perl the body.

I think that the best way to blend both is to make an interpreter in perl or a transpiler to perl (or any other language where we want to steal features from).

The main concern is to interoperate with perl as much as possible while having proper lisp evaluation rules.

The current implementation is in a prototype state and everything is subject to change.

However you are encouraged to test this interpreter and push it to its limits in order to give constructive feedback and help with its improvement and evolution.

The language has the following operators, macros and functions:

set, setq, get, let, lambda, defun, define, fun, macro, defmacro, +, if, when, unless, perl-fun, perl-defun, print, car, cdr, funcall, eval, load, eval-string, try, anon, sub...

It has more, but it's lacking a lot of things a proper language should have.

defun is common lisp defun:

(defun oh (one &optional (two 2) three &keyword four (five 5))
 (list one two three four five))

defun uses common lisp style lambda arguments.

define is scheme's define:

(define oh 3)

This creates a new symbol named 'oh' in the current environment with the value 3

(define (oh x . y) 
 (list x y))

(oh 1 2 3)

This defines a new function named 'oh' using scheme style lambda arguments.

It will return a list:

(1 (2 3))

The operator let works a bit differently than in scheme or cl:

(let (a 1 b 2 c 3)
 (list a b c))

In cl or scheme it would be instead:

(let ((a 1) (b 2) (c 3))
 (list a b c))

defmacro defines a macro with common lisp style lambda arguments:

(defmacro oh (arg1 &rest arg-list)
 `(list ,arg1 ,@arg-list))

macro defines a macro with scheme style lambda arguments:

(macro oh (arg1 . arg-list)
 `(list ,arg1 ,@arg-list))

setq sets or updates a symbol

(setq oh 3)

equivalent to:

(define oh 3)

with the difference that if setq finds the symbol in the parent scope it will mutate that symbol instead, while define will always create a new symbol in the current environment, no matter if another symbol exists.

set is a function instead of an operator and has dual behavior.

It can set symbols like setq if the symbol is quoted and set hash tables or lists if the first element is one of them.

(set 'oh 3)

This is equivalent to:

(setq oh 3)

It will also update it if it exists in a parent scope.

(setq some-list '(1 2 3))

This creates a new symbol named 'some-list' that retains the list: (1 2 3)

The function 'get' will return an element from a list or hash table.

(get some-list 0)

This returns the first element of the list returned by the 'some-list' symbol, which in our case was the 1.

(set some-list 0 24)

This mutates the first element of the list and sets it to the value 24. In our case the list is now: (24 2 3)

get and set when using lists can also receive negative indices, they will access the element starting from the end.

(get some-list -1)

This returns the last element of some list, in our case was the number 3.

They work the same with hash tables, accepting symbols as keys.

(setq some-hash (hash one 1 two 2 three (+ 1 2)))

(get some-hash 'one)

(set some-hash 'one 24)

Lambda works with cl style lambda arguments:

(lambda (x &rest y)
 (list x y))

But lambda is just a macro:

(macro lambda (args . code)
 `(anon (lisp-args ,args) ,@code))

The operator 'anon' creates a lambda with no argument bindings.

lisp-args and scheme-args are just an operator that performs the binding from a lambda argument list.

The 'anon' operator provides no bindings, but when the interpreter evaluates a lambda, it will set a symbol named 'args' with the list of arguments it receives.

((anon args) 1 2 3)

This returns the list of arguments: (1 2 3)

The 'sub' operator creates a lambda and sets it in the current environment.

(sub list args)

(list 1 2 3)

sub and anon do not provide argument bindings. The arguments will be present in the symbol 'args' when a lambda is evaluated.

defun, fun and define are macros using sub and scheme-args or lisp-args

(macro fun (name args . code)
 `(sub ,name (scheme-args ,args) ,@code))

(macro defun (name args . code)
 `(sub ,name (lisp-args ,args) ,@code))

(macro define (name . value)
 (if (listp name)
  `(fun ,(car name) ,(cdr name) ,@value)
  `(setq ,name ,(car value))))

perl-sub creates perl subroutines in a given package:

(perl-sub package-name fun-name code...)

It does not provide argument bindings, being similar to sub, but it actually creates a perl subroutine in a perl package

(perl-sub oh hello_world
 (print "Hello, world"))

Now this function is a perl subroutine named 'hello_world' and it exists in the perl package named 'oh'

Which means that is now accessible from perl:

oh::hello_world();

The interpreter can also call perl subroutines from a module:

(oh::hello_world)

The perl-defun macro provides a way to define perl subroutines using cl style bindings:

(macro perl-defun (module name args . code)
 `(perl-sub ,module ,name (lisp-args ,args) ,@code))

(perl-defun oh print_something (&optional (something 'oh))
 (print something))

(oh::print_something "Hello, world")

The perl-fun macro is similar, but provides scheme style bindings:

(macro perl-fun (module name args . code)
 `(perl-sub ,module ,name (scheme-args ,args) ,@code))

(perl-fun oh print_something something
 (print (car something)))

(oh::print_something "Hello, world")

There is also the function called 'method' which will expect an object as the first argument and the name of the method as the second. It can also take the name of a module as the first argument.

(use IO::Socket)

(method 'IO::Socket 'new 'localhost:8080)

That would return an IO::Socket object if there was a server in localhost listening at port 8080.

perl-sub, perl-fun and perl-defun allow us to create perl subroutines into a perl package.

If the perl package does not exist it will be created, because packages in perl autovivify.

That means that we can create perl classes, since they are just packages.

There is also the 'parent' operator which creates inheritance by pushing to the perl's ISA array on the package.

(use Bot::BasicBot)

(perl-fun bot said (bot args)
 (unless (or (eq (get args) 'NickServ)
             (eq (get args) 'ChanServ))
  'hey))

(parent bot Bot::BasicBot)

(method (method 'bot 'new (qw server irc.libera.chat port 6667 nick superbot)) 'run)

I have to create a better abstraction for creating perl classes.

LICENSE

CC0 1.0 Universal (CC0 1.0) Public Domain Dedication

To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide.

This software is distributed without any warranty.

https://creativecommons.org/publicdomain/zero/1.0/