

Nov 13 21:45 2013 evaluator_core.lua Page 1


--[[ Kamin Core Language Evaluator Module
     KILT -- Kamin Interpreters in Lua Toolset
     H. Conrad Cunningham, Professor
     Computer and Information Science
     University of Mississippi

Developed for CSci 658, Software Language Engineering, Fall 2013

2013-08-21: Built Scheme module from Scheme interpreter evaluator code
2013-08-24: Reverted Scheme/Lisp changes to build Core module
2013-08-29: Modified to use Opcode and Values Factory modules
2013-11-13: Corrected function environment scoping

1234567890123456789012345678901234567890123456789012345678901234567890

The Evaluator takes the semantic representations of the expressions
and the Symbol Tables generated by the Parser, evaluates the
expression, and returns the resulting value.

The implementation of the evaluator uses a table eval indexed by the
OpCodes used in semantic representation.  The corresponding entry for
an OpCode is a closure that evaluates the corresponding semantic
structure.

--]]

-- KILT Interpreter Opcode Global Constants
local opcodes = require "opcodes"

-- KILT tree utilities library
local util = require "utilities"
local treeConcat = util.treeConcat

-- KILT Environment Module
local evm = require "environment"
local globalEnv, newEnv      = evm.globalEnv, evm.newEnv
local getVal, assign, setVal = evm.getVal, evm.assign, evm.setVal 

-- KILT Function Table Module
local ftm = require "funtab"
local defFun, getFun, dumpFun = ftm.defFun, ftm.getFun, ftm.dumpFun

-- KILT Values module
local vm = require "values"
local FALSEVAL, TRUEVAL = vm.FALSEVAL, vm.TRUEVAL
local isFalse, isTrue   = vm.isFalse,  vm.isTrue
local boolAsVal         = vm.boolAsVal
local hasOpCode         = vm.hasOpCode


-- EVALUATION TABLE (indexed by OpCode)

local eval = {}







Nov 13 21:45 2013 evaluator_core.lua Page 2



-- Trap any attempts to evaluate invalid OpCodes 
setmetatable(eval,eval)

eval.__index = function(_,key)
  error("Attempt to evaluate invalid OpCode " .. treeConcat(key),2)
end

-- Function "doeval" evaluates the semantic form "t" in the
-- environment "env".  It calls the entry in the "eval" table based on
-- the opcode stored in element "t[i]".  "i" defaults to 1 if the
-- argument is nil or unspecified.

local function doeval(t,env,i) 
  i = i or 1 
  return eval[t[i]](t,env)
end

-- Function "evalScript" evaluates each semantic form in the script
-- "t" (a list) in the environment "env".  Each semantic form in this
-- script is an abstract syntax tree (AST) for a Scheme expression.

local function evalScript(t,env)
  local res = {}
  for i = 1, #t do
    res[i] = doeval(t[i],env)
  end
  return res
end

local returnVal = function(t,env)
  assert(#t == 2, "OpCode must have one argument.")
  return t[2]
end    

-- The following define entries in the "eval" table for each OpCode
-- type (i.e., for control operation, value operation, and
-- user-defined function).

-- Evaluate t = {FUNDEF, name}
eval[FUNDEF] = returnVal

-- Evaluate t = {VARVAL, name}
eval[VARVAL]  = function(t,env)
  local val = getVal(t[2],env)
  if val then 
    return val
  else
    error("Attempt to access value of undefined variable " ..
          tostring(t[2]))
  end
end








Nov 13 21:45 2013 evaluator_core.lua Page 3


-- Evaluate t = {NUMVAL, number}
eval[NUMVAL]  = returnVal

-- Evaluate t = {IFOP, test, thenExpr, elseExpr}
eval[IFOP] = function(t,env)
  local test, thenExp, elseExp = t[2], t[3], t[4]
  if isTrue(doeval(test,env)) then 
    return doeval(thenExp,env)
  else 
    return doeval(elseExp,env)
  end
end

-- Evaluate t = {WHILEOP, test, body} 
eval[WHILEOP] = function(t,env)
  local test, body = t[2], t[3]
  local res
  while isTrue(doeval(test,env)) do
    res = doeval(body,env)
  end
  return FALSEVAL
end

-- Evaluate t = {SETOP, var, exp}
eval[SETOP] = function(t,env)
  local var, exp = t[2], t[3]
  local val = doeval(exp,env)
  return setVal(var,val,env) -- SIDE EFFECT
end

-- Evaluate t = {BEGINOP, expr1 ...} one or more expr
eval[BEGINOP] = function(t,env)
  local res
  for i = 2, #t do
    res = doeval(t[i],env)
  end
  return res
end
  
-- Evaluate t = {APPLYFUN,name,arg1,arg2,...} zero or more arg
eval[APPLYFUN] = function(t,env)
  local name = t[2]
  local fdef = getFun(name)
  if not fdef then 
    error("Function " .. name .. " not defined.",2)
  end

    -- Entry funTab[name] = {arity, formals, body}
  local numargs = #t - 2
  local arity, formal, body = fdef[1], fdef[2], fdef[3]
  if numargs < arity then
    error("Function "..name.." called with too few arguments.",2)
  elseif numargs > arity then 







Nov 13 21:45 2013 evaluator_core.lua Page 4


    error("Function "..name.." called with too many arguments.",2)
  end

--local localEnv = newEnv(env) -- new environment frame for args
  local localEnv = newEnv()  -- new environment frame for args
  for i = 3, #t do
    local val = doeval(t[i],env)  -- evaluate argument
    local var = formal[i-2]    
    assign(var,val,localEnv)      -- assign to parameter name
  end
  return doeval(body,localEnv)
end

-- Evaluate t = {APPLYOP, operator, arg1, arg2, ...}
eval[APPLYOP] = function(t,env)
  return doeval(t,env,2) -- dispatch again on builtin operator
end


-- Functions for the builtin binary operators on numbers

-- Function "evalArithOp" takes an arithmetic operator function "op"
-- and a string "disp" giving how to display the operator. It returns
-- an evaluation function that evaluates the corresponding value
-- operator in the given environment.

local function evalArithOp(op,disp)
  local msg1  = disp .. " operation requires 2 arguments."
  local msg2  = disp .. " applied to nonnumeric values "
  local arith = function(t,env)
    assert(#t == 4, msg1)
    local left, right = doeval(t[3],env), doeval(t[4],env)
    return op(left,right)
  end
  return arith
end 

-- Lua's numbers are all double floating point numbers.  Function
-- "roundTowardZero" is used to round results of division to give the
-- usual truncating integer division result.

local function roundTowardZero(i) 
  if i < 0 then 
    return math.ceil(i) 
  else
    return math.floor(i) 
  end 
end

-- Functions for the arithmetic operators
local add = function(l,r) return l + r end
local sub = function(l,r) return l - r end
local mul = function(l,r) return l * r end







Nov 13 21:45 2013 evaluator_core.lua Page 5


local div = function(l,r) return roundTowardZero(l / r) end

-- Evaluate t = {APPLYOP, arithmetic_op, expr1, expr2}
eval[ADDOP] = evalArithOp(add,"+")
eval[SUBOP] = evalArithOp(sub,"-")
eval[MULOP] = evalArithOp(mul,"*")
eval[DIVOP] = evalArithOp(div,"*")


-- Functions for builtin relational operators

-- Function "evalRelOp" takes an relational operator function "op" and
-- a string "disp" giving how to display the operator. It returns a
-- "boolean" function that evaluates the corresponding value operator
-- in the given environment.

local function evalRelOp(op,disp)
  local msg1  = disp .. " comparison requires 2 arguments."
  local order = function(t,env)
    assert(#t == 4, msg1)
    local left, right = doeval(t[3],env), doeval(t[4],env)
    return boolAsVal(op(left,right))
  end
  return order
end 

-- Functions for the relational operators
local function lt(l,r) return l < r  end
local function gt(l,r) return l > r  end
local function eq(l,r) return l == r end

-- Evaluate t = {APPLYOP, relational_op, expr1, expr2}
eval[LTOP] = evalRelOp(lt,"<")
eval[GTOP] = evalRelOp(gt,">")
eval[EQOP] = evalRelOp(eq,"=")

-- Evaluate t = {PRINTOP, expression}
eval[PRINTOP] = function(t,env)
  assert(#t == 3, "print operation requires 1 argument.")
  local res = doeval(t[3],env)
  print(valToString(res))  -- SIDE EFFECT
  return res
end


-- MODULE EXPORT LIST

return {
  evalScript    = evalScript
}








