PL λαβ

Lab 2.2[최준혁]: 기본 함수 본문

kos

Lab 2.2[최준혁]: 기본 함수

junhyeokk 2021. 4. 24. 11:38

지난번 구현했던 환경 부분을 완성하였고, 이어서 built-in 함수 car, cdr, cons를 환경에 추가하여 호출을 테스트해보는 부분까지 어느정도 완성했습니다. 기본적으로 참고 페이지(www.lwh.jp/lisp/index.html)의 진행을 따라하는 방식으로 진행하였지만, env는 python class로 구현되었고, 나머지 실행 중 막히는 부분들은 일부 변형하였습니다. Data 관련 코드는 조교님이 작성해주신 boilerplate를 거의 그대로 사용했지만 필요에 따라 조금 수정하였습니다.

 

environment_mark2.py

from boilerplate import *
from builtin_function import *

class Bindings:
    def __init__(self, parent):
        self.parent = parent
        self.symbols = dict()
        # self.children_list = []
        # # 부모 -> 자식으로 가는경우
    def add_symbol(self, symbol, value):
        if symbol.type == Type.SYM:
            self.symbols[symbol.value.upper()] = value
    # def add_child(self, child):
    #     self.children_list.append(child)

    def __str__(self):
        if isNil(self.parent):
            return f"ENV class : root"
        else:
            return f"ENV class : {str(self.parent)}"

    def __repr__(self):
        return self.__str__()

def env_create(parent):
    new_bindings = Bindings(parent)
    # if not isNil(parent):
    #     parent.add_child(new_bindings)
    return new_bindings

def env_get(env, symbol):
    parent = env.parent

    if symbol.value.upper() in env.symbols:
        return env.symbols[symbol.value.upper()]

    if isNil(parent):
        return "Error unbound", nilp()

    return env_get(parent, symbol)

def env_set(env, symbol, value):
    env.add_symbol(symbol, value)
    return ErrorType.ERROR_OK

def listp(expr):
    while not isNil(expr):
        if expr.type != Type.PAIR:
            return False
        expr = expr.cdr()
    return True

def eval_expr(expr, env):
    if expr.type == Type.SYM:
        return ErrorType.ERROR_OK, env_get(env, expr)
    elif expr.type != Type.PAIR:
        return ErrorType.ERROR_OK, expr

    if not listp(expr):
        return ErrorType.ERROR_SYNTAX, nilp()

    op = expr.car()
    args = expr.cdr()

    if op.type == Type.SYM:
        env_val = env_get(env, op)

        if op.value.upper() == "QUOTE":
            if isNil(args) or not isNil(args.cdr()):
                return "Error_Args", nilp()
            return "Error_OK", args.car()
        elif op.value.upper() == "DEF":
            if isNil(args) or isNil(args.cdr()) or not isNil(args.cdr().cdr()):
                return "Error_Args", nilp()
            sym = args.car()
            if sym.type != Type.SYM:
                return "Error_Type", nilp()
            err, val = eval_expr(args.cdr().car(), env)

            env_set(env, sym, val)
            return "Error_OK", sym
        # elif env_val and env_val.type == Type.BUILTIN:
        #     return apply(env_val, args)
    err, op = eval_expr(op, env)
    # if err:
    #     return err
    args = copy_list(args)
    p = args
    while not isNil(p):
        err, p.value[0] = eval_expr(p.car(), env)
        # if err:
        #     return err
        p = p.cdr()
    return apply(op, args)


    return "Error_Syntax", nilp()

if __name__ == "__main__":
    env = env_create(nilp())

    while True:
        parsedlist = Parser(Lexer(Input()._input()).lex())
        print("\n===== === PAR === =====")
        print(parsedlist)
        print("===== === OUT === =====")
        eval_expr(parsedlist, env)
        print(env)

builtin_function.py

from boilerplate import *
from environment_mark2 import *

def make_builtin(fn):
    a = Data()
    a.type = Type.BUILTIN
    a.value = fn
    return a

def copy_list(lst):
    if isNil(lst):
        return nilp()

    a = cons(lst.car(), nilp())
    p = a
    lst = lst.cdr()

    while not isNil(lst):
        p.value[1] = cons(lst.car(), nilp())
        p = p.cdr()
        lst = lst.cdr()

    return a

def apply(fn, args):
    if fn.type == Type.BUILTIN:
        return "Error OK", fn.value(args)
    return Err(fn, "Function Error")

def builtin_car(args):
    return "Error OK", args.car()
    # if isNil(args) or not isNil(args.cdr()):
    #     return "Error Args", nilp()
    #
    # if isNil(args.car()):
    #     return "Error OK", nilp()
    # elif args.car().type != Type.PAIR:
    #     return "Error Type", nilp()
    # else:
    #     return "Error OK", args.car().car()

def builtin_cdr(args):
    return "Error OK", args.cdr()
    # if isNil(args) or not isNil(args.cdr):
    #     return "Error Args", nilp()
    #
    # if isNil(args.car()):
    #     return "Error OK", nilp()
    # elif args.car().type != Type.PAIR:
    #     return "Error Type", nilp()
    # else:
    #     return "Error OK", args.car().cdr()

def builtin_cons(args):
    if isNil(args) or isNil(args.cdr()) or not isNil(args.cdr().cdr()):
        return "Error Args", nilp()
    return "Error OK", cons(args.car(), args.cdr())

if __name__ == "__main__":
    env = env_create(nilp())

    env_set(env, mksym("CAR"), make_builtin(builtin_car))
    env_set(env, mksym("CDR"), make_builtin(builtin_cdr))
    env_set(env, mksym("CONS"), make_builtin(builtin_cons))

    while True:
        parsedlist = Parser(Lexer(Input()._input()).lex())
        # print("\n===== === PAR === =====")
        # print(parsedlist)
        # print("===== === OUT === =====")
        print(eval_expr(parsedlist, env))
        # print(env)

 

builtin_function.py 테스트 결과

테스트로 확인해보면 환경에 변수와 기본 함수 car, cdr, cons가 잘 들어가 동작하는 것을 확인할 수 있습니다. 다만 마지막 두 예시에서는 예상대로라면 각각 a 와 (b, c)가 결과가 되어야 하지만, car의 결과로 (a b c)가 한번에 출력되고 cdr의 결과로 NIL이 출력되는 오류가 있어 그 부분에 대한 수정이 필요합니다. 또한 오류코드를 리턴시에 리턴값과 함께 튜플로 묶어 전달하는 방식을 채택했는데, 동작에 문제는 없지만 급하게 만드느라 처리를 제대로 하지않아 출력시에 에러코드와 함께 중첩된 결과가 나오는 문제도 수정할 예정입니다.

'kos' 카테고리의 다른 글

Lab 3.1: 람다와 클로저  (0) 2021.04.27
Lab 2.1a[박인철]: 환경  (0) 2021.04.27
Lab 2.2a[김예령]: 기본 함수  (0) 2021.04.15
Lab 2.2: 기본 함수  (0) 2021.04.15
Lab 1.2c[TA]: 구문 분석기  (0) 2021.04.15
Comments