PL λαβ

Lab 4.1a[최준혁]: 조건, 비교 본문

kos

Lab 4.1a[최준혁]: 조건, 비교

junhyeokk 2021. 5. 2. 05:52

Code

내장 비교 연산과 조건문까지 구현하였습니다. 마찬가지로 기본 진행방식은 참고 페이지를 따라하였고, 부분부분 파이썬에 맞도록 수정하였습니다. 계속 미루고 있었던 에러 리턴과 처리를 문자열로 통일하여 동작하도록 하였는데, 후에 이 부분은 Error class로 고치면 더 깔끔할것 같습니다. 

 

mycode.py

from boilerplate import *
# from environment_mark2 import *

class Bindings:
    def __init__(self, parent):
        self.parent = parent
        self.symbols = dict()

    def add_symbol(self, symbol, value):
        if symbol.type == Type.SYM:
            self.symbols[symbol.value.upper()] = value

    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 type(parent) is Bindings:
        pass
    elif isNil(parent):
        # return "Error unbound", nilp()
        return nilp()

    return env_get(parent, symbol)

def env_set(env, symbol, value):
    env.add_symbol(symbol, value)
    # return ErrorType.ERROR_OK
    return "Error OK", nilp()

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)
        return "Error OK", env_get(env, expr)
    elif expr.type != Type.PAIR:
        # return ErrorType.ERROR_OK, expr
        return "Error OK", expr

    if not listp(expr):
        # return ErrorType.ERROR_SYNTAX, nilp()
        return "Error Syntax", nilp()

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

    if op.type == Type.SYM:
        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 op.value.upper() == "LAM": # lambda
            if isNil(args) or isNil(args.cdr()):
                return "Error Args", nilp()

            return make_closure(env, args.car(), args.cdr())
        elif op.value.upper() == "IF":
            if isNil(args) or isNil(args.cdr()) or isNil(args.cdr().cdr()) or not isNil(args.cdr().cdr().cdr()):
                return "Error Args", nilp()

            err, cond = eval_expr(args.car(), env)
            if err != "Error OK":
                return err, nilp()
            val = args.cdr().cdr().car() if isNil(cond) else args.cdr().car()
            return eval_expr(val, env)
    err, op = eval_expr(op, env)
    if err != "Error OK":
        return err, nilp()
    args = copy_list(args)
    p = args
    while not isNil(p):
        err, p.value[0] = eval_expr(p.car(), env)
        if err != "Error OK":
            return err, nilp()
        p = p.cdr()
    return apply(op, args)

    return "Error Syntax", nilp()

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

def make_closure(env, args, body):
    if not listp(args) or not listp(body):
        return "Error Syntax", nilp()

    p = args
    while not isNil(p):
        if p.car().type != Type.SYM:
            return "Error Type", nilp()
        p = p.cdr()

    result = cons(env, cons(args, body))
    result.type = Type.CLOSURE

    return "Error OK", result

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 fn.value(args)
    elif fn.type != Type.CLOSURE:
        return "Error Type", nilp()

    env = env_create(fn.car())
    arg_names = fn.cdr().car()
    body = fn.cdr().cdr()

    while not isNil(arg_names):
        if isNil(args):
            return "Error Args", nilp()
        env_set(env, arg_names.car(), args.car())
        arg_names = arg_names.cdr()
        args = args.cdr()

    if not isNil(args):
        return "Error Args", nilp()

    while not isNil(body):
        err, result = eval_expr(body.car(), env)
        if err != "Error OK":
            return err, nilp()
        body = body.cdr()

    return "Error OK", result

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())

def builtin_add(args):
    if isNil(args) or isNil(args.cdr()) or not isNil(args.cdr().cdr()):
        return "Error Args", nilp()
    a = args.car()
    b = args.cdr().car()
    if a.type != Type.INT or b.type != Type.INT:
        return "Error Type", nilp()

    return "Error OK", mkint(args.car().value + args.cdr().car().value)

def builtin_subtract(args):
    if isNil(args) or isNil(args.cdr()) or not isNil(args.cdr().cdr()):
        return "Error Args", nilp()
    a = args.car()
    b = args.cdr().car()
    if a.type != Type.INT or b.type != Type.INT:
        return "Error Type", nilp()

    return "Error OK", mkint(args.car().value - args.cdr().car().value)

