felise/walkthrough.html

273 lines
14 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<title>Felise - A walkthrough</title>
<style>
main,header{width: 900px;max-width: 90vw;margin: 3em auto}
pre {padding:1rem;width:calc(100% - 2rem);color:#EEE;border-radius:10px;background:#262626; margin: 3em auto}
samp > pre{color:lime}
pre{letter-spacing:1px;line-height:1.5em;font-size:1.2em;font-weight:bold}
pre .com{color:#5897bb;font-weight:normal}
pre .value{color:#f35e5e}
pre .type{color:#73ab23}
pre .sp1{color:#45af60}
pre .sp2{color:#d58d08}
pre .err{color:#bd80f9}
h1{color:rebeccapurple}
small{color: #444}
</style>
</head>
<body>
<header>
<h1>Felise<br><small>a concatenative procedural programming language</small></h1>
</header>
<main>
<p>This is a walkthrough of the <i>Felise</i> programming language. <i>Felise</i> is loosely situated as a concatenative programming language. It is an interpreted language (it requires the <i>felise</i> interpreter on the system to run programs) in its present form.</p>
<p>This walkthrough will be done by example. If you would like to follow along in the <abbr title="Read Evaluate Print Loop">repl</abbr>, you can clone the repository <a href="https://tildegit.org/sloum/felise" target="_blank">here</a>. The repl features tab completion for all procedures and has a few quality of life options. Feel free to skip around as your level of experience dictates. If you are fully new to stack based/concatenative programming, maybe try reading a bit of the <a href="https://en.wikipedia.org/wiki/Stack-oriented_programming" target="_blank">wikipedia article</a> on the subject first. With that said, let's get to it&hellip;</p>
<section>
<pre><code><span class="com"># This is a comment
| This is also a comment, but it is
used for docstrings (procedure
documentation). It is multiline.
More on this later. |</span>
<span class="com"># Add/View values to/on the stack
# ===============================</span>
<span class="value">5 1.3</span> <span class="value">"Hello"</span>
stackdump <span class="com"># Take a look at the stack</span>
drop <span class="com"># Throw away TOS ("Hello")</span>
println <span class="com"># Pop and print TOS w/ a newline</span>
print <span class="com"># Pop and print TOS w/ no newline</span>
stackdepth println <span class="com">#=&gt; 0</span>
<span class="com"># Let's do some "math" on the stack
# =================================</span>
<span class="value">2 3</span> + println <span class="com">#=&gt; 5</span>
<span class="value">2 3.2</span> + println <span class="com">#=&gt; 5.2; INT coerces to FLOAT</span>
<span class="value">2.25 3.1</span> + println <span class="com">#=&gt; 5.35</span>
<span class="value">"Hello" "World"</span> + println <span class="com">#=&gt; HelloWorld</span>
<span class="value">"Hello" 55</span> + println <span class="com">#=&gt; Hello55</span>
<span class="value">2 3</span> - println <span class="com">#=&gt; -1</span>
<span class="value">2.25 3.1</span> - println <span class="com">#=&gt; -0.8500000000000001</span>
<span class="value">"Hello World" "o"</span> - println <span class="com">#=&gt; HellWrld</span>
<span class="value">"H3ll0 W0rld" 0</span> - println <span class="com">#=&gt; H3ll Wrld ; 0 is INT &amp; coerces to STRING</span>
[ <span class="value">"Hi" "Hello" "Hi"</span> ] <span class="value">"Hi"</span> - println <span class="com">#=&gt; [ "Hello" ]</span>
[ <span class="value">1 2.25</span> <span class="value">"Hi"</span> ] <span class="value">2.25</span> - println <span class="com">#=&gt; [ 1 "Hi" ]</span>
<span class="value">2 3</span> / println <span class="com">#=&gt; 0</span>
<span class="value">2.25 3.1</span> / println <span class="com">#=&gt; 0.7258064516129032</span>
<span class="value">"Hello World" " "</span> / println <span class="com">#=&gt; [ "Hello" "World" ] ; split into LIST</span>
<span class="value">2 3</span> * println <span class="com">#=&gt; 6</span>
<span class="value">2.25 3.1</span> * println <span class="com">#=&gt; 6.9750000000000005</span>
<span class="value">"Hello World" 2</span> * println <span class="com">#=&gt; Hello WorldHello World</span>
[ <span class="value">"Hello" "World"</span> ] <span class="value">"__"</span> * println <span class="com">#=&gt; "Hello__World" ; Join LIST to STRING</span>
<span class="com"># Let's work with variables
# =========================
# Declare the type then the keyword
# then the variable name</span>
<span class="type">INT</span> <span class="sp1">var!</span> myInt
myInt println <span class="com">#=&gt; 0
# The new var takes on the "zero" value of the type
# using 'myVar' above adds the value of myVar to TOS
# Set the variable as follows:</span>
<span class="value">5</span> <span class="sp1">set!</span> myInt
myInt println <span class="com">#=&gt; 5
# You cannot set a variable to a value of a type that
# does not match the declared type:</span>
<span class="value">"Nope"</span> <span class="sp1">set!</span> myInt <span class="com">#=&gt; ERROR!!!!!</span>
clearstack <span class="com">#=&gt; Clears the stack</span>
<span class="com"># Note: variables are local to the scope they are
# created in, and are accessible to child scopes</span>
<span class="com"># Let's work boolean logic
# ========================
# Tip: false, 0, [], and {} are all falsy</span>
<span class="value">5 3</span> &gt; println <span class="com">#=&gt; true</span>
<span class="value">5 3</span> &lt; println <span class="com">#=&gt; false</span>
<span class="value">5 3</span> &gt;= println <span class="com">#=&gt; true</span>
<span class="value">5 3</span> &lt;= println <span class="com">#=&gt; false</span>
<span class="value">5 3</span> = println <span class="com">#=&gt; false</span>
<span class="value">5 3</span> != println <span class="com">#=&gt; true</span>
<span class="value">5 3</span> = not println <span class="com">#=&gt; true</span>
<span class="value">true false</span> and <span class="com">#=&gt; false</span>
<span class="value">true false</span> or <span class="com">#=&gt; true</span>
<span class="value">5</span> type <span class="type">FLOAT</span> = <span class="com">#=&gt; false</span>
<span class="value">5</span> type <span class="type">INT</span> = <span class="com">#=&gt; false</span>
<span class="value">5 3</span> &gt; <span class="sp2">if</span>
<span class="value">"Bigger"</span> println
<span class="sp2">else</span>
<span class="value">"Smaller"</span> println
<span class="sp2">end</span> <span class="com">#=&gt; Bigger</span>
<span class="com"># Let's cast some values
# ======================
# The available types are:
# INT, FLOAT, STRING, BOOL, TYPE, LIST, DICT</span>
<span class="value">5</span> <span class="type">FLOAT</span> cast println <span class="com"># Our INT (5) is now a FLOAT (5.0)</span>
<span class="value">"5"</span> <span class="type">FLOAT</span> cast println <span class="com"># Our STRING ("5") is now a FLOAT (5.0)</span>
<span class="value">"5"</span> <span class="type">INT</span> cast println <span class="com"># Our STRING ("5") is now an INT (5)</span>
<span class="value">"5"</span> <span class="type">BOOL</span> cast println <span class="com"># Our STRING ("5") is now a BOOL (true)</span>
<span class="value">""</span> <span class="type">BOOL</span> cast println <span class="com"># Our STRING ("") is now a BOOL (false)</span>
<span class="value">0</span> <span class="type">BOOL</span> cast println <span class="com"># Our INT (0) is now a FLOAT (false)</span>
<span class="value">5</span> <span class="type">STRING</span> cast println <span class="com"># Our INT (5) is now a STRING ("5")</span>
<span class="com"># Let's use LISTs and DICTs
# =========================
# Get values (A LIST can hold any type, including LIST)</span>
[ <span class="value">"Hello" "world"</span> ] <span class="value">1</span> &lt;- println <span class="com">#=&gt; Hello</span>
[ <span class="value">"Hello" "world"</span> ] <span class="value">2</span> &lt;- println <span class="com">#=&gt; world</span>
[ <span class="value">"Hello" "world"</span> ] <span class="value">-1</span> &lt;- println <span class="com">#=&gt; world</span>
<span class="com"># It works mostly the same with a DICT
# Note that DICT keys must be STRINGs, but values
# can be of any type (including a DICT)</span>
{ <span class="value">
"age" 33
"weight" 150
"hair" "brown"
"smoker" false</span>
} <span class="value">"age"</span> &lt;- println <span class="com">#=&gt; 33</span>
<span class="com"># Append a new item...</span>
[ <span class="value">"Hello" "world"</span> ] <span class="value">false</span> =&gt; println <span class="com">#=&gt; [ "Hello" "world" false ]</span>
<span class="com"># For a DICT just set a new key as a value...</span>
{ } <span class="value">"happy" true</span> -&gt; println <span class="com">#=&gt; { "happy" true }</span>
<span class="com"># Change the value of an existing index/key...</span>
[ <span class="value">"Hello" "world" false</span> ] <span class="value">3 "!"</span> -&gt; println <span class="com">#=&gt; [ "Hello" "world" "!" ]</span>
{ <span class="value">"age" 33 "weight" 150</span> } <span class="value">"age" 34</span> println <span class="com">#=&gt; { "age" 34 "weight" 150 }</span>
<span class="com"># Let's write a procedure
# =======================
# We'll iterate over building a simple procedure
# Let's call it `square`. It will take a number
# (INT or FLOAT) and multiply it times itself</span>
<span class="sp2">proc</span> square
dup * <span class="com"># dup will add a copy of the top stack item to the stack</span>
<span class="sp2">end</span>
<span class="com"># That works, but let's make sure we don't
# get weird side effects (squaring a non-number)</span>
<span class="sp2">proc</span> square
dup type <span class="type">INT</span> = over type <span class="type">FLOAT</span> = or <span class="sp2">if</span>
dup *
<span class="sp2">else</span>
<span class="value">"`square` expected an INT, but found "</span>
swap type + <span class="com"># Move the STRING under the top stack
# value, get the type, and append it to the STRING</span>
<span class="err">throw</span> <span class="com"># then throw an error with a value of the STRING</span>
<span class="sp2">end
end</span>
<span class="com"># Let's add a docstring so people will know how to use it
# It is standard to include what it takes from the stack,
# if it forward reads a symbol, and what it puts on the stack.
# It is also good to leave a note that describes what happens.</span>
<span class="sp2">proc</span> square
<span class="com">| Stack: INT/FLOAT
Read :
Push : INT/FLOAT
Notes: Takes an INT or FLOAT and pushes the square of the same type to TOS |</span>
dup type <span class="type">INT</span> = over type <span class="type">FLOAT</span> = or <span class="sp2">if</span>
dup *
<span class="sp2">else</span>
<span class="value">"`square` expected an INT, but found "</span>
swap type + <span class="com"># Move the STRING under the top stack
# value, get the type, and append it to the STRING</span>
<span class="err">throw</span> <span class="com"># then throw an error with a value of the STRING</span>
<span class="sp2">end
end</span>
<span class="value">5</span> square println <span class="com">#=&gt; 25</span>
<span class="com"># We can read the docstring, too...</span>
docstring! square println
<span class="com">#=&gt;`square`
# Stack: INT/FLOAT
# Read :
# Push : INT/FLOAT
# Notes: Takes an INT or FLOAT and pushes the square of the same type to TOS |</span>
<span class="com"># Reading Internal Docs
# =====================
# To see all procedures the system knows about:</span>
words println
<span class="com"># The above will add a STRING to TOS that lists every proc name
# that the system knows. When you create a proc, it will show up
# in the STRING produced by `words`
# Now that you know the proc names, you can use `docstring!` to
# see what they do and how to use them:</span>
docstring! net-get println <span class="com">#=&gt;
# `net-get`
# Stack: STRING
# Read :
# Push : DICT
# Notes: Takes a gemini, gopher, or http(s) url STRING from TOS.
# A response DICT will be left on the stack, key "success"
# will be a BOOL declaring whether or not the request was
# successful, key "status" will be the status code of the
# response (or -1 if there is no status code), key "body"
# will be the response body. If the response was successful
# key "body" will be the actual body of the response, if
# it was not successful it will be an error message.
# Try importing a library and seeing the listing in `words` change</span>
<span class="value">"strings"</span> import
words println
<span class="com"># Now you can see things like `string-to-lower`, which was not
# available before. Note: `import` consumes a string with
# a standard builtin package ("math", "paths", "stack",
# and "strings"), but you can also pass it a string that
# represents a filepath to a felise file to import</span>
docstring! string-to-lower println
</code></pre>
<p>
That is enough to get you going with the basics of felise. There are a lot more complicated things present in the language that allow meta-programming, functional programming, interacting with a *nix environment, etc. Plus a lot of built in procs that can do neat things. Play around and you will find cool things to do. The <a href="https://tildegit.org/sloum/felise/src/branch/master/README.md" target="_blank">README.md</a> that comes in the <a href="https://tildegit.org/sloum/felise" target="_blank">source code repository</a> has information about a number of these features, as well as some language "gotchas", installation information, and other good info.
</p>
</section>
</main>
</body>
</html>