Now Reading
#Script Lisp

#Script Lisp

2023-08-04 11:15:25

#Script is designed as a small, expressive and wrist-friendly dynamic scripting language that for optimum familiarity
is modelled after the world’s hottest and ubiquitous scripting Language, JavaScript. Its minimal syntax was impressed
by different small however highly effective languages which closely makes use of capabilities as an alternative of adopting a bigger language grammar
defining totally different bespoke syntax for language constructs.

Small Languages like Smalltalk, regardless of being some of the influential languages in historical past, is legendary for its minimal
syntax that fits on a post card. A language with arguably higher energy
to dimension ratio is Lisp which the inventor of
Smalltalk, Alan Kay has credited it as being
the greatest single programming language ever designed
after realizing:

“the half web page of code on the underside of web page 13… was Lisp in itself. These have been
“Maxwell’s Equations of Software!”

Lisp’s unprecedented elegance and simplicity spawned a myriad of dialects,
some noteworthy implementations illustrating the fantastic thing about its small dimension and expressive energy is lispy by
by Peter Norvig (Director of Google Analysis) that implements a Lisp interpreter in simply 117 strains of Python 3 code (inc. a REPL).

One other compact dialect is Zick Standard Lisp which @zick
has carried out in 42 totally different languages together with a recursive Lisp evaluator in Lisp
carried out in solely 66 strains of code.

A extra full Lisp implementation in C# is the elegant Nukata Lisp
by SUZUKI Hisao which is a
Frequent Lisp-like Lisp-1 dialect with tail name optimization and partially hygienic macros, though
has some notable limitations together with a small standard library,
solely makes use of the double numeric kind and would not comprise .NET Scripting help.

Script Lisp Overview

ScriptLisp is an
enhanced model of Nukata Lisp with a variety of new options
that reuses #Script current scripting capabilities to offer seamless integration with each the remainder of #Script
(see Language Blocks an Expressions)
and .NET together with Scripting of .NET Types, help for all .NET numeric sorts and entry to its complete
library of over 1000+ Script Methods – optimally designed for accessing .NET performance from a dynamic language.

To enhance compatibility with current Frequent Lisp supply code it additionally implements many of the Simplified Common Lisp Reference
in addition to all lacking capabilities required to implement C# LINQ 101 Examples in Lisp:

To enhance readability and familiarity it additionally adopts a variety of Clojure syntax for outlining a
data list and map literals,
anonymous functions,
syntax in Java Interop for .NET Interop,
keyword syntax for indexing collections and accessing index accessors
and Clojure’s common shorter aliases for fn, def, defn – enhancing source-code compatibility with Clojure.

Lisp REPL

Along with being a 1st class language choice in #Script, Lisp’s dynamism and extensibility makes it notably
properly fitted to explanatory programming whose entry through a REPL is now constructed into the newest
x and app dotnet instruments
which may be rapidly put in in any Home windows, macOS or Linux OS (with .NET Core) with:

$ dotnet software set up -g x

Or when you have a earlier model put in, replace to the newest model with:

$ dotnet software replace -g x

The place you may then be capable to deliver up an instantaneous Lisp REPL with:

$ x lisp

The fast demo under reveals the sort of exploratory programming out there the place you possibly can question the scriptMethods out there,
question an objects props, question the Lisp interpreter’s international symbols desk containing all its international state together with all
outlined lisp capabilities, macros and variables:

Annotated REPL Stroll via

Here is an annotated model of the demo under which explains what every of the totally different expressions is doing.

Similar to Sharp Scripts and Sharp Apps the Lisp REPL runs inside the
#Script Pages ScriptContext sandbox that when run from a Sharp App folder,
begins a .NET Core App Server that simulates a totally configured .NET Core App.
On this case it is working within the redis Sharp App listing the place it was in a position to entry its static net property
in addition to its redis-server connection configured in its app.settings.

; fast lisp check!
(+ 1 2 3)

; Record of ScriptMethodInfo that the ScriptContext working this Lisp Interpreter has entry to
scriptMethods

; first script technique
(:0 scriptMethods)

; present public properties of ScriptMethodInfo 
(props (:0 scriptMethods))

; present 1 property per line
(joinln (props (:0 scriptMethods)))

; present each Property Kind and Title
(joinln (propTypes (:0 scriptMethods)))

; view the Names of all avaialble script strategies
(joinln (map .Title scriptMethods))

; view all script strategies beginning with 'a'
(globln "a*" (map .Title scriptMethods))

; view all script strategies beginning with 'env'
(globln "env*" (map .Title scriptMethods))

; print atmosphere data about this machine seperated by areas
(printlns envOSVersion envMachineName envFrameworkDescription envLogicalDrives)

; develop logical drives
(printlns envOSVersion envMachineName envFrameworkDescription "- drives:" (be a part of envLogicalDrives " "))

; view all present international symbols outlined on this Lisp interpreter
symbols

; view all symbols beginning with 'c'
(globln "c*" symbols)

; see what number of symbols are outlined on this interpreter
(rely symbols)

; see what number of script strategies there can be found
(rely scriptMethods)

; view the tactic signature for all script strategies beginning with 'all'
(globln "all*" (map .Signature scriptMethods))

; rely all recordsdata accessible from the configured ScriptContext
(rely allFiles)

; view the general public properties of the primary IVirtualFile
(props (:0 allFiles))

; show the VirtualPath of all out there recordsdata
(joinln (map .VirtualPath allFiles))

; show the tactic signature for all script strategies beginning with 'findFiles'
(globln "findFiles*" (map .Signature scriptMethods))

; see what number of .html recordsdata can be found to this App
(rely (findFiles "*.html"))

; see what number of .js recordsdata can be found to this App
(rely (findFiles "*.js"))

; present the VirtualPath of all .html recordsdata
(joinln (map .VirtualPath (findFiles "*.html")))

; view the VirtualPath's of the first and 2nd .html recordsdata
(:0 (map .VirtualPath (findFiles "*.html")))
(:1 (map .VirtualPath (findFiles "*.html")))

; view the textual content file contents of the first and 2nd .html recordsdata
(fileTextContents (:0 (map .VirtualPath (findFiles "*.html"))))
(fileTextContents (:1 (map .VirtualPath (findFiles "*.html"))))

; show the tactic signatures of all script strategies beginning with 'redis'
(globln "redis*" (map .Signature scriptMethods))

; seek for all Redis Keys beginning with 'urn:' within the redis-server cases this App is configured with
(redisSearchKeys "urn:*")

; show the primary redis search entry
(:0 (redisSearchKeys "urn:*"))

; show the important thing names of all redis keys beginning with 'urn:'
(joinln (map :id (redisSearchKeys "urn:*")))

; discover out the redis-server knowledge kind of the 'urn:tags' key
(redisCall "TYPE urn:tags")

; view all tags within the 'urn:tags' sorted set
(redisCall "ZRANGE urn:tags 0 -1")

; view the string contents of the 'urn:query:1' key
(redisCall "GET urn:query:1")

; parse the json contents of query 1 and show its tag names
(:Tags (parseJson (redisCall "GET urn:query:1")))

; extract the 2nd tag of query 1
(:1 (:Tags (parseJson (redisCall "GET urn:query:1"))))

; clear the Console display screen
clear

; exit the Lisp REPL
stop

Allow options and entry sources with app.settings

You’ll be able to configure the Lisp REPL with any of the sources and options that Sharp Apps and
Gist Desktop Apps have entry to, by making a plain textual content app.settings file with all of the
options and sources you need the Lisp REPL to have entry to, e.g. this Pure Cloud App app.settings
permits the Lisp REPL to make use of Database Scripts in opposition to a AWS PostgreSQL RDS server and question distant
S3 Virtual Files utilizing Virtual File System APIs:

# Notice: values prefixed with '$' are resolved from Atmosphere Variables
title AWS S3 PostgreSQL Internet App
db postgres
db.connection $AWS_RDS_POSTGRES
recordsdata s3
recordsdata.config {AccessKey:$AWS_S3_ACCESS_KEY,SecretKey:$AWS_S3_SECRET_KEY,Area:us-east-1,Bucket:rockwind}

See the plugins app.settings for examples of the way to load and configure
ServiceStack Plugins.

Lisp REPL TCP Server

Along with launching a Lisp REPL from the Console above, you too can open a Lisp REPL into any ServiceStack App
configured with the LispReplTcpServer ServiceStack plugin. This successfully opens a “programmable gateway” into any
ServiceStack App the place it is in a position to carry out dwell queries, entry IOC dependencies, invoke inner Server capabilities and question
the state of a working Server which just like the Debug Inspector
can present invaluable perception when diagnosing points on a distant server.

To see it in motion we’ll allow it certainly one of our manufacturing Apps techstacks.io which as it is a
Vuetify SPA App is simply configured with an empty SharpPagesFeature because it would not use any server-side scripting options.

We’ll allow it in DebugMode the place we are able to allow by setting DebugMode in our App’s appsettings.Manufacturing.json
which is able to launch a TCP Socket Server which by default is configured to take heed to the loopback IP on port 5005.

if (Config.DebugMode)
{
    Plugins.Add(new LispReplTcpServer {
        ScriptMethods = {
            new DbScripts()
        },
        ScriptNamespaces = {
            nameof(TechStacks),
            $"{nameof(TechStacks)}.{nameof(ServiceInterface)}",
            $"{nameof(TechStacks)}.{nameof(ServiceModel)}",
        },
    });
}

ScriptNamespaces behaves like C#’s utilizing Namespace; assertion letting you reference Varieties
by Title as an alternative of its fully-qualified Namespace.

While now you can connect with it with primary telnet, it is a a lot nicer expertise to make use of it with the rlwrap
readline wrap utility which offers an enhanced expertise with line modifying, persistent historical past and completion.

$ sudo apt-get set up rlwrap

Then you possibly can open a TCP Connection to hook up with a brand new Lisp REPL with:

$ rlwrap telnet localhost 5005

The place you now have full scriptability of the working server as allowed by #Script Pages SharpPagesFeature which
permits scripting of all .NET Types by default.

TechStacks TCP Lisp REPL Demo

