Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transpile boolean.py into javascript - Python3-only #72

Open
wants to merge 36 commits into
base: master
Choose a base branch
from
Open

Transpile boolean.py into javascript - Python3-only #72

wants to merge 36 commits into from

Conversation

alisianoi
Copy link
Contributor

@alisianoi alisianoi commented Jun 29, 2017

This will be a long-running PR where you can monitor progress of actual transpilation.

This will be used to identify actual "blockers" (like missing modules, unsupported features) and these "blockers" will be dealt with in separate PR's and then this PR will be rebased accordingly. Or even in this PR if the "blocker" is super small.

So far I've set up an entry_point which in theory should allow the following approach:

pip install boolean.py
transpile

During development that looks like:

cd boolean.py
pip install -e . # install in development mode
transpile

In its simplest form this would generate a __javascript__ directory directly inside the package. However, there will be an ArgumentParser to deal with a) passing transcrypt related args further to transcrypt and b) configuring some other parameters for transpilation.

Current blocker: need to figure out a way to install transcrypt and pyaml as non-necessary dependencies. Probably somehow with extras_require in setup.py

Alexander Lisianoi added 2 commits June 29, 2017 14:30
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Add these as extra dependencies into setup.py
- transcrypt (transpiling)
- pyaml (to read .yml config files)

In development this will install them for you:
pip install -e .

In production you need to specify your intention:
pip install boolean.py[transpile]

Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
@alisianoi
Copy link
Contributor Author

Ok, extras_require are now in place, they will be installed if you do pip install -e .

Now need to interface into transcrypt to make the transpillation call.

Alexander Lisianoi added 2 commits June 29, 2017 16:56
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
* Allow to pass any argument to transcrypt with '--':
transpile -- --license
transpile -- --help

* If you specify transcrypt arguments manually, specify them all:
  (this means you need to specify what to transpile)
transpile -- --esv 6 /path/to/file.py

* To suppress logging from 'transpile':
transpile --quiet -- --esv 6 /path/to/file.py

* 'transpile' without arguments will try to transpile boolean.py

Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
@alisianoi
Copy link
Contributor Author

And now transpile shows that super keyword is the current blocker. Will try to fix it with an automatic conversion first.

@pombredanne
Copy link
Collaborator

I am not sure this would need to be part of the main setup code. Especially since your code uses pathlib which is Python 3. I would be more comfy to have a separate scripts under some etc dir that handles the transpiling (and may have it its own requirements file) and not bring this in the main setup.py

To use it, do the following:
pip install -r etc/requirements.txt
./etc/transpile.py

You may need a development version of transcrypt:
pip install -r etc/requirements.txt
pip install -e your/local/transcrypt
./etc/transpile.py

Transcrypt cannot handle targets that are "above" it in the directory
structure, so the following will not work:
cd ./etc
./transpile.py

Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
@alisianoi
Copy link
Contributor Author

alisianoi commented Jun 30, 2017

Finally moved transpile.py into its own etc directory. The whole thing was crashing bizzarly because I forgot that transcrypt does not like it when it is asked to "look up" the directory structure:

transcrypt ../../some/file.py # crashes with a couple tracebacks and "unprintable error" as error description

Update: will quickly address the unit tests and then finally move on to hacking super

Alexander Lisianoi added 4 commits July 3, 2017 17:53
This is attempt 2 at parsing AST: with hand-written functions that
iterate over the ast tree and represent state.

`ast.NodeTransformer` does not work because it can only visit single
nodes and does not know when it *leaves* nodes. Alternative is `astor`

Signed-off-by: Aleksandr Lisinaoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisinaoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisinaoi <all3fox@gmail.com>
@alisianoi
Copy link
Contributor Author

alisianoi commented Jul 4, 2017

Tests are broken because of the 3.x super keyword. If you run pytest under a 3.x virtual environment, tests pass. Will probably adjust .travis.yml for this branch to run 3.x only. Some time later.

This should be later removed from history. Checking boolean.js helps
others avoid the hassle of running a dev version of transcrypt over a
dev version of boolean.py to take a look at transpilation results.

Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
@alisianoi
Copy link
Contributor Author

alisianoi commented Jul 5, 2017

Ok, I've checked in boolean.js just in case you would like to reproduce the steps I am talking about without the need to:

  1. Get a dev version of Transcrypt that has inspect.isclass
  2. Get a dev version of boolean.py from this branch
  3. Transpile stuff yourself with ./etc/transpile.py

Current problem:

  1. Open up boolean.html, navigate to webbrowser console.
  2. Try to create a BooleanAlgebra object with let algebra = boolean.BooleanAlgebra()

That throws the following:
TypeError: can't assign to properties of (new String("None")): not an object

Several hours of crawling with debugger over boolean.js (I am rubbish at using a debugger) suggest that the self._wrap_type() call returns a string (around line 2466 in BooleanAlgebra constructor) which is on the next line called like a function, which triggers the error.

