GithubHelp home page GithubHelp logo

lips-scheme / lips Goto Github PK

View Code? Open in Web Editor NEW
386.0 10.0 30.0 27.06 MB

Scheme based powerful lisp interpreter in JavaScript

Home Page: https://lips.js.org

License: Other

JavaScript 50.07% Makefile 0.87% Shell 0.03% Scheme 48.77% CSS 0.26%
lisp lisp-interpreter language macros scheme support-bigint r7rs r7rs-scheme r5rs r5rs-scheme

lips's Introduction

LIPS - Scheme Based Powerful Lisp Language

X (formerly Twitter) Follow npm 1.0.0 Complete Build and test Coverage Status Join Gitter Chat NPM Download Count JSDelivr Download count FOSSA Status

GitHub stars Tweet

LIPS is a powerful Scheme-based, Lisp language written in JavaScript. It is based on the Scheme dialect of lisp and the R5RS/R7RS specifications. It has extensions to make it easier to interact with JavaScript and extend the language. It works both in the browser and with Node.js.

The aim of the project is to support full R7RS specification and be compatible with Scheme programming language.

The name is a recursive acronym which stands for LIPS Is Pretty Simple.

Demo

Web REPL Demo

Features

  • Literal regular expression.
  • Asynchronous execution (auto resolving of promises).
  • Possibility to add new syntax (similar to vectors and object).
  • Numerical tower and Big Integer support.
  • Powerful introspection.
  • Great integration with JavaScript.
  • Auto formatting lisp of code (pretty print)
  • Lisp and hygienic Scheme macros and macroexpand.
  • Builtin help system.

Installation

To install you can use npm (or yarn)
NOTE: The version that is on NPM is heavily outdated, use beta version:

npm install @jcubic/lips@beta

or yarn:

yarn add @jcubic/lips@beta

then include the file in the script tag. You can grab the version from unpkg.com

https://unpkg.com/@jcubic/lips@beta

