--[[ Expression Language, Environment Module CSci 450: Organization of Programming Languages, Fall 2016 H. Conrad Cunningham, Professor Computer and Information Science University of Mississippi 1234567890123456789012345678901234567890123456789012345678901234567890 2016-09-13: Separated as module from Assignment #1 code 2016-09-21: Added global variables for module names This module encapsulates the Environment table, which maps variable names to their values. This design supports nested environments. That is not needed in the simple Expression language, but it will been needed for more capabile languages in the future. Public constructor function: newEnv Public accessor functions: findBindings, getVal Public procedures/mutators: assign, setVal Debugging/testing dumpEnv Internal: isEnv, ENV, ENCLOSING --]] --[[ Module names now global local UTILITIES = "utilities" --]] -- Import Utilities module local util = require(UTILITIES) local treeConcat, show_data = util.treeConcat, util.show_data -- printTree not currently used -- Internal singleton constant as "tag" for Environments local ENV = "Env" -- Inernal constant to denote field used for nesting of environments. local ENCLOSING = -1 -- key for enclosing environment pointer -- Internal query function "isEnv" to check whether "env" has the -- structure of an Environment. local function isEnv(env) return type(env) == "table" and env[1] == ENV end -- Public constructor function "newEnv" constructs a new empty -- environment frame enclosed (nested) within "env". If "env" is nil -- or unspecified, it creates a new "outer" environment. local function newEnv(env) local localEnv = {ENV} -- new empty environment frame if isEnv(env) then localEnv[ENCLOSING] = env -- enclosed within "env" end return localEnv end -- Public accessor function "findBinding" returns the environment -- frame in which "s" is defined; else it returns nil. The returned -- value is "env" or an environment frame that encloses it. local function findBinding(s,env) if not isEnv(env) then return nil elseif env[s] ~= nil then -- could be value "false" return env else return findBinding(s,env[ENCLOSING]) end end -- Public procedure (mutator) "assign" sets name "n" to have value "v" -- in environment frame "env". local function assign(n,v,env) env[n] = v end -- Public side-effecting mutator function "setVal" searches for a -- binding of name "n" in environment frame "env" or any enclosing -- environment frame. If "n" is bound, then "setVal" sets the value -- to "v"; otherwise, it binds "n" to "v" in optional environment -- frame "defenv". It also returns the value set. Parameter "defenv" -- defaults to the same as parameter "env" if nil or not specified. -- Note: Fall 2016 Assignment #1 does not need the capability -- provided by the optional parameter. Just call the function -- with three parameters. local function setVal(n,v,env,defenv) defenv = defenv or env local senv = findBinding(n,env) if senv then assign(n,v,senv) elseif isEnv(defenv) then assign(n,v,defenv) else error("Cannot assign " .. tostring(n) .. " in environment " .. show_data(env) .. " or " .. show_data(defenv)) end return v end -- Public accessor function "getVal" returns the value of name "n" in -- the environment frame "env" or some enclosing environment frame; it -- returns nil if not found. local function getVal(n,env) local boundin = findBinding(n,env) return boundin and boundin[n] end -- Public procedure "dumpEnv" prints all bindings from the top of the -- environment frame stack. The top is depth 1, which is the default -- value for argument "depth". This procedure is intended for -- DEBUGGING AND TESTING ONLY. local function dumpEnv(env,depth) local depth = depth or 1 print("Environment frame dump for stack level " .. tostring(depth)) if isEnv(env) then for k,v in pairs(env) do if k ~= 1 and k ~= ENCLOSING then print(k,treeConcat(v)) end end dumpEnv(env[ENCLOSING], depth+1) elseif env == nil then print("End of dump. No frame at depth " .. tostring(depth)) else error("Invalid environment " .. show_data(env)) end end -- MODULE EXPORT LIST -- not exported: isEnv, ENV, ENCLOSING return { newEnv = newEnv, findBinding = findBinding, assign = assign, setVal = setVal, getVal = getVal, dumpEnv = dumpEnv }