Bay 12 Games Forum

Please login or register.

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

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

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #60 on: December 15, 2015, 06:25:52 am »

Version 0.4.0-0.2.0 is imminent. It's not here yet, though. There's a horrible bug that took me a day to figure out and I'm kinda shaky on how to resolve it.

Spoiler: The Bug (click to show/hide)


In other news:

Many operations have been renamed. I know I said they wouldn't, but with environments gone that kind of is not an option anymore.

All the operations in the standard library concerning types are gone. That means the only operation concerning types from before is now `type`.
One new operation has been added, `class?`. This has nothing to do with OOP (although it will be helpful later when implementing OOP to make it blend in seamlessly later), This operation makes use of the new CLASS slot containing a binding of a map from class names (everything can be a class name) to bindings of operations. If it is contained, the corresponding operations is called on the checked value. The operation should consume exactly the checked value and return exactly a boolean. If the class name is not contained in the map in CLASS, the class name is taken to be a type, `type` is called on the value to be checked and the result compared to the given class name.
I plan to change this process so that class names which are stacks are checked against their top element, so that parametrized classes can exist, like `[ :number :stack ]`. Maybe I'd need to add another slot, though, so that parametrized types can exist alongside simple ones…
Logged
Taste my Paci-Fist

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #61 on: December 24, 2015, 08:46:54 pm »

poslin0.4.0-0.2.0-linux-x86 is up. I hope I'll remember to upload the mac version tomorrow and maybe this time there will be a windows version.

There have been many changes.
As already said, environments are gone and have been replaced with dictionaries and sets. Dictionaries aren't called "maps" because English is an awful language for programming concepts and the name "map" is also used for the stuff where you iterate some given operation over an existing collection and I couldn't find a synonym for that concept.

Many operations have been renamed and because I am lazy and because there is not a one-to-one mapping from old to new operations I didn't keep track of how the names changed. I think the changed names all are about dictionaries, sets and slots on the path in some way.

Structs don't have an associated slot anymore. This takes care of some problems before they even came up (not exporting some stuff related to structs would have led to operations using those structs failing, even if these operations were defined in a package that had the aforementioned stuff).
I think classes (not as in OOP, but rather just named predicates operating on all objects) suffer from the same problem, although I am not quite sure what the solution would be there. I think I'll need to make `class?` immediate, so it can obtain the correct bindings at compilation time instead of runtime. Checking of classes that are not known at compile time would need to be done with `~class?` then.


Another problem that came up is naming. As there are now sets as data structures, there's a problem with the immediate operation `set` which creates the necessary code to set the value of an already existing variable. Like this:
Code: [Select]
a
 b get 1+ &
set
which sets `a` to `b+1`. Using the symbol `set` this way is kind of inconvenient because then it cannot be easily used for variable names and also might be confusing, because "setting a variable" and "unordered collection" are very different concepts. So, does anybody have any idea on how to resolve this conflict?

There is a problem with `parent-pop` too. Take a look at this:
Code: [Select]
> 1 [ 2 o( parent-pop ! ) ]
[ 1 [ 2 2 ] ]     ;[ The expected result would probably be [ 1 [ 2 1 ] ] ];
This is a general problem with how the whole `parent-` family of operations does stuff, or rather with how stacks are managed on the path.
`parent-pop` works on the path. Immediate operations like `o(` push a new dictionary onto the path, so the thing we expect to be the parent stack actually isn't. One solution would be to do with the STACK slot what I, for a short time, did with all the other slots: Make it a binding of a stack of bindings of stacks. Then accessing the parent stack would function the correct way and as there is no recursive lookup to be done on the STACK slot in any way, there wouldn't be the same problems. It still seems a bit contrived.


Next in regards to the library are packages (which you may know as modules). I find them to be surprisingly complex to implement.
Next in regard to the interpreter is the programmable reader (I think). That one will also probably be quite hard to do, especially since it kind of relies on the concept of a source input stream. As Poslin doesn't really know any difference between runtime and compilation time that seems like a source for trouble.
« Last Edit: December 24, 2015, 08:48:48 pm by Antsan »
Logged
Taste my Paci-Fist

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #62 on: January 17, 2016, 04:40:28 pm »

poslin0.4.0-0.2.1-1 is up. The additional number at the end has been added because this is the first distribution which contains more than just the standard library. It will increment when some of the supplemental libraries change for a new release and none of the other version numbers change. It will be reset to 1 every time one of the previous version numbers changes.

