
Security News
Crates.io Users Targeted by Phishing Emails
The Rust Security Response WG is warning of phishing emails from rustfoundation.dev targeting crates.io users.
@kiniro/lang
Advanced tools
kiniro-lang
(gitlab / npm / jsdoc) is a toy LISP-like programming language interpreter written in JavaScript suitable for use in both node.js and the browser.
It can be a viable solution as an embedded language for a web application that needs an isolated computational environment with a limited set of interfaces made available, e.g. for game scripting or as a configuration processing DSL.
The interpreter is not optimised for performance, and performance is not the main goal, so don't expect it to be blazingly fast.
It is not recommended for use in production.
See the docs.
The interpreter is written with a heavy use of Promise
s, so there is a little overhead due to async functions being pushed to message queue on some eval
calls. But the use of deferred computations solves the problem of which most of naive language interpreter implementations written in JS suffer - too much recursion
error.
npm install @kiniro/lang
In order to run tests, you need 'grunt' binary installed in your environment.
grunt test
const { Kiniro } = require('@kiniro/lang');
const kiniro = new Kiniro();
kiniro.exec('(+ 2 2)').then(result => {
console.log(result); // 4
});
It is possible to lift a JS function into kiniro world:
const { Kiniro } = require('@kiniro/lang');
const kiniro = new Kiniro();
kiniro.load({ raw: { fun: (x, y) => x + y } });
console.log(await kiniro.exec('(fun 3 4)'));
Or using define:
const { Kiniro } = require('@kiniro/lang');
const kiniro = new Kiniro();
kiniro.define('sayHi', name => console.log(`Hi, ${name}!`));
await kiniro.exec('(sayHi "JavaScript")');
And getting kiniro functions back is possible too:
const { Kiniro } = require("@kiniro/lang")
const kiniro = new Kiniro();
await kiniro.exec(
'(def reduce (f e xs)\
(if (empty xs) \
e \
(f (head xs) (reduce f e (tail xs)))))'
);
console.log(await kiniro.lookup('reduce')(
(x, y) => x + y,
0,
[1,2,3,4]
)); // 10
Note that they are always asynchronous.
The following names are reserved keywords. It is impossible to define a variable named with a keyword or pass a keyword to a higher-order function as argument.
Sequential execution. Evaluates all arguments returning the value of the last one.
Evaluates all arguments within a new scope and returns the value of the last argument.
Variable definitions never leave the scope they are defined in, so the ones wrapped in a begin
statement will not be visible from outer scope. However, it is possible to change the value of a variable from outer scope using set. Think of begin
blocks as of IIFEs.
Differences between do
and begin
can be illustrated with the following code snippet:
;; define initial value
(def x 0)
;; modify the variable within a new scope
(begin
(def x 1)
;; x is now 1
x)
;; in the outer scope x remains unchanged (0)
x
(do (def x 1))
;; but now it is set to 1
x
Bind a list of variables to the corresponding values. Variable definitions can refer to other variables defined earlier in the same let
-statement.
Example:
;; evaluates to 2
(let ((x 1)
(y (+ 1 x))
)
y)
Define a variable or a function in the current scope. Shoul be used before set. Think of def
as of var
in strict-mode javascript.
Example:
;; A function that sums 3 arguments
(def f (a b c)
(+ a b c))
;; A variable
(def x
9)
Assignment operator (equivalent to =
in JS) , changes variable values or object properties.
;; variable definition is required before first set use
(def x 0)
(set x 1)
(def y { a: { b: 0 } })
(set y.a.b { c: 1 })
(set y.a "d" 3)
When modifying object properties, set
returns object values, thus allowing to chain calls:
(def x {})
(set (set (set x "a" 0)
"b" 1)
"c" 3)
A lambda-abstraction, or an anonymous function. Arguments list, which may be empty, can be accessed through the args
variable (same as arguments
in JS).
(\x y ->
(* x y))
If the expression right after the keyword evaluates to cons
, nil
or a wrapped JS value that is truthy, then the next expression will be evaluated, otherwise the last will be chosen.
(if []
0
1) ;; 1
Note that such behavior is unusual for LISP-like languages, where nil
is usually falsy. The reason behind this is that unlift
converts nil
to [], and [] is truthy in JS.
Sequentially searches for a clause with condition expression that evaluates to truthy value. If no such expression is found, throws an error.
cond
expressions translate to sequences of nested if
s.
(cond
((foo bar) baz)
(bar (foo baz)))
is equivalent to
(if (foo bar) baz
(if bar (foo baz)
(throw "cond: none of the conditions is true")))
.
-operator looks up a property of given object. It can be used as infix operator.
The following two expressions are equivalent (note the double parentheses and quotes):
(a.b).c
(. ((. a "b")) "c")
Call a function with arguments given as a list. Similar to Function.prototype.apply, except for the absence of this
. It is not possible to use apply
with keywords as first argument.
(apply + [1 2 3])
;; 6
Throws first argument (or nil
if none given) as exception.
Tries to evaluate the first argument, if it fails, applies second argument (that must be a function) to the thrown value. The exception does not propagate until rethrown again.
(try (throw 1)
(\err -> (+ 1 err))) ;; 2
Evaluates given arguments in parallel.
Evaluates given arguments asynchronously. Returns a promise that resolves to the last expression's value.
Freezes evaluation, waiting until the given promise resolve. If the first argument is not a promise, returns it's value.
(def promise (async (+ 3 4)))
(await promise) ;; 7
(sleep t)
Delay evaluation for t
milliseconds. Returns t
value. When called without arguments, returns nil
.
Cons
truct a new list by given element and another list (i.e. put the element to the beginning of the list).
Check whether given list is empty.
Get the first element of a list. Throws if nil
given.
Get the tail of a list. Throws if nil
given.
Construct a list from arguments.
(list 1 2 3) = (cons 1 (cons 2 (cons 3 nil))) = [1 2 3]
Accepts a single expression and converts it to a list. When called with an atomic expression (i.e. not parentheses), returns it untouched.
Evaluates a list as if it were an expression.
(eval [+ 1 [- 9 4]]) ;; 6
(def x (tail (quote (1 2))))
(set x (cons 3 x))
(set x (cons + x))
(eval x) ;; 5
The following arithmetic functions are supported: +
, -
, *
, /
, ^
, >
, <
, >=
, <=
, ==
.
+
and *
accept arbitrary number of arguments.
Math object is also available.
or
, and
.Both can be called with arbitrary number of arguments.
Negates the first argument.
true
, false
Logical constants.
Bracketed lists translate into list
function calls.
[a b c] => (list a b c)
Object syntax is similar to the one used in JS, however, commas are optional.
$
is a right-associative infix application operator, allows to flatten nested parentheses.
(a $ b $ c) => (a (b (c)))
(a $ b c d) => (a (b c d))
((a $ b) c) => ((a (b)) c)
FAQs
A Lisp-like language interpreter.
We found that @kiniro/lang demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
The Rust Security Response WG is warning of phishing emails from rustfoundation.dev targeting crates.io users.
Product
Socket now lets you customize pull request alert headers, helping security teams share clear guidance right in PRs to speed reviews and reduce back-and-forth.
Product
Socket's Rust support is moving to Beta: all users can scan Cargo projects and generate SBOMs, including Cargo.toml-only crates, with Rust-aware supply chain checks.