On this demo we’ll discover among the potentialities of scripting the dwell techstacks.io Server the place we are able to
resolve IOC dependencies to ship out tweets utilizing its registered ITwitterUpdates dependency, view the supply and cargo a distant
parse-rss lisp operate into the brand new Lisp interpreter hooked up to the TCP connection,
use it to parse Hacker News RSS Feed right into a .NET Assortment the place it may be extra simply queried utilizing its built-in capabilities
which is used to assemble an electronic mail physique with HN’s present Prime 5 hyperlinks.

It then makes use of DB Scripts to discover its configured AWS RDS PostgreSQL RDBMS, itemizing its DB tables and viewing its
column names and definitions earlier than retrieving the Electronic mail addresses of all Admin customers, sending them every an electronic mail with HN’s Prime 5 Hyperlinks by
publishing 5x SendEmail Request DTOs utilizing the publishMessage ServiceStack Script to the place
they’re processed within the background by its configured MQ Server that makes use of it to execute the
SendEmail ServiceStack Service the place it makes use of its configured AWS SES SMTP Server to lastly ship out the Emails:

Password Safety

Since TCP Server successfully opens your distant Server as much as being scripted you may need to make sure the TCP Server is simply accessible
inside a trusted community, successfully treating it the identical as Redis Security Model.

A safe method can be to go away the default of solely binding to IPAddress.Loopback so solely trusted customers with SSH entry will
be capable to entry it, which they’re going to nonetheless be capable to entry remotely through Native PC > ssh > telnet 127.0.0.1 5005.

Similar to Redis AUTH you too can add password safety for an extra layer of Safety:

Plugins.Add(new LispReplTcpServer {
    RequireAuthSecret = true,
    ...
});

Which is able to solely enable entry to customers with the configured AuthSecret:

SetConfig(new HostConfig { 
    AdminAuthSecret = "secretz" 
});

Annotated Lisp TCP REPL Transcript

; resolve `ITwitterUpdates` IOC dependency and assign it to `twitter`
(def twitter (resolve "ITwitterUpdates"))

; view its concrete Kind Title
(typeName twitter)

; view its technique names 
(joinln (strategies twitter))

; view its technique signatures 
(joinln (methodTypes twitter))

; use it to ship tweet from its @webstacks account
(.Tweet twitter "Who's utilizing #Script Lisp? https://sharpscript.internet/lisp")

; view all out there scripts in #Script Lisp Library Index gist.github.com/3624b0373904cfb2fc7bb3c2cb9dc1a3
(gistindex)

; view the supply code of the `parse-rss` library
(load-src "index:parse-rss")

; assign the XML contents of HN's RSS feed to `xml`
(def xml (urlContents "https://information.ycombinator.com/rss"))

; preview its first 1000 chars
(subString xml 0 1000)

; use `parse-rss` to parse the RSS feed right into a .NET Assortment and assign it to `rss`
(def rss (parse-rss xml))

; view the `title`, `description` and the primary `merchandise` within the RSS feed:
(:title rss)
(:description rss)
(:0 (:objects rss))

; view the hyperlinks of all RSS feed objects
(joinln (map :hyperlink (:objects rss)))

; view the hyperlinks and titles of the highest 5 information objects
(joinln (map :hyperlink (take 5 (:objects rss))))
(joinln (map :title (take 5 (:objects rss))))