Or rather, it contains more parts of the standard library than is loaded by default with poslin1. Yes, that's right, packages have been implemented!
The libraries in the distribution have been moved to their own subfolder: `lib`.
If you want to use packages, either run
Code: [Select]
./poslin1 ./lib/package.poslin
or
Code: [Select]
./poslin2
The latter later also will load other supplemental libraries, as soon as they are implemented, which might be undesirable for some.


I have to give special thanks to my brother here, who gave me invaluable criticism of some of the things I was going to do. Neither `import` nor `@` would exist without him and the syntax for defining and loading packages would have been mashed together into one big horrible mess instead of being neatly separated.


I would never have guessed how damn complex this would be. The code is clunky and inefficient, but it does what I want it to do and as this stuff should be running at compilation time only and only sparsely then this shouldn't be too much of a problem. I think. Hopefully later optimization stuff will help to make it faster.

To the package system!
If you work in almost any modern programming language there is some way to avoid name clutter. This is normally called something like "modules" or "namespaces" or in the case of Common Lisp "packages" (although it also has namespaces, which are the division between different kinds of things that may be accessed via symbols, like functions and variables, which inhabit different namespaces, but I diverge).

Poslin has namespaces for avoiding naming conflicts between different types of objects (such as operations, variables and class names) already with its slots. This doesn't help with avoiding naming conflicts between different operations. That's what packages are for.
They give you, uhm, loadable units of stuff that belongs together. They have some content (stuff which is defined inside them) and some exports (stuff which can be easily accessed from outside the package).
To define a package, you use this syntax:
Code: [Select]
my-package
[ [ OP sum product foo ]
  [ IMM foo ]
| std
]pkg
The first line is the name of the package, the second and third line describe which definitions in which slots are exported and the fourth line contains all the packages this package uses, meaning that it can access everything the mentioned packages export. So, if another package should use `my-package` later, it would have access to the operations `sum`, `product` and `foo` and would also recognize `foo` as immediate if it also is defined to be immediate in `my-package` itself (this behavior may change later, so that an operation that is exported as immediate is just taken to be immediate).

