pull/1/head
韩天峰 3 years ago
commit d2ee68f22a
  1. 407
      cases/mixed.py
  2. 10
      cases/test.php
  3. 25
      cases/test.py
  4. 119
      cases/test2.py
  5. 5
      cases/tmp.py
  6. 824
      conv.php
  7. 9
      dump.py
  8. 4
      py2php.php

@ -0,0 +1,407 @@
# Python中单行注释用#表示,#之后同行字符全部认为被注释
""" 与之对应的是多行注释
用三个双引号表示这两段双引号当中的内容都会被视作是注释
"""
values = []
kv = {'hello': 'world'}
# 获得一个整数
values[0] = 3
# 获得一个浮点数
values[1] = 10.0
c = 1 + 1 # => 2
d = 8 - 1 # => 7
e = 10 * 2 # => 20
f = 35 / 5 # => 7.0
g = 5 // 3 # => 1
h = -5 // 3 # => -2
j = 5.5 // 3.0 # => 1.0 # works on floats too
k = -5.0 // 3.0 # => -2.0
# Modulo operation
values[10] = 7 % 3 # => 1
# Exponentiation (x**y, x to the yth power)
values[11] = 2 ** 3 # => 8
# Enforce precedence with parentheses
values[12] = 1 + 3 * 2 # => 7
values[13] = (1 + 3) * 2 # => 8
_ = True # => True
_ = False # => False
_ = not True # => False
_ = not False # => True
# Boolean Operators
# Note "and" and "or" are case-sensitive
_ = True and False # => False
_ = False or True # => True
_ = True + True # => 2
_ = True * 8 # => 8
_ = False - 5 # => -5
_ = 0 == False # => True
_ = 1 == True # => True
_ = 2 == True # => False
_ = -5 != False # => True
_ = bool(0) # => False
_ = bool(4) # => True
_ = bool(-6) # => True
_ = 0 and 2 # => 0
_ = -5 or 0 # => -5
# Equality is ==
_ = 1 == 1 # => True
_ = 2 == 1 # => False
# Inequality is !=
_ = 1 != 1 # => False
_ = 2 != 1 # => True
# More comparisons
_ = 1 < 10 # => True
_ = 1 > 10 # => False
_ = 2 <= 2 # => True
_ = 2 >= 2 # => True
# Seeing whether a value is in a range
_ = 1 < 2 and 2 < 3 # => True
_ = 2 < 3 and 3 < 2 # => False
# Chaining makes this look nicer
_ = 1 < 2 < 3 # => True
_ = 2 < 3 < 2 # => False
_ = a = [1, 2, 3, 4] # Point a at a new list, [1, 2, 3, 4]
_ = b = a # Point b at what a is pointing to
_ = b is a # => True, a and b refer to the same object
_ = b == a # => True, a's and b's objects are equal
_ = b = [1, 2, 3, 4] # Point b at a new list, [1, 2, 3, 4]
_ = b is a # => False, a and b do not refer to the same object
_ = b == a # => True, a's and b's objects are equal
# Strings are created with " or '
_ = "This is a string."
_ = 'This is also a string.'
# Strings can be added too! But try not to do this.
_ = "Hello " + "world!" # => "Hello world!"
# String literals (but not variables) can be concatenated without using '+'
_ = "Hello " "world!" # => "Hello world!"
# A string can be treated like a list of characters
_ = "This is a string"[0] # => 'T'
# You can find the length of a string
_ = len("This is a string") # => 16
# You can also format using f-strings or formatted string literals (in Python 3.6+)
name = "Reiko"
_ = f"She said her name is {name}." # => "She said her name is Reiko"
# You can basically put any Python statement inside the braces and it will be output in the string.
_ = f"{name} is {len(name)} characters long." # => "Reiko is 5 characters long."
# None is an object
_ = None # => None
# Don't use the equality "==" symbol to compare objects to None
# Use "is" instead. This checks for equality of object identity.
_ = "etc" is None # => False
_ = None is None # => True
# None, 0, and empty strings/lists/dicts/tuples all evaluate to False.
# All other values are True
_ = bool(None) # => False
_ = bool(0) # => False
_ = bool("") # => False
_ = bool([]) # => False
_ = bool({}) # => False
_ = bool(()) # => False
# Python has a print function
print("I'm Python. Nice to meet you!") # => I'm Python. Nice to meet you!
# By default the print function also prints out a newline at the end.
# Use the optional argument end to change the end string.
print("Hello, World", end="!") # => Hello, World!
# Simple way to get input data from console
input_string_var = input("Enter some data: ") # Returns the data as a string
# Note: In earlier versions of Python, input() method was named as raw_input()
# There are no declarations, only assignments.
# Convention is to use lower_case_with_underscores
some_var = 5
# Accessing a previously unassigned variable is an exception.
# See Control Flow to learn more about exception handling.
# if can be used as an expression
# Equivalent of C's '?:' ternary operator
_ = "yahoo!" if 3 > 2 else 2 # => "yahoo!"
def test():
if 3 > 2:
return 'yahoo'
else:
return 2
# Lists store sequences
li = []
# You can start with a prefilled list
other_li = [4, 5, 6]
# Add stuff to the end of a list with append
li.append(1) # li is now [1]
li.append(2) # li is now [1, 2]
li.append(4) # li is now [1, 2, 4]
li.append(3) # li is now [1, 2, 4, 3]
# Remove from the end with pop
li.pop() # => 3 and li is now [1, 2, 4]
# Let's put it back
li.append(3) # li is now [1, 2, 4, 3] again.
# Access a list like you would any array
_ = li[0] # => 1
# Look at the last element
_ = li[-1] # => 3
# Looking out of bounds is an IndexError
_ = li[4] # Raises an IndexError
# You can look at ranges with slice syntax.
# The start index is included, the end index is not
# (It's a closed/open range for you mathy types.)
_ = li[1:3] # Return list from index 1 to 3 => [2, 4]
_ = li[2:] # Return list starting from index 2 => [4, 3]
_ = li[:3] # Return list from beginning until index 3 => [1, 2, 4]
_ = li[::2] # Return list selecting every second entry => [1, 4]
_ = li[::-1] # Return list in reverse order => [3, 4, 2, 1]
# Use any combination of these to make advanced slices
# li[start:end:step]
# Make a one layer deep copy using slices
li2 = li[:] # => li2 = [1, 2, 4, 3] but (li2 is li) will result in false.
# Remove arbitrary elements from a list with "del"
del li[2] # li is now [1, 2, 3]
# Remove first occurrence of a value
li.remove(2) # li is now [1, 3]
li.remove(2) # Raises a ValueError as 2 is not in the list
# Insert an element at a specific index
li.insert(1, 2) # li is now [1, 2, 3] again
# Get the index of the first item found matching the argument
li.index(2) # => 1
li.index(4) # Raises a ValueError as 4 is not in the list
# Tuples are like lists but are immutable.
tup = (1, 2, 3)
tup[0] # => 1
tup[0] = 3 # Raises a TypeError
type((1)) # => <class 'int'>
type((1,)) # => <class 'tuple'>
type(()) # => <class 'tuple'>
_ = len(tup) # => 3
_ = tup + (4, 5, 6) # => (1, 2, 3, 4, 5, 6)
_ = tup[:2] # => (1, 2)
_ = 2 in tup # => True
# You can unpack tuples (or lists) into variables
a, b, c = (1, 2, 3) # a is now 1, b is now 2 and c is now 3
# You can also do extended unpacking
# Tuples are created by default if you leave out the parentheses
d, e, f = 4, 5, 6 # tuple 4, 5, 6 is unpacked into variables d, e and f
# respectively such that d = 4, e = 5 and f = 6
# Now look how easy it is to swap two values
e, d = d, e # d is now 5 and e is now 4
# Look up values with []
invalid_dict = {1: "123"}
_ = invalid_dict["one"] # => 1
_ = invalid_dict.get('one') # => 1
# Here is a prefilled dictionary
filled_dict = {"one": 1, "two": 2, "three": 3}
# Get all keys as an iterable with "keys()". We need to wrap the call in list()
# to turn it into a list. We'll talk about those later. Note - for Python
# versions <3.7, dictionary key ordering is not guaranteed. Your results might
# not match the example below exactly. However, as of Python 3.7, dictionary
# items maintain the order at which they are inserted into the dictionary.
_ = list(filled_dict.keys()) # => ["three", "two", "one"] in Python <3.7
_ = list(filled_dict.keys()) # => ["one", "two", "three"] in Python 3.7+
# Get all values as an iterable with "values()". Once again we need to wrap it
# in list() to get it out of the iterable. Note - Same as above regarding key
# ordering.
_ = list(filled_dict.values()) # => [3, 2, 1] in Python <3.7
_ = list(filled_dict.values()) # => [1, 2, 3] in Python 3.7+
# Check for existence of keys in a dictionary with "in"
_ = "one" in filled_dict # => True
_ = 1 in filled_dict # => False
# _ = {'a': 1, **{'b': 2}} # => {'a': 1, 'b': 2}
# _ = {'a': 1, **{'a': 2}} # => {'a': 2}
# Sets store ... well sets
empty_set = set()
# Initialize a set with a bunch of values. Yeah, it looks a bit like a dict. Sorry.
some_set = {1, 1, 2, 2, 3, 4} # some_set is now {1, 2, 3, 4}
# Do set intersection with &
# 计算交集
other_set = {3, 4, 5, 6}
filled_set = {1, 2, 3}
_ = filled_set & other_set # => {3, 4, 5}
# Do set union with |
# 计算并集
_ = filled_set | other_set # => {1, 2, 3, 4, 5, 6}
# Do set difference with -
# 计算差集
_ = {1, 2, 3, 4} - {2, 3, 5} # => {1, 4}
# Do set symmetric difference with ^
# 这个有点特殊,计算对称集,也就是去掉重复元素剩下的内容
_ = {1, 2, 3, 4} ^ {2, 3, 5} # => {1, 4, 5}
# Check if set on the left is a superset of set on the right
_ = {1, 2} >= {1, 2, 3} # => False
# Check if set on the left is a subset of set on the right
_ = {1, 2} <= {1, 2, 3} # => True
if some_var > 10:
print("some_var is totally bigger than 10.")
elif some_var < 10: # This elif clause is optional.
print("some_var is smaller than 10.")
else: # This is optional too.
print("some_var is indeed 10.")
for animal in ["dog", "cat", "mouse"]:
# You can use format() to interpolate formatted strings
print("{} is a mammal".format(animal))
for i in range(4):
print(i)
animals = ["dog", "cat", "mouse"]
for i, value in enumerate(animals):
print(i, value)
x = 0
while x < 4:
print(x)
x += 1 # Shorthand for x = x + 1
# Handle exceptions with a try/except block
try:
# Use "raise" to raise an error
raise IndexError("This is an index error")
except IndexError as e:
pass # Pass is just a no-op. Usually you would do recovery here.
except (TypeError, NameError):
pass # Multiple exceptions can be handled together, if required.
finally: # Execute under all circumstances
print("We can clean up resources here")
# Instead of try/finally to cleanup resources you can use a with statement
# 代替使用try/finally语句来关闭资源
with open("myfile.txt") as f:
for line in f:
print(line)
# Writing to a file
# 使用with写入文件
contents = {"aa": 12, "bb": 21}
with open("myfile1.txt", "w+") as file:
file.write(str(contents)) # writes a string to a file
with open("myfile2.txt", "w+") as file:
file.write(json.dumps(contents)) # writes an object to a file
# Reading from a file
# 使用with读取文件
with open('myfile1.txt', "r+") as file:
contents = file.read() # reads a string from a file
print(contents)
# print: {"aa": 12, "bb": 21}
with open('myfile2.txt', "r+") as file:
contents = json.load(file) # reads a json object from a file
print(contents)
# print: {"aa": 12, "bb": 21}
# Python offers a fundamental abstraction called the Iterable.
# An iterable is an object that can be treated as a sequence.
# The object returned by the range function, is an iterable.
filled_dict = {"one": 1, "two": 2, "three": 3}
our_iterable = filled_dict.keys()
print(our_iterable) # => dict_keys(['one', 'two', 'three']). This is an object that implements our Iterable interface.
# We can loop over it.
for i in our_iterable:
print(i) # Prints one, two, three
# However we cannot address elements by index.
our_iterable[1] # Raises a TypeError
# An iterable is an object that knows how to create an iterator.
our_iterator = iter(our_iterable)
# Our iterator is an object that can remember the state as we traverse through it.
# We get the next object with "next()".
next(our_iterator) # => "one"
# It maintains state as we iterate.
next(our_iterator) # => "two"
next(our_iterator) # => "three"
# After the iterator has returned all of its data, it raises a StopIteration exception
next(our_iterator) # Raises StopIteration
# We can also loop over it, in fact, "for" does this implicitly!
our_iterator = iter(our_iterable)
for i in our_iterator:
print(i) # Prints one, two, three
# You can grab all the elements of an iterable or iterator by calling list() on it.
list(our_iterable) # => Returns ["one", "two", "three"]
list(our_iterator) # => Returns [] because state is saved
# Use "def" to create new functions
def add(x, y):
print("x is {} and y is {}".format(x, y))
return x + y # Return values with a return statement
# Calling functions with parameters
add(5, 6) # => prints out "x is 5 and y is 6" and returns 11
# Another way to call functions is with keyword arguments
add(y=6, x=5) # Keyword arguments can arrive in any order.
# You can define functions that take a variable number of
# positional arguments
def varargs(*args):
return args
varargs(1, 2, 3) # => (1, 2, 3)

@ -0,0 +1,10 @@
<?php
$operator = PyCore::import("operator");
$builtins = PyCore::import("builtins");
function varargs(...$xxx) {
return $xxx;
}
varargs(1, 2, 3);

@ -0,0 +1,25 @@
from vllm_wrapper import vLLMWrapper
model = vLLMWrapper('Qwen/Qwen-72B-Chat', tensor_parallel_size=2)
import sys
response, history = model.chat(query="你好", history=None)
print(response)
response, history = model.chat(query="给我讲一个年轻人奋斗创业最终取得成功的故事。", history=history)
print(response)
response, history = model.chat(query="给这个故事起一个标题", history=history)
print(response)
def test(name, n, hello, **kwargs):
model2 = vLLMWrapper('Qwen/Qwen-72B-Chat', tensor_parallel_size=2)
def it(m):
return "hello world"
s = it(model2)
return model2

@ -0,0 +1,119 @@
"""
This script creates an interactive web demo for the ChatGLM3-6B model using Gradio, a Python library for building quick and easy UI components for machine learning models. It's designed to showcase the capabilities of the ChatGLM3-6B model in a user-friendly interface, allowing users to interact with the model through a chat-like interface.
Usage:
- Run the script to start the Gradio web server.
- Interact with the model by typing questions and receiving responses.
Requirements:
- Gradio (required with 3.39 version, not support for 4.x), Transformers, and other necessary Python libraries should be installed.
- The model checkpoint should be accessible at the specified paths.
Note: The script includes a modification to the Chatbot's postprocess method to handle markdown to HTML conversion, ensuring that the chat interface displays formatted text correctly.
"""
import os
import mdtex2html
from transformers import AutoModel, AutoTokenizer
import gradio as gr
MODEL_PATH = os.environ.get('MODEL_PATH', 'THUDM/chatglm3-6b')
TOKENIZER_PATH = os.environ.get("TOKENIZER_PATH", MODEL_PATH)
tokenizer = AutoTokenizer.from_pretrained(TOKENIZER_PATH, trust_remote_code=True)
model = AutoModel.from_pretrained(MODEL_PATH, trust_remote_code=True, device_map="auto").eval()
def postprocess(self, y):
if y is None:
return []
for i, (message, response) in enumerate(y):
y[i] = (
None if message is None else mdtex2html.convert((message)),
None if response is None else mdtex2html.convert(response),
)
return y
gr.Chatbot.postprocess = postprocess
def parse_text(text):
"""copy from https://github.com/GaiZhenbiao/ChuanhuChatGPT/"""
lines = text.split("\n")
lines = [line for line in lines if line != ""]
count = 0
for i, line in enumerate(lines):
if "```" in line:
count += 1
items = line.split('`')
if count % 2 == 1:
lines[i] = f'<pre><code class="language-{items[-1]}">'
else:
lines[i] = f'<br></code></pre>'
else:
if i > 0:
if count % 2 == 1:
line = line.replace("`", "\`")
line = line.replace("<", "&lt;")
line = line.replace(">", "&gt;")
line = line.replace(" ", "&nbsp;")
line = line.replace("*", "&ast;")
line = line.replace("_", "&lowbar;")
line = line.replace("-", "&#45;")
line = line.replace(".", "&#46;")
line = line.replace("!", "&#33;")
line = line.replace("(", "&#40;")
line = line.replace(")", "&#41;")
line = line.replace("$", "&#36;")
lines[i] = "<br>" + line
text = "".join(lines)
return text
def predict(input, chatbot, max_length, top_p, temperature, history, past_key_values):
chatbot.append((parse_text(input), ""))
for response, history, past_key_values in model.stream_chat(tokenizer, input, history,
past_key_values=past_key_values,
return_past_key_values=True,
max_length=max_length, top_p=top_p,
temperature=temperature):
chatbot[-1] = (parse_text(input), parse_text(response))
yield chatbot, history, past_key_values
def reset_user_input():
return gr.update(value='')
#
def reset_state():
return [], [], None
with gr.Blocks() as demo:
gr.HTML("""<h1 align="center">ChatGLM3-6B</h1>""")
chatbot = gr.Chatbot()
with gr.Row():
with gr.Column(scale=4):
with gr.Column(scale=12):
user_input = gr.Textbox(show_label=False, placeholder="Input...", lines=10).style(
container=False)
with gr.Column(min_width=32, scale=1):
submitBtn = gr.Button("Submit", variant="primary")
with gr.Column(scale=1):
emptyBtn = gr.Button("Clear History")
max_length = gr.Slider(0, 32768, value=8192, step=1.0, label="Maximum length", interactive=True)
top_p = gr.Slider(0, 1, value=0.8, step=0.01, label="Top P", interactive=True)
temperature = gr.Slider(0, 1, value=0.6, step=0.01, label="Temperature", interactive=True)
history = gr.State([])
past_key_values = gr.State(None)
submitBtn.click(predict, [user_input, chatbot, max_length, top_p, temperature, history, past_key_values],
[chatbot, history, past_key_values], show_progress=True)
submitBtn.click(reset_user_input, [], [user_input])
emptyBtn.click(reset_state, outputs=[chatbot, history, past_key_values], show_progress=True)
demo.queue().launch(share=False, server_name="127.0.0.1", server_port=8501, inbrowser=True)

