in

#Script Lisp, Hacker News


        

                         

# Scriptis designed as a small, expressive and wrist -friendly dynamic scripting language that for maximum familiarity is modelled after the world’s most popular and ubiquitous scripting Language, JavaScript. Its minimal syntax was inspired by other small but powerful languages ​​which heavily utilizes functions instead of adopting a larger language grammar defining different bespoke syntax for language constructs.

Small Languages ​​like Smalltalk, despite being one of the most influential languages ​​in history, is famous for its minimalsyntax that fits on a post card. A language with arguably better power to size ratio isLispwhich the inventor of Smalltalk, Alan Kay has credited it as being thegreatest single programming language ever designedAfter realizing:

“the half page of code on the bottom of page 13… was Lisp in itself. These were“Maxwell’s Equations of Software!”

Lisp’s unprecedented elegance and simplicityspawned amyriad of dialects, some noteworthy implementations illustrating the beauty of its small size and expressive power islispyby byPeter Norvig(Director of Google Research) that implements a Lisp interpreter in just117 lines of Python 3 code(inc. a REPL).

Another compact dialect isZick Standard Lispwhich@ zickhas implemented in42 different languages ​​ (including a) recursive Lisp evaluator in Lispimplemented in only66 lines of code

A more complete Lisp implementation in C # is the elegantNukata LispbySUZUKI Hisaowhich is a Common Lisp-likeLisp-1dialect with tail call optimization and partially hygienic macros, although has some notable limitations including asmall standard library, only uses thedoublenumeric type and doesn’t contain .NET Scripting support.

Script Lisp Overview

ScriptLispis an enhanced version ofNukata Lispwith a number of new features that reuses# Scriptexisting scripting capabilities to provide seamless integration with both the rest of# Script(seeLanguage Blocks an Expressions) and .NET includingScripting of .NET Types, support for all .NET numeric types and access to its comprehensive library of over1000 Script Methods– optimally designed for accessing .NET functionality from a dynamic language.

To improve compatibility with existing Common Lisp source code it also implements most of theSimplified Common Lisp Referenceas well as all missing functions required to implement C # LINQ 101 Examples in Lisp:

To improve readability and familiarity it also adopts a number ofClojuresyntax for defining adata list and mapliterals,anonymous functions, syntax in (Java Interop) for .NET Interop,keyword syntax for indexing collectionsand accessing index accessors and Clojure’s popular shorter aliases forfn,def,defn– improving source-code compatibility with Clojure .

Lisp REPL

In addition to being a 1st class language option in# Script, Lisp’s dynamism and extensibility makes it particularly well suited for explanatory programming whose access via a REPL is now built into the latestwebandappdotnet tools which can be quickly installed in any Windows, macOS or Linux OS (with.NET Core) with:

$ dotnet tool install -g web

Or if you have a previous version installed, update to the latest version with:

$ dotnet tool update -g web

Where you’ll then be able to bring up an instant Lisp REPL with:

$ web lisp

The quick demo below shows the kind of exploratory programming available where you can query thescriptMethodsavailable, query an objectsprops, query the Lisp interpreter’s globalsymbolstable containing all its global state including all defined lisp functions, macros and variables:

YouTube:youtu.be/RR7yk6ReNnQ

Annotated REPL Walk through

Here’s an annotated version of the demo below which explains what each of the different expressions is doing.

Just likeSharp Scriptsand (Sharp Apps) the Lisp REPL runs within the# Script PagesScriptContextsandboxthat when run from a Sharp App folder, starts a .NET Core App Server that simulates a fully configured .NET Core App. In this case it’s running in theredis Sharp Appdirectory where it was able to access its static web assets as well as its redis-server connection configured in itsapp.settings.

;quick lisp test!(   (1)   (2)   (3) );List of ScriptMethodInfo that the ScriptContext running this Lisp Interpreter has access toscriptMethods;first script method(:  (0)  scriptMethods);show public properties of ScriptMethodInfo(props):  (0)  scriptMethods));show 1 property per line(joinln (props):  (0)  scriptMethods)));show both Property Type and Name(joinln (propTypes):  (0)  scriptMethods)));view the Names of all avaialble script methods(joinln (map .Name scriptMethods));view all script methods starting with 'a'(globln"A *"(map .Name scriptMethods));view all script methods starting with 'env'(globln"env *"(map .Name scriptMethods));print environment info about this machine seperated by spaces(printlns envOSVersion envMachineName envFrameworkDescription envLogicalDrives);expand logical drives(printlns envOSVersion envMachineName envFrameworkDescription""- drives:"(join envLogicalDrives""));view all current global symbols defined in this Lisp interpretersymbols;view all symbols starting with 'c'(globln"C *"symbols);see how many symbols are defined in this interpreter(count symbols);see how many script methods there are available(count scriptMethods);view the method signature for all script methods starting with 'all ''(globln"all *"(map .Signature scriptMethods));count all files accessible from the configured ScriptContext(count allFiles);view the public properties of the first IVirtualFile(props):  (0)  allFiles));display the VirtualPath of all available files(joinln (map .VirtualPath allFiles));display the method signature for all script methods starting with 'findFiles ''(globln"findFiles *"(map .Signature scriptMethods));see how many .html files are available to this App(count) findFiles"* .html"));see how many .js files are available to this App(count) findFiles"*. js"));show the VirtualPath of all .html filesjoinln (map .VirtualPath (findFiles" (* .html)  ")));view the VirtualPath's of the 1st and 2nd .html files(:  (0)  (map. VirtualPath (findFiles"* .html"))) (:  (1)  (map. VirtualPath (findFiles"* .html")));view the text file contents of the 1st and 2nd .html files(fileTextContents (:  (0)  (map. VirtualPath (findFiles"* .html")))) (fileTextContents (:  (1)  (map. VirtualPath (findFiles"* .html"))));display the method signatures of all script methods starting with 'redis ''(globln"Redis *"(map .Signature scriptMethods));search for all Redis Keys starting with 'urn:' in the redis-server instances this App is configured withredisSearchKeys"urn: *");display the first redis search entry(:  (0)  (redisSearchKeys"urn: *"));display the key names of all redis keys starting with 'urn : '(joinln (map: id) redisSearchKeys" (urn:  ************************************************************) ")));find out the redis-server data type of the 'urn : tags' keyredisCall"TYPE urn: tags");view all tags in the 'urn: tags' sorted setredisCall" (ZRANGE urn: tags 0 -1)  ");view the string contents of the 'urn: question: 1 'keyredisCall"GET urn: question: 1");parse the json contents of question 1 and display its tag names() (ParseJson) redisCall""GET urn: question: 1"")));extract the 2nd tag of question 1(:  (1)  (: Tags) parseJson (redisCall"GET urn: question: 1"))));clear the Console screenclear;exit the Lisp REPLquit

Enable features and access resources with app.settings

You can configure the Lisp REPL with any of the resources and features thatSharp AppsandGist Desktop Appshave access to, by creating a plain textapp.settingsfile with all the features and resources you want the Lisp REPL to have access to, e.g. thisPure Cloud App app.settingsallows the Lisp REPL to useDatabase Scriptsagainst a AWS PostgreSQL RDS server and query remoteS3 Virtual FilesusingVirtual File System APIs:

# Note: values ​​prefixed with '$' are resolved from Environment Variables name AWS S3 PostgreSQL Web App db postgres db.connection $ AWS_RDS_POSTGRES files s3 files.config {AccessKey: $ AWS_S3_ACCESS_KEY, SecretKey: $ AWS_S3_SECRET_KEY, Region: us-east-1, Bucket: rockwind}

See theplugins app.settingsfor examples of how to load and configureServiceStack Plugins.

Lisp REPL TCP Server

In addition to launching a Lisp REPL from the Console above, you can also open a Lisp REPL into any ServiceStack App configured with theLispReplTcpServerServiceStack plugin. This effectively opens a“programmable gateway”into any ServiceStack App where it’s able to perform live queries, access IOC dependencies, invoke internal Server functions and query the state of a running Server which like theDebug Inspectorcan provide invaluable insight when diagnosing issues on a remote server.

To see it in action we’ll enable it one of our production Appstechstacks.iowhich as it’s a Vuetify SPA App is only configured with an emptySharpPagesFeatureas it doesn’t use any server-side scripting features.

We’ll enable it inDebugModewhere we can enable by settingDebugModein our App’sappsettings.Production. jsonwhich will launch a TCP Socket Server which by default is configured to listen to theloopbackIP on port5005.

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

ScriptNamespacesbehaves like C # ‘susing Namespace;statement letting you reference Types byNameinstead of its fully-qualified Namespace.

Whilst you can now connect to it with basictelnet, It’s a much nicer experience to use it with therlwrapreadline wrap utility which provides an enhanced experience with line editing, persistent history and completion.

$ sudo apt-get install rlwrap

Then you can open a TCP Connection to connect to a new Lisp REPL with:

$ rlwrap telnet localhost 5005

Where you now have full scriptability of the running server as allowed by# Script PagesSharpPagesFeaturewhich allowsscripting of all .NET Typesby default.

TechStacks TCP Lisp REPL Demo

In this demo we’ll explore some of the possibilities of scripting the livetechstacks.ioServer where we canresolveIOC dependencies to send out tweets using its registeredITwitterUpdatesdependency, view the source and load a remoteparse-rsslisp function into the new Lisp interpreter attached to the TCP connection, use it to parseHacker News RSS Feedinto a .NET Collection where it can be more easily queried using its built-in functions which is used to construct an email body withHN’s current Top 5 links.

It then usesDB Scriptsto explore its configured AWS RDS PostgreSQL RDBMS, listing its DB tables and viewing its column names and definitions before retrieving the Email addresses of allAdminusers, sending them each an email with HN’s Top 5 Links by publishing5xSendEmailRequest DTOs using thepublishMessageServiceStack Script to where they’re processed in the background by its configured (MQ Server) that uses it to execute theSendEmailServiceStack Service where it uses its configured AWS SES SMTP Server to finally send out the Emails:

YouTube:youtu.be / HO 523 cFkDfk

Password Protection

Since TCP Server effectively opens your remote Server up to being scripted you’ll want to ensure the TCP Server is only accessible within a trusted network, effectively treating it the same as (Redis Security Model) .

A secure approach would be to leave the default of only binding toIPAddress.Loopbackso only trusted users with SSH access will be able to access it, which they’ll still be able to access remotely viaLocal PC>ssh>telnet .0.1 5005.

Just likeRedis AUTHyou can also add password protection for an additional layer of Security:

Plugins. ************************************************************ (Add)  (newLispReplTcpServer{     RequireAuthSecret=true,     ... });

Which will only allow access to users with theconfigured AuthSecret:

(SetConfig)  (newHostConfig{     AdminAuthSecret="Secretz"});

Annotated Lisp TCP REPL Transcript

;resolve `ITwitterUpdates` IOC dependency and assign it to` twitter`def twitter (resolve""ITwitterUpdates"));view its concrete Type Name(typeName twitter);view its method names(joinln (methods twitter));view its method signatures(joinln (methodTypes twitter));use it to send tweet from its @webstacks accountTwitter twitter"Who's using #Script Lisp? Https: //sharpscript.net/lisp"");view all available scripts in #Script Lisp Library Index gist. github.com/ (B)  cfb2fc7bb3c2cb9dc1a3(gistindex);view the source code of the `parse-rss` libraryload-src"index: parse-rss");assign the XML contents of HN's RSS feed to `xml`def xml (urlContents""https: //news.ycombinator .com / rss"")));preview its first 1000 charssubString xml  (0)1000);use `parse-rss` to parse the RSS feed into a .NET Collection and assign it to `rss`(def rss (parse-rss xml));view the `title`,` description` and the first `item` in the RSS feed:(: title rss) (: description rss) (:  (0)  (: items rss));view the links of all RSS feed items(joinln (map: link (: items rss)));view the links and titles of the top 5 news items(joinln (map: link (take  (5)  (: items rss)))) (joinln (map: title (take  (5)  (: items rss))));construct a plain-text numbered list of the top 5 HN Links and assign it to `body`(joinln (map-index # (str%  (2)  (: title%  (1) )) (take  (5)  (: items rss )))) (joinln (map-index #) str (padLeft ( (1) *************************************************************  %  (2) )  (2) )""."(: title%  1) )) (take5)  (: items rss)))) def body (joinln     (map-index # (str) padLeft ( (1)   %2)  (2) )""."(: title%  (1)  )" n"(: link%1)" n") (take  (5)  (: items rss))))));view all TechStacks PostgreSQL AWS RDS tables(dbTableNames) (joinln dbTableNames);view the column names and definitions of the `technology` tablejoinln (dbColumnNames"Technology")) joinln (dbColumns"Technology"));search for all `user` tables(globln"* user *"(dbTableNames));view how many Admin Users with Emails there aredbScalar"select count (email) from custom_user_auth where roles like '% Admin%'"");  (assign the Admin Users email to the `emails` list)  (def emails (map: email) dbSelect"select email from custom_user_auth where roles like '% Admin%'"")) );search for all `operation` script methods(globln"* operation *"scriptMethods);search for all `email` Request DTOs(globln"* Email *"metaAllOperationNames);view the properties available on the `SendEmail` Request DTO(props (SendEmail.));search for all `publish` script methods that can 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: Subject""Top 5 HN Links": Body body}))

Run and watch Lisp Scripts

The sameSharp Scriptsfunctionality for# Scriptis also available to Lisp scripts where you can use thewebandappdotnet tools torunandwatchstand-alone Lisp scripts with the. lfile extension, eg:

$ web run lisp.l $ web watch lisp.l

To clarify the behavioral differences between the Lisp REPL’s above which uses thesame Lisp interpreterto maintain state changes across each command, thewatchScript is run with anew Lisp Interpreterwhich starts with a (fresh) copy of the Global symbols table so any state changes after eachCtrl Ssave point is discarded.

Watchlispscripts

This quick demo illustrates the same functionality inSharp Scriptsis also available inlispscripts where it provides instant feedback whilst you develop in real-time:

YouTube:youtu.be/rIgEP8ssikY

Annotated Lisp watch script

;;DB sqlite;db.connection northwind.sqlite;files s3;files.config {AccessKey: $ AWS_S3_ACCESS_KEY, SecretKey: $ AWS_S3_SECRET_KEY, Region: us-east-1, Bucket: rockwind};->;delete remove.txt filesh (str ( (if)  isWin" (del)  "" (RM)  ")''remove.txt"));View all `northwind.sqlite` RDBMS Tables(textDump (dbTableNames) {: caption" (Northwind)  "});Display first `customer` row in Single Row View showing all Table ColumnstextDump (dbSelect"select * from customer limit 1")));Display all Customers in Londondef city" (London)  ") textDump (dbSelect"select Id, CompanyName, ContactName from customer where city=@city""{: city city}) );View all root files and folders in configured S3 Virtual File Provider(joinln (map # (str (.Name%)"/"") (allRootDirectories vfsContent))) (joinln (map .Name (allRootFiles vfsContent)));Show first 10 * .png files in S3 VFS Provider(def pattern (or(first ARGV)"* .png")) joinln (map .VirtualPath (take10(findFiles vfsContent pattern)))))

(Page Arguments)

You can also use the same syntax for declaring anyapp.settingspage arguments used in# ScriptandcodeScripts:

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

(Executing Lisp in .NET)

Lisp like all# Scriptlanguages ​​are executed within aScriptContextthat defines all functionality available to them, ie:

varcontext=newScriptContext{     Args={...},//Global Arguments available to all Scripts, Pages, Partials, etc    ScriptMethods={...},//  (Additional Methods)      ScriptBlocks={...},//Additional Script Blocks    FilterTransformers={..},//  (Additional Stream Transformers)      PageFormats={...},//Additional Text Document Formats    Plugins={...},//Encapsulated Features eg Markdown, Protected or ServiceStack Features    ScanTypes={...},//Auto register Methods , Blocks and Code Page Types    ScanAssemblies={...},//Auto register all Methods, Blocks and Code Page Types in Assembly}.Init();

    Where you can customize the puresandboxedScriptContextyour Script is executed within by extending it with:

To renderlispYou’ll first need to register the Lisp Language with theScriptContextyou’re using:

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

Then useRenderLisp(ie instead ofRenderScript) to render Lisp code, eg:

//render lispvaroutput=context.  (RenderLisp)  ("(dateFormat now "HH: mm: ss ")");//Asyncvaroutput=awaitcontext.  (RenderLispAsync)  ("(dateFormat now "HH: mm: ss ")"");

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

(var)output=(context) .RenderScript(""{{now |>dateFormat ('HH: mm: ss')}}");varoutput=awaitcontext.  (RenderScriptAsync)  ("{{now |>dateFor mat ('HH: mm: ss')}}");

Finer grained control

The high-level APIs above wraps the finer-grained functionality below which works by rendering aSharpPageconfigured with thelisplanguage in aPageResultthat all languages ​​use:

(var)context=(new)ScriptContext{     ScriptLanguages ​​={ScriptLisp.Language} }.Init();vardynamicPage=context.  (LispSharpPage)  ("(dateFormat now "HH: m M: SS ")");//render lisp//var dynamicPage=context.SharpScriptPage ("{{now |>dateFormat ('HH: mm: ss')}} "); // render #Scriptvaroutput=newPageResult( (dynamicPage) ).  (RenderScript)  ();//Asyncvaroutput=await(new)   PageResult  ( (dynamicPage) ).RenderScriptAsyn C();

Evaluating Lisp Script Results

If you instead wanted to access return values ​​instead of its rendered output, use theEvaluateLisp ()APIs:

varresult=context.EvaluateLisp("(return (  1 1))");//=2

The generic overloads below utilizes ServiceStack’sAuto Mapping utilsto convert the return value into your preferred type, e.g:

(double)result=(context) .EvaluateLispdouble>(''(return (  1 1 ))");//=2.0stringresult=context.  (EvaluateLisp) string>("(return (  1 1))");//="2"

Which can also be used for more powerful conversions like converting an Object Dictionary into your preferred POCO:

(var)result=(context) .EvaluateLispCustomer>()     "(return (dbSingle "select * from customer where id=@ id "{: id id}))",     newObjectDictionary{         ["id"]=(1)      });

.NET Interop

The syntax for .NET Interop is inspired directly from Clojure’s syntax used forJava Interop. SeeScripting .NET Type Resolutionfor how to configure Types and imported Namespaces you want your Lisp scripts to have access to.

Member Access

The'.'prefix if for accessing an instance members which can be used for retrieving a properties public properties, fields and invoking instance methods, e.g:

  • (. Property instance)
  • (Field instance)
  • (Method instance … args)

Indexer Access

Use':'prefix for accessing a Types indexer or for indexing collections, eg:

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

It can also be used to access an instance public Properties and Fields:

  • (: Property instance)
  • (Field instance)

However for readability we recommend using'.'prefix above to convey instance member access.

Constructor Access

Use'.'suffixfor creating instances of Types:

  • (Type. … args)
  • (Namespace.Type. … args)

You can also create instances using thenew script method, which as it accepts astringType Name can be used to create generic classes with multiple generic args, eg:

  • (new “Type” … args)
  • (new “Type“… args)

Static Member Access

Use the'/'separator to access a Type’s static members or to invoke its static methods, eg:

  • (StaticType / Property)
  • (StaticType / Field)
  • (StaticType / Const)
  • (StaticType / Method … args)

Use'.'dot notation for specifying the fully-qualified T ype name or to reference its Inner classes, e.g:

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

Script Methods

Use'/'prefix to reference any Script Method registered in yourScriptContext:

  • (/ scriptMethod … args)

Script Methods without arguments can be referenced as anargument bindingthat when referenced as an argument (i.e. without brackets) are implicitly evaluated, in-effect making them a calculated property:

  • / methodAsBinding

While a'/'prefix indicates a reference to ascript method, for readability it can be excluded as when there’s no existing symbol defined in the Lisp interpreter’s symbol table it will fallback to referencing a script method:

  • (scriptMethod … args)
  • methodAsBinding

This does mean that when there exists asymbolof the same name defined you will need to use the'/'prefix to reference a script method.

Generic Types

All references above support referencing generic types and methods with a single generic Type argument, e.g:

  • (StaticType / Method)
  • GenericStaticType/ Member)
  • GenericStaticType/ Method)
  • GenericType.)

As','is one of Lisp’s few syntax tokens (unquoting) it prevents them from being able to use them to specify multiple generic arguments.

Instead you’ll need to use theConstructor functionfor referencing constructors with multiple generic arguments where you’ll also need to specify the types of the exact constructor you want to call, e.g:

  • (/ C “Tuple(String, int) “)

The difference between the/ Cscript method constructor function and Lisp’sCfunction is that the script method only returns a reference to the constructor which you’ll need to invoke with arguments to create an instance:

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

Whilst Lisp’sCfunction will auto- invoke the constructor function with the supplied arguments in a single expression:

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

Likewise when needing to invoke generic methods with multiple generic args you’ll need to use (Function) :

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

Or Script Lisp’sFfunction for invoking a function reference in a single expression:

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

For more examples and information seeScripting .NET Types.

Property Setters

You can populate multiple properties on an instance using theset script method, eg:

  • (set instance {: Prop arg …})

Alternatively properties can be set individually with:

  • (.Prop instance arg)

Lisp Lists vs .NET Collections

A potential source of friction when interoperating with .NET is that Lisp Lists are (Cons Cells) so that a code or data list in Lisp, i.e:

'(1 2 3) [1 2 3]

Is implemented as a Linked List of Cons cells:

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

Which is what Lisp’s core functions expect to operate on, namely:

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

These core Lisp functions can’t be used against .NET collections directly, instead you can use(to-cons collection)to convert a .NETIEnumerablecollection into a cons list, eg:

(cdr (to-cons netEnumerable))

Should you need to do the inverse you can use(to-list cons-list)to convert a cons list to a .NET List, eg:

(to-list (range 10))

We’ve made Script Lisp’s consCellanIEnumerableso thatall other built-in Lisp functionscan operate on both cons cells and .NET Collections where instead of iterating a list with(do-list)you can use(do-seq)to iterate both .NET Collections and cons cells, e.g:

(do-seq (x collection) (println x))

Annotated .NET Interop Example

To see what this looks like in action here’s an annotated simple real-world example that heavily utilizes .NET interop:

;define function and assign to `parse-rss` value in Lisp interpreters symbols table(defn parse-rss [xml]     ;define local variables used within this scope    (let((to) (doc) (channel) (items) (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) and assign to `to`        (def to (ObjectDictionary.))         ;  (create empty List of ObjectDictionary and assign to `items`)          (def items (List. ))         ;descend into firstXML element and assign to `channel`        (def channel (first) .Descendants doc" (channel)  ")))         ;use `XLinqExtensions.FirstElement ()` extension method to assign channels first XML element to `el`        (def el (XLinqExtensions / FirstElement channel))          ;iterate through all elements up to the firstand add them as top-level entries in `to`        (while ( (not)=(.LocalName (.Name el))""item")             ;add current XML element name and value entry to `to`            (.Add to (.LocalName (.Name el)) (.Value el))             ;move to next element using `XLinqExtensions.NextElement ()` extension method            (def el (XLinqExtensions / NextElement el)))          ;add all rss's to `items` list        ;iterate through all `channel` child  (XML elements)          (doseq (elItem) .Descendants channel"item"))             ;create empty ObjectDictionary and assign to `item`            (def item (ObjectDictionary.))                          ;use `XLinqExtensions.FirstElement ()` to assignfirst XML element to `el`            (def el (XLinqExtensions / FirstElement elItem))             (while el                 ;add current XML element name and value entry to `item`                (.Add item (.LocalName (.Name el)) (.Value el))                 ;move to next element using `XLinqExtensions.NextElement ()` extension method                (def el (XLinqExtensions / NextElement el)))              ;add `item` ObjectDictionary to` items` List            (.Add items item))          ;add `items` ObjectDictionary List to` to` at key `items`        Add to"items"items)         ;return `to` ObjectDictionary        to     ) )

For comparison, this would be the equivalent implementation in C #:

(public)staticObjectDictionary(ParseRss)  (stringxml) {     varto=newObjectDictionary();     varitems=newList();      vardoc=XDocument.  (Parse)  (xml);     varchannel=doc.Descendants(" (channel)  ").  (First)  ();     varEL=channel.  (FirstElement)  ();     while(EL.Name(************************************************************!="item")     {         to[el.Name.LocalName]=(el) .  (Value) ;         EL=EL.  (NextElement)  ();     }      varelItems=channel.Descendants("item");     foreach( VARelIteminelItems)     {         varitem=newObjectDictionary();         EL=elItem.  FirstElement  ();         while(EL!=null)         {             item[el.Name.LocalName]=(el) .  (Value) ;             EL=EL.  (NextElement)  ();         }          items.  Add(item);     }      to["items"]=items;     returnto; }

Importing Global Scripts

Importing scripts in Lisp is essentially a 2-stage process of parsing Lisp source code into anSExpression, (basically Lisp’s AST of tokenized elements captured in a2-field Cons Cell) then evaluating it in a Lisp interpreter where any defined symbols are captured in its Symbols table.

Lisp Script captures its “standard library” in a Global Interpreter which serves as the starting template for all other Lisp Interpreters which starts with a copy of the Global symbols table which you can further populate with your own common functions usingLisp.Import (), eg:

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

Loading Scripts

Loading scripts within a Lisp script works similarly except they’re only loaded into that Lisp interpreters symbol table, a new one of which is created in each newPageResult.

Scripts loaded locally are loaded from theScriptContextconfiguredVirtual Files Providerwhich for# Script PagesSharpPagesFeatureis configured to use the App’s cascading virtual file sources.

A newScriptContextstarts with an emptyMemoryVirtualFileswhich you can write files to with:

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

You can load these scripts by symbol name where it assumes a. Lextension, by quoting the argument so Lisp doesn’t try to evaluate it as an argument, e.g:

(load'lib1)  lib-calc  (4)   5) );  (=9)

Alternatively you can specify the virtual path to the script. You can load multiple scripts with the same definitions, in Lisp this just updates the value assigned to the symbol name with the latest definition, e.g:

(load"lib1.l")  lib-calc  (4)   5) );  (=9)   ( (load)"/ dir / lib2.l")  lib-calc  (4)   5) );=20

Import Scripts from URLs

Inspired by Denoyou can also import remote scripts from URLs, eg:

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

Locally Cached

LikeDenoall remote resources are cached after first use so after it’s loaded once it only loads the locally cached version of the script (where it will still work in an airplane without an internet connection). This cache is maintained under a.lispfolder at your configured Virtual Files provider (that can be deleted to clear any caches).

For Sharp Scripts or Apps using theweborappdotnet tools it’s stored in its own cache folder that can be cleared with:

$ web --clean

Import Scripts from Gists

There’s also first-class support for gists which you can reference withgist:, eg:

(load"gist: 2f  (d) ************************************************************************************************************************************************************************************ (BA)  ee 55865607 f1fa2c3e"")

This will load all gist files in gist order, if you only to load a single file from a gist you can specify it with:

(load"gist: 2f  (d) ************************************************************************************************************************************************************************************ (BA)  ee 55865607 f1fa2c3e / lib1.l"")

Script Lisp Library Index

To provide human readable names to remote Lisp Scripts and a discoverable catalog where anyone can share their own scripts, you reference gists by name listed in the# Script Lisp Library Indexwhich is itself a self-documenting machine and human readable gist of named links to external gists maintained by their

Index library references can be loaded using the formatindex:, eg:

Which also support being able to reference individual gist files:

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

If you’d like to share your own Lisp Scripts with everyone and publish your library to the index, just add a link to your gist with your preferred name in theGist Index Comments.

Viewing Script Source Code

You can view the source code of any load script references withload-src, eg:

(load-src 'lib) load-src"/ dir / lib2.l") load-src"https://example.org/lib.l") load-src"gist: 2f 14 d  (BA)  ee 55865607 f1fa2c3e / lib1.l") load-src"index: lib-calc")

Disable Remote Imports

Should you wish, you can prevent anyone from loading remote scripts with:

(Lisp) .AllowLoadingRemoteScripts=(false) ;

# Script Pages Integration

Whilst Lisp is a small, powerfully expressive functional dynamic language it’s not great for use as a templating language. Whilst there have beenseveral attempts to create a HTML DSL in Lisp, nothing is better than havingno syntaxwhich is the defaultTemplate modefor# Scriptwhere it will emit everything that’s not in a Template or Language Expression as verbatim text.

A nice USP of Script Lisp is that you’re never forced into going “full Lisp”, you can utilize# Scripttemplate expressions andScript Blockshandlebars syntax that provides the ideal DSL for usage in a Template Language for generating HTML and utilize your preferredLisporCodeScript Languages ​​for any computational logic you want included in your page usingLanguage Blocks and Expressions.

(Implementation)

Despite being implemented in different languages ​​a# Scriptpage containing multiple languages, eg:

 

Still only produces asingle page AST, where when first loaded# Scriptparses the page contents as a contiguousReadOnlyMemorywhere page slices of anyLanguage Blocks and Expressionson the page are delegated to theScriptContextregisteredScriptLanguagesfor parsing which returns a fragment which is added to the pages AST:

When executing the page, each language is responsible for rendering its own fragments which all write directly to the pagesOutputStreamto generate the pages output.

The multi-languages ​​support in# Scriptis designed to be extensible where everything about the language is encapsulated within itsScriptLanguageimplementation so that if you omit its registration:

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

Any language expressions and language blocks referencing it become inert and its source code emitted as plain-text.

Lisp Argument Scopes

One differentiator between Lisp andCodelanguages ​​is thatcodeutilizes the containing page current scope for all its argument references where as Lisp stores all its definitions within the Lisp interpreter symbol table attached to thePageResult, so whilst Lisp scripts can access arguments within the pages scope, in order for the outer page to access any Lisp symbols they need to be exported, e.g:

 

Exporting Lisp Functions

Lisp functions can also be exported for usage in the rest of the page by calling(to-delegate lispFn)to convert it into a .NET Delegate, e.g:

 

Although an easier way to define functions in Lisp is to use thedefn Script Blockwhich wraps this in a convenient syntax:

 

Controlling Lisp output

One of Lisp’s famous traits iseverything is an expressionwhich is typically desired within a language, but may not be what you want when generating output. Eg traditionally Lisp uses (setq) to set a variable which also returns its value that# Scriptwill emit as it automatically emits all statement return values.

You could usedefwhich is an alias forsetqwhich returnsnull, other options include wrapping all statements within an emptyletexpression where only the last expression is returned, or you could use aLanguage Block Modifierto ignore the entirelispblock output and only export the result you want to be able to control precisely where to emit it:

 

can use either ‘q’, ‘quiet’ or ‘mute’ block modifier to ignore output

Another way to generate output from Lisp is to use its built-in print functions below:

  • (print … args)– write all arguments to theOutputStream
  • (println … args)– write all arguments to theOutputStreamfollowed by a (new line)
  • (printlns … args)– write all arguments to theOutputStreamwith a''space delimiterfollowed by anew line
  • (pr … args)– same asprintbut (HTML encode) ************************** (all arguments)
  • (prn … args)– same asprintlnbut (HTML encode) ************************** (all arguments)

(Learn #Script Lisp)

A great resource for learning Script Lisp is seeing it in action by seeing how to implementC #’s (LINQ Examples in Lisp) :

Explore APIs in real-time

We can also take advantage of Lisp’s dynamism and interactivity to explore APIs in real-time, a great way to do this is via Awatched Lisp scripton the side where it provides instant feedback after eachCtrl Ssave point or a active (Lisp REPL) .

  • symbols– List all symbols in Lisp interpreter – most symbols are named afterstandard Lispor (clojure functions)
  • (symbol-type symbol)– Display the Symbol’s Value Type
  • scriptMethods– List all available Script Method Names registered inScriptContext

  • scriptMethodTypes– List all available Script Method Type information
  • (joinln collection)– Display the string output of each item in the collection on a separate line
  • (globln pattern collection)– Only display lines matching the glob pattern
  • (typeName instance)– View the instance Type Name
  • (props instance)– Display the Property names of an Instance public properties
  • (fields instance)– Display the Field names of an Instance public fields
  • (methods instance)– Display the Method names of an Instance public methods
  • (propTypes instance)– Get the PropertyInfo [] of an Instance public properties
  • (fieldTypes instance)– Get the FieldInfo [] of an Instance public fields
  • (methodTypes instance)– Get the Script Method Infos of an Instance public methods
  • (staticProps instance)– Display the Property names of an Instance public static properties
  • (staticFields instance)– Display the Field names of an Instance public static fields
  • (staticMethods instance)– Display the Method names of an Instance public static methods
  • (staticPropTypes instance)– Get the PropertyInfo [] of an Instance public static properties
  • (staticFieldTypes instance)– Get the FieldInfo [] of an Instance public static fields
  • (staticMethodTypes instance)– Get the Script Method Infos of an Instance public static methods

You can view theScripts API ReferenceandScripts Documentationon this website to interactively explore the available APIs, we’ll work on providing further interactive documentation for the built-in Lisp functions, in the mean-time the best resource aretheir implementation.

For reference, here’s a quick list of all built-in Lisp symbols:

-% ** gensym-counter ** version * / /=_append _nreverse >=1- 1  1st 2nd 3rd abs all? and any? append apply assoc assoc-key assoc-value assq atom atom? average butlast C caaar caadr caar cadar caddr cadr car cdaar cdadr cdar cddar cdddr cddr cdr ceiling cons cons? consp cos count debug dec decf def defmacro defn defun dispose dolist doseq doseq-while dotimes dump dump-inline elt empty? end? endp enumerator enumeratorCurrent enumeratorNext eq eql equal error even? every every? exit exp expt F f    false filter filter-index first flatmap flatten floor gensym glob globln group-by htmldump identity if inc incf instance? intern isqrt it last length let letrec list list? 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 next not not=nreverse nth nthcdr null number? odd? or order-by pop pr prin1 princ print println printlns prn prs push push-end random range reduce remove remove-if rest return reverse round rplaca rplacd second seq? setcar setcdr set-difference sets sin skip skip-while some some? sort 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 unless upper-case when where where-index while zero? zerop zip zip-where

Common Lisp by convention uses a* psuffix forPredicatefunctions but we prefer Clojure’s (and Ruby’s) more readable*?suffix convention, for source-code compatibility we include both for (core Lisp predictsand just*?for others.

        

    

Brave Browser
(Read More)
Payeer

What do you think?

Leave a Reply

Your email address will not be published. Required fields are marked *

GIPHY App Key not set. Please check settings

The Hitchhiker’s Guide to the Galaxy: 40 years of parody and predictions, Hacker News

The Hitchhiker’s Guide to the Galaxy: 40 years of parody and predictions, Hacker News

Maleficent: Mistress Of Evil Teaser – Aishwarya Rai Bachchan Transforms Into Maleficent For Angelina Jolie's Film – NDTV News, Ndtv.com

Maleficent: Mistress Of Evil Teaser – Aishwarya Rai Bachchan Transforms Into Maleficent For Angelina Jolie's Film – NDTV News, Ndtv.com