GordonCharlton / Quackery

a lightweight, open-source language for recreational and educational programming inspired by Forth and Lisp
http://rosettacode.org/wiki/Category:Quackery
33 stars 5 forks source link

`loadfile` breaks when loading from another folder #1

Closed olus2000 closed 2 years ago

olus2000 commented 2 years ago

When loading a file from a subfolder loadfile gets not just the file's name but the whole path to it (accepts either relative or absolute, just as python does). This makes the circular dependency prevention useless in the form it has right now. loadfile only checks the string it is passed versus any defined words, and the file's author can't be expected to guess from where their file will be loaded.

Another problem occurs when a couple of files that depend on each other are stored in a subfolder as a package. They are unable to reference each other because relative paths are interpreted as relative to the Quackery instance.

I see three possible solutions:

1. Do nothing

Quackery is "a lightweight, open-source language for recreational and educational programming", so it may be out of the scope of its development to handle this issue.

2. Describe loading from subfolders as a bad practice

Actively discouraging working with subfolders is not unthinkable given that Quackery isn't meant to be an enterprise language, but it generates an ugly work space full of unorganised projects. I personally would prefer to work in subfolders even if it means manually resolving any circular dependencies and devising a programatic way of handling relative paths.

3. Add functionality to Quackery

My proposition is to add an ancillary stack file.path with a default path of "./" on the top. Every file handling word will use the file name along with this stack's top item to construct the path of the file that should be affected. Whenever a file would be loaded that would require a different default path this path would just be put on the stack for the time of loading and released after that. New paths can be created in relation to the old ones by joining strings. It wouldn't break portability with any python-based Quackery system because python recognises / as a path separator universally, and in other implementations it could be easily implemented too or left as en environmental dependency based on the underlying OS.

GordonCharlton commented 2 years ago

Hi.

Thank you for the feedback. File handling is the most under-developed part of Quackery, mostly because it is not one of my programming strengths, so I am delighted that someone has picked up on it. For the time being at least, I'll be going with Solution 1, although long term I do like Solution 3. For various reasons I am taking an extended break from Quackery at the moment - mostly to clear my head and be able to return to it afresh. (I have M.E. It's a thing I have to do.)

I see that you committed "path support for file operations" to your fork a few minutes ago. Glad you are "going for it". Interested to see that you are going for a Python level solution. My initial reaction was to add a Quackery level extension redefining the file handling words to know about the file.path ancillary stack, but I haven't thought it through at all.

Also kudos for the correction to matchitem- I thought I had found all the times I omitted a nested, but now I know that I missed at least one. :-) And your finding that particular one leads me to feel that I have at least succeeded in the goal of it being "possible to understand the entirety of Quackery in short order".

Gordon.

olus2000 commented 2 years ago

Thanks for responding so fast!

I've been charmed by Quackery ever since I saw it (which is for about a week 😃) and when I found a way to improve it I jumped at the opportunity. I also started by thinking of Quackery wrappers for the file handing functions as a proof of concept and I think they would not be hard to implement, so a path handling extension may be the 4th solution. The slight inconvenience there is that filename checking for loadfile has to be duplicated in the wrapper and core loadfile filename check would end up being unused.

I went for a Python solution because I wanted to make the change in the Quackery core and it turns out that base file handling words are in Python. That was actually convenient for me because I have loads more experience writing Python than Quackery. It wasn't too hard to find my way in the code and I hope my contributions don't change that.

As for the lacking nested: I found it when trying to write a very bad lookup table word. Had I designed it better I wouldn't have to use such a convoluted predicate and wouldn't stumble into the problem. It wasn't easy to track down though: the only easily accessible symptom I had were zeroes being left on the stack and I had to use a lot of echostackss and echoreturns along with the source code provided in The Book of Quackery to find out where the problem was. Although I guess "an afternoon" is not that long for tracking down a bug in a language's source code.

Don't feel rushed to act on my issue, I'm happy to have received any feedback. You already did an amazing job making the language. My code will be there whenever you find time and will to check it out.

Alex.

GordonCharlton commented 2 years ago

Ha! This is the value of beta testing - we all stumble differently, and it is hard to stumble intentionally in a way that you would not stumble naturally.