This is my best guess right now and I am still looking at it :(

@alisianoi
Copy link
Contributor Author

alisianoi commented Jul 5, 2017

Finally! type in python can have more than one argument. Yet Transcrypt looks only at the 0th argument to type and ignores the rest (see compiler.py, lines 1410 - 1414). So, the boolean.py type call that should create a new type named foo.__name__ is translated into Transcrypt's instruction "what is the type of foo.__name__?". The answer to that question is "a string". And so self._wrap_type() goes downhill from there.

Alexander Lisianoi added 5 commits July 6, 2017 08:12
That function uses type built-in to register a new type. Transcrypt
does not support that and, to be honest, I do not see the need for
registering a new type. Removing _wrap_type makes no unit test fail.

Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Transcrypt does not "alias" basestring as string

Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
The first switch forces a transpilation from scratch
The second switch forces EcmaScript 6, which allows to have generators

Refs: TranscryptOrg/Transcrypt#358
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
This file is generated by transcrypt and is included temporarily to
allow others to just load it and see what works and what does not.

Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
@alisianoi
Copy link
Contributor Author

Ok, the generators work with -e 6 and retranspilation. Current runtime error is:

algebra = boolean.BooleanAlgebra()
expr = algebra.parse("x")
TypeError: tok.isalpha is not a function [Learn More]

@alisianoi
Copy link
Contributor Author

alisianoi commented Jul 12, 2017

Ok, expr0 = algebra.parse("apples and oranges") now works, let me point out what does not work:

  1. expr0.get_symbols() and expr0.get_literals() not working because itertools.chain not implemented.
  2. boolean.__eq__(expr0, algebra.AND(algebra.Symbol("apples"), algebra.Symbol("oranges"))) fails because frozenset is not defined
  3. expr0 == algebra.AND(algebra.Symbol("apples"), algebra.Symbol("oranges")) compares as false even though the object on the right is created alright. Same for ===
  4. expr0 = algebra.parse("apples and")does not complain (though it should) and the resulting expr0.args are [Object, undefined]
  5. algebra.Symbol("a") == algebra.Symbol("a") compares as false when should be true. Same for ===
  6. algebra.FALSE < algebra.TRUE throws a "cannot convert to String" error
  7. A more complex parse seems to fail: expr2 = algebra.parse("x and y or a and b") returns an "and" object and does not let (?) you go down to the structure of the expression. The topmost object should be "or". Probably some overwriting is taking place.

However, some comparison starts to work if you use dunder methods in boolean:

  1. boolean.__eq__(algebra.Symbol("a"), algebra.Symbol("a")) is true, with other symbols -- false.

@pombredanne
Copy link
Collaborator

  1. expr0.get_symbols() and expr0.get_literals() not working because itertools.chain not implemented.

Use something else.... chain is just a short hand for consuming nested loops.

  1. boolean.__eq__(expr0, algebra.AND(algebra.Symbol("apples"), algebra.Symbol("oranges"))) fails because frozenset is not defined

Use a plain set instead. Alternatively implementing an immutable set is quick enough.

  1. expr0 == algebra.AND(algebra.Symbol("apples"), algebra.Symbol("oranges")) compares as false even though the object on the right is created alright. Same for ===

What does JS do here for comparisons? is the __eq__ function called? ... and what does expr0 contain?

  1. expr0 = algebra.parse("apples and")does not complain (though it should) and the resulting expr0.args are [Object, undefined]

OK, so there is some validation code that is not handled at all then? The parse does not work?
What is expr0 in this case?

  1. algebra.Symbol("a") == algebra.Symbol("a") compares as false when should be true. Same for ===

is the symbol __eq__ called? Do not hesitate to put debug prints

  1. algebra.FALSE < algebra.TRUE throws a "cannot convert to String" error

This is not a big problem. Doing this kinda of comparison is not super important I think, is it?

  1. A more complex parse seems to fail: expr2 = algebra.parse("x and y or a and b") returns an "and" object and does not let (?) you go down to the structure of the expression. The topmost object should be "or". Probably some overwriting is taking place.

ok. so still some stuff not right in the guts of parsing then?

However, some comparison starts to work if you use dunder methods in boolean:

  1. boolean.__eq__(algebra.Symbol("a"), algebra.Symbol("a")) is true, with other symbols -- false.

Hum, this likely means that dunder methods may not be used at all by Transcrypt?

@@ -1,4 +1,5 @@
*.pyc
__javascript__
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, you both ignored this and committed code that is in the dir.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I did that on purpose: currently, having __javascript__/boolean.js let's you avoid the hassle of getting a vendored version of Transcrypt and making it work. Later I will remove __javascript__/boolean.js and the .gitignore will stay.

yield TOKENS[tok.lower()], tok, position
except KeyError:
value = TOKENS.get(tok.lower())
if value is not None:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can write: if value: instead? is there cases where we we get an empty token otherwise?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fix

@@ -0,0 +1,15 @@
logging:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to tell Transcrypt to use two or four spaces rather than tabs in the generated JS?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I know, no.

Alexander Lisianoi added 3 commits July 18, 2017 10:52
Javascript objects don't throw an error if you access non-existent keys.
Transcrypt can imitate Python dictionaries but it does not always work.

Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
…ctionary

Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Alexander Lisianoi added 2 commits August 1, 2017 13:48
The '.toString' makes these objects "easier to compare" in js runtime.

Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Transcrypt does not support issubclass

Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Alexander Lisianoi added 16 commits August 21, 2017 19:41
Also, drop preprocess.py, there is no preprocessing of the code.

Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Also add extended truth-value testing flag and a --browser flag

Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
Signed-off-by: Aleksandr Lisianoi <all3fox@gmail.com>
@pombredanne
Copy link
Collaborator

I just rebased your branch and pushed this as alisianoi-transpile here. I could have but did not dare force push to your repo after the rebase! I might merge this rebased branch instead

@pombredanne pombredanne changed the title [WIP] Transpile boolean.py into javascript Transpile boolean.py into javascript - Python3-only Apr 5, 2018
@pombredanne
Copy link
Collaborator

When merged we will need to start having a Python2-only maintenance branch as this branch will only support Python3

@pombredanne pombredanne mentioned this pull request Apr 5, 2018
@bigbug
Copy link

bigbug commented Aug 2, 2020

Hi, I was kinda bored and created a (partial) port of this library for javascript. You can check it out here: https://github.com/bigbug/boolean

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants