Skip to content

Commit 0a5783b

Browse files
committed
Prepare for release
1 parent 6f46706 commit 0a5783b

File tree

7 files changed

+129
-15
lines changed

7 files changed

+129
-15
lines changed

README.md

Lines changed: 108 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33

44
#pseudo-python
55

6-
A Python to JavaScript / Ruby / C++ / Go / C# / PHP translator
6+
A restricted Python to idiomatic JavaScript / Ruby / Go / C# translator
77

88
[Pseudo](https://github.com/alehander42/pseudo) is a framework for high level code generation: it is used by this compiler to translate a subset of Python to all Pseudo-supported languages
99

1010
## Supported subset
1111

12-
Pseudo supports a very clear and somehow limited subset of a language:
12+
pseudo-python compiles to `pseudo ast`. Pseudo supports a very clear and somehow limited subset of a language:
1313

1414
* basic types and collections and standard library methods for them
1515

@@ -37,35 +37,53 @@ Pseudo supports a very clear and somehow limited subset of a language:
3737
* conditionals (if / else if / else)
3838
* standard math/logical operations
3939

40-
## pseudo-python compiler
40+
## Installation
4141

42-
pseudo-python checks if your program is using a valid pseudo-translatable subset of Python, type checks it according to pseudo type rules and generates a `<filename>.pseudo.yaml` output file containing pseudo-ast code
4342

44-
[TODO]
45-
You can directly run `pseudo-python <filename.py> <lang>` e.g.
43+
```bash
44+
pip install pseudo-python
45+
```
46+
47+
## Usage
4648

4749
```bash
4850
pseudo-python <filename.py> ruby
49-
pseudo-python <filename.py> cpp
51+
pseudo-python <filename.py> csharp
5052
```
51-
etc for all the supported pseudo languages (javascript, c++, c#, go, ruby and python)
53+
etc for all the supported pseudo targets (javascript, c#, go, ruby and python)
54+
55+
## examples
56+
57+
Each example contains a detailed README and working translations to Python, JS, Ruby, Go and C#, generated by Pseudo
58+
59+
[fibonacci](examples/fib)
60+
61+
[a football results processing command line tool](examples/football)
62+
63+
[a verbal expressions-like library ported to all the target languages](examples/verbal_expressions)
64+
5265

5366
## Error messages
5467

5568
A lot of work has been put into making pseudo-python error messages as clear and helpful as possible: they show the offending snippet of code and
5669
often they offer suggestions, list possible fixes or right/wrong ways to write something
5770

58-
![Screenshot of error messages](http://i.imgur.com/8W7QNgZ.png)
71+
![Screenshot of error messages](http://i.imgur.com/Et3X9W1.png)
72+
73+
Beware, pseudo and especially pseudo-python are still in early stage, so if there is anything weird going on, don't hesitate to submit an issue
5974

6075
## Type inference
6176

77+
pseudo-python checks if your program is using a valid pseudo-translatable subset of Python, type checks it according to pseudo type rules and then generates a pseudo ast and passes it to pseudo for code generation.
78+
79+
6280
The rules are relatively simple: currently pseudo-python infers everything
6381
from the usage of functions/classes, so has sufficient information when the program is calling/initializing all
6482
of its functions/classes (except for no-arg functions)
6583

6684
Often you don't really need to do that for **all** of them, you just need to do it in a way that can create call graphs covering all of them (e.g. often you'll have `a` calling `b` calling `x` and you only need to have an `a` invocation in your source)
6785

68-
You can also add type annotations. We are trying to respect existing Python3 type annotation conventions and currently pseudo-python recognizes `int`, `float`, `str`, `bool`, `List[<type>]`,
86+
You can also use type annotations. We are trying to respect existing Python3 type annotation conventions and currently pseudo-python recognizes `int`, `float`, `str`, `bool`, `List[<type>]`,
6987
`Dict[<key-type>, <value-type>]`, `Tuple[<type>..]`, `Set[<type>]` and `Callable[[<type>..], <type>]`
7088

7189
Beware, you can't just annotate one param, if you provide type annotations for a function/method, pseudo-python expects type hints for all params and a return type
@@ -106,3 +124,83 @@ but methods in children should accept the same types as their equivalents in the
106124
The easiest way to play with the type system is to just try several programs: `pseudo-python` errors should be enough to guide you, if not,
107125
you can always open an issue
108126

127+
## How does Pseudo work?
128+
129+
130+
The implementation goal is to make the definitions of new supported languages really clear and simple.
131+
132+
If you dive in, you'll find out
133+
a lot of the code/api transformations are defined using a declarative dsl with rare ocassions
134+
of edge case handling helpers.
135+
136+
That has a lot of advantages:
137+
138+
* Less bugs: the core transformation code is really generalized, it's reused as a dsl and its results are well tested
139+
140+
* Easy to comprehend: it almost looks like a config file
141+
142+
* Easy to add support for other languages: I was planning to support just python and c# in the initial version but it is so easy to add support for a language similar to the current supported ones, that I
143+
added support for 4 more.
144+
145+
* Easy to test: there is a simple test dsl too which helps all
146+
language tests to share input examples [like that](pseudo/tests/test_ruby.py)
147+
148+
However language translation is related to a lot of details and
149+
a lot of little gotchas, tuning and refining some of them took days. Pseudo uses different abstractions to streamline the process and to reuse logic across languages.
150+
151+
```ruby
152+
PSEUDO AST:
153+
NORMAL CODE PSEUDO STANDARD LIBRARY INVOCATIONS
154+
|| ||
155+
|| ||
156+
|| API TRANSLATOR
157+
|| ||
158+
|| ||
159+
|| \/
160+
|| IDIOMATIC TARGET LANGUAGE
161+
|| STANDARD LIBRARY INVOCATIONS
162+
|| ||
163+
\/ \/
164+
STANDARD OR LANGUAGE-SPECIFIC MIDDLEWARES
165+
e.g.
166+
name camel_case/snake_case middleware
167+
convert-tuples-to-classes middleware
168+
convert-exception-based errors handling
169+
to return-based error handling middleware
170+
etc
171+
172+
||
173+
||
174+
||
175+
||
176+
TARGET LANGUAGE CODE GENERATOR
177+
178+
defined with a dsl aware
179+
that handles formatting
180+
automatically
181+
||
182+
||
183+
||
184+
\/
185+
186+
OUTPUT
187+
```
188+
189+
190+
## What's the difference between Pseudo and Haxe?
191+
192+
They might seem comparable at a first glance, but they have completely different goals.
193+
194+
Pseudo wants to generate readable code, ideally something that looks like a human wrote it/ported it
195+
196+
Pseudo doesn't use a target language runtime, it uses the target language standard library for everything (except for JS, but even there is uses `lodash` which is pretty popular and standard)
197+
198+
Pseudo's goal is to help with automated translation for cases
199+
like algorithm generation, parser generation, refactoring, porting codebases etc. The fact that you can write compilers targetting Pseudo and receiver translation to many languages for free is just a happy accident
200+
201+
202+
## License
203+
204+
Copyright © 2015 2016 [Alexander Ivanov](https://twitter.com/alehander42)
205+
206+
Distributed under the MIT License.

examples/fib.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
def fib(n):
2+
if n <= 1:
3+
return 1
4+
else:
5+
return fib(n - 1) + fib(n - 2)
6+
7+
print(fib(4))

examples/football.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,4 @@ def result_points(team, host, away, goals):
2828
print('usage: football <stats-file> <team>')
2929
else:
3030
results = load_results(sys.argv[1])
31-
# print(result_points(sys.argv[2], *results[0]))
3231
print(calculate_points(results, sys.argv[2]))

pseudo_python/api_translator.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from pseudo_python.builtin_typed_api import builtin_type_check
2+
from pseudo_python.errors import PseudoPythonTypeCheckError
23

34
class Standard:
45
'''
@@ -61,6 +62,8 @@ def __init__(self, type, message):
6162
self.message = message
6263

6364
def expand(self, args):
65+
if len(args) < 2:
66+
raise PseudoPythonTypeCheckError('%s expects more args' % self.message)
6467
q = builtin_type_check(self.type, self.message, args[1], [args[0]])[-1]
6568
return {'type': 'standard_method_call', 'receiver': args[1], 'args': [args[0]], 'message': self.message, 'pseudo_type': q}
6669

pseudo_python/ast_translator.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,9 @@ def _translate_expr(self, value, location):
649649
def _translate_return(self, value, location):
650650
value_node = self._translate_node(value)
651651
whiplash = self.type_env.top[self.current_class][self.function_name]
652-
if whiplash[-1] and whiplash[-1] != value_node['pseudo_type']:
652+
if value_node is None:
653+
raise type_check_error("expected a non-void return type for %s" % self.function_name, location, self.lines[location[0]], wrong_type='Void')
654+
elif whiplash[-1] and whiplash[-1] != value_node['pseudo_type']:
653655
raise type_check_error(
654656
"expected %s return type for %s" % (serialize_type(whiplash[-1]), self.function_name), location, self.lines[location[0]], wrong_type=value_node['pseudo_type'])
655657
elif whiplash[-1] is None:

pseudo_python/builtin_typed_api.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@ def builtin_type_check(namespace, function, receiver, args):
2222
fs = TYPED_API[namespace]
2323
if fs == 'library':
2424
fs = TYPED_API['_%s' % namespace]
25+
# print(namespace, function, receiver, args, TYPED_API[namespace])
26+
# input(0)
27+
if function not in fs:
28+
raise PseudoPythonTypeCheckError('wrong usage of %s' % str(function))
2529
x = fs[function]
30+
2631
a = namespace + '#' + function if receiver else namespace + ':' + function
2732
if namespace == 'List' or namespace == 'Set' or namespace == 'Array':
2833
generics = {'@t': receiver['pseudo_type'][1]}
@@ -205,7 +210,8 @@ def xor_(l, r):
205210
'repeat': ['Int', ['List', '@t']],
206211
'push_many': [['List', '@t'], 'Void'],
207212
'remove': ['@t', 'Void'],
208-
'length': ['Int']
213+
'length': ['Int'],
214+
'join': [['List', 'String'], 'String']
209215
},
210216

211217
'Dictionary': {
@@ -216,7 +222,6 @@ def xor_(l, r):
216222
'String': {
217223
'find': ['String', 'Int'],
218224
'to_int': ['Int'],
219-
'join': [['List', 'String'], 'String'],
220225
'split': ['String', ['List', 'String']],
221226
'c_format': [['Array', 'String'], 'String'],
222227
'upper': ['String'],

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
setup(
44
name='pseudo-python',
5-
version='0.2.2',
5+
version='0.2.6',
66
description='a python3 to pseudo compiler',
77
author='Alexander Ivanov',
88
author_email='alehander42@gmail.com',

0 commit comments

Comments
 (0)