M content/config.nuj => content/config.nuj +0 -1
@@ 1,5 1,4 @@
@[ :title "Nujel.net"
- :author "Ben"
:logo "img/logo.png"
:logo-text "Nujel"
:favicon "favicon.png" ]
M content/docs.html => content/docs.html +1 -4
@@ 7,13 7,10 @@ hide-prev-next: #t
<h1>Documentation</h1>
-<h2>Stable</h2>
-<p>These documents describe parts of the language that are very unlikely to change except for details.</p>
+<p>Apart from the source itself, there is this "book" which might help if you are already experienced with another Lisp dialect.</p>
<nav>
{{ [join [sort [map [navigation 1] [fn [v] [render-link v]]]] "<br/>"] }}
</nav>
<br/>
<br/>
-<h2>Experimental</h2>
-<p>If there is no documentation available for a feature/system then it might change drastically in future versions, so please keep that in mind when writing code using those parts of the language.</p>>
\ No newline at end of file
M content/language/chapters/1-1_parentheses.html => content/language/chapters/1-1_parentheses.html +6 -8
@@ 5,16 5,14 @@ date: "2022-08-31"
<h1>Parentheses</h1>
<p>Nujel uses the S-Expression notation as used by languages such as <i>Lisp</i>, <i>Scheme</i> and <i>Clojure</i>, but with one difference: Nujel defaults to using brackets instead of parentheses. This is mainly because brackets are much easier to type with a default US keyboard layout. You can still use parentheses if you must, since the two are completely interchangeable.</p>
-<pre class="source source-nujel">
-[+ 1 2] ; => 3
-(+ 1 2) ; => 3
-</pre>
+{{ [component :Nujel #nil
+"[+ 1 2] ; => 3
+(+ 1 2) ; => 3"] }}
<p>Dotted pairs are also supported:</p>
-<pre class="source source-nujel">
-[car '[a . b]] ; => a
+{{ [component :Nujel #nil
+"[car '[a . b]] ; => a
[cdr '[a . b]] ; => b
[car [cons 1 2]] ; => 1
-[cdr [cons 1 2]] ; => 2
-</pre>
+[cdr [cons 1 2]] ; => 2"] }}
<p>And as you can see Nujel uses <code>car</code> and <code>cdr</code> to access the first or rest parts of lists. To build a new pair you can use <code>cons</code>.</p>
M => +3 -3
@@ 5,14 5,14 @@ date: "2022-08-31"
<h1>Comments</h1>
<p>Comments use Scheme syntax, and some SRFI's have been implemented directly.</p>
<pre class="source source-nujel">
; A single semicolon comments out everything until the next line
{{ [component :Nujel #nil
"; A single semicolon comments out everything until the next line
(+ 1 #;2 3) ; You can use #; to comment out the following form, should be SRFI-62 compatible!
; => 4
#|
| Nujel also allows for SRFI-30 like nested Multi-line comments
|#
[comment [println "A message, never to be printed."]] ; Nujel also has a comment macro, this however returns #nil unlike #;
[comment [println \"A message, never to be printed.\"]] ; Nujel also has a comment macro, this however returns #nil unlike #;"] }}
</pre>
<p>Apart from that it is recommended to use a single <code>;</code> at the end of a line to comment whatever is happening on that particular line.</p>
M content/language/chapters/1-3_numbers.html => content/language/chapters/1-3_numbers.html +3 -3
@@ 7,8 7,8 @@ date: "2022-08-31"
<p>Nujel supports normal decimal notation and treats <code>,</code> and <code>_</code> as whitespace characters so you can split big numbers for increased legibility. There is also special syntax for binary, octal and hexadecimal literals, scientific notation however is <b>not</b> supported.</p>
-<pre class="source source-nujel">
-9 ; probably not that suprising
+{{ [component :Nujel #nil
+"9 ; probably not that suprising
; => 9
100,0; possible, but probably shouldn't be commited that way
@@ 38,5 38,5 @@ date: "2022-08-31"
0x123 ; Using an 0x prefix does NOT work and results in a read error being thrown
; => :read-error
--100 ; You can also write negative numbers
+-100 ; You can also write negative numbers"] }}
</pre>=
\ No newline at end of file
M content/language/chapters/1-4_arithmetic.html => content/language/chapters/1-4_arithmetic.html +10 -12
@@ 14,8 14,8 @@ date: "2022-08-31"
<p>Nujel supports most standard operators for integers as well as floating-point numbers (or fixnums and flonums). There is no operation called mod/modulo, but instead it is called <code>rem</code>, just like in <i>Clojure</i>.</p>
<p>Division will also always return a flonum, if you want to do an integer division you can use <code>div/int</code> instead. Apart from that doing operations between fixnums and flonums will result in a flonum, with fixnums only being returned when both operands are fixnums.</p>
-<pre class="source source-nujel">
-[+ 1 2 3 4] ; You can add as many numbers as you want
+{{ [component :Nujel #nil
+"[+ 1 2 3 4] ; You can add as many numbers as you want
; => 10
[+ 1 2 [+ 3 4]]
@@ 33,9 33,8 @@ date: "2022-08-31"
[+ 1 [- [+ 1 1]]] ; Results of a calculation can also be negated
; => -1
-[+ 1 "drei"] ; You can only calculate with numbers
-; => :type-error
-</pre>
+[+ 1 \"drei\"] ; You can only calculate with numbers
+; => :type-error"] }}
<h3>2. Exceptions</h3>
<p>Dividing through zero generates an exception, which is probably not that suprising, this however also extends to floating point operations where NaN as well as +/-Inf are invalid values that Nujel will not create, instead raising an Exception.</p>
@@ 45,22 44,21 @@ date: "2022-08-31"
<p>This seems to be an inconsitency in how integers and floating-point numbers are handled, since I can't think of time where I wanted a NaN/Inf value it seems reasonable to me to handle this case just like integers, meaning raising an exception.</p>
</box-wrap>
-<pre class="source source-nujel">
-[/ 10 0]
+{{ [component :Nujel #nil
+"[/ 10 0]
; => :float-inf
[div/int 10 0]
-; => :divide-by-zero
-</pre>
+; => :divide-by-zero"] }}
<h3>3. Conversion</h3>
<p>Nujel also provides a couple of functions for converting between the different numeric formats.</p>
<p>In general, Nujel will never implicitly convert a flonum to a fixnum, since that might mean loosing precision, if it does it is considered a bug in the implementation.</p>
<p>It will however implicitly convert fixnum's to flownum's whenever that seems to be the least suprising choice.</p>
-<pre class="source source-nujel">
-[int 1.1]
+{{ [component :Nujel #nil
+"[int 1.1]
; => 1
[float 1]
-; => 1.0
+; => 1.0"] }}
</pre>
M content/language/chapters/1-5_symbols-keywords.html => content/language/chapters/1-5_symbols-keywords.html +8 -10
@@ 13,19 13,18 @@ date: "2022-09-07"
<h3>1. Symbols</h3>
<p>Symbols in Nujel behave pretty much the same as in most other Lisps, in that they are implictly looked up unless quoted.</p>
-<pre class="source source-nujel">
-asd ; Every symbol is implicitly looked up, with an exception thrown on failure
+{{ [component :Nujel #nil
+"asd ; Every symbol is implicitly looked up, with an exception thrown on failure
; => :unbound-variable
'asd ; By quoting a symbol this implicit look up can be disabled
; => asd
-[string->symbol "asd"] ; You can also turn any old string into a symbol, this is sometimes called interning a string
+[string->symbol \"asd\"] ; You can also turn any old string into a symbol, this is sometimes called interning a string
; => asd
-[== 'asd [string->symbol "asd"]] ; The two values actually point to the same underlying data structure
-; => #t
-</pre>
+[== 'asd [string->symbol \"asd\"]] ; The two values actually point to the same underlying data structure
+; => #t"] }}
<box-wrap box-color="yellow">
<h3>Implementation detail</h3>
@@ 35,8 34,8 @@ asd ; Every symbol is implicitly looked up, with an exception thrown on failure
<h3>2. Keywords</h3>
<p>Since quoting can become quite tedious there is another very similar data type, keywords. They share most functionality with symbols with the main difference being that they will not be implicitly looked up.</p>
<p>By pre- or suffixing a symbol with a <code>:</code> the reader will return a keywords.</p>
-<pre class="source source-nujel">
-:asd ; A keyword
+{{ [component :Nujel #nil
+":asd ; A keyword
; => :asd
asd: ; Another keyword
@@ 46,8 45,7 @@ asd: ; Another keyword
; => #t
[= :asd [symbol->keyword 'asd]]
-; => #t
-</pre>
+; => #t"] }}
<h3>3. Usage</h3>
<p>Regarding their usage, keywords should be used when the keyword itself has a useful meaning, symbols should be used to label other values which could work just as well with another symbol associated.</p>
M content/language/chapters/1-6_quote-quasiquote.html => content/language/chapters/1-6_quote-quasiquote.html +12 -14
@@ 10,16 10,15 @@ date: "2022-08-31"
<p>If you want to simply return a value as is, meaning a symbol without resolving it, or a list instead of evaluating it as a from then you can use <code>quote</code>.</p>
<p>Since this is quite the common occurence there is a special reader form to make this more convenient: <code>'</code>. By prefixing any symbol with a single quote it will be used as is, without any attempt to resolve it.</p>
<p>Quote can not only be used to inhibit the implicit behaviour of symbol resolution, but can also stop expressions from being evaluated and instead being passed along as simple lists. This allows for the easy inclusion of literal lists.</p>
-<pre class="source source-nujel">
- 'a ; You can quote symbols
- ; => a
-
- '[1 2 3] ; Or forms, making them plain lists
- ; => [1 2 3]
-
- '(1 2 3) ; With parentheses or brackets
- ; => [1 2 3]
-</pre>
+{{ [component :Nujel #nil
+"'a ; You can quote symbols
+; => a
+
+'[1 2 3] ; Or forms, making them plain lists
+; => [1 2 3]
+
+'(1 2 3) ; With parentheses or brackets
+; => [1 2 3]"] }}
<h3>2. Quasiquote</h3>
<p>What is one supposed to do if one wants to quote only parts of an expression though? You could build it up using <code>cons</code>,<code>list</code> and quote but there is a more convenient way for that: <code>quasiquote</code>.</p>
@@ 28,13 27,12 @@ date: "2022-08-31"
<h3>Reasoning</h3>
<p>I chose the Clojure style over Scheme/Lisp because using a <code>,</code> for unquoting would make it non-whitespace, making its use as a thousands separator very problematic.</p>
</box-wrap>
-<pre class="source source-nujel">
-`[1 2 ~[+ 1 1 1]] ; To unquote you can use a tilde
+{{ [component :Nujel #nil
+"`[1 2 ~[+ 1 1 1]] ; To unquote you can use a tilde
; => [1 2 3]
`[1 ~@[list 2 3]] ; And ~@ for unquote-splicing
-; => [1 2 3]
-</pre>
+; => [1 2 3]"] }}
<box-wrap box-color="yellow">
<h3>Implementation detail</h3>
<p>Unlike in other Lisp/Scheme implementations Quasiquote is just a regular macro, this shouldn't make a difference in most cases but might trip you up if you delve deeper into the runtime.</p>
M content/language/chapters/1-7_variables.html => content/language/chapters/1-7_variables.html +9 -12
@@ 8,23 8,21 @@ date: "2022-09-02"
<h3>1. Defining variables</h3>
<p>You can define new variables using <code>def</code> and give old variables a new value using <code>set!</code>.</p>
-<pre class="source source-nujel">
-my-temp ; You can access a variables value by evaluating the symbol
+{{ [component :Nujel #nil
+"my-temp ; You can access a variables value by evaluating the symbol
; => :unbound-variable
[def my-temp 123] ; Of course it needs to be defined first
; => 123
my-temp
-; => 123
-</pre>
+; => 123"] }}
<h3>2. Setting variables</h3>
<p>If you wish to change the value already associated with a given symbol, you can use <code>set!</code> to achieve that.</p>
-<pre class="source source-nujel">
-[def my-temp 123] ; Gotta define it first before we can use set!
+{{ [component :Nujel #nil
+"[def my-temp 123] ; Gotta define it first before we can use set!
[set! my-temp 234]
my-temp
-; => 234
-</pre>
+; => 234"] }}
<h3>3. Let blocks</h3>
<p>You can use <code>let</code> to create a new context for assignments, you still have access to all symbols defined in parent contexts.</p>
@@ 33,8 31,7 @@ my-temp
<p>Currently you can shadow already defined variables, this is highly discouraged and will probably become an error in later Nujel versions. This is because it has already led to some bugs and makes some optimizations more complicated.</p>
</box-wrap>
<p>Unlike most other Lisps using <code>let</code> is discouraged in most situations, since you can just use <code>def</code> within a function call in order to define temporary variables, it is available though and should work like in <i>Scheme</i> or <i>CL</i>.</p>
-<pre class="source source-nujel">
-[let [[new-temp 123]]
+{{ [component :Nujel #nil
+"[let [[new-temp 123]]
[println new-temp]] ; => 123
-new-temp ; => :unbound-variable
-</pre>
+new-temp ; => :unbound-variable"] }}
M content/language/chapters/1-8_functions.html => content/language/chapters/1-8_functions.html +17 -22
@@ 8,50 8,45 @@ date: "2022-09-01"
<h3>1. Defining functions</h3>
<p>Defining a function is best done with the <code>[defn]</code> macro if it is named, or <code>[fn]</code> for anonymous functions.</p>
-<pre class="source source-nujel">
-[defn double [α] [* 2 α]]
+{{ [component :Nujel #nil
+"[defn double [α] [* 2 α]]
[double 2] ; => 4
[def double [fn [b] [* 2 b]]]
-[double 2] ; => 4
-</pre>
+[double 2] ; => 4"] }}
<h3>2. Defining functions with a rest argument</h3>
<p>In order to define a function that has a certain number of named arguments, and then a final catch-all argument you can use the dotted pair notation, just like in <i>Scheme</i>.</p>
-<pre class="source source-nujel">
-[defn multiply-vals [val . l]
+{{ [component :Nujel #nil
+"[defn multiply-vals [val . l]
[map l [fn [v] [* v val]]]]
[multiply-vals 2 1 2 3]
-; => [2 4 6]
-</pre>
+; => [2 4 6]"] }}
<h3>3. Defining a functions with any amount of arguments</h3>
<p>If you want to define a function that can take an arbitrary amount of arguments, you can just omit the brackets surrounding the argument list, this is just like you may be used from <i>Scheme</i>.</p>
-<pre class="source source-nujel">
-[defn my-list l l]
+{{ [component :Nujel #nil
+"[defn my-list l l]
[my-list 1 2 3 4]
-; => [1 2 3 4]
-</pre>
+; => [1 2 3 4]"] }}
<h3>4. Documenting functions</h3>
<p>You can document your functions behaviour by having your functions start with a string literal (don't worry it will be optimized out of the final bytecode).</p>
<p>Adding multiple string literals to the beginning results in the docstring being all strings joined by a linebreak, this makes it easier to add multi-line docstrings.</p>
<p>In order to look up the docstring for any given function (even built-in ones) you can use the <code>[describe]</code> function </p>
-<pre class="source source-nujel">
-[defn double [α]
- "Return α multiplied by 2"
+{{ [component :Nujel #nil
+"[defn double [α]
+ \"Return α multiplied by 2\"
[* 2 α]]
-[double 2] ; => 4
-</pre>
+[double 2] ; => 4"] }}
<h3>5. Function decorators</h3>
<p>In order to tell the compiler some additional information about the function you are declaring, you can add keywords to the beginning of your function body.</p>
<p>Right now we only support <code>:export</code> and <code>:export-as</code>, <code>:inline</code> also exists but is still very buggy. More are very likely to follow though.</p>
-<pre class="source source-nujel">
-[defn double [α]
+{{ [component :Nujel #nil
+"[defn double [α]
:inline
- "Return α multiplied by 2"
+ \"Return α multiplied by 2\"
[* 2 α]]
[double 2] ; => 4
-;; Doesn't really make much difference here, but helps especially with some simple predicates like [zero?]
-</pre>>
\ No newline at end of file
+;; Doesn't really make much difference here, but helps especially with some simple predicates like [zero?]"] }}<
\ No newline at end of file
M content/language/chapters/1-9_modules.html => content/language/chapters/1-9_modules.html +20 -22
@@ 19,14 19,14 @@ date: "2022-08-31"
<h3>1. Importing from built-in modules</h3>
<p>Let's start with some simple example on how to import a function from a builtin stdlib module:</p>
-<pre class="source source-nujel">
-[import [red] :ansi]
-[println [red "Test"]] ; This should be printed in red in most terminal
+
+{{ [component :Nujel #nil
+"[import [red] :ansi]
+[println [red \"Test\"]] ; This should be printed in red in most terminal
[import [blue green] :ansi] ; You can also import multiple symbols at once
-[println [cat [blue "Te"] [green "st"]]]
+[println [cat [blue \"Te\"] [green \"st\"]]]
[import [yellow :as ansi-yellow] :ansi] ; Renaming is also possible
-[println [ansi-yellow "Test"]]
-</pre>
+[println [ansi-yellow \"Test\"]]"] }}
<box-wrap box-color="yellow">
<h3>Expected changes</h3>
@@ 35,31 35,29 @@ date: "2022-08-31"
<h3>2. Writing your own modules</h3>
<p>You don't need to do anything special, every Nujel source file can be treated as a module. When it is imported for the first time all top-level code will be executed and all values marked for :export will be added to a map. The name is also determined by the path in the filesystem.</p>
-<pre class="source source-nujel">
-; test.nuj
+{{ [component :Nujel #nil
+"; test.nuj
[defn test []
- :export ; The easiest way to export is to decorate your [defn] forms with the :export keyword, it will export the function with the name specified
- [println "Test has been called!"]]
+ :export ; The easiest way to export is to decorate your [defn] forms with the :export keyword, it will export the function with the name specified
+ [println \"Test has been called!\"]]
[defn test-2 []
- :export-as test-zwei ; You can also use the :export-zwei decorator if you want different internal and external names
- [println "Test has been called!"]]
+ :export-as test-zwei ; You can also use the :export-zwei decorator if you want different internal and external names
+ [println \"Test has been called!\"]]
[def internal-value 123]
-[export exported-value internal-value] ; Exporting arbitrary values is best done with the [export] macro, it has pretty much the same form as [def]
-</pre>
+[export exported-value internal-value] ; Exporting arbitrary values is best done with the [export] macro, it has pretty much the same form as [def]"] }}
<h3>3. Importing your own modules</h3>
<p>To import modules from the filesystem, mainly ones you wrote yourself, you need to specify a relative path to the file, without the .nuj ending.</p>
-<pre class="source source-nujel">
-; test.nuj
+{{ [component :Nujel #nil
+"; test.nuj
[defn test []
- :export
- [println "Test has been called!"]]
+ :export
+ [println \"Test has been called!\"]]
; main.nuj
-[import [test] "./test"]
-[test] ; => "Test has been called!"
-</pre>
+[import [test] \"./test\"]
+[test] ; => \"Test has been called!\""] }}
<h3>4. Executing modules</h3>
<p>One uncommon feature of the Nujel module system might be that there is no big distinction between executables and libraries, executable modules just have to export a [main] function which then might be called with the commandline arguments.</p>
@@ 69,4 67,4 @@ date: "2022-08-31"
<box-wrap box-color="green">
<h3>Reasoning</h3>
<p>I got inspired by <a href="https://en.wikipedia.org/wiki/Oberon_(programming_language)">Oberon</a> here, and so far it has already become quite convenient to be able to bundle multiple "executables" with Nujel. It should become even more interesting once adding custom modules into a Nujel binary becomes easier and we enable for the default module to be changed easily. Then we could build specialized Nujel runtimes for specific programs with the end user not needing to know that it was built with Nujel.</p>
-</box-wrap>>
\ No newline at end of file
+</box-wrap>
M content/language/index.html => content/language/index.html +1 -1
@@ 4,7 4,7 @@ nav-title: "The Nujel programming language"
date: "2022-08-31"
+++
-<h1>An Introduction to the language</h1>
+<h1>The Nujel programming language</h1>
<p>
Most functions/macros borrow their name directly from <i>Clojure</i> or <i>Common Lisp</i>, while the reader syntax is most similar to <i>(Guile) Scheme</i>, with some changes due to different semantics.
</p>
M content/news.html => content/news.html +1 -1
@@ 4,4 4,4 @@ hide-prev-next: #t
+++
<h1>News</h1>
-<p>Started work on a SSG for this website</p>
+{{ [component :PostOverview] }}
A content/news/2022-09-11.html => content/news/2022-09-11.html +12 -0
@@ 0,0 1,12 @@
++++
+title: "Added a news section"
+author: "Ben"
+type: :post
+summary: "Mostly exciting due to all the progress that happened in the SSG"
+hide-prev-next: #t
+hide-in-nav: #t
+date: "2022-09-11"
++++
+
+<p>Finally added a news section, which might not be that exciting but this is actually the culmination of a lot of backend work in the SSG, since we can now automatically search through all content and show a summarized list and link to the detailed version, residing at a canonical URL. By now the entire template system has been rewritten in a more component oriented style, which should soon have a JSX like notation which can be used throughout the codebase. This is mainly because I am not a huge fan of Markdown, and would greatly prefer something like JSX.</p>
+
M ssg/content.nuj => ssg/content.nuj +2 -4
@@ 1,5 1,5 @@
[import [build :as loader/build] "loader"]
-[import [parse-frontmatter frontmatter] "theme"]
+[import [parse-frontmatter frontmatter get-frontmatter] "theme"]
[import [build-prev-next-list] "navigation"]
[defn get-out-path [ctx path]
@@ 20,9 20,7 @@
[defn load-frontmatter [path name content-frontmatter]
[when-not [path/content? path] [return #nil]]
- [def fm [parse-frontmatter [frontmatter [slurp path]]]]
- [tree/set! fm :href name]
- [tree/set! fm :depth [- [length [split name "/"]] 1]]
+ [def fm [get-frontmatter path name [frontmatter [slurp path]]]]
[tree/set! content-frontmatter [string->keyword name] fm]]
[defn build-content [ctx path content-frontmatter build-fun]
M ssg/context.nuj => ssg/context.nuj +0 -1
@@ 3,7 3,6 @@
:export
@[ :path path
:title "Some site"
- :author "Anonymous"
:content-root-dir "content"
:deploy-dir "deploy"
M ssg/loader/html-content.nuj => ssg/loader/html-content.nuj +0 -2
@@ 10,14 10,12 @@
[cat path "/" [car parts]]
[car parts]]]
[cdr! parts]
- [println path]
[mkdir path]]]
[defn build [ctx path]
"Loader that just copies the file over"
[def dest-path [get-out-path ctx path]]
[mkdir-safe [path/dirname dest-path]]
- [println dest-path]
[spit dest-path [render ctx path]]
[return dest-path]]
A ssg/pretty.nuj => ssg/pretty.nuj +14 -0
@@ 0,0 1,14 @@
+[defn pp-nujel-top [source i pp]
+ [def len [buffer/length i]]
+ [while [< i len]
+ [def c [buffer/ref source i]]
+ [case c
+ [\; [set! i [pp-nujel-comment-eol source [inc i] pp]]]
+ [otherwise [pp 'char-write c]
+ [inc/int i]]]]]
+
+[defn pretty-print-nujel [source]
+ :export
+ [def pp [make-string-output-port]]
+ [pp-nujel-top source 0 pp]
+ [pp 'return-string]]
M ssg/theme.nuj => ssg/theme.nuj +25 -10
@@ 1,5 1,9 @@
[import [build :as navigation/build] "navigation"]
+[defn Nujel [source]
+ [import [pp-nujel] :pretty/nujel]
+ [pp-nujel source :html]]
+
[def themes @[]]
[defn add-theme [name mod]
[tree/set! themes name [module/load mod [current-closure]]]]
@@ 67,15 71,17 @@
:export
[cdr [split-frontmatter text]]]
+[defn get-frontmatter [path name raw-fm]
+ :export
+ [def fm [parse-frontmatter raw-fm]]
+ [when-not [tree/has? fm :type] [tree/set! fm :type :page]]
+ [tree/set! fm :href name]
+ [tree/set! fm :depth [- [length [split name "/"]] 1]]
+ [return fm]]
+
[defn parse-content [ctx path raw-content *page-meta*]
"Parses content, evaluating all embedded Nujel forms"
:export
- [defn render-link [target]
- [render-link* ctx path target]]
- [defn get-href [target]
- [get-href* ctx path target]]
- [defn navigation [depth]
- [navigation/build ctx depth]]
[eval [cons 'cat [theme/split raw-content]]]]
[defn parse-frontmatter [fm]
@@ 136,6 142,12 @@
[defn navigation [depth]
[navigation/build [get-ctx] depth]]
+[defn get-posts []
+ [def ret #nil]
+ [doseq [p [tree/values [tree/ref [get-ctx] :frontmatter]] ret]
+ [when [== :post [tree/ref p :type]]
+ [cons! p ret]]]]
+
[defn include-resource [res-path]
[include-resource* [get-ctx]
[fmt "{}/{res-path}" [get-component-path]]
@@ 169,7 181,7 @@
[component-fun props children]]
[defn template [name props children]
- [def template-fun [tree/ref [tree/ref [get-ctx] :templates] name]]
+ [def template-fun [tree/ref [tree/ref [get-ctx] :templates] [or name :page]]]
[when-not template-fun [exception "Can't find a template named: " name]]
[template-fun props children]]
@@ 198,8 210,11 @@
[defn render [ctx path]
:export
[def page-parts [split-frontmatter [slurp path]]]
- [set-page-ctx! ctx path [parse-frontmatter [car page-parts]]]
- [def content [parse-content ctx path [cdr page-parts] [get-meta]]]
- [def ret [template :main #nil content]]
+ [def meta [get-frontmatter path
+ [string/cut path [inc [length [tree/ref ctx :content-root-dir]]]]
+ [car page-parts]]]
+ [set-page-ctx! ctx path meta]
+ [def content [parse-content ctx path [cdr page-parts] meta]]
+ [def ret [template [tree/ref [get-meta] :type] meta content]]
[reset-page-ctx!]
[return ret]]
M => +5 -8
@@ 1,8 1,5 @@
<div>
by {{ [or [ref [get-ctx] :author] "Anonymous"] }}
</div>
<div>
{{ [and [tree/ref [get-meta] :date]
[cat "Published " [tree/ref [get-meta] :date]]]
}}
</div>
{{
[case [tree/ref [get-meta] :type]
[:page [component :PageFooter]]
[otherwise ""]]
}}
A ssg/theme/default/components/Nujel.html => ssg/theme/default/components/Nujel.html +1 -0
@@ 0,0 1,1 @@
+<pre class="source source-nujel">{{ [Nujel children] }}</pre>
A => +12 -0
@@ 0,0 1,12 @@
<div>
{{ [when [tree/ref [get-ctx] :author]
[cat "by " [tree/ref [get-ctx] :author]]] }}
</div>
<div>
{{ [and [tree/ref [get-meta] :date]
[cat "Published " [tree/ref [get-meta] :date]]]
}}
{{ [and [tree/ref [get-meta] :modified-date]
[cat "Last modified " [tree/ref [get-meta] :modified-date]]]
}}
</div>
A ssg/theme/default/components/PostOverview.html => ssg/theme/default/components/PostOverview.html +3 -0
@@ 0,0 1,3 @@
+{{
+ [join [map [get-posts] [fn [p] [component :PostSnippet p #nil]]] ""]
+}}<
\ No newline at end of file
A ssg/theme/default/components/PostSnippet.html => ssg/theme/default/components/PostSnippet.html +10 -0
@@ 0,0 1,10 @@
+<div class="post-snippet">
+ <h4 class="post-date">{{ [tree/ref props :date] }}</h4>
+ <h3>
+ <a href="{{ [get-href [tree/ref props :href]] }}">
+ {{ [tree/ref props :title] }}
+ </a>
+ </h3>
+ <p>{{ [tree/ref props :summary] }}</p>
+ <a href="{{ [get-href [tree/ref props :href]] }}">Read more</a>
+</div>
M ssg/theme/default/resources/main.css => ssg/theme/default/resources/main.css +206 -148
@@ 1,75 1,75 @@
body {
- background: #fcfdff;
- color: #333;
- font-family:'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
- line-height:1.3rem;
- padding: 0;
- margin: 0;
+ background: #fcfdff;
+ color: #333;
+ font-family:'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
+ line-height:1.3rem;
+ padding: 0;
+ margin: 0;
}
h1,h2,h3,h4,h5,h6 {
- margin: 0 0 0.5em 0;
+ margin: 0 0 0.5em 0;
}
h1,h2,h3,h4,h5,h6,p,li {
- line-height:1.4em;
+ line-height:1.4em;
}
h1,h2,h3 {
- font-family: Georgia, 'Times New Roman', serif;
+ font-family: Georgia, 'Times New Roman', serif;
}
ul {
- margin:0 0 2em;
- padding:0;
+ margin:0 0 2em;
+ padding:0;
}
ul > li {
- margin:0 0 0.5em;
- list-style-position: inside;
+ margin:0 0 0.5em;
+ list-style-position: inside;
}
-pre {
- overflow: auto;
- margin-left: 1em;
- padding: 0.5em;
- border-left: 1px dashed;
- background: #fcfdff;
- color: #333;
- padding: .75em .5em;
- font-family: 'Courier New', Courier, monospace;
+pre {
+ overflow: auto;
+ margin-left: 1em;
+ padding: 0.5em;
+ border-left: 1px dashed;
+ background: #fcfdff;
+ color: #333;
+ padding: .75em .5em;
+ font-family: 'Courier New', Courier, monospace;
}
nav.top-chapter-nav {
- border-bottom: solid 1px #000;
- padding-bottom:0.5rem;
- margin-bottom:1rem;
+ border-bottom: solid 1px #000;
+ padding-bottom:0.5rem;
+ margin-bottom:1rem;
}
nav.top-chapter-nav a {
- margin-top:1rem;
- display:inline-block;
+ margin-top:1rem;
+ display:inline-block;
}
nav.bottom-chapter-nav {
- border-top: solid 1px #000;
- padding-top:0.5rem;
- margin-top:3rem;
+ border-top: solid 1px #000;
+ padding-top:0.5rem;
+ margin-top:3rem;
}
nav.chapter-nav::after {
- content:'';
- display:block;
- position:relative;
- clear:both;
+ content:'';
+ display:block;
+ position:relative;
+ clear:both;
}
nav.chapter-nav .prev-chapter {
- float:left;
+ float:left;
}
nav.chapter-nav .next-chapter {
- float:right;
+ float:right;
}
footer {
@@ 77,99 77,99 @@ footer {
}
footer div {
- font-size: .9em;
- text-align: right;
- margin:0px auto;
- width:100%;
- display:block;
- max-width:940px;
+ font-size: .9em;
+ text-align: right;
+ margin:0px auto;
+ width:100%;
+ display:block;
+ max-width:940px;
}
.top-nav {
- padding-top:0.5rem;
- text-align:right;
+ padding-top:0.5rem;
+ text-align:right;
}
.top-nav a {
- display:inline-block;
- margin-left:1rem;
- border-radius:0.5em;
+ display:inline-block;
+ margin-left:1rem;
+ border-radius:0.5em;
}
main {
- margin:0px auto;
- width:100%;
- display:block;
- max-width:940px;
+ margin:0px auto;
+ width:100%;
+ display:block;
+ max-width:940px;
}
header nav {
- font-size: 1.2em;
- margin:0px auto;
- width:100%;
- display:block;
- max-width:940px;
+ font-size: 1.2em;
+ margin:0px auto;
+ width:100%;
+ display:block;
+ max-width:940px;
}
header nav::after {
- content:'';
- display:block;
- position:relative;
- clear:both;
+ content:'';
+ display:block;
+ position:relative;
+ clear:both;
}
header nav a {
- line-height:2rem;
- vertical-align: top;
+ line-height:2rem;
+ vertical-align: top;
}
.top-nav a.logo-link {
- float:left;
- line-height:2rem;
- margin-left:0;
+ float:left;
+ line-height:2rem;
+ margin-left:0;
}
.logo-link span {
- line-height:2rem;
- vertical-align: top;
+ line-height:2rem;
+ vertical-align: top;
}
.logo-link img {
- display:inline-block;
- width:auto;
- height:2rem;
- margin-right:0.5rem;
+ display:inline-block;
+ width:auto;
+ height:2rem;
+ margin-right:0.5rem;
}
a {
- text-decoration: none;
- color: #1155AA;
+ text-decoration: none;
+ color: #1155AA;
}
a:focus,
a:hover {
- text-decoration: none;
- color: #28F;
+ text-decoration: none;
+ color: #28F;
}
a.anchor {
- color: black;
+ color: black;
}
.date {
- font-style: italic;
+ font-style: italic;
}
.title {
- margin-bottom: 1em;
+ margin-bottom: 1em;
}
.article-meta {
- margin-bottom: 2.2em;
+ margin-bottom: 2.2em;
}
.archive-title {
- font-size: 1em;
+ font-size: 1em;
}
.article-title {
- font-size: 2em;
+ font-size: 2em;
}
.article-content {
- margin-left: 2.2em;
+ margin-left: 2.2em;
}
.tag-low { font-size: .8em; font-weight: 200 }
@@ 178,86 178,74 @@ a.anchor {
tt { font-size: .9em; font-family: 'Courier New', Courier, monospace; }
warning-text {
- color:#f31;
+ color:#f31;
}
box-wrap {
- border: solid 1px #f31;
- border-left-width: 8px;
- background: #f314;
- color: #333;
- padding:0.6rem;
- box-sizing:border-box;
- display: block;
- margin-bottom:0.6rem;
- border-radius: 0 0 1rem 0;
+ border: solid 1px #f31;
+ border-left-width: 8px;
+ background: #f314;
+ color: #333;
+ padding:0.6rem;
+ box-sizing:border-box;
+ display: block;
+ margin-bottom:0.6rem;
+ border-radius: 0 0 1rem 0;
}
box-wrap[box-color="red"] {
- border-color: #f31;
- background-color: #f314;
+ border-color: #f31;
+ background-color: #f314;
}
box-wrap[box-color="yellow"] {
- border-color: #f81;
- background-color: #f814;
+ border-color: #f81;
+ background-color: #f814;
}
box-wrap[box-color="green"] {
- border-color: #581;
- background-color: #5814;
+ border-color: #581;
+ background-color: #5814;
}
box-wrap > *:last-child {
- margin-bottom:0;
+ margin-bottom:0;
}
main > h3 {
- border-top: solid 1px #000;
- padding-top:0.5rem;
- margin-top:3rem;
+ border-top: solid 1px #000;
+ padding-top:0.5rem;
+ margin-top:3rem;
}
code {
- background: #00000030;
- font-family: 'Courier New', Courier, monospace;
- color:#000;
- padding:0.05rem 0.2rem;
- display: inline-block;
- border-radius: 2px;
-}
-
-.source {
- margin-left: 0;
- display: block;
- padding: 0.6rem;
- border-radius:0;
- border: none;
- border-left: dotted 1px #13f;
- color: #000;
- background-color: #13f2;
- line-height:1.3rem;
+ background: #00000030;
+ font-family: 'Courier New', Courier, monospace;
+ color:#000;
+ padding:0.05rem 0.2rem;
+ display: inline-block;
+ border-radius: 2px;
}
table {
- border-collapse: collapse;
- width:100%;
- overflow-x:auto;
+ border-collapse: collapse;
+ width:100%;
+ overflow-x:auto;
}
table tbody tr:first-child td {
- border-top: none;
+ border-top: none;
}
table th {
- background-color: #13f2;
- text-align:left;
- padding:0.3rem 0.3rem 0.1rem;
+ background-color: #13f2;
+ text-align:left;
+ padding:0.3rem 0.3rem 0.1rem;
}
table td {
- border:solid 1px #13f4;
- padding:0.1rem 0.3rem;
+ border:solid 1px #13f4;
+ padding:0.1rem 0.3rem;
}
/* Stolen from lisppaste for the colorize output of 3bmd */
@@ 292,27 280,97 @@ span.paren6:hover { color : inherit; background-color : #FFBAFF; }
@media (max-width: 680px) {
- main {
- padding-top: 0em
- }
+ main {
+ padding-top: 0em
+ }
- .title {
- margin-left: 0.1em;
- }
+ .title {
+ margin-left: 0.1em;
+ }
- .article-meta {
- margin-bottom: 1em;
- }
+ .article-meta {
+ margin-bottom: 1em;
+ }
- .article, .article-content {
- margin-left: 0em;
- text-align: justify;
- }
+ .article, .article-content {
+ margin-left: 0em;
+ text-align: justify;
+ }
+
+ img {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+ width: 100%;
+ }
+}
+
+body[page-type="post"] > main {
+ padding: 2rem 0;
+ margin: 2rem auto;
+ border-top: solid 1px #000;
+ border-bottom: solid 1px #000;
+}
+
+.post-date {
+ float:right;
+ font-size:0.8rem;
+ color:#000a;
+}
- img {
- display: block;
- margin-left: auto;
- margin-right: auto;
- width: 100%;
- }
+.byline {
+ clear:right;
+ float:right;
+ font-size:0.8rem;
+ color:#000a;
}
+
+.post-snippet {
+ padding:0.6rem;
+ border: solid 1px #581;
+ background-color: #5814;
+ border-top-width: 8px;
+}
+
+.post-snippet::after {
+ content:'';
+ display:block;
+ position:relative;
+ clear:both;
+}
+
+h2 a,
+h3 a,
+h4 a,
+h5 a,
+h6 a{
+ color:inherit;
+}
+
+.source {
+ margin-left: 0;
+ display: block;
+ padding: 0.6rem;
+ border-radius:0;
+ border: none;
+ color: #d7dae0;
+ background-color: #000;
+ line-height:1.3rem;
+}
+
+.nujel-hl-0 {color:#2d3139;}
+.nujel-hl-1 {color:#e06c75;}
+.nujel-hl-2 {color:#98c379;}
+.nujel-hl-3 {color:#e5c07b;}
+.nujel-hl-4 {color:#528bff;}
+.nujel-hl-5 {color:#c678dd;}
+.nujel-hl-6 {color:#56b6c2;}
+.nujel-hl-7 {color:#d7dae0;}
+.nujel-hl-8 {color:#7f848e;}
+.nujel-hl-9 {color:#f44747;}
+.nujel-hl-10 {color:#98c379;}
+.nujel-hl-11 {color:#e5c07b;}
+.nujel-hl-12 {color:#528bff;}
+.nujel-hl-13 {color:#7e0097;}
+.nujel-hl-14 {color:#56b6c2;}
+.nujel-hl-15 {color:#d7dae0; font-weight: bold;}<
\ No newline at end of file
R ssg/theme/default/templates/main.html => ssg/theme/default/templates/page.html +1 -1
@@ 7,7 7,7 @@
<link rel="stylesheet" href="{{ [include-resource "resources/main.css"] }}"/>
{{ [component :Favicon] }}
</head>
- <body>
+ <body page-type="{{ [keyword->string [or [tree/ref props :type] :page]] }}">
<header>
{{ [component :Header] }}
</header>
A ssg/theme/default/templates/post.html => ssg/theme/default/templates/post.html +25 -0
@@ 0,0 1,25 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <title>{{ [page-title] }}</title>
+ <meta http-equiv="content-type" content="text/html;" charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <link rel="stylesheet" href="{{ [include-resource "resources/main.css"] }}"/>
+ {{ [component :Favicon] }}
+ </head>
+ <body page-type="{{ [keyword->string [or [tree/ref props :type] :post]] }}">
+ <header>
+ {{ [component :Header] }}
+ </header>
+ <main>
+ <h4 class="post-date">{{ [tree/ref props :date] }}</h4>
+ {{ [when [tree/ref props :author]
+ [cat "<h4 class=\"byline\">by " [tree/ref props :author] "</h4>"]] }}
+ <h1>{{ [tree/ref props :title] }}</h1>
+ {{ children }}
+ </main>
+ <footer>
+ {{ [component :Footer] }}
+ </footer>
+ </body>
+</html>