Bay 12 Games Forum

Please login or register.

Login with username, password and session length
Advanced search  
Pages: 1 2 3 [4] 5

Author Topic: Programming Language: Poslin  (Read 10651 times)

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #45 on: September 13, 2015, 10:06:27 am »

Poslin0.2.4 is up. Nevermind, I was too fast and my current connection too slow. It'll go up tomorrow.

Structs have been added to the standard library. They are implemented as arrays. They do not print nicely as the printer currently isn't programmable (which I hope to change in the near future).

A struct is defined like this:
Code: [Select]
foo [ a b ]struct
This will define the `foo` type (this won't be returned by the `type` operation, use `type*` instead), an operation for creating an instance of `foo` called `new-foo` and two operations per slot, one for getting (`foo->a`), the other for setting (`foo<-a`) the value of the respective slot.
Structs are, like almost anything else, immutable. If you want mutable structs, you will need to use bindings in the slots and manipulate those. Maybe a later iteration of structs will rectify this.

The primary operations `array-size` and `string-size` have been added. Also the operation `stack-size` has been added to the standard library.
« Last Edit: September 13, 2015, 11:11:37 am by Antsan »
Logged
Taste my Paci-Fist

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #46 on: September 14, 2015, 09:15:00 am »

Poslin0.2.4 for linux-x86 is actually up now.
A question: How many of the versions should I keep online? Currently it seems like there is no point to keeping more than the last one, but maybe I am wrong. Thoughts?

`fold-stack` and `zip-stacks` have been optimized a bit.
Both were defined recursively in the obvious manner. This is really really very suboptimal, as this way arguments which don't change between any of the calls are passed again and again, on every step of the recursion. That is lots of overhead. The trick is to use a local function inside which only passes the parameters that actually change. Removing the argument passing stuff (which entails a lot of juggling around environments) was also easier this way, which makes the overhead of recursion even smaller (with only one lexical environment ever opened).

I think I forgot to mention `repeat`. This is an immediate operation which takes a thread and a positive integer. It creates a thread which is just the given operation repeated so many times. So, instead of writing
Code: [Select]
drop-here & drop-here & drop-here &
it is possible to write
Code: [Select]
drop-here & 3 repeat
Actually the latter is actually equivalent to
Code: [Select]
[ drop-here & drop-here & drop-here & ]&
which leaves one thread on the stack instead of three.
« Last Edit: September 14, 2015, 09:18:01 am by Antsan »
Logged
Taste my Paci-Fist

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #47 on: September 15, 2015, 06:15:46 pm »

Say goodbye to the most common case of nested ifs and say hello to `]cond`:
Code: [Select]
signum
[ [ [ dup-here & negative? &
    | -1 ]
    [ dup-here & zero? &
    | 0 ]
    [ dup-here & positive? &
    | 1 ]
  ]cond
  drop-here & <<
]o

`splice` splices a stack into the current stack:
Code: [Select]
[ 1 2 3 [ 4 5 6 ] ]
> splice !
[ 1 2 3 4 5 6 ]

`apply` calls an operation on a given stack as if it was the current stack, then splices the resulting stack:
Code: [Select]
[ 1 2 3 [ 4 5 ] ]
> + & apply !
[ 1 2 3 9 ]
> [ 6 7 8 ]
[ 1 2 3 9 [ 6 7 8 ] ]
> + & apply !
[ 1 2 3 9 6 15 ]

`:[` has been in for a while now, but I think I never mentioned it. It opens a stack locally as the current stack:
Code: [Select]
[ a b [ c d e ] ]
> :[
[ c d e ]
> f g
[ c d e f g ]
> ]
[ a b [ c d e f g ] ]

Also now I only need to remember to update the version number in one place.
« Last Edit: September 15, 2015, 06:18:48 pm by Antsan »
Logged
Taste my Paci-Fist

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #48 on: September 23, 2015, 10:10:43 am »

Poslin0.2.6-0.1.0 is up. From this version on every distribution will have two version numbers – the first one for the binary runtime, the second one for the standard library. As this is the first version of the standard library with a version number, it's version 0.1.0.


The runtime 0.2.6 does not contain `print` anymore.
It instead contains `write-char`, `read-char`, `open`, `close`, `.stdout` and `.stdin`.
`open` takes two parameters: A string containing the path to the file to be opened and one of the symbols `write`, `read` or `rw`.
`.stdout` and `.stdin` constantly return the respective streams.


`print` is implemented in the standard library now. It takes a string and writes it to the standard output stream.

`var-mod` has been changed to consume one argument immediately and convert it to a thread. So you can now write
Code: [Select]
i 1+ var-modinstead of
Code: [Select]
i 1+ & var-mod
`run-isolated` is an immediate operation to convert an operation into a thread which does not consume arguments. The operation converted is expected to return exactly one result to the return stack.
Code: [Select]
> 1 2 + !
[ 3 ]
> 4 + run-isolated !
[ 3 4 7 ]

Defining a struct now also defines a matching closing bracket with which an instance of the defined struct can be created.
Code: [Select]
> foo [ a b ]struct
[]
> [ 1 2 ]foo
[ [ |unique7442 1 2 ]array ]
> dup-here ! foo->b !
[ [ |unique7442 1 2 ]array 2 ]
> foo->a & << !
[ 1 2 ]
This one is unfinished and only works right when all the slots values are already computed when the closing bracket is reached.
« Last Edit: September 23, 2015, 10:12:53 am by Antsan »
Logged
Taste my Paci-Fist

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #49 on: September 24, 2015, 07:07:15 pm »

Okay, I am quite happy with the standard library as it is now, but in the process of coming up with an interface for a test framework I realized that the error handling, as far as it can be called that, is completely unusable. While it probably would be possible to write try/catch constructs it would be entirely impossible to write a version that somehow knows how to handle errors thrown by misuse of a primary operation or the use of `error`.

In short: Currently there is no way to handle something like
Code: [Select]
0 reciprocal !
That is completely unacceptable but unfortunately I am currently a bit stumped on how to devise a better system.

A big hurdle is that all Poslin code needs to be sequentially processable, or, put another way, there is no such thing as code blocks or something which can be associated with a wrapping error handler or something.
What can be wrapped with an error handler, though, is a thread. This brings me to the first option:
This scheme requires two new Types (`:Error` and `:CheckedThread`) and 6 or 8 new primary operations (`new-error`, `error-message`, `error-data`, `handle-error`, `checked-thread-body`, `checked-thread-handler`; maybe `h<-` and `h->`, who knows what kind of crazy exception systems can be done with those two). `error` would become obsolete, moving to the standard library as a simpler version of `new-error` that doesn't bother with metadata.
One boon if this kind of system is that it doesn't do any kind of stack unwinding or anything like that, so you don't loose information. The downside of this approach is that, when wrapping with `handle-error` you need to anticipate every place in the wrapped thread the handled error could come from and make sure that it is handled correctly. I guess it would be possible to write a more… reasonable exception handling facility on top of this, which would be enough for me (because Poslin is all about building reasonable stuff on simple mechanisms), but, well it's just a guess.
Also, the additional handler stack seems a little bit like overkill. But only a little bit. Also I just realized that it might be possible to discard of checked threads (and the corresponding operations) entirely and implement `handle-error` with `h<-` and `h->`.

So, any thoughts on this? An alternative idea on how to do this? Tweaks to the proposed systems?
Logged
Taste my Paci-Fist

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #50 on: September 27, 2015, 09:34:52 am »

The proposed error handling system was bunk – it didn't do any return stack unwinding, which can be kind of inconvenient, because the state of the return stack after en error happens isn't really predictable that easily in all circumstances.

The current proposal (which I discussed with my little brother, who has been helping me with that kind of stuff for a while now) brings two new types (`:Exception` and `:HandledThread`) as well as 6 new operations (`unwind` (to replace `error`), `handle` (to construct handled threads), `exception-message`, `exception-data`, `exception-stack` and `thread-handle`). It also makes the interpreter slightly more complicated, which is a real bummer.

`unwind` consumes two objects: A string and some other kind of object. From these objects it constructs an `:Exception` object, with the string accessible with `exception-message`, the other object with `exception-data`. This object is put onto the current stack.
Afterwards, the return stack is unwound. This starts with the program counter (because other operations than `unwind` might start this process), pushing it onto the `exception-stack` of the exception object on top of the current stack (unwrapping it firs, if it is a handled thread). If the object is a handled thread, then the handle of said thread is called and `unwind` itself is done. If it is not a handled thread, the same happens with the popped off elements of the return stack, until a handled thread is found, whose "thread" part is pushed onto the `exception-stack` like all the other threads before while the handle is called.
If no handled thread is found, the `exception-message` is printed. If Poslin is running interactively, the user is then returned to the REPL, if Poslin is running non-interactively, the process is terminated.

The thread-front of a handled thread is just the thread-front of its thread part, the thread-back is the thread-back with the handle attached.

This scheme retains all information and thus allows implementing restarts and does return stack unwinding as expected.

`error` would then be
Code: [Select]
error
[ error unwind &
]o?
Huh, that was way easier than expected.
« Last Edit: October 01, 2015, 05:28:46 am by Antsan »
Logged
Taste my Paci-Fist

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #51 on: October 01, 2015, 06:40:01 pm »

Poslin0.3.0rc1-0.1.1 for linux-x86 is up.

The first release candidate of the 0.3.0 version of the runtime now includes the exception handling system outlined in the last post.
I tested the basic functionality, but I did not do much with it, yet, meaning there might be unexpected consequences of some kind. Most primary operations should now use the poslin exceptions. There is no prompt for whether you want to continue, you just end up in the REPL by default after an unhandled exception occurs.
Errors not handled by Poslin are handled as before. If you encounter one of those, please tell me!

The `error` operation has been moved to the standard library.
`new-exception` creates an exception object.
`throw` takes an exception object and throws it, but discards the previously collected exception stack in the process. I think to correct this it would be sensible to make `new-exception` and `throw` primary instead of `unwind`. Hrm… Well, not now.
Logged
Taste my Paci-Fist

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #52 on: October 02, 2015, 03:32:58 pm »

Poslin0.3.0rc1-0.1.2-linux-x86 is up.

The short life of `unwind` is already over. `new-exception` and `throw` now are primary operations.
`new-exception` takes as arguments a string, some object and a stack (which contains all the stuff collected from unwinding the return stack). It constructs an exception object out of those. You yourself probably won't want to use it.
`throw` throws a given exception.

The standard library has the new `signal`, which constructs an exception from a string and arbitrary data (supplying the empty stack for you) and then throws it. This is what you will want to use later when throwing some kind of exception which is not just a general "error".
If you just want to throw a general error with an error message, use `error`.

The standard library currently doesn't make use of `signal` itself and just throws errors for now. The primary operators are a bit better in this regard, but I think before there is an official specification (how "official" can a specification I make public for my own amusement even be?) these might be inconsistent and maybe even not quite useful in some instances for loosing some actually important information.
Logged
Taste my Paci-Fist

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #53 on: October 06, 2015, 09:17:29 am »

Poslin0.3.0rc3-0.1.4-linux-x86 is up

There was a bug that didn't handle some type errors as intended, throwing an implementation error when a type error was about to be handled. I am still not sure why it didn't throw an implementation error when I didn't try to handle it, but whatever – it's fixed now.
Also did some cleanup in the code base of the runtime.

The types `:Exception` and `:HandledThread` are now in the standard library – i forgot to include them the last time.
A new immediate operation has been added: `...`. You can use it to signify an undefined operation like this:
Code: [Select]
foo
[ ...
]o
It just throws an exception if `foo` should be called.
Logged
Taste my Paci-Fist

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #54 on: October 07, 2015, 05:45:18 pm »

Poslin0.3.0rc4-0.1.5-linux-x86 is up.

There was a bug in the runtime where the immediateness value of the noop `.` wasn't the false value but the empty stack (which is equivalent to NIL in Common Lisp, because that's the empty list). This worked because the runtime checks whether the value of the immediateness binding was equal to the true value, but it lead to problems when I wrote an operation assuming that all immediateness values were booleans.

