...
 
Commits (2)
......@@ -44,6 +44,10 @@
Code of Honor
</a>
<a href="https://dl.lxsameer.com/lxsameer.pub.gpg" class="navbar-item">
GPG Key
</a>
<a href="/about/" class="navbar-item {% if page.url == '/about/' %}is-active{% endif %}">
About Me
</a>
......@@ -60,6 +64,17 @@
</span>
</a>
<a class="navbar-item" href="https://social.lxsameer.com/">
<span class="icon is-large is-hidden-mobile">
<i class="fab fa-mastodon fa-lg"></i>
</span>
<span class="is-large is-hidden-desktop">
<i class="pad fab fa-mastodon fa-lg"></i>
ActivityPub
</span>
</a>
<a class="navbar-item" href="https://www.linkedin.com/in/lxsameer/">
<span class="icon is-large is-hidden-mobile">
<i class="fab fa-linkedin fa-lg"></i>
......@@ -101,7 +116,7 @@
</div>
<footer class="footer">
<div class="content has-text-centered">
<p>© 2010-2019 Sameer Rahmani</p>
<p>© 2010-2020 Sameer Rahmani</p>
</div>
</footer>
<script src="/assets/js/main.js?1"></script>
......
......@@ -2,7 +2,8 @@
layout: default
---
<div class="column is-half-desktop is-offset-one-quarter-desktop">
<div class="column is-half-desktop is-offset-one-quarter-desktop para">
<section class="section is-size-4-desktop is-size-5-mobile">
<p class="text-centered">
<img
......@@ -12,13 +13,20 @@ layout: default
/>
</p>
<p class="info text-justified">
Hi! I'm <b>Sameer</b> and I usally write about
Hi! I'm <b>Sameer</b> and I usually write about
technology and software development (hmmm more like nagging instead of writing). Since
I'm lazy, my posts are short so I can write more often.
<p class="info text-justified">
If you have any comment on the articles or anything in general either send me an
email for connect to me via my <a class="home" href="https://activitypub.rocks/">activity pub</a>
feed on <a class="home" href="https://social.lxsameer.com">[email protected]</a>.
</p>
</p>
</section>
</div>
<div class="column is-half-desktop is-offset-one-quarter-desktop">
<section class="section">
<h3 class="header is-size-4">
......
---
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.
.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 */
......@@ -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
......