From ee583894e2aead420f469fcca8585e523a7bdc96 Mon Sep 17 00:00:00 2001 From: Sameer Rahmani Date: Sat, 4 Jan 2020 12:46:22 +0000 Subject: [PATCH] serene-simple-version post has been added --- _layouts/default.html | 17 +- _layouts/home.html | 10 +- _posts/2020-01-03-serene-simple-version.md | 260 +++++++++++++++++++++ _sass/code.scss | 147 +++++++----- assets/styles/web.sass | 26 ++- 5 files changed, 392 insertions(+), 68 deletions(-) create mode 100644 _posts/2020-01-03-serene-simple-version.md diff --git a/_layouts/default.html b/_layouts/default.html index 4d71ff0..095b5fd 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -44,6 +44,10 @@ Code of Honor + + GPG Key + + About Me @@ -60,6 +64,17 @@ + + + + + + + + ActivityPub + + + @@ -101,7 +116,7 @@
-

© 2010-2019 Sameer Rahmani

+

© 2010-2020 Sameer Rahmani

diff --git a/_layouts/home.html b/_layouts/home.html index d4abd9a..0f88074 100644 --- a/_layouts/home.html +++ b/_layouts/home.html @@ -2,7 +2,8 @@ layout: default --- -
+ + +

diff --git a/_posts/2020-01-03-serene-simple-version.md b/_posts/2020-01-03-serene-simple-version.md new file mode 100644 index 0000000..7694349 --- /dev/null +++ b/_posts/2020-01-03-serene-simple-version.md @@ -0,0 +1,260 @@ +--- +layout: post +title: "Step 2: Serene (Simple) version" +date: 2020-01-03 +categories: Programming +tags: Serene language +theme: dark +--- +As you might already know I'm working on [my own programming language ](programming/my-new-programming-language/) +for a while now. I'm still on early stages and I'm working on +[choosing the right platform](/programming/choosing-the-target-platform/) +for [#Serence](https://social.lxsameer.com/tags/Serene) and +I'm trying spend time on enough research and make decision based +on facts, scientific papers and collected data from experiments +rather than rushing into things and end up with a mess. +I believe those languages that take their time and move slowly +but with great research, plan and design are more successful in +the long term (Thanks to **Pouya** for pointing it out). Take **Clojure** +as an example. They are taking their time experimenting and validating +their hypothesis. As the result, Clojure is a well designed, stable +and highly backward compatible language with amazing productivity +pace. However, some other languages like Python are extremely +popular and consequently has more contributors. Dealing with all +those contributors caused Python to move faster than should. As a +result they ended up with some bad choices and horrible designs that +fixing them requires a humongous effort. Little by little, it becomes +harder and harder to fix those and move away from them. GIL is a good example, +instead of fixing the issue and removing GIL they are introducing +[something else](https://lwn.net/Articles/754162/) that might become +an other problem to fix the original one. In order to avoid these kind +of problem as much as possible I'm trying to take my time and do as +many as experiments that I need. + +As I mentioned [earlier](/programming/choosing-the-target-platform/) +I think **GraalVM** and **Truffle** is the right answer for +Serene. But to verify my initial Idea I decided to run an experiment. +The experiment is all about implementing a Lisp in two environments. +A pure Java implementation vs a **Truffle** implementation. +I'll update the experiment files accordingly at the +[experiment reports](https://gitlab.com/serene-lang/experiment-reports/tree/master/001) +repository. + +I spend several days and implemented the pure java version. There repository +of the simple version is available in [gitlab repo](https://gitlab.com/serene-lang/simple-version). +I didn't paid too much attention to the details and created a very simple lisp +that follows the specification below. + +> Node: In this post where ever I use the name **Serene** for the implementation, +> I'm referring to the simple version. + +## Data structures +Since I tried to avoid unnecessary work I didn't do much and implemented +only one collection type which is the most important and essential data +structure of Lisp, the mighty List. While my final goal is to have functional +data structures, this List is not a functional one and is a simple linked +list. You can find the implementation under `serene.simple.ListNode`. + +For the number types I just added the support for `Long` and `Double` numbers +via `serene.simple.SNumber` class which act as a dispatcher between two inner +classes. + +For Strings, boolean types and `nil`, I just used the equivalent Java data +structures directly. + +## Reader/Parser +Instead of using a parser generator or a sophisticated parser, I just created +a simple read ahead position based parser that reads two chars and call the +appropriate method to create the corresponding `Node`. the `serene.simple.Node` +is an abstract class which has just one important method, `eval`. The whole +purpose of the reader is to parse code and create an AST like data structure +which each node extends the `Node` class (I should've create the interface for +it but too lazy to change it now). The `eval` method of `ListNode` is a bit +special. It calls the `eval` method on all the elements on the list +and the call the first element as a function and pass the rest of elements +as the arguments to that function. First rule of lisp :)) + +The `eval` method of `ListNode` contains more details regarding to java +interop as well which I leave it out of this blog post. + +## Scope +Scope are simply a mapping between symbol names and values. There are two +different scopes, both implemented `serene.simple.IScope` and extends +`serene.simple.AScope` abstract class that contains the logic for symbol +lookup and insertion. These two classes are `serene.simple.Scope` which +is the general scope and it has a parent/child type of relationship with +other instances of the same class or `serene.simple.RootScope` which is +the top level scope. Beside that `RootScope` is pre-populated with all +the builtin functions and types. + +## Special forms +Serene's [special forms](https://courses.cs.northwestern.edu/325/readings/special-forms.php) +are pretty limited. All of them all classes which extend `serene.simple.SpecialForm` +abstract class and inherit from `Node` indirectly. The difference between +special form evaluation and function evaluation is that in case of special forms +Serene does not evaluate the arguments and leave the evaluation to the special form +itself. Here is the list of Serene's special forms: + +`def`: Creates a binding between a symbol name and the given value: +```cl +(def name "serene") +``` + +`fn`: Creates an anonymous function: +```clojure +(def inc (fn (x) (+ 1 x))) +``` +`quote`: Prevent the evaluation of the given argument and return it as it is: + +```clojure +(quote (1 2 3 4)) ;; => (1 2 3 4) +``` +`if`: Evaluated the body based on the return value of the given predicate. + +```clojure +(if (= x 1) + (...) ;; if x is 1 + (...)) ;; if x is not 1 +``` + +`let`: Setup a local scope and runs its body using that scope. + +```cl +(let ((x 1) + (y 2)) + (println x y)) +``` + +`do`: Simple group several expressions together. + +```clojure +(do + (println ...) + (if ....)) +``` + +`cond`: Gets several predicates and only evaluates the body corresponding +to the first truthy predicate. + +```cl +(cond + ((= x 1) (body1...) + ((= x 2) (body2...)) + (true (else...)))) +``` + +## Builtin Function +All the build in function are created by extending the `serene.simple.builtin.AFn` +abstract class and follow the same `Node` convention. Here is a list of the most +important built in functions: + +`(println ....)`: Prints all the arguments on the stdout. + +`(quit)`: Quits the program. + +`(conj coll x...)`: Returns a new list by adding the given arguments. + +`(count coll)`: Returns the number of elements in the given COLL. + +`(reverse coll)`: Return a new list which is the reverse of COLL. + +`(list 1 2 3..)`: Creates a list from the given arguments. + +`(first coll)`: Returns the first element of the given COLL. + +`(rest coll)`: Return all the elements beside the first of the given COLL. + +`(doc fn)`: Return the documentation for the given symbol if any. + +`(reduce f coll initial)`: Reduce the COLL by applying F to its elements with the +INITIAL as the default value. F takes two arguments 1) the accumulation 2) the element. + +`(new Class arg1 arg2...)`: Create a new instance of the given CLASS by passing the given +arguments to its constructor. + +### Example program +Here is an example program in Serene simple version (`benchmarks/fib.srns` in the repo): + +```clojure +;; We have a reduce function but just in case... +(def reduce1 + (fn (f xs initial-value) + (cond + ((first xs) (reduce f (rest xs) (f initial-value (first xs)))) + (true initial-value)))) + +;; A simple map function implementation in serene +(def map + (fn (f xs) + (reduce (fn (acc x) (cons acc (f x))) xs (list)))) + +(def range-list + (fn (x y init) + (if (< y x) + (do + (conj (range-list x (+ y 1) init) y)) + init))) + +(def range + (fn (x) + (range-list x 0 (list)))) + +(def fib + (fn (n) + (def fib-iter + (fn (x y z) + (if (= x 0) + z + (fib-iter (- x 1) + z + (+ y z))))) + (fib-iter n 0 1))) + +(def benchmark-fn + (fn (x) + (let ((start (now))) + (println (fib x)) + (- (now) start)))) + + +(def run-benchmark + (fn (times) + (map (fn (x) + (println "Benchmark: " x) + (println "Took: " (benchmark-fn 500))) + (range times)))) + +(run-benchmark 20) + +``` + + +## What is missing ? +Since Serene (simple) is an experimental language and I'll abandon it eventually, +I didn't bother with details and tried to get to the point as soon as possible. +So I sacrificed lots of details. Here is a list of the most important missing +details: + +* A namespace functionality. Because creating and compiling dynamic classes + is a rabbit hole and tons of work which doesn't make sense for a toy + project. +* Unified function interface. +* Requiring different namespaces +* A sophisticated parser. My Reader implementation is really cheap that + suits a toy project. It might worth investigating on different solutions + including using a parser generator or a head of time read implementation. +* Primitive functions in Serene. I implemented lots of primitive functions + in java rather than Serene itself mostly because of two reasons. Lack of + macros and namespaces. +* Decent [functional] data structures. The only data structure I implemented + is list. +* Quality code. The general quality of this implementation is not great, I + sacrificed quality for time. + +## Conclusion +I'm not going to improve the simple version anymore at this stage. I'm going to run +some benchmarks to measure different aspect of the current implementation and then +I'll move to the **Truffle** version to continue the +[experiment(001)](https://gitlab.com/serene-lang/experiment-reports/tree/master/001). +Please let me know if you have any comment or question on this topic. As always +I'm available throw social media and email. diff --git a/_sass/code.scss b/_sass/code.scss index c5279c4..06b8dc7 100644 --- a/_sass/code.scss +++ b/_sass/code.scss @@ -1,65 +1,88 @@ -.highlight .hll { background-color: #ffffcc } -.highlight .c { color: #0099FF; font-style: italic } /* Comment */ -.highlight .err { color: #AA0000; background-color: #FFAAAA } /* Error */ -.highlight .k { color: #006699; font-weight: bold } /* Keyword */ -.highlight .o { color: #555555 } /* Operator */ -.highlight .cm { color: #0099FF; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #009999 } /* Comment.Preproc */ -.highlight .c1 { color: #0099FF; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #0099FF; font-weight: bold; font-style: italic } /* Comment.Special */ -.highlight .gd { background-color: #FFCCCC; border: 1px solid #CC0000 } /* Generic.Deleted */ -.highlight .ge { font-style: italic } /* Generic.Emph */ -.highlight .gr { color: #FF0000 } /* Generic.Error */ -.highlight .gh { color: #003300; font-weight: bold } /* Generic.Heading */ -.highlight .gi { background-color: #CCFFCC; border: 1px solid #00CC00 } /* Generic.Inserted */ -.highlight .go { color: #AAAAAA } /* Generic.Output */ -.highlight .gp { color: #000099; font-weight: bold } /* Generic.Prompt */ -.highlight .gs { font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #003300; font-weight: bold } /* Generic.Subheading */ -.highlight .gt { color: #99CC66 } /* Generic.Traceback */ -.highlight .kc { color: #006699; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #006699; font-weight: bold } /* Keyword.Declaration */ -.highlight .kn { color: #006699; font-weight: bold } /* Keyword.Namespace */ -.highlight .kp { color: #006699 } /* Keyword.Pseudo */ -.highlight .kr { color: #006699; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #007788; font-weight: bold } /* Keyword.Type */ -.highlight .m { color: #FF6600 } /* Literal.Number */ -.highlight .s { color: #CC3300 } /* Literal.String */ -.highlight .na { color: #330099 } /* Name.Attribute */ -.highlight .nb { color: #336666 } /* Name.Builtin */ -.highlight .nc { color: #00AA88; font-weight: bold } /* Name.Class */ -.highlight .no { color: #336600 } /* Name.Constant */ -.highlight .nd { color: #9999FF } /* Name.Decorator */ -.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ -.highlight .ne { color: #CC0000; font-weight: bold } /* Name.Exception */ -.highlight .nf { color: #CC00FF } /* Name.Function */ -.highlight .nl { color: #9999FF } /* Name.Label */ -.highlight .nn { color: #00CCFF; font-weight: bold } /* Name.Namespace */ -.highlight .nt { color: #330099; font-weight: bold } /* Name.Tag */ -.highlight .nv { color: #003333 } /* Name.Variable */ -.highlight .ow { color: #000000; font-weight: bold } /* Operator.Word */ -.highlight .w { color: #bbbbbb } /* Text.Whitespace */ -.highlight .mf { color: #FF6600 } /* Literal.Number.Float */ -.highlight .mh { color: #FF6600 } /* Literal.Number.Hex */ -.highlight .mi { color: #FF6600 } /* Literal.Number.Integer */ -.highlight .mo { color: #FF6600 } /* Literal.Number.Oct */ -.highlight .sb { color: #CC3300 } /* Literal.String.Backtick */ -.highlight .sc { color: #CC3300 } /* Literal.String.Char */ -.highlight .sd { color: #CC3300; font-style: italic } /* Literal.String.Doc */ -.highlight .s2 { color: #CC3300 } /* Literal.String.Double */ -.highlight .se { color: #CC3300; font-weight: bold } /* Literal.String.Escape */ -.highlight .sh { color: #CC3300 } /* Literal.String.Heredoc */ -.highlight .si { color: #AA0000 } /* Literal.String.Interpol */ -.highlight .sx { color: #CC3300 } /* Literal.String.Other */ -.highlight .sr { color: #33AAAA } /* Literal.String.Regex */ -.highlight .s1 { color: #CC3300 } /* Literal.String.Single */ -.highlight .ss { color: #FFCC33 } /* Literal.String.Symbol */ -.highlight .bp { color: #336666 } /* Name.Builtin.Pseudo */ -.highlight .vc { color: #003333 } /* Name.Variable.Class */ -.highlight .vg { color: #003333 } /* Name.Variable.Global */ -.highlight .vi { color: #003333 } /* Name.Variable.Instance */ -.highlight .il { color: #FF6600 } /* Literal.Number.Integer.Long */ - pre.highlight { padding: 4px 8px; } + +/* Punctuation */ +.highlight .cm { + color: #aaaaaa !important; } + +/* Comment.Multiline */ +.highlight .cp { + color: #aaaaaa !important; } + +/* Comment.Preproc */ +.highlight .c1 { + color: #aaaaaa !important; } + +/* Comment.Single */ +.highlight .cs { + color: #aaaaaa !important; } + +//.highlight pre { background-color: #272822; } +.highlight pre { background-color: #373269; } +.highlight .hll { background-color: #272822; } +//.highlight .c { color: #75715e } /* Comment */ +.highlight .c { color: #aaaaaa } /* Comment */ +.highlight .cl { color: #aaaaaa } /* Comment */ +.highlight .err { color: #960050; background-color: #1e0010 } /* Error */ +.highlight .k { color: #66d9ef } /* Keyword */ +.highlight .l { color: #ae81ff } /* Literal */ +.highlight .n { color: #f8f8f2 } /* Name */ +.highlight .o { color: #f92672 } /* Operator */ +.highlight .p { color: #f8f8f2 } /* Punctuation */ +.highlight .cm { color: #75715e } /* Comment.Multiline */ +.highlight .cp { color: #75715e } /* Comment.Preproc */ +.highlight .c1 { color: #75715e } /* Comment.Single */ +.highlight .cs { color: #75715e } /* Comment.Special */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .kc { color: #66d9ef } /* Keyword.Constant */ +.highlight .kd { color: #66d9ef } /* Keyword.Declaration */ +.highlight .kn { color: #f92672 } /* Keyword.Namespace */ +.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */ +.highlight .kr { color: #66d9ef } /* Keyword.Reserved */ +.highlight .kt { color: #66d9ef } /* Keyword.Type */ +.highlight .ld { color: #e6db74 } /* Literal.Date */ +.highlight .m { color: #ae81ff } /* Literal.Number */ +.highlight .s { color: #e6db74 } /* Literal.String */ +.highlight .na { color: #a6e22e } /* Name.Attribute */ +.highlight .nb { color: #f8f8f2 } /* Name.Builtin */ +.highlight .nc { color: #a6e22e } /* Name.Class */ +.highlight .no { color: #66d9ef } /* Name.Constant */ +.highlight .nd { color: #a6e22e } /* Name.Decorator */ +.highlight .ni { color: #f8f8f2 } /* Name.Entity */ +.highlight .ne { color: #a6e22e } /* Name.Exception */ +.highlight .nf { color: #a6e22e } /* Name.Function */ +.highlight .nl { color: #f8f8f2 } /* Name.Label */ +.highlight .nn { color: #f8f8f2 } /* Name.Namespace */ +.highlight .nx { color: #a6e22e } /* Name.Other */ +.highlight .py { color: #f8f8f2 } /* Name.Property */ +.highlight .nt { color: #f92672 } /* Name.Tag */ +.highlight .nv { color: #f8f8f2 } /* Name.Variable */ +.highlight .ow { color: #f92672 } /* Operator.Word */ +.highlight .w { color: #f8f8f2 } /* Text.Whitespace */ +.highlight .mf { color: #ae81ff } /* Literal.Number.Float */ +.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */ +.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */ +.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */ +.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */ +.highlight .sc { color: #e6db74 } /* Literal.String.Char */ +.highlight .sd { color: #e6db74 } /* Literal.String.Doc */ +.highlight .s2 { color: #e6db74 } /* Literal.String.Double */ +.highlight .se { color: #ae81ff } /* Literal.String.Escape */ +.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */ +.highlight .si { color: #e6db74 } /* Literal.String.Interpol */ +.highlight .sx { color: #e6db74 } /* Literal.String.Other */ +.highlight .sr { color: #e6db74 } /* Literal.String.Regex */ +.highlight .s1 { color: #e6db74 } /* Literal.String.Single */ +.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */ +.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */ +.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */ +.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */ +.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */ + +.highlight .gh { } /* Generic Heading & Diff Header */ +.highlight .gu { color: #75715e; } /* Generic.Subheading & Diff Unified/Comment? */ +.highlight .gd { color: #f92672; } /* Generic.Deleted & Diff Deleted */ +.highlight .gi { color: #a6e22e; } /* Generic.Inserted & Diff Inserted */ diff --git a/assets/styles/web.sass b/assets/styles/web.sass index 4f3d257..5a8b51a 100644 --- a/assets/styles/web.sass +++ b/assets/styles/web.sass @@ -56,7 +56,20 @@ body.light background: $purple color: $light +a.home + color: $blue !important + &:hover + text-decoration: underline + + body.dark + .para + p, h1, h2, h3, h4, h5, strong + padding-top: 1em + padding-bottom: 0.5em + line-height: 1.5 + color: darken($white, 10%) + color: $light background: $purple footer.footer @@ -69,11 +82,10 @@ body.dark &:hover color: darken($white, 30%) - - content.post text-align: justify - + blockquote + font-style: italic h1 @extend .is-size-2 h2 @@ -90,6 +102,7 @@ body.dark padding-bottom: 0.5em line-height: 1.5 color: darken($white, 10%) + strong color: white p @@ -105,7 +118,12 @@ body.dark text-decoration: underline font-size: 1.3rem - line-height: 2rem + + pre + code + font-size: 0.8em + line-height: 1em !important + code.highlighter-rouge background: darken($purple, 10%) color: $light