Skip to content

Latest commit

 

History

History
295 lines (285 loc) · 11 KB

readme_py.md

File metadata and controls

295 lines (285 loc) · 11 KB

GO语言词法分析器

使用go和python分别实现

Python部分

已经写完了,可能存在部分未知问题 | 简单做一个描述

Token和Pos结构

# 定义 token 类型
class Token:
    def __init__(self, token_type, value,pos):
        self.token_type = token_type
        self.value = value
        self.pos=pos

# 定义 pos 类型 也就是 存储位置信息
class Pos:
    def __init__(self,offset,line,col) -> None:
        self.offset=offset
        self.line=line
        self.col=col

Scanner结构|主要部分

# 定义词法分析器类
class Scanner:
    def __init__(self, input_string):
        self.input_string = input_string
        self.position = 0
        self.line = 1
        self.col = 1
        self.keywords ={'break','case','chan','const','continue','default','defer','else','fallthrough','for','func','go','goto','if','import','interface','map','package','range','return','select','struct','switch','type','var'}

第一步:提取标识符

# 提取标识符
def extract_identifier(self):
    start_position = self.position
    start_line=self.line    
    start_col=self.col
    while self.position < len(self.input_string) and (self.input_string[self.position].isalnum() or self.input_string[self.position] == '_'):
        self.update_pos()
    value = self.input_string[start_position:self.position]
    pos = Pos(start_position, start_line,start_col)
    
    return Token('KEYWORD' if value in self.keywords else 'IDENT', value,pos)

第二步:对于操作符/分割符号进行解析|注意这个符号有时候有歧义

# 对于操作符号 进行提取
def extract_operator(self):
    start_position = self.position
    start_line=self.line    
    start_col=self.col
    operator = self.input_string[self.position]
    self.update_pos()
    if operator == '+':
        # ++
        if self.input_string[self.position] == '+':
            operator += '+'
            self.update_pos()
        # +=
        elif self.input_string[self.position] == '=':
            operator += '='
            self.update_pos()
    elif operator == '-':
        # --
        if self.input_string[self.position] == '-':
            operator += '-'
            self.update_pos()
        # -=
        elif self.input_string[self.position] == '=':
            operator += '='
            self.update_pos()
        # 负数的情况 在这里是不需要进行考虑的
    elif operator == '*':
        # *=
        if self.input_string[self.position] == '=':
            operator += '='
            self.update_pos()
    elif operator == '/':
        # // 单行注释
        if self.input_string[self.position] == '/':
            return self.extract_single_line_comment()
        # /* 多行注释
        elif self.input_string[self.position] == '*':
            return self.extract_multi_line_comment()
        # /= 
        elif self.input_string[self.position] == '=':
            operator += '='
            self.update_pos()
    elif operator == '%':
        # %=
        if self.input_string[self.position] == '=':
            operator += '='
            self.update_pos()
    elif operator == '&':
        # &^
        if self.input_string[self.position] == '^':
            operator += '^'
            self.update_pos()
        # &=
        elif self.input_string[self.position] == '=':
            operator += '='
            self.update_pos()
    elif operator == '|':
        # |=
        if self.input_string[self.position] == '=':
            operator += '='
            self.update_pos()
    elif operator == '^':
        # ^=
        if self.input_string[self.position] == '=':
            operator += '='
            self.update_pos()
    elif operator == '<':
        # <<
        if self.input_string[self.position] == '<':
            operator += '<'
            self.update_pos()
            # <<=
            if self.input_string[self.position] == '=':
                operator += '='
                self.update_pos()
        # <=
        elif self.input_string[self.position] == '=':
            operator += '='
            self.update_pos()
    elif operator == '>':
        # >>
        if self.input_string[self.position] == '>':
            operator += '>'
            self.update_pos()
            # >>=
            if self.input_string[self.position] == '=':
                operator += '='
                self.update_pos()
        # >=
        elif self.input_string[self.position] == '=':
            operator += '='
            self.update_pos()
    value = operator
    pos = Pos(start_position, start_line,start_col)
    return Token('OP/SEP', value,pos)

第三步:解析数字|可以有一个更简单的做法(但我们不采用)