Two tips I learned from my experience with the Forth community.

  1. The first time you write a piece of code, you are exploring the problem space. The better you know the problem space, the the better subsequent rewrites of that code will be.

All of Quackery was rewritten many times before it went on GitHub.

  1. Have a very tight code - debug cycle. Forth wisdom is that, ideally, words should short enough fit onto a single line and consist of about seven lower level words. The name they give this is "Factoring". Factor out the smallest parts of the problem and test each one in the shell before putting them together, then test again.

Most of my difficulties happened when I was overconfident and ignored this advice because it was "so simple I could not possibly get it wrong!" (Especially before I had enough of Quackery working to code shell - debugging was such a nightmare!)

I can understand preferring the language you are familiar with. When I started I did not know Python at all, other than I could see it would provide a path of least resistance. By the time I was half-way through developing Quackery I had done so many internet searches to figure out how to do something in Python that Google invited me to take their Google programmer test!

I still only know the parts of Python that were required to code Quackery. I wonder sometimes if I failed to find the best way to do some things, and have made hard work out of a piece of code that could be better, simpler, faster if I had learned more Python.

(Part of the problem for me was that many of Python's capabilities are hidden in the syntax of the language, and it is hard to find, for example, the syntax for slice notation, if you do not know that it is called slice notation, or that you should use the name of the function without the parentheses to reference a function, and append parentheses to a variable to make it perform the function it references. This is one of the things I like most about concatenative languages - there is hardly any syntax - every piece of functionality has a name.)

Don't worry about me rushing. I value thinking time far more than coding time. :-)

GordonCharlton commented 2 years ago

Hi,

I'm back after my Christmas break.

Before I get to making the updates on The Book of Quackery, I thought I would warm up by coding a solution to the file path issue.

Would you be able to take a look at my solution and tell me if you see any problems. I would appreciate a second pair of eyes to see it. I have the uneasy feeling that it was too easy and I might have missed something, but I cannot think what I forgot.

The updated version is here: https://www.dropbox.com/s/azc68kaw7wgnacm/quackery.py

Notes: I have done this without looking at your solution, as I wanted to see how much I remembered about coding in Python after a long break. The added code sections are each marked with the comment ***** .

(edit - I have also incorporated your corrections to backup and bailed.)

(2nd edit: Also I have simplified protected release protected put to protected replace in bailed.)

olus2000 commented 2 years ago

Great work! Your code for filepath is way cleaner than mine. Here are some issues and questions I have.

From least important:

  1. is wrap$ and is words on lines 1560 and 1564 respectively are misaligned with other is <word> parts of other definitions.
  2. Instead of simply concatenating the file name to the path I would use python's os.path.join. In most cases the result will be identical, but os.path.join is smart and does some additional processing. For example joining 'aaa' with 'bbb' produces a path 'aaa/bbb' (with the slash or a backslash depending on the system), and joining 'aaa/' with '/bbb' produces an absolute path '/bbb'.
  3. Having no default value on the filepath stack requires any program that would like to change the path relatively to the current working directory has to check if the filepath stack is empty. If there was some default value on the stack at all times (for example .) such programs could just filepath share and join the relative path they need.

None of those are serious in any way and I haven't found any bugs when going through the diff. I didn't do extensive testing but it didn't break my previous programs and extensions, except from issue 3. which forced a quick fix for my extension loader.

GordonCharlton commented 2 years ago
  1. Thank you. Corrections made. Your message was timely, I am close to pushing the revisions to GitHub - today I finished doing hypertext links in TBoQ, in the contents pages and the numbered notes in the code sections. (I expect to upload tomorrow, I will have a good opportunity to do some final checks in the evening.)

  2. Valid points, but I have tried to minimise the number of Python libraries used, and os.path is not strictly necessary. Python is "smart", Quackery assumes the programmer is smart. :-)

  3. I imagine that Windows might require a different default path (I don't have a Windows machine to check, but I know it uses backslash instead of slash) and I would rather not introduce more system dependant code that I can't test. I note that the user could set a default path by putting $ "/" filepath put or similar in extensions.qky

Edit: (Also, with or without a default value, putfile, releasefile, and sharefile have to address the possibility that filepath is an empty list, either by making it an error condition or treating it as a meaningful state.)