Okay a first take at this new "worklog" style newsletter.
This week we "pushed to master" with the new hyperlink.academy site. You can take a sneak peak if you like but stuff there is still extremely under-construction so please be generous.
Pushing to master is a step before even a public alpha but it still feels worth celebrating. I remember how good it felt the first time I published anything related to fathom online. What's interesting is that it never stopped feeling good. Every little change I'd push online was a thrill, and it is to this day.
Next we're going to run a small course called (of course) The Meta Course. A course for creating courses. The plan is to get some cool folks together and try out an experimental structure to help us develop some interesting ways of learning some interesting things. Along the way we'll get to test out the site, all the integration work with discourse, and just get a feel for what learning looks like in this new system we're exploring.
I'm also excited to start refactoring now that I've laid out the vast majority of the pages and features of the site. There's a lot of redundant code that I'm just itching to delete and pull into functions.
There's also a lot of opportunities to make things easier to change and iterate on. For example, I plan on pulling out all of the copytext from the functions defining layout and store them in one easily modifiable and discoverable place in files.
Last week I set myself a collection of loosely related tasks for my experimental note taking language: Create an error system, implement a filter/query function, and start defining the mapping between fancynote and html in fancynote itself.
Almost none of that. Instead I took a step back and started laying down the groundwork to hopefully get there
I simplified the parser function, removing a full 8 lines (more impressively stated as 26%)
function generateSubtree(input:Token[]): [Atom | List, number] {let AST:AST = []if(input[0].value !== "[") return [{type: 'atom', value: input[0].value}, 1] // If it's not a list just return// if it is a list, process every elementfor(let i = 1; i < input.length;){let token = input[i].valueif(token === ']') return [{type: 'list', children: AST}, i+1] // if the list is closed returnlet [block, length]= generateSubtree(input.slice(i)) // other wise process the rest of the tokens, either a single token or a listAST.push(block) // add that to the list you've goti += length // move forward by however many tokens you processed}throw Error('input does not terminate')}export function parser(input:string):AST {let tokens = lexer(input)let output:AST = []let position = 0while(position < tokens.length) {let [node, lengthParsed] = generateSubtree(tokens.slice(position))output.push(node)position += lengthParsed}return output}
I also simplified the lexer
, the program which handles creating the list of
tokens this one needs, by removing it's wierd newlines handling. This was to
enable printing out the program again (more on that later)
I also started experimenting with an emacs mode for writing fancynote. I lasted a whole week before trying to do more text-editor-y stuff lol.
The emacs mode looks like this:
(provide 'fancynote)(defconst fancynote-mode-syntax-table (let ((table (make-syntax-table)))(modify-syntax-entry ?\[ "(]" table)(modify-syntax-entry ?\] ")]" table)table))(add-to-list 'auto-mode-alist '("\\.fn\\'" . fancynote-mode))(define-derived-mode fancynote-mode nil "Fancynote""major mode for editing fancynote files.":syntax-table fancynote-mode-syntax-table(set (make-local-variable 'indent-line-function) 'indent-relative)(display-line-numbers-mode)(auto-fill-mode))
Mostly it just sets it up so files I open that end with .fn
work the way I
expect. Maybe it'll be a good basis for experiments but I'm not feeling to
excited about trying to make sense of elisp.
Instead I'm going to do a broader survey of my options in building text-editing interfaces. I started taking some notes here.
I also started the bare minimum of a REPL, which I talked about last time as one of the ways I hope to make fancynote as rich an experience as something like Notion or Roam.
let command = ''rl.prompt()rl.on('line', (line)=>{command += '' + linetry {let ast = parser(command)let text = astToString(ast)console.log(text)command = ''rl.prompt()} catch(e) {command += '\n'rl.prompt(true)}})
It's a small loop. It asks the user for input with rl.prompt()
then if that
input terminates it parses it, and then turns that parsed AST back into a
string and returns it. Astute among you may have noticed that this does
absolutely nothing.
However, writing astToString
was more complicated than you
might think. When you parse a file you lose a lot of contextual information.
Things like spaces and newlines aren't important for the structure of your
program so the parser just skips over them. Turning the data-structure of your
program back into a human readable string, means you have to keep enough
information to do so.
Anyways, this REPL is going to be the basis for future experiments with computational work!
This week I wanted to set up a proper time for writing. It didn't happen so I'm going to try again.
By my newsletter next week I will: