git @ Cat's Eye Technologies Robin / master src / Language / Robin / Eval.hs
master

Tree @master (Download .tar.gz)

Eval.hs @masterraw · history · blame

-- Copyright (c) 2012-2024, 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-Robin

module Language.Robin.Eval where

import Data.Int

import Language.Robin.Expr
import Language.Robin.Env (Env, find, insert)

--
-- This evaluator is written in continuation-passing style.
--
-- Every evaluation function has this signature:
--
--     Env -> Expr -> (Expr -> Expr) -> Expr
--
-- (This is actually the `Evaluable` type from `Robin.Expr`.)
--
-- The first argument is the Robin environment, which is directly visible
-- (and modifiable, during `eval`) by the Robin program.
--
-- The second argument is the expression to be evaluated.
--
-- The third argument is the continuation.  Once the expression has been
-- evaluated, the continuation will be applied with the result of the
-- evaluation.
--

eval :: Evaluable

--
-- When evaluating a symbol, look it up in the environment to obtain a
-- value.  Then continue the current continuation with that value.
--

eval env sym@(Symbol s) cc =
    case find s env of
        Just value ->
            cc value
        Nothing ->
            errMsg cc "unbound-identifier" sym

--
-- Evaluating a list means we must make several evaluations.  We
-- evaluate the head to obtain something to apply, which must be an
-- operator.  We then apply the operator, passing it the tail of the list.
--

eval env (List (applierExpr:actuals)) cc =
    eval env applierExpr (\applier ->
        case applier of
            Operator _ fun ->
                fun env (List actuals) cc
            other ->
                errMsg cc "inapplicable-object" other)

--
-- Everything else just evaluates to itself.  Continue the current
-- continuation with that value.
--

eval env e cc =
    cc e

--
-- Evaluate, handling abort values
--

evalB ecc env e cc =
    eval env e (\value ->
        case value of
            Abort _ -> ecc value
            _       -> cc value)

--
-- Helper functions
--

errMsg cc msg term = cc $ abortVal msg term

makeFexpr :: Expr -> Expr -> Expr -> Evaluable
makeFexpr defineTimeEnv formals body =
    \callTimeEnv actuals cc ->
        let
            env = makeFexprEnv callTimeEnv actuals defineTimeEnv formals
        in
            eval env body cc

makeFexprEnv callTimeEnv actuals defineTimeEnv argList =
    let
        (List [(Symbol argFormal), (Symbol envFormal)]) = argList
        newEnv' = insert argFormal actuals defineTimeEnv
        newEnv'' = insert envFormal callTimeEnv newEnv'
    in
        newEnv''


--
-- Assertions
--

evalExpect pred msg ecc env expr cc =
   eval env expr (\value ->
       case pred value of
           True -> cc value
           False -> errMsg ecc msg value)

evalToBoolean = evalExpect (isBoolean) "expected-boolean"
evalToList = evalExpect (isList) "expected-list"
evalToNumber = evalExpect (isNumber) "expected-number"

evalTwoNumbers :: (Int32 -> Int32 -> (Expr -> Expr) -> Expr) -> Evaluable
evalTwoNumbers fn env (List [xexpr, yexpr]) cc =
    evalToNumber cc env xexpr (\(Number xv) ->
        evalToNumber cc env yexpr (\(Number yv) ->
            (fn xv yv cc)))
evalTwoNumbers fn env other cc = errMsg cc "illegal-arguments" other