git @ Cat's Eye Technologies Ebfer / master src / ebfer / eval.py
master

Tree @master (Download .tar.gz)

eval.py @masterraw · history · blame

# Copyright (c) 2025-2026 Chris Pressey, Cat's Eye Technologies.
# This file is distributed under a 2-clause BSD license.  See LICENSES/ dir.
# SPDX-License-Identifier: LicenseRef-BSD-2-Clause-X-Ebfer

import logging
from typing import Dict, List

from .term import Term, IntValue, Ctor, is_variable
from .ast import Development, Rule


logger = logging.getLogger(__name__)


Value = Term


def eval_term(t: Term, env: Dict[str, Value], functions: Dict[str, Rule]) -> Value:
    if isinstance(t, IntValue):
        return t
    elif not t.subterms:
        if t.symbol in env:
            return env[t.symbol]
        else:
            return t  # atom
    elif t.symbol == 'add':
        a = eval_term(t.subterms[0], env, functions)
        b = eval_term(t.subterms[1], env, functions)
        assert isinstance(a, IntValue) and isinstance(b, IntValue), \
            f"Need 2 ints, not {a} and {b}"
        return IntValue(a.value + b.value)
    elif t.symbol == 'mul':
        a = eval_term(t.subterms[0], env, functions)
        b = eval_term(t.subterms[1], env, functions)
        assert isinstance(a, IntValue) and isinstance(b, IntValue), \
            f"Need 2 ints, not {a} and {b}"
        return IntValue(a.value * b.value)
    else:
        if t.symbol not in functions:
            raise ValueError(f"Unknown function: {t.symbol}")
        fun = functions[t.symbol]
        evaluated_args = [eval_term(arg, env, functions) for arg in t.subterms]
        return eval_function(fun, evaluated_args, functions)


def eval_function(fun: Rule, args: List[Value], functions: Dict[str, Rule]) -> Value:
    env = {}
    for (formal, actual) in zip(fun.lhs.subterms, args):
        assert is_variable(formal)
        assert isinstance(formal, Ctor)
        env[formal.symbol] = actual
    return eval_term(fun.rhs, env, functions)


def eval_ebfer(development: Development) -> Value:
    main_functions = [f for f in development.functions if f.lhs.symbol == "main"]
    if len(main_functions) != 1:
        raise AssertionError("No unique main function")
    functions = {f.lhs.symbol: f for f in development.functions}
    return eval_function(main_functions[0], [], functions)