# 提取数字 分为float/int/imag 补充实现提取虚数的情况
    def extract_number(self):
        start_position = self.position
        start_line = self.line
        start_col = self.col
        
        # 定义进制标识符和对应的进制
        prefixes = {'0x': 16, '0o': 8, '0b': 2}
        # 检查正负号
        if self.input_string[self.position]=='-':
            self.update_pos()
        # 检查是否存在进制标识符
        for prefix, base in prefixes.items():
            if self.input_string[self.position:self.position+len(prefix)] == prefix:
                self.position += len(prefix)  # 跳过进制标识符
                break
        
        # 提取整数部分
        while self.position < len(self.input_string) and self.input_string[self.position].isdigit():
            self.update_pos()
        
        # 提取小数部分
        if self.position < len(self.input_string) and self.input_string[self.position] == '.':
            self.update_pos()  # 跳过小数点
            while self.position < len(self.input_string) and self.input_string[self.position].isdigit():
                self.update_pos()
        
        # 提取科学计数法标识的数字
        if self.position < len(self.input_string) and (self.input_string[self.position] == 'e' or self.input_string[self.position] == 'E'):
            self.update_pos()  # 跳过'e'或'E'
            if self.position < len(self.input_string) and (self.input_string[self.position] == '+' or self.input_string[self.position] == '-'):
                self.update_pos()  # 跳过正负号
            while self.position < len(self.input_string) and self.input_string[self.position].isdigit():
                self.update_pos()
        
        # 判断是不是 虚数 注意 虚数识别仅仅需要识别到他的复数部分就行了 
        is_imag=False
        if self.position < len(self.input_string) and (self.input_string[self.position] == 'i'):
            is_imag=True
        is_float=False
        # 计算数字
        pos = Pos(start_position, start_line, start_col)
        num_str = self.input_string[start_position:self.position]
        if '.' in num_str or 'e' in num_str or 'E' in num_str:  # 浮点数或科学计数法标识的数字
            num = float(num_str)
            is_float=True
        elif 'x' in num_str or 'X' in num_str:  # 十六进制
            num = int(num_str, 16)
        elif 'o' in num_str or 'O' in num_str:  # 八进制
            num = int(num_str, 8)
        elif 'b' in num_str or 'B' in num_str:  # 二进制
            num = int(num_str, 2)
        else:  # 十进制整数
            num = int(num_str)
        if is_imag:
            self.update_pos()
            return Token('IMAG',self.input_string[start_position:self.position],pos)
        if is_float:
            return Token('FLOAT', num, pos)
        return Token('INT', num, pos)

第四步:解析一下字符类型|`"'这三种 |注意转义处理

# 提取字符串 
def extract_string_and_byte(self):
    pre=self.input_string[self.position]
    start_position = self.position 
    start_line=self.line    
    start_col=self.col
    self.update_pos()
    while self.position < len(self.input_string) and self.input_string[self.position] != pre:
        self.update_pos()
    if self.position == len(self.input_string):
        raise ValueError("字符串解析存在错误")
        self.position-=1
    self.update_pos()
    # 对于字符中存在的换行进行转义处理 避免换行显示出来 
    value = self.input_string[start_position:self.position].replace('\n', '\\n').strip()
    pos = Pos(start_position, start_line,start_col)
    return Token("BYTE" if pre=="'" else 'STRING',value,pos)

第五步:解析注释信息 | 单行和多行注释

 # 提取单行注释
def extract_single_line_comment(self):
    start_position = self.position-1
    start_line=self.line    
    start_col=self.col-1
    while self.position < len(self.input_string) and self.input_string[self.position] != '\n':
        self.update_pos()
    value = self.input_string[start_position:self.position].strip()
    pos = Pos(start_position, start_line,start_col)
    return Token('COMMENT', value,pos)

# 提取多行注释
def extract_multi_line_comment(self):
    start_position = self.position-1
    start_line=self.line
    start_col=self.col-1
    while self.position < len(self.input_string) - 1 and self.input_string[self.position:self.position + 2] != '*/':
        self.update_pos()
    if self.position == len(self.input_string) - 1:
        raise ValueError("多行注释出现错误")
    
    # 对于字符中存在的换行进行转义处理 避免换行显示出来 
    value = self.input_string[start_position:self.position].replace('\n', '\\n').strip()
    self.position += 2
    pos = Pos(start_position, start_line,start_col)
    return Token('COMMENT', value+'*/',pos)

补充一下 我们需要更新位置信息

# 更新位置信息函数
def update_pos(self):
    if self.input_string[self.position]== '\n':
        self.line += 1
        self.col = 0
    else:
        self.col += 1
    self.position+=1

最后来写调用scan函数

 # 词法分析函数
def scan(self):
    tokens = []
    while self.position < len(self.input_string):
        char = self.input_string[self.position]
        if char.isspace():
            self.update_pos()
        elif char.isalpha() or char == '_':
            token = self.extract_identifier()
            if token.value in self.keywords:
                token.token_type = 'KEYWORD'
            tokens.append(token)
        elif char.isdigit():
            tokens.append(self.extract_number())
        elif char == '"' or char=='`':
            tokens.append(self.extract_string_and_byte())
        elif char == "'":
            tokens.append(self.extract_string_and_byte())
        elif char in '+-*%=()&|^<>!.:;/{}|[]^<>\\,':# 某些字符可能没有嵌入 后续完善
            tokens.append(self.extract_operator())
        else:
            raise ValueError(f"错误/未知字符 '{char}' 出现在 {self.position}")
    return tokens

Go部分

正在完善