def builtin_multiply(args):
    if isNil(args) or isNil(args.cdr()) or not isNil(args.cdr().cdr()):
        return "Error Args", nilp()
    a = args.car()
    b = args.cdr().car()
    if a.type != Type.INT or b.type != Type.INT:
        return "Error Type", nilp()

    return "Error OK", mkint(args.car().value * args.cdr().car().value)

def builtin_divide(args):
    if isNil(args) or isNil(args.cdr()) or not isNil(args.cdr().cdr()):
        return "Error Args", nilp()
    a = args.car()
    b = args.cdr().car()
    if a.type != Type.INT or b.type != Type.INT:
        return "Error Type", nilp()

    return "Error OK", mkint(args.car().value // args.cdr().car().value)

def builtin_numeq(args):
    if isNil(args) or isNil(args.cdr()) or not isNil(args.cdr().cdr()):
        return "Error Args", nilp()
    a = args.car()
    b = args.cdr().car()
    if a.type != Type.INT or b.type != Type.INT:
        return "Error Type", nilp()

    result = mksym("T") if a.value == b.value else nilp()
    return "Error OK", result

def builtin_less(args):
    if isNil(args) or isNil(args.cdr()) or not isNil(args.cdr().cdr()):
        return "Error Args", nilp()
    a = args.car()
    b = args.cdr().car()
    if a.type != Type.INT or b.type != Type.INT:
        return "Error Type", nilp()

    result = mksym("T") if a.value < b.value else nilp()
    return "Error OK", result

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))
    env_set(env, mksym("+"), make_builtin(builtin_add))
    env_set(env, mksym("-"), make_builtin(builtin_subtract))
    env_set(env, mksym("*"), make_builtin(builtin_multiply))
    env_set(env, mksym("/"), make_builtin(builtin_divide))
    env_set(env, mksym("T"), mksym("T"))
    env_set(env, mksym("="), make_builtin(builtin_numeq))
    env_set(env, mksym("<"), make_builtin(builtin_less))

    while True:
        parsedlist = Parser(Lexer(Input()._input()).lex())
        # print("\n===== === PAR === =====")
        # print(parsedlist)
        # print("===== === OUT === =====")
        err, result = eval_expr(parsedlist, env)
        if err != "Error OK":
            print(err)
        else:
            print(result)
        # print(env)

 

boilerplate.py

실습시간에 나왔던 0이 None Type으로 처리되는 문제는 조교님이 제시해주신 해답을 살짝 수정해서 적용하였습니다.

### ......

def Parser(tokenlist):
    if len(tokenlist) == 0:
        return Err(Nil(), ErrorType.NO_INPUT_FILE)
    LA = tokenlist.pop(0)
    if LA.type == Keyword.LPAREN:
        if tokenlist[0].type == Keyword.RPAREN:
            return nilp()
        L = []
        while tokenlist[0].type != Keyword.RPAREN:
            L.append(Parser(tokenlist))
        tokenlist.pop(0)
        LR = iCons(L)
        return LR
    elif LA.type == Keyword.RPAREN:
        return Err(LA, ErrorType.UNEXPECTED_TOKEN)
    else:
        if LA.value.isdigit():      # int
            return Data(Type.INT, int(LA.value))
        elif LA.value.replace('.', '', 1).isdigit():        # float
            return Data(Type.REAL, float(LA.value))
        else:
            return Data(Type.SYM, str(LA.value))
            
### ......

 

TEST

>> (if t 3 4)
3
>> (if nil 3 4)
4
>> (if 0 t nil)
T
>> (= 3 3)
T
>> (< 11 4)
NIL
>> (define fact (lambda (x) (if (= x 0) 1 (* x (fact (- x 1))))))
fact
>> (fact 3)
6
>> (fact 10)
3628800
>> 

'kos' 카테고리의 다른 글

Lab 5.1a[김예령]: 한글 스킴  (0) 2021.05.04
Lab 5.1a[최준혁]: 한글 스킴  (0) 2021.05.04
Lab 4.1a[김예령]: 조건, 비교  (0) 2021.04.29
Lab 3.1a[김예령]: 람다와 클로저  (0) 2021.04.29
Lab 3.1a[최준혁]: 람다와 클로저  (0) 2021.04.27
Comments