commit
d2ee68f22a
8 changed files with 1403 additions and 0 deletions
@ -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("<", "<") |
||||
line = line.replace(">", ">") |
||||
line = line.replace(" ", " ") |
||||
line = line.replace("*", "*") |
||||
line = line.replace("_", "_") |
||||
line = line.replace("-", "-") |
||||
line = line.replace(".", ".") |
||||
line = line.replace("!", "!") |
||||
line = line.replace("(", "(") |
||||
line = line.replace(")", ")") |
||||
line = line.replace("$", "$") |
||||
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…
Reference in new issue