Aug 25 20:15 2013 repl.lua Page 1 --[[ REPL Module Read-Evaluate-Print Loop command line interface 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 2012-08-17: Separated into a module derived from Core, etc. 2012-08-21: Added explicit dependency setters 2012-08-22: Added require of new Utilities module 2012-08-24: Cleaned dependencies and comments 1234567890123456789012345678901234567890123456789012345678901234567890 This module implements a simple, text-based, interactive user interface to the interpreter, commonly called a REPL. It repeatedly prompts for an expression, READs and parses the user's input expression, EVALUATEs it, and then PRINTs the result. It calls the Parser and Evaluator. Before processing, the REPL removes any leadings spaces and end-of-line comment from each line. A comment is any text beginning with a semicolon ";" and continuing until the end of the line. If it detects unbalanced parentheses on a line, it prompts the user for the continuation of that line until the parentheses balance. The REPL continues to read expressions until the user enters the command "quit". --]] -- KILT tree utilities library local util = require "utilities" local treeConcat, printTree = util.treeConcat, util.printTree -- REPL MODULE DEPENDENDENCY WIRING -- globalEnv table local globalEnv local function setGlobalEnv(e) globalEnv = e end -- parse(string) function local parse local function setParse(p) parse = p end -- trimSpaces(string) function local trimSpaces local function setTrimSpaces(ts) trimSpaces = ts end -- trimComment(string) function Aug 25 20:15 2013 repl.lua Page 2 local trimComment local function setTrimComment(tc) trimComment = tc end -- getCommand(string) function local getCommand local function setGetCommand(gc) getCommand = gc end -- evalScript function local evalScript local function setEvalScript(es) evalScript = es end -- valToString(value) local valToString local function setValToString(vts) valToString = vts end -- REPL prompts, with defaults local mainPrompt, contPrompt = "> ", ">>" local function setPrompts(mp,cp) mainPrompt, contPrompt = mp, cp end -- REPL functions -- The function "readExpression" interactively reads an expression -- entered at the command line. local function readExpr() local line, ch local input = {} local bal = 0 -- #left parentheses - #right parentheses local fullinput = false -- loop until a complete expression is entered while not fullinput do if bal == 0 then io.write(mainPrompt) elseif bal > 0 then io.write(contPrompt) end -- bal < 0 means a syntax error, caught below line = io.read("*l") -- trim leading spaces and end-of-line comment line = trimSpaces(line) line = trimComment(line) -- check for balanced parentheses, need for additional input for i = 1, #line do ch = string.sub(line,i,i) if ch == "(" then bal = bal + 1 elseif ch == ")" then bal = bal - 1 end Aug 25 20:15 2013 repl.lua Page 3 if bal < 0 then -- found ) before matching ( break end end input[#input+1] = line if bal < 0 then -- too many )'s, syntax error, restart input print("Syntax error. Input ignored: "..table.concat(input,"\n")) input, bal = {}, 0 elseif line ~= "" and bal == 0 then -- complete input expression fullinput = true end -- bal > 0, continue gathering input lines end return table.concat(input,"\n") end -- Procedure "readEvalPrint" drives the Read-Evaluate-Print-Loop -- command line interface. It repeatedly reads an expression, parses -- it, evaluates it, and prints the resulting value until the command -- "quit" is entered. local function readEvalPrint() local wired = globalEnv and parse and trimSpaces and trimComment and getCommand and evalScript and valToString if not wired then error("Dependencies for module REPL are not set.") end local input, script, result local quitting = false while not quitting do input = readExpr() -- READ (then parse) local cmd = getCommand(input) quitting = (cmd == "quit") if not quitting then script = nil local parseok, parsemsg = pcall(function() script = parse(input) print("DEBUG: after parse in repl -> "..treeConcat(script)) end) if parseok then local evalok, evalmsg = pcall(function() result = evalScript(script,globalEnv) -- EVALUATE print("DEBUG: after evalScript in repl -> " .. treeConcat(result)) end) if evalok then local output = valToString(result[1]) print(output) -- PRINT else print(evalmsg) print("Evaluation failed for " .. input) print("Input ignored.") Aug 25 20:15 2013 repl.lua Page 4 end else print(parmsg) print("Parsing failed for " .. input) print("Input ignored.") end end input = "" end end -- MODULE EXPORT LIST return { readEvalPrint = readEvalPrint, -- main function setGlobalEnv = setGlobalEnv, -- dependency setters setParse = setParse, setTrimSpaces = setTrimSpaces, setTrimComment = setTrimComment, setGetCommand = setGetCommand, setEvalScript = setEvalScript, setValToString = setValToString, setPrompts = setPrompts -- prompt parameter setter }