Python-构建篇-解释器-5~6

前言

《构建篇-解释器》最后两篇

05

控制流结构

在上一期中我提到了关键变量doom,那么这一期我们就要来用到doom

While

我们可以思考下如何实现循环?

在上一期中,我提到了一个callback赋值的点,那就是将内容写入临时文件,再调用stcode函数;实际上实现while也可以用到这种方法,不过要更复杂些而已

首先你得明确,你想要的while的语法格式是什么?

while 条件:
    ....
while(条件){
    ...
}

第一种?第二种?

在这里我将采用第二种来说;

知道了语法格式,我们就需要去获取执行的代码块内容以及代码块部分的行数

我们只有知道了行数才能去将doom设置为相对应需要跳过的行数,这样就不会重复执行代码

那么这该怎么获取呢?

我们先新增一个函数,目的是让这个函数收集while相关的内容,让这个函数接收四个参数:文件路径、keyword、整数值(来决定返回值)、整数值(防重复收集)

我们让函数先打开文件并读所有行,再初始化一些变量以让它追踪while的开始、代码块边界以及while语句是否处于 if 语句块内

我们要让函数在遍历文件的每一行时,检查是否找到以关键词开头且格式正确的函数定义;

如果找到了,让其开始收集while函数的内容,直到遇到函数体的结束为止。同时,它需要处理 if 语句块,并在遇到结束的} 时结束当前代码块的收集

我们让这个函数在收集完while函数的内容后,根据 参数4从收集到的函数内容列表中选取特定项

再让其根据参数3的值,返回不同的结果

如果 参数3为1,返回选取项的行数加1。

如果 参数3为2,判断是否处于 if 语句块内,返回包含函数内容以及0或1的列表

def searchdef2(file_path, keyword, wbornum, whilenum):
    with open(file_path, 'r', encoding='utf-8') as file:
        lines = file.readlines()
    in_function = False
    in_block = False
    collecting = False
    function_content = []
    current_content = []
    block_start_line = -1
    in_if_block = False 

    for i, line in enumerate(lines):
        stripped_line = line.strip()
        if stripped_line.startswith(keyword) and '(' in stripped_line and ')' in stripped_line:
            last_close_parenthesis = stripped_line.rfind(')')
            if last_close_parenthesis != -1:
                if stripped_line[last_close_parenthesis + 1:].strip() == '{':
                    in_function = True
                    collecting = True
                    block_start_line = i
                    continue

        if collecting:
            if stripped_line == '{':
                in_block = True
            elif stripped_line == '}':
                in_block = False
                if in_if_block:
                    in_if_block = False 
                else:
                    collecting = False
                    function_content.append(''.join(current_content).strip() + '\n}')
                    current_content = []
            else:
                if 'if' in stripped_line and '(' in stripped_line and ')' in stripped_line:
                    in_if_block = True  
                current_content.append(line)
            if in_function and not in_block:
                in_block = True
    full_content = function_content[whilenum]
    dpmvcjd = full_content.splitlines()
    doom2 = len(dpmvcjd) + 1
    if wbornum == 1:
        return doom2
    elif wbornum == 2:
        if in_if_block == True:
            return [full_content,0]
        else:
            return [full_content,1]

需要注意的是,每当if 'while' in line等于true进入执行时都需要让while_meet变量+1,while_meet变量用于控制获取的while语句,避免重复

同时我们将doom的值更新,以此来防止重复代码被重复执行

除此之外,我们还需要新增两个函数,第一个函数将用来获取其while语句括号内的循环次数

第二个函数,先global全局变量ppz

再将获取到的代码块内容写入临时文件

重新赋值ppz为temp,也就是临时文件的路径

def zddeget2(contentb):
    pattern = r'while\((.*?)\)'
    match = re.search(pattern, contentb)
    if match:
        value_inside_brackets = match.group(1)
        return value_inside_brackets
def temp_while(temp_content):
    global ppz
    with tempfile.NamedTemporaryFile(mode='w+', delete=False, encoding='utf-8') as temp_file:
        temp_file.write(temp_content)
        temp_file.seek(0)
        temp = temp_file.name
        ppz = temp
        return True
if 'while' in line:
    while_meet+=1
    function_meet = 1
    doom_while = searchdef2(filepathdpc2,'while',2,while_meet-1)
    function_meet = 0
    get_keycontent_while = zddeget2(line)
    if temp_while(doom_while[0]):
        for i_bmx in range(int(get_keycontent_while)-1):
            stcode(ppz,env,1)
        doom = int(get_keycontent_while)

If-支持else

同样语法格式,我这里以

if(条件){
...
}

为主

与while等同,同样是先获取if语句代码块的内容+代码块内容行数

