composable io
Continuing again from the last post. What if I have a stream of bytes from a socket and I want to encode it as a string using utf8 and then I want to lex those strings in to tokens? What if I want a composition stack from the basic io
Continuing again from the last post. What if I have a stream of bytes from a socket and I want to encode it as a string using utf8 and then I want to lex those strings in to tokens? What if I want a composition stack from the basic io
Picking up from where we left off, I wanted to next write about how to do bulk operations as part of io - but the purpose of defining the io library was to then simulate inlining the methods to create the C code. Here's the problem: input: Range
Let's try and write the simplest io library - moving objects from one array to another; such as characters in a string. One at a time. We can look at bulk later. input: Range = {start: 1, stop: 10}. output: Array[Integer, 10]. // example: input map> [+ 1] copy&
Initially I intended to have multiple backends but the path of least resistance is to use C as the backend for now and to come back to the idea of multiple backends later. Given that - just what do I need to do to kick start things; how much effort
This isn't entirely a post about fibonacci. But it's a good example to work off of to solve one of the more vexing and annoying aspects of manual memory management. Re-running code with state in a reusable fashion. Let's get our baseline: [self next-fibonacci-from:
Closures are amazing. As a concept they allow you to 'set and forget' the scope you're working with. This is best combined with garbage collection so that nothing can disappear on you while the closure waits. In Smalltalk we typically use them for late-evaluation of loops
Sometimes you don't want to write out a full class description with types annotated. You don't care that there's inefficiencies in memory, that the type of the object needs to be stored in memory with its value. Sometimes you just want to smash out
Almost all uses of deferred evaluation are about cleaning up things that are dangling. Freeing memory, releasing contexts, closing files, etc. There's two kinds of 'thing' we need to consider when we want to clean up: 1) something on the stack that owns on to stuff
I think it's a good idea to write simple programs from time to time to see what you might have right or wrong with the language design. So let's try writing a program that counts the number of vowels in a string. vowels :: 'aeiou'
Right now () creates a subexpression. It is an expression in the parse tree which means something like foo () has no meaning in the language where foo is a variable. Given that foo[index] does have a meaning, and foo {a: b} also has a meaning (array indexing and object focusing)
It dawned on me looking at some old notes that I once had an idea for generic container using the sub-index syntax. Instead of Array of: Person you'd write Array[Person]. I wondered - well, can't I do that in STZ right now? and do I
Instead of writing an interpreter, what if we built the language so it compiles every layer from the very bottom to the very top. What limitations would we need to place on ourselves to make that possible? The first thing we need to consider is what the output of the
stz
Template strings solve a lot of text based feedback problems. Instead of having string builders and concatenations and memory allocation headaches, we can instead create a composite string using a template. [self make-html: page] [self: Progam, page: Page -> String | `<html> <head> <title>
stz
Signed and Unsigned Integers are pretty core to programming in a not-assembler level language. For STZ I'd like to represent Signed and Unsigned Integers as, internally, binary data. bits: (Binary of: 8) = 0b01010101. How many bits you have determines what kind of storage it can fit in to.
stz
Meta-programming is an important capability to have in a language. It's not necessary if you want to build every bell and whistle in to a language at its core, but it allows you to incrementally improve a language if you know you won't have it all
stz
Consider this innocent method: [list add: element] [List of: element, element -> list | ...code goes here...] And now ruin its day: list :: 1. element :: 2. What does this mean? We've now effectively locked the method signature to only one use case: 1 add: 2 which will fail
stz
We twisted our arm a little bit to make stuff in a second allocator and then copy the result back in to the first allocator. It'd be better if it were allocated in the first allocator to begin with and we didn't have any temporary stuff
stz
STZ is multithreaded. Therefore globals that aren't const are very bad. We still need a way to conveniently pass around some 'global like' information though. Let's consider the most important one - memory management. There's a school of thought that says you
stz
The idea that we can support different kinds of coroutines libraries in STZ makes me happy. So much so I think it's worth abandoning the Smalltalk-80 fork API and providing the libraries as-is, with some nice wrapping to clean them up. Tina provides us not only with asynchronous
stz
Let's approach coroutines as something we utilise rather than get in the language. Using the minicoro library and the ability to use C code directly from STZ, let's see what it'll take to utilise. Starting with the basics - let's create a
stz
It will come as no surprise to people who know me that using <> as a grouping mechanism for any kind of information is distasteful. It visually conflicts with less than, greater than, etc. It's already visually conflicting with select> and do>. I made all
stz
(Integer | 1,) (my-object | b | c) (MyType | my-object | b | c,) The | is used in a lot of places to delineate between 'the type' and 'the code' inside of list creation (type | ...elements...), methods and blocks [type | ...code...], new object {type | ...code list...}. We have also been using
stz
There's two ways to pass objects as parameters in a method. Either as a copy or as a reference. If we're simply using the data, we pass it as a copy. If we intend to modify it we pass it as a reference. Copying large objects
stz
Just for fun let's try and write a low level program using mmap to read a file and write it to stdout. requires: 'libc'. [main: arguments] [filename := arguments[0] else| panic. // Open file or die fd := (libc open: filename flags: O_RDONLY) else| panic. --- [libc