or from jsDelivr (that's seems a bit faster)

https://cdn.jsdelivr.net/npm/@jcubic/lips@beta/dist/lips.min.js

Bookmarklet REPL

You can also run the REPL on any page while you learn Scheme using the bookmarklet:

https://github.com/jcubic/lips/blob/master/lib/js/bookmark.js

Create any link in your bookmarks, edit it and copy-paste the content of that file. After you click on the link it will create the REPL at the bottom of the page. (NOTE: It may not work on every page because of content security policy; e.g. google.com or gihub.com)

If you have trouble with creating the bookmarklet, you can open LISP Scheme home page where you can find a link that you can drag to your bookmarks.

Usage

The simplest way is to include the lips code in the script tag:

<script type="text/x-scheme" bootstrap>
(let ((what "world")
      (greet "hello"))
   (display (string-append greet " " what)))
</script>

or use the src attribute:

<script type="text/x-scheme" bootstrap src="example.scm"></script>

Bootstrapping Scheme system

Big part of LIPS is written in LIPS itself, but to use full power of LIPS you need to load those additional Scheme files. The easiest way is to add bootstrap attribute on first script tag with text/x-scheme type. By default, it will use CDN from jsdelivr. To load each file using builtin load function (that will fetch the file using AJAX and evaluate it).

<script src="https://cdn.jsdelivr.net/npm/@jcubic/lips@beta/dist/lips.min.js" bootstrap></script>

You can also specify the path where LIPS should search for standard library.

<script src="https://cdn.jsdelivr.net/npm/@jcubic/lips@beta/dist/lips.min.js"
        bootstrap="https://cdn.jsdelivr.net/npm/@jcubic/lips@beta/dist/std.xcb">
</script>

You can use bootstrap="./std.xcb" if there is std.xcb file in local directory. You can also bootstrap with std.scm or std.min.scm but xcb file is the fastest, because it's already parsed and compiled into binary format.

Running LIPS programmatically

var {exec} = require('@jcubic/lips'); // node
// or
var {exec} = lips; // browser

exec(string).then(function(results) {
     results.forEach(function(result) {
        console.log(result.toString());
     });
});

When running exec you will also need to bootstrap the language and loaded files from /lib/ directory.

Documentation about beta version can be found in Wiki.

Standalone executable

NOTE: Executable don't require bootstrapping lib files.

If you install lips globally with:

npm install -g @jcubic/lips@beta

you can run the interpreter from the terminal:

LIPS: Scheme interactive terminal

You can also run code in a string with:

lips -c '(let ((what "World")) (display (string-append "Hello " what)))'

and you can run a file using:

cat > foo.scm <<EOF
(let ((what "World"))
  (display (string-append "Hello " what))
  (newline))
EOF

lips foo.scm

You can also write executable files that use lips using shebang (SRFI-22)

cat foo.scm
#!/usr/bin/env lips

(let ((what "World"))
  (display (string-append "Hello " what))
  (newline))

chmod a+x foo.scm
./foo.scm

Executables also return a S-Expression according to SRFI-176 use lips --version or lips -V.

FOSDEM'23 Presentation [Video]

FOSDEM 2023 - LIPS Scheme: Powerful introspection and extensibility

Limitations

Performance

Because LIPS is tree walking interpreter, sometimes it may be slow. Especially if you want to process long arrays and use callback function. If the array is quite large each piece of code inside the callback may slow down the processing. For example see:

script reference.scm

That generates reference documentation for all builtin functions and macros. The slow part is (names.sort name-compare) (Array::sort) that take quite time to calculate, because the array with functions and macros is quite large. If you came into performance issue, you can write the part of the code in JavaScript. If you want to do this in LIPS Scheme you can use something like this:

(let ((fn (self.eval "(function(a, b) {
                         /* any complex code in JS */
                         return a.localeCompare(b);
                      })")))
   (arr.sort fn))

Another example of slow performance is using LIPS with React, the more code you put into components the slower the app will become.

Examples:

The issue with performance is tracked in #197.

JavaScript callbacks

Another limitation is when using JavaScript libraries that require normal values but get a Promise instead. This can happen with React/Preact and when the component returns a Promise. Some macros can be async (return a Promise), which will break the React app when used in components. An example of a macro that is async is do macro. So when using React/Preact and when you need to use a promise, use promise quotation and useEffect.

Supported SRFI

built-in

description spec
Feature-based conditional expansion construct SRFI-0
Homogeneous numeric vector datatypes SRFI-4
Basic String Ports SRFI-6
Running Scheme Scripts on Unix SRFI-22
Error reporting mechanism SRFI-23
Basic Format Strings SRFI-28
Basic Syntax-rules Extensions SRFI-46
An interface to access environment variables SRFI-98
Syntax parameters SRFI-139
Custom macro transformers SRFI-147
Version flag SRFI-176
Command line SRFI-193

require (load "./lib/srfi/<number>.scm")

They should be loaded as R7RS libraries in final 1.0.0 version

description spec
List Library SRFI-1
AND-LET*: an AND with local bindings, a guarded LET* special form SRFI-2
receive: Binding to multiple values SRFI-8
#, external form SRFI-10
Notation for Specializing Parameters without Currying SRFI-26
Basic hash tables SRFI-69
Boxes SRFI-111
Syntactic combiners for binary predicates SRFI-156
Multiple-value boxes SRFI-195
Procedures and Syntax for Multiple Values SRFI-210
Evaluating expressions in an unspecified order SRFI-236

in Web (e.g. in Web REPL) you can use URL:

(load "https://cdn.jsdelivr.net/npm/@jcubic/lips@beta/lib/srfi/<NUMBER>.scm")

Links

Articles

Press

Projects that use LIPS

Roadmap

1.0

  • Full support for R5RS
  • Full support for R7RS
    • R7RS libraries (import/export/define-library).
    • Continuations.
    • Tail Call Optimization (TCO).
    • Fully tested Numerical Tower.
  • Fully working binary compiler (for faster parsing and loading std lib).
  • Finish syntax-rules (ignore limitations of current approach).
    • Objects.
    • Vectors.

Future Plans

  • Picture language (possibly inspired by P5.js, see SRFI-203).
  • Stepper/Debugger.
  • Allow to use read/port in syntax extensions (similar to CL reader macros).
  • Proper expansion time for both macro systems.
  • Fully working and tested R7RS hygienic Macros (syntax-rules).
  • All recursive function in JS don't consume stack.

WIP Side projects

  • KISS (Chrome extension REPL).
  • SMILE (Web IDE), need to start over.

How you can help

I'm working on version 1.0. If you find any bugs, you can help by reporting them. If you have some Scheme code that doesn't work (note about the limitations) don't hesitate to report an issue.

You can also propose a feature or improvement to the library, it doesn't always have to be defects.

If you enjoy the library, you can write about it on a blog post and share information about it or write on Social Media. Don't forget to link to the project website (it's good for SEO).

Of course, if you want you can also contribute with code, but there are way easier ways to help.

I would also love to see if you use the library, I may even share the links of projects that use it.

Acknowledgments

Special thanks to Lassi Kortela for helping with Scheme code.

License

Released under MIT license
Copyright (c) 2018-2024 Jakub T. Jankiewicz

FOSSA Status

lips's People

Contributors

dependabot[bot] avatar dragoncoder047 avatar ge-nome avatar jcubic avatar mridultailor avatar rhtweedie avatar semitonegene avatar snyk-bot avatar stevibee avatar warpedwartwars avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

lips's Issues

Problem with quasiquote

lips> (let ((name 'x)) `(let ((name 'y)) `(list ',name)))
(let ((name (quote y)))
  (quasiquote (list (quote x))))

it should be (quote (unquote name)) the value should not be evaluated if only one unquote.

Different types of special parser transformers

options for specials or types of specials:

add-special!          :key -> (make-string key)
add-list-special!     &(1 2 3) -> (address 1 2 3)

this will make #f and #t work also #\x

this will transform:
#(1 2 3) as list-special into

(vector 1 2 3)

other posible name is add-splice-special that remove the list maybe also symbol-special that will work only with symbols so :key will be transformed to (make-key key) but :(1 2 3) will be parse error.

Better format of strings in terminal

Right now they are just string output so \\ and \" are ignored and they don't look like strings but like symbols. Solution is to use JSON.stringify and replace newlines.

Rewrite macroexpand

macroexpand is dummy, it invoke macros and ignore names errors so it don't work with quasiquote.

It need to be rewritten using evaluate function or its duplicate. So it will have scope of macros but will not invoke functions. It need to process each macro that create new variables using evaluate.

Migrate unit tests to ava

Ava allows to run unit tests using async file load, so they can be written in Scheme.

Tests that can should be rewritten in scheme. Tests that are testing the API like parser can be written in ava in JS.

  • Quote and Quasiquote
  • Y and factorial using trampoline
  • Tokenizer (move tokenizer unit tests and extend later to test rest of the tokens)
  • Parser (extend to test all specials literals and splice)
  • Cycles
  • Scope - use code from exec code will already be parsed once
  • Parallel invocation - start async code testing with timings

Help for defined function don't remove indent

If you define macro the help is fine but for functions it include the text before the line.

lips> (help ..)
(.. foo.bar.baz)

Macro that gets value from nested object where argument is comma separated symbol
lips> (help string->symbol)
(string->symbol string)

   Function convert string to LIPS symbol.

You can't parse special forms as list

This break parser:

lips.parse(lips.tokenize('(quasiquote list (unquote-splicing (list)))'))

because parser thinks that unquote-splicing came from read macros not from literal thing.

(env) in terminal is slow

If you type this in Dev tools console:

$.terminal.active().echo((await lips.exec('(env)'))[0].toString(), {keepWords: true})

it's fast but for unknow reason it's slow when typing (env) in terminal.

Add colors to pprint in terminal

It would be nice if output code from pprint have colors. the function need to be updated in terminal.js file, to not use stdout.

Problem invoking setTimeout as function

This don't work:

(setTimeout (lambda () (print "x")) 1000)

give error:

Illegal invocation

probably because of this context.

this works:

((. window "setTimeout") (lambda () (print "x")) 1000)

Notes:

  • you should use timer function (timer 1000 (print "x"))
  • setTimeout call valueOf on LNumber so delay work
  • in second case dot make binded version of the function so you can call it (first case use evaluate)

List of false is broken

The printer don't work with list of false values:

lips> (list false false)
( )

it's handled by interpreter only printer is affected:

lips> (map not (list false))
(true)

Hygienic macro syntax-rules

Hygienic macro syntax-rules

  • Handling of ellipsis.
  • Handling identifiers.
  • Proper macro expand.
  • Ellipsis alias (nested syntax-rules).
  • Proper working identifiers
  • Matching literal atoms
  • Matching symbol as last cdr in pattern
  • Spread ellipsis (x ... ...)
  • SRFI 46 (named ellipsis)
  • vectors
    • pattern
    • template
  • objects
    • pattern
    • template
  • Example Usage:
    • SRFI 26
    • SRFI 156
    • define-values from R7RS spec
    • SRFI 197
    • SRFI 210
    • SRFI 239
    • R6RS do macro
    • Gauche ellipsis test from (SRFI-149)
  • Ellipsis (pattern language) extensions from R7RS
    • Escape ellipsis

List of issues

Error in macroexpand

With macro:

(define-macro (define-symbol-macro . rest)
  "(define-symbol-macro (name . args) . body)
   
   Macro that creates special symbol macro for evaluator similar to build in , or `.
   It's like alias for real macro. Similar to CL reader macros but it receive already
   parsed code like normal macros."
   ;; this is executed in two different ways one when there are no macro and the other
   ;; if there is macro defined, in second case it will put list as first element
   ;; of the body even is it's called like this (define-symbol-macro (# code) 
   (let* ((def (if (pair? (car rest)) (caar rest) (car rest)))
          (symbol (car def))
          (code (cdr rest)))
     `(begin (add-special! ',symbol) (define-macro ,def ,@code))))

code:

 (macroexpand (define-symbol-macro (# arg) `(list->array (list ,@arg))))

output:

(begin
  (add-special! (quote list->array (quote )))
  (define-macro (list->array (quote list))
    (quasiquote (list->array (quote (unquote list))))
    ))

Sequence of parser macros

with this code:

(define-symbol-macro (# arg)
  "#(1 2 3)

   Parser macro for defining arrays."
  `(list->array (list ,@arg)))

(define-symbol-macro ({} expr)
  "(make-object :name value)

   Macro that create JavaScript object using key like syntax."
  (object-expander expr))

there is problem while executing:

#({}(:foo 10))

The problem probably is with this:

lips.parse(lips.tokenize('`(1 ,(:foo 10))'))[0].toString()
"(quasiquote (1 (unquote (:foo 10))))"
lips.parse(lips.tokenize('`(,(:foo 10))'))[0].toString()
"(quasiquote (unquote (:foo 10)))"

second code should give list with single value:

(quasiquote ((unquote (:foo 10))))

R7RS S-Expression and Block Comments

The parser need to be modified to allow #;(foo bar) it can't be implemented using parser extensions.

Test case:

(let ((foo 10) #;(bar 20))
  (* foo 2))

Make a way to easily unwrap lips numbers to native numbers

Same example as #3

(define log (. console "log"))
(define (foo x y) (log x) (log y))
(foo 10)

give

LNumber {value: 10n}
Nil {}

You can run lips code easily but it's hard to create function, like substring in lips without JS code.

JS code:

        substring: function(string, start, end) {
            return string.substring(start.valueOf(), end && end.valueOf());
        },

LIPS code:

(define (substring string start end)
  (let ((end (if (null? end) undefined ((. end "valueOf"))))
        (start ((. start "valueOf"))))
    ((. string "substring") start end)))

The other issue is that end is nil instead of undefined so you need to check if it's null?

Solution may be value function that convert nil to undefined and LNumber to number.

(define (value obj)
  (if (null? obj)
      undefined
      (if (number? obj)
          ((. obj "valueOf"))
          obj)))

(define (substring string start end)
  ((. string "substring") (value start) (value end)))

list function is recursive

list function call Pair.fromArray(args) that just convert everything into list so this don't work:

(list->array (list 1 2 3 (list->array (list 4 5 6))))
;; => [1, 2, 3, (4 5 6)]

it should return:

[1, 2, 3, [4 5 6]]

list should not convert array to list. is it should be shallow.

R7RS: Byte vectors

ref: https://small.r7rs.org/wiki/NumericVectorsCowan/17/

Starting point:

(define (u8-vector . args)
   (Uint8Array.from (list->vector args)))

(define (make-u8-vector k . fill)
 (let ((v (new Uint8Array k)))
    (if (not (null? fill))
       (--> v (fill (car fill))))
    v))

It require core updates:

  • Native instance objects should be marked with class
  • SRFI-4
  • bytevectors
  • write unit tests

Common Lisp reader macros

Consider adding reader macros same as in CL. They work like this:

 (get-macro-character #\{) → NIL, false
 (not (get-macro-character #\;)) → false
;; The following is a possible definition for the single-quote reader macro in standard syntax:

 (defun single-quote-reader (stream char)
   (declare (ignore char))
   (list 'quote (read stream t nil t))) → SINGLE-QUOTE-READER
 (set-macro-character #\' #'single-quote-reader) → T

 (defun semicolon-reader (stream char)
   (declare (ignore char))
   ;; First swallow the rest of the current input line.
   ;; End-of-file is acceptable for terminating the comment.
   (do () ((char= (read-char stream nil #\Newline t) #\Newline)))
   ;; Return zero values.
   (values)) → SEMICOLON-READER
 (set-macro-character #\; #'semicolon-reader) → T

TODO:

  • character streams
  • parser to use character stream
  • read function to use stream or string
  • parser need to be called with env (to get functions)
  • parser macros (reader macros)

More scheme compatible

There are few things to do to make LIPS more scheme:

  • #f and #t that works with vectors (this will require to have special list and symbol parser macros) added exeption to parser they are mark as symbols and defined in R5RS.scm. they are parsed as values so you can use '(#f #t).
  • <, >, ==, <= and >= variable number of arguments
  • input/output ports + string ports
  • correct letrec, let and let*
  • list? that return false on cycles
  • hex, ocal and binary number literals
  • exact inexact literals (added using add-special!)
  • assoc and memeber functions
  • character literals - it need to be object.
  • hex character literal #\x40 ==> @
  • string objects
  • R5RS string and character functions
  • remove old empty list and replace it with proper nil that represents ()
  • '#(a b) should quote vector (added using add-special! same as #)
  • vector functions
  • R7RS Symbols in form |foo bar|
  • R7RS hex literals inside symbols and strings
  • ''#f is (quote #f)

Improvements to parser

Stuff to do in parser to allow of:

#\x '#(1 2 3) #(1 2 3) -> (vector 1 2 3)

'#(0 (2 2 2 2) "Anna")
> #(0 (2 2 2 2) Anna)

it will require to write better tokenizer that don't use regex as base or don't use specials in first stage of tokenizing: Two step parser:

lips.tokenize(`'x`)
(2) ["'", "x"]
lips.tokenize(`'#x`)
(3) ["'", "#", "x"]

first stage should just return 'x and '#x that will be matched against longest mached symbol special.

It will also require to write two type of special parser macros:

  • Symbol macros - for #\x #\ will be make-character or something like that that will crate string, symbol macros also be use for #(1 2 3) as (macro (1 2 3) because list will be not processed.
  • List macros - for #(1 2 3) that will be converted to (vector 1 2 3)

Alternative names simple-parser-macro (or literal) and splice-parser-macro.

Parser will need to be updated with both types of parer macros.

Ref from duplicate:

options for specials or types of specials:

add-special!          :key -> (make-string key)
add-list-special!     &(1 2 3) -> (address 1 2 3)

Recognize JavaScript iterators by type system

type/typecheck and repr should recognize iterators.

Working check:

(define (iterator? x)
   "(iterator? x)

     Function check if value is JavaScript iterator object"
   (and (object? x) (procedure? (. x Symbol.iterator))))

Broken example lisp code

I've tried to run this macro that I've created for scheme log ago:

(define-macro (defstruct name . fields)
  "Macro implementing structures in guile based on assoc list."
      (let ((names (map (lambda (symbol) (gensym)) fields))
	    (struct (gensym))
	    (field-arg (gensym)))
	`(if (not (every-unique ',fields))
	    (error 'defstruct "Fields must be unique")
	    (begin
	      (define (,(make-name name) ,@names)
		(map cons ',fields (list ,@names)))
	      ,@(map (lambda (field)
		       `(define (,(make-getter name field) ,struct)
			  (cdr (assq ',field ,struct)))) fields)
	      ,@(map (lambda (field)
		       `(define (,(make-setter name field) ,struct ,field-arg)
			  (assq-set! ,struct ',field ,field-arg)
			  ,field-arg)) fields)
	      (define (,(make-predicate name) ,struct)
		(and (struct? ,struct)
		     (let ((result #t))
		       (for-each (lambda (x y)
				   (if (not (eq? x y)) (set! result #f)))
				 ',fields
				 (map car ,struct))
		       result)))))))

it have few issues (found by running in demo page):

  • indent for define-macro - it should be special like define
  • parenthesis matching is broken
  • it don't evaluate this macro (show error that parenthesis are not balanced)

Numerical tower

TODO:

  • Complex Type
    • Complex-Float
    • Complex Integer
    • Complex Rational
      • (make-rectangular 1/2 2/4)
      • (/ 10+10i 10+2i) works in Kawa (Lips convert to Float)
      • Complex NaN e.g. +nan.0+5.0i
      • Complex Infinite e.g. 3.0+inf.0i
    • Mixed complex 1/2+0.1i (all combinations)
    • Rational Complex literal
      • inexact #i1/2+2/4i -> (exact->inexact 1/2+2/4i).
    • big num complex 10e+10i.
  • Float Type
  • Rational
  • BigInteger
  • Number literals
  • hex octal and binary and decimal literals (#b #o #x #d)
    • binary/hex/octal with complex and rational #b1/100 #b100+100i
  • big literal numbers in scientific notation should parse as big int
  • #e #i exact inexact literals that do conversion
    • #b#x #x#b replace in tokenizer (1/4 == 0.25)
    • #i#b1/100 inexact rational binary
    • big exact fractions #e1e-1000 or #e1.2e-1000
  • +nan.0 and -nan.0 return by parser
  • proper negative 0
  • Case insensitive mnemonics
  • string->number should parse all tokens #[ieoxb]
  • proper casting and all combination of operations
  • properly working string->number
  • Unit tests for operations (all types)
    • sqrt
    • abs
    • /
      • nan
      • inf
    • number->string (check if they are ok)
    • string->number
    • +
      • nan
      • inf
    • -
      • nan
      • inf
    • *
      • nan
      • inf
    • modulo integer integer (test types)
    • quotient integer integer (test types)
    • reminder int int (test types)
    • max
    • min
    • gcd integer ...
    • lcm integer ...
    • exp
    • expt
    • log
    • trigonometry
      • sin
      • cos
      • tan
      • asin
      • acos
      • atan
    • round, truncate, floor, ceiling
    • positive? / negative?
    • exact->inexact
    • inexact->exact

Bugs

Parser bug when handling parser extensions

Found this funky expression in Racket Docs:

`(1 ```,,@,,@(list (+ 1 2)) 4)

Here is simplified example that don't work in LIPS

`(1 `,@(list (+ 1 2)) 4)

this works:

```(`,,,,1)

Maybe related:

lips> `(```,,,,@(list 1 2))
((quasiquote (quasiquote (quasiquote (unquote (unquote unquote 1 2))))))

vs

#|kawa:2|# `(```,,,,@(list 1 2))
((quasiquote (quasiquote (quasiquote (unquote (unquote (unquote 1 2)))))))

Bad indent of let

Example code:

(let* ((nodes (document.querySelectorAll ".terminal .cmd-prompt"))
       (len nodes.length)
       (i 0))
    (while (< i len)
       (let ((div (. nodes i)))
         (print div.innerHTML)
         (++ i))))

renders when pasting as this:

(let* ((nodes (document.querySelectorAll ".terminal .cmd-prompt"))
       (len nodes.length)
       (i 0))
(while (< i len)
       (let ((div (. nodes i)))
         (print div.innerHTML)
         (++ i))))

it don't indent code inside let properly.

Variable documentation

it should work for any variable when using define, including syntax-rules.

(define foo 10 "this is foo variable")
(define bar null "this is bar variable")

implementations ideas:

  • global Map, WeakMap - will not work for define inside function
  • Value object - it will be not very good for interop with JS and messing with the introspection inside lips code it can be invisible to env.get, doc will be special case or it will unbox Value when it's last in chain, Value is already used in get.
  • proxy that have slot for doc, it will only work for objects.
lips.env.set('x', new Proxy(lips.LNumber(100), {}))
await lips.exec('(= x 100)')
// [true]

it will be problematic for native values.

Can't use set-obj! on functions

You can set prototype of a function when constructing class.

My attempt to create class macro:

(define-macro (define-class spec . body)
   (let* ((class (gensym))
          (constructor-s (string->symbol "constructor"))
          (constructor (find-first (lambda (x) (eq? (car x) constructor-s)) body)))
    `(let ((,class (lambda ,@(cdr constructor))))
        (set-obj! ,class 'prototype (--> Object (create ,(cadr spec))))
        (define-global ,(car spec) ,class))))

code:

 (set-obj! ,class 'prototype (--> Object (create ,(cadr spec))))

don't work you get error that function is not an object.

Error in parsing strings

This throw exception:

await lips.exec(`(list "xxxx\\ xxx" "xxxx\\'xxx")`)

escaped space and escaped quote, check jQuery Terminal.

Wrong cycle detection

Naive cycle dectection don't work properly:

(let ((x '(1 2))) (list x x))
;; ==> ((1 2) #0#)

this is marked as cycle, which is wrong.
There is need to be implemented proper cycle detection algorithm.

let should work like Promise.all if called on promises

Example code:

(define title (lambda (url)
   (--> (fetch url) (text) (match /<title>([^>]+)<\/title>/) 1)))

(let ((a (title "https://jcubic.github.io/lips/"))
      (b (title "https://terminal.jcubic.pl")))
  (display a)
  (display b))

Second promise is resolved when first finish:

It should work the same as with this code:

((lambda (a b)
   (display a)
   (display b))
 (title "https://jcubic.github.io/lips/")
 (title "https://terminal.jcubic.pl"))

AJAX requests should be paralel for let, only let* and letrec can run in sequence.

Getting started guide

  • All features one by one
  • Lisp intro
  • LIPS introduction for scheme and common lips programmers

Parser swallow slashes while parsing strings

When --> docs is parsed

  "Helper macro that simplify calling methods on objects. It work with chaining

   usage: (--> ($ \"body\")
               (css \"color\" \"red\")
               (on \"click\" (lambda () (display \"click\"))))

          (--> document (querySelectorAll \"div\"))
          (--> (fetch \"https://jcubic.pl\") (text) (match /<title>([^<]+)<\\/title>/) 1)
          (--> document (querySelectorAll \".cmd-prompt\") 0 \"innerText\")"

it have regex as string /<title>([^<]+)<\\/title>/, the escaped slash (for string) is ignored by the parser, so you can't execute that code if you use (help -->) and copy paste.

  • fix issue
  • write unit tests

Add source maps

min files should be source maps for dist file or for src file.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.