新增一个函数,接收三个参数;

先读取文件内容,再逐行检查是否遇到函数定义,遇到则记录,将其存储在一个列表中,每一个元素是一个元组

最后根据wbornum参数的值,返回不同的结果:

如果wbornum为1,返回每个函数的行数

如果为2,返回每个函数的内容以及其中的else块内容

如果wbornum不是1或2,则函数返回None

def searchdef(file_path, keyword, wbornum):
    with open(file_path, 'r', encoding='utf-8') as file:
        lines = file.readlines()
    function_contents = []
    function_count = 0  
    block_nesting = 0  
    else_started = False  
    for i, line in enumerate(lines):
        stripped_line = line.strip()
        if stripped_line.startswith(keyword) and '(' in stripped_line and ')' in stripped_line:
            if '{' in stripped_line:
                function_count += 1  
                block_nesting += 1 
                function_name = f"{keyword}{function_count}"
                function_contents.append((function_name, [])) 
                continue
        if block_nesting > 0:
            if stripped_line == '{':
                block_nesting += 1  
            elif stripped_line == '}':
                block_nesting -= 1  
                if block_nesting == 0:
                    function_contents[-1][1].append(line)
            else:
                if stripped_line.startswith('else'):
                    else_started = True
                function_contents[-1][1].append(line)
    results = []
    for func_name, content in function_contents:
        content_str = ''.join(content).strip()
        if wbornum == 1:
            results.append(len(content_str.splitlines()) + 1 if content_str else 0)
        elif wbornum == 2:
            else_content = [line for line in content if line.strip().startswith('else')]
            results.append((content_str, ''.join(else_content).strip()))
    return results if wbornum in (1, 2) else None

 在得到代码块内容后我们需要对其进行清洗,去除多余的空白符,换行符,空格符

def remove_empty_lines(text):
    lines = text.split("\n")  
    non_empty_lines = [line for line in lines if line.strip() != ""]  
    return "\n".join(non_empty_lines)

 但是呢?

这就完了?

肯定不是,我们还需要获取if语句括号内的表达式,并新增一个函数用来处理这个表达式

这个函数将会处理一个表达式,在处理过程中如果遇到变量则使用env对象的get_variables方法来替换值

如果最后表达式成立则返回True,反之返回False

def execute_if_statement(condition, env):
    variable_pattern = r'\{(\w+)\}'
    simple_comparison_pattern = r'(\d+|\w+)[ ]?([><=]{2}[ ]?)(\d+|\w+)'
    simple_match = re.match(simple_comparison_pattern, condition)
    if simple_match:
        left, operator, right = simple_match.groups()
        left_value = env.get_variable(left) if left.strip('{}') else int(left) if left.isdigit() else left
        right_value = env.get_variable(right) if right.strip('{}') else int(right) if right.isdigit() else right
        if operator == '==':
            return left_value == right_value
        elif operator == '>':
            return left_value > right_value
        elif operator == '<':
            return left_value < right_value
        elif operator == '>=':
            return left_value >= right_value
        elif operator == '<=':
            return left_value <= right_value
    else:
        try:
            condition_result = env.evaluate_expression2(condition)
            return bool(condition_result)
        except Exception as e:
            return False

 如果返回true,我们就将执行为true时的代码块内容写入临时文件

反之则为false时的代码块内容写入临时文件

并根据其返回的if语句代码块行数来重新赋值doom

if 'if' in line:
    doomn = searchdef(filepathdpc2,'if',2)
    doonmnew = doomn[newelseif_count][0].split('}else{')
    doom_true = remove_empty_lines(doonmnew[0])
    try:
        doom_false = remove_empty_lines(doonmnew[1])
    except IndexError:
        doom_false = ''
    get_keycontent = zddeget(line)
    if execute_if_statement(get_keycontent, env):
        if(temp_while(doom_true)):
            stcode(ppz,env,1)
    else:
        if(doom_false != ''):
            if(temp_while(doom_false)):
                stcode(ppz,env,1)
    newelseif_count+=1
    doom = searchdef(filepathdpc2,'if',1)[newelseif_count-1]

Function

这是我自己设置的语法格式

function(a){
...
}

同样还是先获取代码块内容和行数

先新增一个函数

函数接收三个参数:文件路径、关键字和函数定义索引

函数开始收集直到遇到对应的右大括号的代码块

此过程中,如果遇到左大括号,表示进入一个新的代码块;

如果遇到右大括号,表示退出当前的代码块

同时,函数能处理if语句块,以确保正确地收集if语句内的内容。

收集到的函数定义被存储在列表中

最后,函数根据提供的索引从这个列表中提取相应的函数定义,并返回该函数定义的代码块以及其行数

def searchdef_def(file_path, keyword,whilenum):
    with open(file_path, 'r', encoding='utf-8') as file:
        lines = file.readlines()
    in_function = False
    in_block = False
    collecting = False
    function_content = []
    current_content = []
    block_start_line = -1
    in_if_block = False 
    for i, line in enumerate(lines):
        stripped_line = line.strip()
        if stripped_line.startswith(keyword) and '(' in stripped_line and ')' in stripped_line:
            last_close_parenthesis = stripped_line.rfind(')')
            if last_close_parenthesis != -1:
                if stripped_line[last_close_parenthesis + 1:].strip() == '{':
                    in_function = True
                    collecting = True
                    block_start_line = i
                    continue
        if collecting:
            if stripped_line == '{':
                in_block = True
            elif stripped_line == '}':
                in_block = False
                if in_if_block:
                    in_if_block = False 
                else:
                    collecting = False
                    function_content.append(''.join(current_content).strip() + '\n}')
                    current_content = []
            else:
                if 'if' in stripped_line and '(' in stripped_line and ')' in stripped_line:
                    in_if_block = True  
                current_content.append(line)
            if in_function and not in_block:
                in_block = True
    full_content2 = function_content[whilenum]
    dpmvcjd = full_content2.splitlines()
    doom23 = len(dpmvcjd) + 1
    return [doom23,full_content2]

与while相同的是,我们要让每一次进入都会让变量fun_meet+1

然后就是获取function语句括号内容+写入临时文件+设置doom新值

最后我们还要更新字典的键的值

def zddeget3(contentb):
    pattern = r'function\((.*?)\)'
    match = re.search(pattern, contentb)
    if match:
        value_inside_brackets = match.group(1)
        return value_inside_brackets
def temp_while3(temp_content):
    with tempfile.NamedTemporaryFile(mode='w+', delete=False, encoding='utf-8') as temp_file:
        temp_file.write(temp_content)
        temp_file.seek(0)
        temp = temp_file.name
        return temp
if 'function' in line:
    fun_meet+=1
    get_keycontent = zddeget3(line)
    function_meet = 1
    get_keycontent2 = searchdef_def(filepathdpc2,'function',fun_meet-1)
    doom = int(get_keycontent2[0])
    temp_build = temp_while3(get_keycontent2[1])
    functiond[get_keycontent] = temp_build

06

扩展-补充

获取参数

参数的获取也是十分重要的,假设你设计了一个函数(例如md5)

//用户写的 
//md5('abc')

虽然解释器知道这一行内容是一个函数的执行,但问题是不知道其是否有参数,不知道具体值,那就是啥用也没有了,解释器就无法对其进行处理

所以需要获取参数

我们可以思考下,写一个函数

这个函数运用到了正则表达式,它需要收集三个参数

keyword,text,env

正则表达式会匹配以参数keyword开头的(...)的参数text内容,并获取括号内的内容;

接着用,来分割括号内容合成参数

当遇到变量时就调用env对象的get_variables方法来替换值,最后返回一个列表

def get_codekey(keyword, text, env):
    pattern = rf".*?{re.escape(keyword)}\((.*?)\)"
    match = re.search(pattern, text)
    if match:
        args_raw = match.group(1)
        def replace_variables(arg):
            for var_match in re.finditer(r'\{(\w+)\}', arg):
                var_name = var_match.group(1)
                arg = arg.replace('{' + var_name + '}', str(env.get_variable(var_name)))
            return arg
        args = [replace_variables(arg.strip()) for arg in args_raw.split(',') if arg.strip()]
        return tuple(args[:2]) if len(args) >= 2 else (args[0] if args else None, None)
    else:
        return None, None
def get_codekey2(keyword, text, env):
    pattern = rf".*?{re.escape(keyword)}\(((?:[^()]|//|/|\\)*)\)"
    match = re.search(pattern, text)
    if match:
        args_raw = match.group(1).strip()
        def replace_variable(matchobj):
            var_name = matchobj.group(1).strip()
            return str(env.get_variable(var_name))
        args_processed = re.sub(r'\{(\w+)\}', replace_variable, args_raw)
        if ',' in args_processed:
            return [arg.strip() for arg in args_processed.split(',') if arg.strip()]
        else:
            return [args_processed]
    else:
        return None

多参函数推荐使用codekey2,单参函数推荐使用codekey

GUI

实际上很简单,创建一个类

再创建一个方法,

这个方法管理你想要加入的组件

组件放置我推荐使用place

将组件的引用和名称合成列表放进一个存储GUI组件的列表里

当用户需要操作时,需要填入参数:其组件一开始设置的名字

然后遍历GUI组件列表拿到引用,该怎么操作就怎么操作就行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值