To actually fill a package with content you need to open it, define the content and then close it. The new content will not be in the package until the package is closed again. I have no idea whether embedded packages are a thing currently. I suspect they are not and it is too late in the day to check now.
You can do it like this:
Code: [Select]
my-package
p( sum
   [ 0 + ~ fold-stack &
   ]o

   product
   [ 1 * ~ fold-stack &
   ]o

   foo  ;[ not that this operation makes much sense, but it's an example, what do you expect? ];
   [ dup-here & sum & << product & + &
   ]i
)p

Packages can also be added to the use list of the current package with `import`.
I personally don't recommend using it, as it may hide package uses somewhere they are not expected but I assume such an operation could have its uses and some programmers may prefer it that way.
Code: [Select]
another-package
[ [ OP bar ]
| std ;[ Not importing `std` will lead to a completely empty environment in the defined package, meaning that you can do nothing it it.
         That means you also cannot leave the package anymore.
         Always include `std` into this list unless you want to import something else here which provides a replacement for the absolute essentials.
      ];
]pkg

another-package
p( my-package import
   ...
)p
Is exactly equivalent to
Code: [Select]
another-package
[ [ OP bar ]
| std my-package
]pkg

another-package
p( ...
)p

Some people may prefer to not use packages at all (for whatever strange reason). These people still will need some way of accessing what packages of other people export. For them there's the immediate `@` operation. It basically does an import of the provided package's exports into the current dictionary on the path.
So, if you want to use what `my-package` exports without using packages, just do
Code: [Select]
my-package @
Note that `import` wouldn't work here, as it would try to add `my-package` to the uses-list of the current package, which would fail horribly when there is no current package in the first place. You are not in the `std` package after loading the package library.


Next up on the library are modules (or load systems), I think. Always having to provide complete paths in the code being written instead of having some other system to do that kind of bookkeeping for you can be tedious, especially when your file organization is different from someone who wrote a library you want to use.
Modules would provide a way of giving a set of poslin files a name under which they can be found.

Next up on the interpreter front is not the programmable reader (that one can wait, especially since I'll need to rewrite the standard library again by including the definition for reading strings in there…) but bytes, byte streams and conversion of some object types to bytes.
I definitely want Poslin to be Unicode compatible, so UTF-8 and ASCII conversion or characters will be a requirement of poslin0.
Logged
Taste my Paci-Fist

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #63 on: March 09, 2016, 03:33:38 pm »

There will be a new version soon. No major changes in the interpreter (bindings have been stripped of documentation strings), no changes in the standard library, I think.

Formerly packages contained a fixed set of slots that would be ex-/imported. You can now add to these slots. This is used in the following library, for instance, which contains a slot for generic operations.
So, if you want packages to preserve the content of a slot containing a binding of a dictionary or set, you can add that slot like this:
Code: [Select]
DICT-SLOT register-pkg-dict-slot
SET-SLOT register-pkg-set-slot
Slots that are neither sets nor dictionaries cannot be managed by packages.


Now to the new exciting stuff. There are generic operations now!
What are generic operations? Well, they are operations which have methods and which check their arguments for their type first to determine which of their methods to call.
For instance you could write a generic operation `lookup` that operates on all kinds of collections:
Code: [Select]
lookup [ collection key ]generic
This just registers the generic function. The names of the arguments given here are completely immaterial. What counts here is the number of arguments, nothing else.
Then you can go on and define methods for this generic operation:
Code: [Select]
lookup
[ :Set ::Any  ;[ The types of the arguments ];
| set-lookup &  ;[ This works like a normal operation. You could actually consume less or more arguments than given in the generic operation definition ];
]method

lookup
[ :Dict ::Any
| dict-lookup &
]method

lookup
[ :Array ::Natural
| array-lookup &
]method

lookup
[ :Stack ::Natural
| stack-lookup &
]method
and so on.

As already mentioned this does not manage arguments in any way. This just checks whether the top values before calling the method have the specified classes. If you want variable-like arguments as in other programming languages, use the curly braces like this:
Code: [Select]
lookup
[ :Set ::Any
| [ var ]
  { [ set key ]args
    set get key get set-lookup &
  }&
]method
Logged
Taste my Paci-Fist

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #64 on: August 08, 2016, 06:41:57 pm »

It is soon. :P Version 0.5.0-0.2.1-1 is out.

Not much has changed. There has been some code cleanup. There now is a Makefile and a README with installation instructions.

There aren't any downloads anymore for the moment. I have access to neither Windows nor a Mac. I don't know how to provide an installable package beyond the Makefile on Linux and distributing a non-installable version for download seems kind of weird. Dependencies are minimal either way and easily obtained.
Logged
Taste my Paci-Fist

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #65 on: August 09, 2016, 04:25:41 pm »

Version 0.5.1-0.2.1-1 is out.
Bindings are now printed with an identifying integer, which I already find to be extremely awesome – seeing whether two bindings are equal or not is tremendously useful. The integer identifying a binding is only decided at the time it is first printed.
When stepping is active, the prompt now is a `*` instead of a `>`.

I made a 15 minute video explaining the interpreter, immediate and delayed symbols as well as what the path is good for. If you've read this thread it probably contains nothing new.
Logged
Taste my Paci-Fist

Heretic

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #66 on: August 16, 2016, 10:07:28 am »

Hm. Once i did postfix lisp... this, at the beginnig, looked close to it, but now probadly not.
Logged

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #67 on: August 16, 2016, 10:28:37 am »

Heh. I came up with this when learning about Lisp and postfix notation at the same time.

Another tutorial video is in the making. That one will be about the most basic of stuff, that is, "Hello, world!", defining operations, recursion and loops.
Logged
Taste my Paci-Fist

Antsan

  • Bay Watcher
    • View Profile
Re: Programming Language: Poslin
« Reply #68 on: September 15, 2016, 01:47:14 am »

Poslin0.6.0-0.3.0 is out.

The path now is represented by a binding. The previously primary path operations `path-top`, `path-pop`, `path-access`, `path-set` and `path-length` (now `path-size`) are now implemented in the standard library.
The new primary operation `path-binding` returns the binding containing the path. This binding is guaranteed to stay the same throughout a given poslin session.
There is a new operation `path` in the standard library, which just returns the stack representing the current path.

There are no checks whether values put into the path binding are well-formed. For now the result of putting something other than a dictionary of the right form onto the path will, in most cases, result in a crash. The cases where there won't be a crash are the edge cases, so don't put anything weird into the path binding.
Logged
Taste my Paci-Fist
Pages: 1 ... 3 4 [5]