@ -0,0 +1,5 @@
def varargs(*xxx):
return xxx
varargs(1, 2, 3) # => (1, 2, 3)

@ -0,0 +1,824 @@
<?php
use PhpyTool\Tool;
if ($argc < 2) {
die("Usage: php conv.php [python-file]\n");
}
require_once __DIR__ . '/src/Tool.php';
$result = shell_exec('python dump.py ' . $argv[1]);
$json = json_decode($result);
if ($json->_type != 'Module') {
echo "invalid python module\n";
}
function debug($v)
{
global $translator;
if ($translator->mode == 'cli') {
echo 'Error: Unsupported Python Syntax, Line: ' . $v->lineno . PHP_EOL;
debug_print_backtrace();
var_dump($v);
} else {
header('Content-Type: application/json');
echo json_encode($v, JSON_PRETTY_PRINT);
}
die;
}
function if_empty_debug($if_expr, $v)
{
if (empty($if_expr)) {
debug($v);
}
}
function if_not_empty_debug($if_expr, $v)
{
if (!empty($if_expr)) {
debug($v);
}
}
class Translator
{
private array $keywords = ['abs', 'aiter', 'all', 'anext', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip'];
private array $keywordsMap = [];
private int $indentLevel = 0;
private string $indentStr = "\t";
public string $mode;
private array $definedFunctions = [];
private array $builtinTypes = [
'ArithmeticError',
'AssertionError',
'AttributeError',
'BaseException',
'BaseExceptionGroup',
'BlockingIOError',
'BrokenPipeError',
'BufferError',
'BytesWarning',
'ChildProcessError',
'ConnectionAbortedError',
'ConnectionError',
'ConnectionRefusedError',
'ConnectionResetError',
'DeprecationWarning',
'EOFError',
'Ellipsis',
'EncodingWarning',
'EnvironmentError',
'Exception', 'ExceptionGroup', 'False',
'FileExistsError', 'FileNotFoundError', 'FloatingPointError',
'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning',
'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError',
'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError',
'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError',
'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError',
'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning',
'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning',
'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError',
'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError',
'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError'
];
function __construct()
{
$this->keywordsMap = array_flip($this->keywords);
$this->builtinTypes = array_flip($this->builtinTypes);
}
function setMode($mode)
{
$this->mode = $mode;
}
function setIndent(string $indent)
{
$this->indentStr = $indent;
}
function prepare($root)
{
foreach ($root->body as $body) {
if ($body->_type == 'FunctionDef') {
$this->definedFunctions[$body->name] = 1;
}
}
}
function parseAttribute($attr)
{
switch ($attr->_type) {
case 'Name':
return '$' . $attr->id;
case 'Attribute':
return $this->parseAttribute($attr->value) . '->' . $attr->attr;
case 'Constant':
return 'PyCore::str("' . $attr->s . '")';
case 'Call':
return $this->parseCall($attr);
default:
var_dump(__METHOD__, __LINE__);
debug($attr);
}
}
function parseFunc($fn)
{
switch ($fn->_type) {
case 'Name':
$id = $fn->id;
if (isset($this->keywordsMap[$id])) {
return 'PyCore::' . $id;
} elseif (isset($this->definedFunctions[$id])) {
return $id;
} elseif (isset($this->builtinTypes[$id])) {
return '$builtins->' . $id;
} else {
return '$' . $id;
}
case 'Attribute':
return $this->parseAttribute($fn->value) . '->' . $fn->attr;
default:
debug($fn);
}
}
function parseCall($call): string
{
$fn = $this->parseFunc($call->func);
if_empty_debug($fn, $call);
$args = $call->args;
$kwargs = $call->keywords;
$args_list = [];
foreach ($args as $arg) {
$args_list[] = $this->parseValue($arg);
}
foreach ($kwargs as $arg) {
$name = $arg->arg;
$args_list[] = $name . ': ' . $this->parseValue($arg->value);
}
if (empty($args_list)) {
return "$fn()";
} else {
return "$fn(" . implode(', ', $args_list) . ")";
}
}
function parseConstant($value): string
{
return Tool::valueToRepr($value->value);
}
function parseTuple($tuple)
{
$ids = [];
foreach ($tuple->dims as $dim) {
$ids[] = $this->parseTarget($dim);
}
return '[' . implode(', ', $ids) . ']';
}
function parseListComp($listComp)
{
$code = '';
$this->indentLevel++;
$code .= $this->getIndent() . '$___ = [];' . PHP_EOL;
$recipient = '$___[]';
$generators = $this->parseGenerators($listComp->generators, $recipient, $captures);
$code .= $this->getIndent() . $generators;
$code .= $this->getIndent() . 'return $___;' . PHP_EOL;
$this->indentLevel--;
$code .= $this->getIndent() . '})(' . implode(',', $captures) . ')';
return '(function(' . implode(',', $captures) . ') {' . PHP_EOL . $code;
}
function parseValue($value)
{
switch ($value->_type) {
case 'Call':
return $this->parseCall($value);
case 'Constant':
return $this->parseConstant($value);
case 'Name':
return $this->parseName($value);
case 'List':
return $this->parseList($value);
case 'Dict':
return $this->parseDict($value);
case 'Set':
return $this->parseSet($value);
case 'Tuple':
return $this->parseTuple($value);
case 'ListComp':
return $this->parseListComp($value);
case 'BinOp':
return $this->parseBinOp($value);
case 'JoinedStr':
return $this->parseJoinedStr($value);
case 'Subscript':
return $this->parseSubscript($value);
case 'FormattedValue':
return $this->parseValue($value->value);
case 'UnaryOp':
return $this->parseTarget($value);
case 'BoolOp':
return $this->parseBoolOp($value);
case 'Compare':
return $this->parseTest($value);
case 'IfExp':
return $this->parseIfExp($value);
case 'Yield':
return $this->parseYield($value);
default:
debug($value);
break;
}
}
function parseSlice($slice, $op = 'Load')
{
if ($slice->_type != 'Constant') {
$_args[] = $slice->lower ? $this->parseTarget($slice->lower) : 'null';
$_args[] = $slice->upper ? $this->parseTarget($slice->upper) : 'null';
$_args[] = $slice->step ? $this->parseTarget($slice->step) : 'null';
$target = 'PyCore::slice(' . implode(', ', $_args) . ')';
} else {
$target = $this->parseTarget($slice);
}
if ($op == 'Store') {
return $slice->id . '->__setitem__(' . $target . ', $__value)';
} elseif ($op == 'Del') {
return $slice->id . '->__delitem__(' . $target . ')';
} else {
return $slice->id . '->__getitem__(' . $target . ')';
}
}
function parseImportFrom($node)
{
$module = $node->module;
$imports = [];
foreach ($node->names as $name) {
$type = $name->name;
$as = empty($name->asname) ? $type : $name->asname;
$imports[] = "\$$as = PyCore::import('$module')->$type";
}
return implode(';' . PHP_EOL, $imports);
}
function parseImport($node)
{
$name = $node->names[0];
$module = $name->name;
$as = empty($name->asname) ? $module : $name->asname;
return "\$$as = PyCore::import('$module')";
}
function parseTarget($target)
{
switch ($target->_type) {
case 'Name':
return '$' . $target->id;
case 'UnaryOp':
$operand = $this->parseTarget($target->operand);
switch ($target->op->_type) {
case 'USub':
return '-' . $operand;
case 'Not':
return '!' . $operand;
default:
debug($target);
break;
}
break;
case 'Tuple':
return $this->parseTuple($target);
case 'Subscript':
return $this->parseSubscript($target);
case 'IfExp':
return $this->parseIfExp($target);
case 'Attribute':
return $this->parseAttribute($target);
case 'Constant':
return $this->parseConstant($target);
case 'List':
return $this->parseList($target);
case 'Set':
return $this->parseSet($target);
case 'Call':
return $this->parseCall($target);
case 'BinOp':
return $this->parseBinOp($target);
case 'Compare':
return $this->parseTest($target);
case 'Slice':
return $this->parseSlice($target);
default:
debug($target);
break;
}
}
function parseIfExp($exp)
{
$test = $this->parseTest($exp->test);
$orelse = $this->parseValue($exp->orelse);
$body = $this->parseValue($exp->body);
return $test . ' ? ' . $body . ' : ' . $orelse;
}
function parseExpr($node)
{
switch ($node->_type) {
case 'Expr':
if (is_string($node->value->value)) {
return '/** ' . $node->value->value . ' */' . PHP_EOL;
} else {
return $this->parseValue($node->value) . ';';
}
default:
return $this->parseValue($node->value) . ';';
}
}
function parseArguments($args): string
{
$names = [];
foreach ($args->args as $arg) {
$names[] = '$' . $arg->arg;
}
if ($args->vararg) {
$names[] = '...$' . $args->vararg->arg;
}
return implode(', ', $names);
}
function parseReturn($node)
{
return 'return ' . $this->parseValue($node->value);
}
function parseTest($test)
{
$left = $this->parseValue($test->left);
$ops = $test->ops;
$comparators = $test->comparators;
$op = $ops[0]->_type;
$comparator = $comparators[0];
switch ($op) {
case 'Is':
case 'Eq':
return $left . ' == ' . $this->parseValue($comparator);
case 'NotEq':
return $left . ' != ' . $this->parseValue($comparator);
case 'In':
return $this->parseValue($comparator) . '->__contains__(' . $left . ')';
case 'Gt':
return $left . ' > ' . $this->parseValue($comparator);
case 'Lt':
return $left . ' < ' . $this->parseValue($comparator);
case 'GtE':
return $left . ' >= ' . $this->parseValue($comparator);
case 'LtE':
return $left . ' <= ' . $this->parseValue($comparator);
default:
debug($test);
}
}
function parseIf($node)
{
$expr = $this->parseTest($node->test);
$this->indentLevel++;
$body = $this->parseBody($node->body);
$orelse = empty($node->orelse) ? '' : $this->parseBody($node->orelse);
$this->indentLevel--;
return 'if (' . $expr . ') {' .
PHP_EOL . $body .
PHP_EOL . $this->getIndent() . '}' .
($orelse ? ' else {' . PHP_EOL . $orelse . PHP_EOL . $this->getIndent() . '}' : '') .
PHP_EOL;
}
function parseIter($iter)
{
switch ($iter->_type) {
case 'Call':
return $this->parseCall($iter);
case 'List':
return $this->parseList($iter);
case 'Set':
return $this->parseSet($iter);
case 'Dict':
return $this->parseDict($iter);
case 'Name':
return $this->parseName($iter);
default:
debug($iter);
}
}
function parseSubscript($node)
{
$code = '';
if ($node->value) {
$code .= $this->parseValue($node->value);
}
if ($node->slice) {
$code .= $this->parseSlice($node->slice, $node->ctx->_type);
}
return $code;
}
function parseFor($node)
{
$target = $this->parseTarget($node->target);
$iter = $this->parseIter($node->iter);
$code = '$__iter = PyCore::iter(' . $iter . ');' . PHP_EOL;
$code .= $this->getIndent() . 'while($current = PyCore::next($__iter)) {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->getIndent() . $target . ' = $current;' . PHP_EOL;
$code .= $this->parseBody($node->body);
$this->indentLevel--;
return $code . $this->getIndent() . PHP_EOL . $this->getIndent() . '}';
}
private function getIndent()
{
return str_repeat($this->indentStr, $this->indentLevel);
}
function parseFunctionDef($node)
{
$name = $node->name;
$args = $this->parseArguments($node->args);
$fn = PHP_EOL . $this->getIndent() . 'function ' . $name . '(' . $args . ') {' . PHP_EOL;
$this->indentLevel++;
$fn .= $this->parseBody($node->body) . PHP_EOL;;
$this->indentLevel--;
$fn .= $this->getIndent() . '}' . PHP_EOL . PHP_EOL;
return $fn;
}
private function addLine($line, array &$lines)
{
if ($this->mode == 'cli') {
// echo $line . PHP_EOL;
}
$lines[] = $line;
}
function parseBody($tree)
{
$lines = [];
foreach ($tree as $node) {
switch ($node->_type) {
case 'ImportFrom':
$line = $this->parseImportFrom($node) . ';';
break;
case 'Assign':
$target = $this->parseTarget($node->targets[0]);
$value = $this->parseValue($node->value);
if ($node->targets[0]->_type == 'Subscript') {
$this->addLine('$__value = ' . $value . ';', $lines);
$line = "$target;";
} else {
$line = "$target = $value;";
}
break;
case 'AugAssign':
$target = $this->parseTarget($node->target);
$value = $this->parseValue($node->value);
$line = "$target += $value;";
break;
case 'Import':
$line = $this->parseImport($node) . ';';
break;
case 'Expr':
$line = $this->parseExpr($node);
break;
case 'FunctionDef':
$line = $this->parseFunctionDef($node);
break;
case 'Return':
$line = $this->parseReturn($node) . ';';
break;
case 'If':
$line = $this->parseIf($node);
break;
case 'For':
$line = $this->parseFor($node);
break;
case 'While':
$line = $this->parseWhile($node);
break;
case 'Try':
$line = $this->parseTry($node);
break;
case 'Raise':
$line = $this->parseRaise($node) . ';';
break;
case 'Subscript':
$line = $this->parseSubscript($node) . ';';
break;
case 'With':
$line = $this->parseWith($node);
break;
case 'Compare':
$line = $this->parseTest($node);
break;
case 'Delete':
$line = $this->parseDelete($node);
break;
default:
debug($node);
$line = '';
break;
}
$this->addLine($line, $lines);
}
foreach ($lines as &$line) {
$line = $this->getIndent() . $line;
}
return implode(PHP_EOL, $lines);
}
function convert($tree)
{
$this->prepare($tree);
$output = '<?php' . PHP_EOL;
$output .= '$operator = PyCore::import("operator");' . PHP_EOL;
$output .= '$builtins = PyCore::import("builtins");' . PHP_EOL;
$output .= $this->parseBody($tree->body);
echo $output;
echo PHP_EOL;
}
private function parseGenerators($generators, $recipient, &$captures)
{
$code = '$elts = [];' . PHP_EOL;
foreach ($generators as $k => $generator) {
$target = $this->parseTarget($generator->target);
$iter = $generator->iter->id;
$name = '$' . $iter;
$captures[] = $name;
$code .= $this->getIndent() . 'foreach(' . $name . ' as ' . $target . ') {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->getIndent() . '$elts[' . $k . '] = ' . $target . ';' . PHP_EOL;
$this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL;
}
$code .= $this->getIndent() . $recipient . ' = $elts;' . PHP_EOL;;
return $code;
}
private function parseBinOp($value)
{
$op = $value->op->_type;
$left = $this->parseTarget($value->left);
$right = $this->parseValue($value->right);
switch ($op) {
case 'Mod':
return $left . ' % ' . $right;
case 'Add':
return $left . ' + ' . $right;
case 'Sub':
return $left . ' - ' . $right;
case 'Mult':
return $left . ' * ' . $right;
case 'Div':
return $left . ' / ' . $right;
default:
return '$operator->' . strtolower($op) . '(' . $left . ' , ' . $right . ')';
}
}
private function parseJoinedStr($value)
{
$list = [];
foreach ($value->values as $v) {
$list[] = $this->parseValue($v);
}
return implode(' . ', $list);
}
private function parseList($target)
{
$values = [];
foreach ($target->elts as $e) {
$values[] = $this->parseValue($e);
}
return 'new PyList([' . implode(', ', $values) . '])';
}
private function parseYield($value)
{
return 'yield ' . $this->parseValue($value->value) . ';';
}
private function parseWith(mixed $node)
{
$items = $node->items;
$body = $node->body;
$code = '';
$finally_code = '';
$parseItem = function ($call, $target) use (&$code, &$finally_code) {
$call = $this->parseCall($call);
$target = empty($target) ? '$__' : $this->parseTarget($target);
$code .= $target . '__object = ' . $call . ';' . PHP_EOL;;
$code .= $target . ' = ' . $target . '__object->__enter__();' . PHP_EOL;
$this->indentLevel++;
$finally_code .= $this->getIndent() . $target . '__object->__exit__();' . PHP_EOL;
$this->indentLevel--;
};
foreach ($items as $k => $item) {
if ($item->context_expr->_type == 'Tuple') {
$context_expr = $item->context_expr->dims;
$optional_vars = $item->optional_vars->dims;
foreach ($context_expr as $k2 => $dim) {
$parseItem($dim, $optional_vars[$k2]);
}
} else {
$parseItem($item->context_expr, $item->optional_vars);
}
}
$code .= 'try {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->parseBody($body);
$this->indentLevel--;
$code .= PHP_EOL . '} finally {' . PHP_EOL;
$code .= $finally_code;
$code .= '}' . PHP_EOL;
return $code;
}
private function parseDict($dict)
{
$code = '[' . PHP_EOL;
foreach ($dict->values as $k => $v) {
if (empty($dict->keys[$k])) {
debug($v);
}
$key = $dict->keys[$k];
$this->indentLevel++;
$code .= $this->getIndent() . $this->parseTarget($key) . ' => ' . $this->parseTarget($v) . ',' . PHP_EOL;
$this->indentLevel--;
}
return 'new PyDict(' . $code . '])';
}
private function parseBoolOp($value)
{
switch ($value->op->_type) {
case 'And':
return $this->parseAnd($value->values);
case 'Or':
return $this->parseOr($value->values);
default:
debug($value);
break;
}
}
private function parseAnd($values)
{
$targets = [];
foreach ($values as $v) {
$targets[] = $this->parseTarget($v);
}
return implode(' && ', $targets);
}
private function parseOr($values)
{
$targets = [];
foreach ($values as $v) {
$targets[] = $this->parseTarget($v);
}
return implode(' || ', $targets);
}
private function parseDelete(mixed $node)
{
$code = '';
foreach ($node->targets as $target) {
$code .= $this->getIndent() . $this->parseSubscript($target) . ';' . PHP_EOL;
}
return $code;
}
private function parseSet($target)
{
$values = [];
foreach ($target->elts as $e) {
$values[] = $this->parseValue($e);
}
return 'new PySet([' . implode(', ', $values) . '])';
}
private function parseWhile(mixed $node)
{
$test = $this->parseTest($node->test);
$code = $this->getIndent() . 'while(' . $test . ') {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->parseBody($node->body);
$this->indentLevel--;
return $code . $this->getIndent() . PHP_EOL . $this->getIndent() . '}';
}
private function parseTry(mixed $node)
{
$code = 'try {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->parseBody($node->body);
$this->indentLevel--;
$code .= PHP_EOL . '}';
if ($node->handlers) {
$code .= ' catch(PyError $e) {' . PHP_EOL;
$this->indentLevel++;
foreach ($node->handlers as $k => $handler) {
if ($handler->type->_type == 'Name') {
$id = $handler->type->id;
$type = isset($this->builtinTypes[$id]) ? '$builtins->' . $id : '$' . $id;
} else {
$types = [];
foreach ($handler->type->dims as $dim) {
$id = $dim->id;
$types[] = isset($this->builtinTypes[$id]) ? '$builtins->' . $id : '$' . $id;
}
$type = 'new PyTuple([' . implode(', ', $types) . '])';
}
$code .= ($k == 0 ? $this->getIndent() . 'if' : ' elseif') . ' (PyCore::isinstance($e, ' . $type . ')) {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->parseBody($node->body);
$this->indentLevel--;
$code .= PHP_EOL . $this->getIndent() . '}';
}
$code .= ' else {';
$this->indentLevel++;
$code .= PHP_EOL . $this->getIndent() . 'throw $e;';
$this->indentLevel--;
$code .= PHP_EOL . $this->getIndent() . '}' . PHP_EOL;
$this->indentLevel--;
$code .= $this->getIndent() . '}';
}
if ($node->finalbody) {
$code .= ' finally {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->parseBody($node->finalbody);
$this->indentLevel--;
$code .= PHP_EOL . $this->getIndent() . '}';
}
return $code;
}
private function parseRaise(mixed $node)
{
return 'throw ' . $this->parseCall($node->exc);
}
private function parseName($value)
{
return '$' . $value->id;
}
}
error_reporting(E_ERROR);
// web or cli
if (!empty($argv[2])) {
$mode = $argv[2];
} else {
$mode = 'cli';
}
$translator = new Translator();
$translator->setMode($mode);
$translator->setIndent(' ');
$translator->convert($json);

@ -0,0 +1,9 @@
import ast
import json
from ast2json import ast2json
import sys
if __name__ == "__main__":
file_name = sys.argv[1]
tree = ast2json(ast.parse(open(file_name).read()))
print(json.dumps(tree, indent=4))

@ -0,0 +1,4 @@
<?php
$file = __DIR__ . '/cases/' . $_GET['file'];
echo shell_exec('php conv.php ' . $file . ' web');
Loading…
Cancel
Save