; assemble a plain-text numbered checklist of the highest 5 HN Hyperlinks and assign it to `physique`
(joinln (map-index #(str %2 (:title %1)) (take 5 (:objects rss))))
(joinln (map-index #(str (padLeft (1+ %2) 2) ". " (:title %1)) (take 5 (:objects rss))))
(def physique (joinln 
    (map-index #(str (padLeft (1+ %2) 2) ". " (:title %1) "n" (:hyperlink %1) "n") (take 5 (:objects rss)))))

; view all TechStacks PostgreSQL AWS RDS tables
(dbTableNames)
(joinln dbTableNames)

; view the column names and definitions of the `expertise` desk
(joinln (dbColumnNames "expertise"))
(joinln (dbColumns "expertise"))

; seek for all `consumer` tables
(globln "*consumer*" (dbTableNames))

; view what number of Admin Customers with Emails there are
(dbScalar "choose rely(electronic mail) from custom_user_auth the place roles like '%Admin%'")

; assign the Admin Customers electronic mail to the `emails` checklist
(def emails (map :electronic mail (dbSelect "choose electronic mail from custom_user_auth the place roles like '%Admin%'")))

; seek for all `operation` script strategies
(globln "*operation*" scriptMethods)

; seek for all `electronic mail` Request DTOs
(globln "*electronic mail*" metaAllOperationNames)

; view the properties out there on the `SendEmail` Request DTO
(props (SendEmail.))

; seek for all `publish` script strategies that may publish messages
(globln "publish*" scriptMethods)

; create and publish 5x `SendEmail` Request DTOs for processing by TechStacks configured MQ Server
(doseq (to emails) (publishMessage "SendEmail" { :To to :Topic "Prime 5 HN Hyperlinks" :Physique physique }))

Run and watch Lisp Scripts

The identical Sharp Scripts performance for #Script can be out there to Lisp scripts the place you should utilize the x and app
dotnet instruments to run and watch stand-alone Lisp scripts with the .l file extension, e.g:

$ x run lisp.l
$ x watch lisp.l

To make clear the behavioural variations between the Lisp REPL’s above which makes use of the identical Lisp interpreter to take care of state modifications throughout every command,
the watch Script is run with a new Lisp Interpreter which begins with a recent copy of the World symbols desk so any state modifications after every
Ctrl+S save level is discarded.

Watch lisp scripts

This fast demo illustrates the identical performance in Sharp Scripts can be out there in lisp scripts
the place it offers instantaneous suggestions while you develop in real-time:

Annotated Lisp watch script

;<!--
; db sqlite
; db.connection northwind.sqlite
; recordsdata s3
; recordsdata.config {AccessKey:$AWS_S3_ACCESS_KEY,SecretKey:$AWS_S3_SECRET_KEY,Area:us-east-1,Bucket:rockwind}
;-->

; delete take away.txt file
(sh (str (if isWin "del" "rm") " take away.txt"))

; View all `northwind.sqlite` RDBMS Tables
(textDump (dbTableNames) { :caption "Northwind" } )

; Show first `buyer` row in Single Row View displaying all Desk Columns
(textDump (dbSelect "choose * from buyer restrict 1"))

; Show all Clients in London
(def metropolis "London")
(textDump (dbSelect "choose Id, CompanyName, ContactName from buyer the place metropolis = @metropolis" { :metropolis metropolis } ))

; View all root recordsdata and folders in configured S3 Digital File Supplier
(joinln (map #(str (.Title %) "/") (allRootDirectories vfsContent)))
(joinln (map .Title (allRootFiles vfsContent)))

; Present first 10 *.png recordsdata in S3 VFS Supplier
(def sample (or (first ARGV) "*.png"))
(joinln (map .VirtualPath (take 10 (findFiles vfsContent sample))))

Web page Arguments

You too can use the identical syntax for declaring any app.settings web page arguments utilized in #Script and code Scripts:

<!--
 db sqlite
 db.connection northwind.sqlite
-->

However for compatibility with any Lisp syntax highlighters and code editors they can be prefixed with a ; line remark as seen above.

Executing Lisp in .NET

Lisp like all #Script languages are executed inside a ScriptContext that defines all performance out there to them, i.e:

var context = new ScriptContext {
    Args = { ... },              // World Arguments out there to all Scripts, Pages, Partials, and so forth
    ScriptMethods = { ... },     // Further Strategies
    ScriptBlocks = { ... },      // Further Script Blocks 
    FilterTransformers = { .. }, // Further Stream Transformers
    PageFormats = { ... },       // Further Textual content Doc Codecs
    Plugins = { ... },           // Encapsulated Options e.g. Markdown, Protected or ServiceStack Options

    ScanTypes = { ... },         // Auto register Strategies, Blocks and Code Web page Varieties
    ScanAssemblies = { ... },    // Auto register all Strategies, Blocks and Code Web page Varieties in Meeting
}.Init();

The place you possibly can customise the pure sandboxed ScriptContext your Script is executed inside by extending it with:

To render lisp you may first must register the Lisp Language with the ScriptContext you are utilizing:

var context = new ScriptContext {
    ScriptLanguages = { ScriptLisp.Language }
}.Init();

Then use RenderLisp (i.e. as an alternative of RenderScript) to render Lisp code, e.g:

// render lisp
var output = context.RenderLisp("(dateFormat now "HH:mm:ss")"); 

// async
var output = await context.RenderLispAsync("(dateFormat now "HH:mm:ss")"); 

These APIs match the high-level APIs for rendering regular #Script:

var output = context.RenderScript("{> dateFormat('HH:mm:ss') }"); 
var output = await context.RenderScriptAsync("{> dateFormat('HH:mm:ss') }"); 

Finer grained management

The high-level APIs above wraps the finer-grained performance under which works by rendering a SharpPage configured with the lisp
language in a PageResult that each one languages use:

var context = new ScriptContext {
    ScriptLanguages = { ScriptLisp.Language }
}.Init();
var dynamicPage = context.LispSharpPage("(dateFormat now "HH:mm:ss")");          // render lisp
//var dynamicPage = context.SharpScriptPage("{> dateFormat('HH:mm:ss') }"); // render #Script
var output = new PageResult(dynamicPage).RenderScript();

//async
var output = await new PageResult(dynamicPage).RenderScriptAsync();

Should you want the return worth as an alternative you possibly can entry it from:

var outcome = new PageResult(dynamicPage).EvaluateResult(out var returnValue)
    ? ScriptLanguage.UnwrapValue(returnValue)
    : null;

In case your script supply code would not change you possibly can re-use dynamicPage which helps you to re-evaluate your supply code’s cached AST.

Evaluating Lisp Script Outcomes

Should you as an alternative wished to entry return values as an alternative of its rendered output, use the EvaluateLisp() APIs:

var outcome = context.EvaluateLisp("(return (+ 1 1))"); //= 2

The generic overloads under makes use of ServiceStack’s Auto Mapping utils
to transform the return worth into your most popular kind, e.g:

double outcome = context.EvaluateLisp<double>("(return (+ 1 1))"); //= 2.0
string outcome = context.EvaluateLisp<string>("(return (+ 1 1))"); //= "2"

Which can be used for extra highly effective conversions like changing an Object Dictionary into your most popular POCO:

var outcome = context.EvaluateLisp<Buyer>(
    "(return (dbSingle "choose * from buyer the place id=@id" { :id id }))",
    new ObjectDictionary {
        ["id"] = 1
    });

.NET Interop

The syntax for .NET Interop is impressed immediately from Clojure’s syntax used for Java Interop.
See Scripting .NET Type Resolution for the way to configure Varieties and imported Namespaces you need your
Lisp scripts to have entry to.

Member Entry

The '.' prefix if for accessing an occasion members which can be utilized for retrieving a properties public properties, fields and
invoking occasion strategies, e.g:

  • (.Property occasion)
  • (.Discipline occasion)
  • (.Technique occasion …args)

Indexer Entry

Use ':' prefix for accessing a Varieties indexer or for indexing collections, e.g:

  • (:key indexer)
  • (:”string key” dictionary)
  • (:n checklist)
  • (:n array)
  • (:n enumerable)
  • (:n indexer)

It can be used to entry an occasion public Properties and Fields:

  • (:Property occasion)
  • (:Discipline occasion)

Nonetheless for readability we suggest utilizing '.' prefix above to convey occasion member entry.

Constructor Entry

Use '.' suffix for creating cases of Varieties:

  • (Kind. …args)
  • (Namespace.Kind. …args)

You too can create cases utilizing the new script method, which because it accepts a
string Kind Title can be utilized to create generic lessons with a number of generic args, e.g:

  • (new “Kind” …args)
  • (new “Kind<T1,T2,T3>” …args)

Static Member Entry

Use the "https://sharpscript.internet/" separator to entry a Kind’s static members or to invoke its static strategies, e.g:

  • (StaticType/Property)
  • (StaticType/Discipline)
  • (StaticType/Const)
  • (StaticType/Technique …args)

Use '.' dot notation for specifying the fully-qualified Kind title or to reference its Interior lessons, e.g:

  • (Namespace.StaticType/Member)
  • (Namespace.StaticType.InnerType/Member)

Script Strategies

Use "https://sharpscript.internet/" prefix to reference any Script Technique registered in your ScriptContext:

Script Strategies with out arguments may be referenced as an argument binding
that when referenced as an argument (i.e. with out brackets) are implicitly evaluated, in-effect making them a calculated property:

Whereas a "https://sharpscript.internet/" prefix signifies a reference to a script method, for readability it may be excluded as when
there is not any current image outlined within the Lisp interpreter’s image desk it’s going to fallback to referencing a script technique:

  • (scriptMethod …args)
  • methodAsBinding

This does imply that when there exists a image of the identical title outlined you will have to make use of the "https://sharpscript.internet/" prefix to reference a script technique.

Generic Varieties

All references above help referencing generic sorts and strategies with a single generic Kind argument, e.g:

  • (StaticType/Technique<T>)
  • (GenericStaticType<T>/Member)
  • (GenericStaticType<T>/Technique<T>)
  • (GenericType<T>.)

As ',' is certainly one of Lisp’s few syntax tokens (unquoting) it prevents them
from with the ability to use them to specify a number of generic arguments.

As an alternative you may want to make use of the Constructor function for referencing constructors with a number of generic
arguments the place you may additionally must specify the varieties of the precise constructor you need to name, e.g:

  • (/C “Tuple<String,int>(String,int)”)

The distinction between the /C script technique constructor operate and Lisp’s C operate is that the script technique solely returns a reference
to the constructor which you may must invoke with arguments to create an occasion:

  • ((/C “Tuple<String,int>(String,int)”) “A” 1)

While Lisp’s C operate will auto-invoke the constructor operate with the equipped arguments in a single expression:

  • (C “Tuple<String,int>(String,int)” “A” 1)

Likewise when needing to invoke generic strategies with a number of generic args you may want to make use of Function:

  • ((/F “Tuple.Create<String,int>(String,int)”) “A” 1)

Or Script Lisp’s F operate for invoking a operate reference in a single expression:

  • (F “Tuple.Create<String,int>(String,int)” “A” 1)

For extra examples and data see Scripting .NET Types.

Property Setters

You’ll be able to populate a number of properties on an occasion utilizing the set script method, e.g:

  • (set occasion { :Prop arg … })

Alternatively properties may be set individually with:

Lisp Lists vs .NET Collections

A possible supply of friction when interoperating with .NET is that Lisp Lists are Cons Cells
so {that a} code or knowledge checklist in Lisp, i.e:

'(1 2 3)
[1 2 3]

Is carried out as a Linked Record of Cons cells:

(1 . (2 . (3 . null)))

Which is what Lisp’s core capabilities count on to function on, specifically:

automobile cdr caar cadr cdar cddr caaar caadr cadar caddr cdaar cdadr cddar cdddr append mapcar consp cons?
listp checklist? memq member assq assoc nreverse final nconc dolist dotimes mapcan mapc nthcdr nbutlast 

These core Lisp capabilities cannot be used in opposition to .NET collections immediately, as an alternative you should utilize
(to-cons assortment) to transform a .NET IEnumerable assortment right into a cons checklist, e.g:

(cdr (to-cons netEnumerable))

Ought to you have to do the inverse you should utilize (to-list cons-list) to transform a cons checklist to a .NET Record, e.g:

(to-list (vary 10))

We have made Script Lisp’s cons Cell an IEnumerable in order that all different built-in Lisp capabilities can function on each
cons cells and .NET Collections the place as an alternative of iterating a listing with (do-list) you should utilize (do-seq) to iterate
each .NET Collections and cons cells, e.g:

(do-seq (x assortment) (println x) )

Annotated .NET Interop Instance

To see what this appears like in motion this is an annotated easy real-world instance that closely makes use of .NET interop:

; outline operate and assign to `parse-rss` worth in Lisp interpreters symbols desk
(defn parse-rss [xml]
    ; outline native variables used inside this scope
    (let ( (to) (doc) (channel) (objects) (el) )
        ; use XDocument.Parse() to parse xml string argument containing xml and assign to `doc`
        (def doc (System.Xml.Linq.XDocument/Parse xml))
        ; create empty ObjectDictionary (wrapper for Dictionary<string,object>) and assign to `to`
        (def to  (ObjectDictionary.))
        ; create empty Record of ObjectDictionary and assign to `objects`
        (def objects (Record<ObjectDictionary>.))
        ; descend into first <channel> XML factor and assign to `channel`
        (def channel (first (.Descendants doc "channel")))
        ; use `XLinqExtensions.FirstElement()` extension technique to assign channels first XML factor to `el`
        (def el  (XLinqExtensions/FirstElement channel))

        ; iterate via all components as much as the primary <merchandise> and add them as top-level entries in `to`
        (whereas (not= (.LocalName (.Title el)) "merchandise")
            ; add present XML factor title and worth entry to `to`
            (.Add to (.LocalName (.Title el)) (.Worth el))
            ; transfer to subsequent factor utilizing `XLinqExtensions.NextElement()` extension technique
            (def el (XLinqExtensions/NextElement el)))

        ; add all rss <merchandise>'s to `objects` checklist
        ; iterate via all `channel` little one <merchandise> XML components
        (doseq (elItem (.Descendants channel "merchandise"))
            ; create empty ObjectDictionary and assign to `merchandise`
            (def merchandise (ObjectDictionary.))
            
            ; use `XLinqExtensions.FirstElement()` to assign <merchandise> first XML factor to `el`
            (def el (XLinqExtensions/FirstElement elItem))
            (whereas el
                ; add present XML factor title and worth entry to `merchandise`
                (.Add merchandise (.LocalName (.Title el)) (.Worth el))
                ; transfer to subsequent factor utilizing `XLinqExtensions.NextElement()` extension technique
                (def el (XLinqExtensions/NextElement el)))

            ; add `merchandise` ObjectDictionary to `objects` Record
            (.Add objects merchandise))

        ; add `objects` ObjectDictionary Record to `to` at key `objects`
        (.Add to "objects" objects)
        ; return `to` ObjectDictionary
        to
    )
)

For comparability, this is able to be the equal implementation in C#:

public static ObjectDictionary ParseRss(string xml)
{
    var to = new ObjectDictionary();
    var objects = new Record<ObjectDictionary>();

    var doc = XDocument.Parse(xml);
    var channel = doc.Descendants("channel").First();
    var el = channel.FirstElement();
    whereas (el.Title != "merchandise")
    {
        to[el.Name.LocalName] = el.Worth;
        el = el.NextElement();
    }

    var elItems = channel.Descendants("merchandise");
    foreach (var elItem in elItems)
    {
        var merchandise = new ObjectDictionary();
        el = elItem.FirstElement();
        whereas (el != null)
        {
            merchandise[el.Name.LocalName] = el.Worth;
            el = el.NextElement();
        }

        objects.Add(merchandise);
    }

    to["items"] = objects;
    return to;
}

Importing World Scripts

Importing scripts in Lisp is actually a 2-stage strategy of parsing Lisp supply code into an SExpression,
(mainly Lisp’s AST of tokenized components captured in a 2-field Cons Cell) then evaluating it in a
Lisp interpreter the place any outlined symbols are captured in its Symbols desk.

Lisp Script captures its “normal library” in a World Interpreter which serves because the beginning template for all different Lisp Interpreters
which begins with a replica of the World symbols desk which you’ll be able to additional populate with your personal frequent capabilities utilizing Lisp.Import(), e.g:

Lisp.Import(@"
(defun fib (n)
    (if (< n 2)
        1
        (+ (fib (- n 1))
        (fib (- n 2)) )))");

Loading Scripts

Loading scripts inside a Lisp script works equally besides they’re solely loaded into that Lisp interpreters image desk, a brand new one
of which is created in every new PageResult.

Scripts loaded regionally are loaded from the ScriptContext configured Virtual Files Provider
which for #Script Pages SharpPagesFeature is configured to make use of the App’s cascading digital file sources.

See Also

A brand new ScriptContext begins with an empty MemoryVirtualFiles
which you’ll be able to write recordsdata to with:

var context = new ScriptContext {
    ScriptLanguages = { ScriptLisp.Language },
    ScriptMethods = { new ProtectedScripts() },
};
context.VirtualFiles.WriteFile("lib1.l", "(defn lib-calc [a b] (+ a b))");
context.VirtualFiles.WriteFile("/dir/lib2.l", "(defn lib-calc [a b] (* a b))");
context.Init();

You’ll be able to load these scripts by image title the place it assumes a .l extension, by quoting the argument so
Lisp would not attempt to consider it as an argument, e.g:

(load 'lib1)

(lib-calc 4 5) ;= 9

Alternatively you possibly can specify the digital path to the script. You’ll be able to load a number of scripts with the identical definitions,
in Lisp this simply updates the worth assigned to the image title with the newest definition, e.g:

(load "lib1.l")

(lib-calc 4 5) ;= 9

(load "/dir/lib2.l")

(lib-calc 4 5) ;= 20

Import Scripts from URLs

Impressed by Deno you too can import distant scripts from URLs, e.g:

(load "https://instance.org/lib.l")

Domestically Cached

Like Deno all distant sources are cached after first use so after it is loaded as soon as it solely masses the regionally cached
model of the script (the place it’s going to nonetheless work in an airplane with out an web connection). This cache is maintained
underneath a .lisp folder at your configured Digital Recordsdata supplier (that may be deleted to clear any caches).

For Sharp Scripts or Apps utilizing the net or app dotnet instruments it is saved in its personal cache folder that may be cleared with:

$ x --clean

Import Scripts from Gists

There’s additionally first-class help for gists which you’ll be able to reference with gist:<gist-id>, e.g:

(load "gist:2f14d629ba1852ee55865607f1fa2c3e")

This can load all gist recordsdata in gist order, should you solely to load a single file from a gist you possibly can specify it with:

(load "gist:2f14d629ba1852ee55865607f1fa2c3e/lib1.l")

Script Lisp Library Index

To supply human readable names to distant Lisp Scripts and a discoverable catalog the place anybody can share their very own scripts,
you reference gists by title listed within the #Script Lisp Library Index
which is itself a self-documenting machine and human readable gist of named hyperlinks to exterior gists maintained by their
respective authors.

Index library references may be loaded utilizing the format index:<title>, e.g:

Which additionally help with the ability to reference particular person gist recordsdata:

(load "index:lib-calc/lib1.l")

If you would like to share your personal Lisp Scripts with everybody and publish your library to the index, simply add a hyperlink
to your gist along with your most popular title within the Gist Index Comments.

Viewing Script Supply Code

You’ll be able to view the supply code of any load script references with load-src, e.g:

(load-src 'lib)
(load-src "/dir/lib2.l")
(load-src "https://instance.org/lib.l")
(load-src "gist:2f14d629ba1852ee55865607f1fa2c3e/lib1.l")
(load-src "index:lib-calc")

Disable Distant Imports

Do you have to want, you possibly can forestall anybody from loading distant scripts with:

Lisp.AllowLoadingRemoteScripts = false;

#Script Pages Integration

While Lisp is a small, powerfully expressive useful dynamic language it isn’t nice to be used as a templating language.
While there have been several attempts to create a HTML DSL in Lisp, nothing is healthier
than having no syntax which is the default Template mode for #Script the place it’s going to emit all the pieces that is not in a
Template or Language Expression as verbatim textual content.

A pleasant USP of Script Lisp is that you just’re by no means compelled into going “full Lisp”, you possibly can make the most of #Script template expressions and
Script Blocks handlebars syntax that gives the best DSL for utilization in a Template Language for producing HTML
and make the most of your most popular Lisp or Code Script Languages for any computational logic you need included in your web page
utilizing Language Blocks and Expressions.

Implementation

Regardless of being carried out in numerous languages a #Script web page containing a number of languages, e.g:

Nonetheless solely produces a single web page AST, the place when first loaded #Script parses the web page contents as a contiguous
ReadOnlyMemory<char> the place web page slices of any Language Blocks and Expressions
on the web page are delegated to the ScriptContext registered ScriptLanguages for parsing which returns a fraction which is
added to the pages AST:

When executing the web page, every language is liable for rendering its personal fragments which all write on to the pages OutputStream
to generate the pages output.

The multi-languages help in #Script is designed to be extensible the place all the pieces concerning the language is encapsulated inside its
ScriptLanguage implementation in order that should you omit its registration:

var context = new ScriptContext {
//    ScriptLanguages = { ScriptLisp.Language }
}.Init();

Any language expressions and language blocks referencing it turn out to be inert and its supply code emitted as plain-text.

Lisp Argument Scopes

One differentiator between Lisp and Code languages is that code makes use of the containing web page present scope
for all its argument references the place as Lisp shops all its definitions inside the Lisp interpreter image desk
hooked up to the PageResult, so while Lisp scripts can entry arguments inside the pages scope, to ensure that the
outer web page to entry any Lisp symbols they should be exported, e.g:

Exporting Lisp Capabilities

Lisp capabilities can be exported for utilization in the remainder of the web page by calling (to-delegate lispFn) to transform it
right into a .NET Delegate, e.g:

Though a better solution to outline capabilities in Lisp is to make use of the defn Script Block which wraps
this in a handy syntax:

Controlling Lisp output

One in all Lisp’s well-known traits is all the pieces is an expression which is often desired inside a language, however might not
be what you need when producing output. E.g historically Lisp makes use of setq
to set a variable which additionally returns its worth that #Script will emit because it robotically emits all assertion return values.

You can use def which is an alias for setq which returns null, different choices embrace wrapping all statements inside an empty let
expression the place solely the final expression is returned, or you would use a Language Block Modifier
to disregard all the lisp block output and solely export the outcome you need to have the ability to management exactly the place to emit it:

can use both ‘q’, ‘quiet’ or ‘mute’ block modifier to disregard output

One other solution to generate output from Lisp is to make use of its built-in print capabilities under:

  • (print …args) – write all arguments to the OutputStream
  • (println …args) – write all arguments to the OutputStream adopted by a new line
  • (printlns …args) – write all arguments to the OutputStream with a ' ' area delimiter adopted by a new line
  • (pr …args) – identical as print however HTML encode all arguments
  • (prn …args) – identical as println however HTML encode all arguments

dorun

dorun can be utilized for executing a “assertion block” (sequence of expressions)
with side-effects that you just need to discard the return kind (akin to void return kind):

(dorun println (map fizzbuzz (vary 1 100)))

Or you should utilize do to return the final expression within the assertion block:

(do (+ 1 1) (+ 2 2))       ; 4
(do (+ 1 1) (+ 2 2) nil))  ; null
(do ())                    ; null

Study #Script Lisp

An ideal useful resource for studying Script Lisp is seeing it in motion by seeing the way to implement C#’s 101 LINQ Examples in Lisp:

Discover APIs in real-time

We are able to additionally make the most of Lisp’s dynamism and interactivity to discover APIs in real-time, an effective way to do that is through
a watched Lisp script on the facet the place it offers instantaneous suggestions after every Ctrl+S save level
or a energetic Lisp REPL.

  • symbols – Record all symbols in Lisp interpreter – most symbols are named after standard Lisp
    or clojure functions
  • (symbol-type image) – Show the Image’s Worth Kind
  • scriptMethods – Record all out there Script Technique Names registered in ScriptContext
  • scriptMethodTypes – Record all out there Script Technique Kind info
  • (joinln assortment) – Show the string output of every merchandise within the assortment on a separate line
  • (globln sample assortment) – Solely show strains matching the glob sample
  • (typeName occasion) – View the occasion Kind Title
  • (props occasion) – Show the Property names of an Occasion public properties
  • (fields occasion) – Show the Discipline names of an Occasion public fields
  • (strategies occasion) – Show the Technique names of an Occasion public strategies
  • (propTypes occasion) – Get the PropertyInfo[] of an Occasion public properties
  • (fieldTypes occasion) – Get the FieldInfo[] of an Occasion public fields
  • (methodTypes occasion) – Get the Script Technique Infos of an Occasion public strategies
  • (staticProps occasion) – Show the Property names of an Occasion public static properties
  • (staticFields occasion) – Show the Discipline names of an Occasion public static fields
  • (staticMethods occasion) – Show the Technique names of an Occasion public static strategies
  • (staticPropTypes occasion) – Get the PropertyInfo[] of an Occasion public static properties
  • (staticFieldTypes occasion) – Get the FieldInfo[] of an Occasion public static fields
  • (staticMethodTypes occasion) – Get the Script Technique Infos of an Occasion public static strategies

You’ll be able to view the Scripts API Reference and Scripts Documentation
on this web site to interactively discover the out there APIs, we’ll work on offering additional interactive documentation
for the built-in Lisp capabilities, within the mean-time the very best useful resource are
their implementation.

For reference, this is are a fast checklist of all built-in Lisp symbols:

- % * *gensym-counter* *model* / /= _append _nreverse + < <= = > >= 1- 1+ 1st 2nd third abs all? and any? 
append apply assoc assoc-key assoc-value assq atom atom? common butlast C caaar caadr caar cadar caddr 
cadr automobile cdaar cdadr cdar cddar cdddr cddr cdr ceiling cons cons? consp cos rely debug dec decf def 
defmacro defn defun dispose do dolist dorun doseq doseq-while dotimes dump dump-inline elt empty? finish?
endp enumerator enumeratorCurrent enumeratorNext eq eql equal error even? each each? exit exp expt F f++
false filter filter-index first flatmap flatten flooring gensym glob globln group-by htmldump identification if 
inc incf occasion? intern isqrt it final size let letrec checklist checklist? listp load load-src logand logior 
logxor lower-case make-symbol map mapc mapcan mapcar map-index map-where max member memq min mod nbutlast 
nconc new new-map subsequent not not= nreverse nth nthcdr null quantity? odd? or order-by pop pr prin1 princ 
print println printlns prn prs push push-end random vary cut back take away remove-if relaxation return reverse 
spherical rplaca rplacd second seq? setcar setcdr set-difference units sin skip skip-while some some? type 
sort-by sqrt str string string? string-downcase string-upcase subseq sum symbol-name symbol-type t take 
take-while tan terpri textdump third to-array to-cons to-delegate to-dictionary to-list true truncate 
union until upper-case when the place where-index whereas zero? zerop zip zip-where

Frequent Lisp by conference makes use of a *p suffix for predicate capabilities however we choose Clojure’s (and Ruby’s)
extra readable *? suffix conference, for source-code compatibility we embrace each for
core Lisp predicts and simply *? for others.

Source Link

What's Your Reaction?
Excited
0
Happy
0
In Love
0
Not Sure
0
Silly
0
View Comments (0)

Leave a Reply

Your email address will not be published.

2022 Blinking Robots.
WordPress by Doejo

Scroll To Top