There were a few bugs with forgotten `&`s in the standard library. Well, this is one of the problems with having to write `&` everywhere… :-[
But, rejoice, I've got a solution, and it's already implemented.
The standard library now contains three new operations:
`immediate?` which takes a symbol and returns whether it currently is immediate
`&(`, an immediate operation which makes every operation immediate, but changes every operation which was not immediate before to instead put its former thread onto the current stack.
`&[` is the same as `&(`, only that it also opens up a local stack.

So, instead of
Code: [Select]
2 + & 3 * &
you can now write
Code: [Select]
&( 2 + 3 * )
and instead of
Code: [Select]
foo [ 2 + & 3 * & ]o
you can now write
Code: [Select]
2+ &[ 2 + 3 * ]o

Of course this only works for operations which have been defined before `&(`/`&[` has been called.
Both also open up their own local operation and immediateness environments, so every operation defined inside their scope will not be available outside that scope.
Logged
Taste my Paci-Fist

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #55 on: October 09, 2015, 11:46:40 am »

Poslin0.3.0rc5-0.1.6 is up.

The exceptions thrown by the primary operations now contain all the necessary information. Before for some operations arguments were lost.

The standard library has grown again.
`even?` and `odd?` have been added. They've got the obvious meaning.
`&|` is to `|`what `&[` is to `[`. That is, it closes the current stack and then opens a new one with `&[`.
`top-or-self` returns the top of a given stack or, if the given object is not a non-empty stack, the object itself.
`exception-type` returns the `top-or-self` of the `exception-data` of an exception.
`exception-type?` checks whether an exception is of a given type.
Code: [Select]
> 0 / !  ;[ this does not pop bottom because `/` tries to make the reciprocal of 0 before an attempt is made to pop the next argument ];


Division by zero

[ [[EXCEPTION ZERO-DIVISION-ERROR {P{reciprocal} P{*}}+0 "Division by zero"]] ]
> ZERO-DIVISION-ERROR exception-type? !
[ <TRUE> ]
`to-bottom` sends an object to the bottom of the current stack.
Code: [Select]
[ 1 2 3 ]
> a to-bottom !
[ a 1 2 3 ]

`]catch` is an immediate operation and allows you to catch errors of a specific type with their correct handler.
Code: [Select]
[ body &
| ZERO-DIVISION-ERROR
  [ zero-devision-handle & ]
  TYPE-ERROR
  [ type-error-handle & ]
]catch
In the latter part (where all the handlers are defined) a local variable `exception` is defined which contains the caught exception.
Code: [Select]
[ do & some stuff &
| MY-EXCEPTION
  [ exception get exception-data &
    do & stuff with & data &
  ]
]catch
Of course this means that any variables named `exception` in the surrounding scope are shadowed.
Exception types which are not caught are just rethrown.


There's a challenge on codegolf.stackexchange.com: Showcase your language one vote at a time. It allows to show code snippets of length 1-number of votes. So, if any of you are on there I'd be happy if you gave me a vote.
It's easier to do that than trying to write a tutorial – a tutorial requires way better structure.
« Last Edit: October 09, 2015, 11:53:19 am by Antsan »
Logged
Taste my Paci-Fist

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #56 on: October 27, 2015, 10:14:07 am »

I've been doing some work on packages and I hit a wall pretty fast.
I was writing an operation to import one package into another. As a package is only an environment containing all of the slots required for an environment on the path safe for `STACK` that basically consists of merging all the slots in the two packages in the correct order.
This merging process is easy for slots which contain an environment: Split the environment of the importing package into the parent and the environment without the parent, make that parent the… uhm… root ancestor of the environment of the imported package and then make the resulting environment the parent of the environment of the importing package. Safe for the pretty awful description I just gave that is a really straightforward process.
It's not so easy for something like the FEATURES slot, which contains a stack. Should the stacks just be appended? Hm…
Strangely enough, the IMM slot contains an environment, although it is structurally kind of similar to FEATURES – it either contains an object or it doesn't. So, those two should actually contain stacks of sets.

The idea is to get rid of environments and replace them with stacks of maps. The interpreter can deal with that easily (it's just a fold) and operations like `env-lookup` can be implemented in the standard library.
At the same time IMM and FEATURES would now contain stacks of sets.
That means maps and sets would need to become primary types with the associated primary operations, while environments would vanish.

Any thoughts on this? Is anyone actually still reading, or are all of you just taking a look whether finally something interesting happens?
Logged
Taste my Paci-Fist

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #57 on: November 22, 2015, 01:13:20 pm »

I didn't really do anything new, but I discovered that the startup scripts provided in the last three distributions were erroneous. As such there's now the final release for 0.3.0 for linux-x86. I also finally managed to put together a distribution for OS X on x86_64.
They're available at the usual place.

The next version will hopefully be 0.4.0, where I get rid of environments. That only means that everything regarding environments will be moved to the standard library, so hopefully code that relies on the standard library (as if there was any ::)) should be unaffected by that change.
« Last Edit: November 22, 2015, 01:15:59 pm by Antsan »
Logged
Taste my Paci-Fist

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #58 on: November 25, 2015, 11:44:39 am »

I am currently in the middle of the rewrite to get rid of environments.
The interpreter already has been changed – the `OP` slot now contains a stack of maps while the `IMM` and `FEATURES` slots contain stacks of sets. The primary
operations for manipulating sets and maps have been added while the primary operations for environments have been removed. That was harder than expected.
A side effect of the change is that environments now except anything as keys and values, when before only symbols were valid as keys and bindings valid as values. Of course you can try to take advantage of this, but the interpreter will choke on non-binding values in the operation environment.
Unless there's some hidden bugs or strange edge cases the rewrite of the interpreter should be done for now, although I think it might be a bit… chaotic at this point. Lots of duplicated code and insufficient abstraction, which bit me multiple times through this rewrite.

The standard library now needs to be rewritten – at least partly.
Because sets are now in as data structure, having operation names like `map-set`, `env-set` or `set-set` seemed really strange. That's why `env-set` and all similar operations now instead are named `env-insert` and the like. `path-set` is an exception – it still is called `path-set`, as the operation is conceptually considerably different from what is done to maps or sets.
The added operations are:
Code: [Select]
.empty-set
set-lookup
set-insert
set-drop

.empty-map
map-lookup
map-insert
map-drop
map-domain
`.empty-map` and `.empty-set` always return the same object, although I guess using `map-drop`/`set-drop` to make a map/set empty won't leave you with the same object at all. This is only of interest for usage of the operation `same?`, not for `compare`.

Implementing `env-lookup` proves to be surprisingly hard. Fortunately I don't need it for the conditional creation of operations in the standard library because I know that I only need to operate on the top map in the operation environment at the points where I had used an environment lookup before. Still, every operation defined on `env-lookup` will move quite a bit to the back in the standard library.
Also there will need to be operations corresponding to the stacks of sets. I'm not quite sure what to call these yet.
Logged
Taste my Paci-Fist

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #59 on: November 28, 2015, 12:32:29 pm »

Yesterday I implemented `env-lookup` on paper and discovered a fatal flaw: The implementation of it depends on the operations `drop-here` and `dup-here` who, in turn, depend on `env-lookup`. This leads to an endless recursion. As far as I know, as the system currently stands, there is no way around this.

But!
I thought about environments a bit and then a bit more.

First: Using environments on the path seems to be completely superfluous. In the whole standard library I have not a single case of pushing a more complex environment onto the path – I always just used a completely shallow environment whose slots had been modified. That is completely equivalent to just using a map instead of an environment.
So I can make the Path a stack of maps instead of a stack of environments. This way the problem of the endless recursion in `env-lookup` goes away.

Second: I don't think environments are actually useful. Well, they will be useful for implementing packages, but that's about it. Anything else can be achieved by just making a new binding – everything works the same outwards, unless you want to change the parent environment, which never came up anywhere at all (well, unless we're talking about packages, where changing the parent environment is like fiddling with imports).

So, I guess I'll remove all the environment stuff from the interpreter altogether. They'll probably have a comeback as packages some time in the future, though. And then there will need to be an operation to flatten a package into a map, but that shouldn't be too hard, right? ::)
Logged
Taste my Paci-Fist
Pages: 1 2 3 [4] 5