Source code for curry.tools.icy.commands

from . import options
from .resolve import resolve
import importlib, os, sys, traceback
curry = importlib.import_module(__package__[:__package__.find('.')])
_compile = importlib.import_module(curry.__name__ + '.interpreter.compile')
_import = importlib.import_module(curry.__name__ + '.interpreter.import_')

__all__ = ['COMMANDS', 'eval', 'load']

def cmdLoad(repl):
  '''Executes a :load command.  Sets ``repl.module``.'''
  assert repl.command == ':load'
  try:
    arg, = repl.args
  except ValueError:
    if repl.args:
      raise ValueError("Too many arguments provided to ':load'")
    else:
      raise ValueError("No argument provided to ':load'")
  load(repl, arg)

[docs] def load(repl, arg): ''' Loads the module that the file name or module name ``arg`` names, as :load does. Sets ``repl.module`` and ``repl.loaded``. ''' filename = os.path.abspath(arg) dirname = os.path.dirname(filename) currypath = [dirname] + curry.path basename = os.path.basename(filename) modulename = os.path.splitext(basename)[0] # A loaded module is not read again in this process. Every :load after an # edit of its source says so (import_ says it once per edit), and so does # a :load of another file of the same module name. loaded = curry.modules.get(modulename) if loaded is not None: _import.warn_edited(loaded, always=True) # The argument may be a module name; the file it names is the one in # its directory, when there is one. for candidate in filename, filename + '.curry': if os.path.isfile(candidate): _import.warn_other_file(loaded, candidate) break repl.module = curry.import_(modulename, currypath=currypath) # The argument, so that a switch of the backend can load the module again. repl.loaded = arg # The expressions of :eval and :type import the loaded module, so the # front end must find it. if dirname not in curry.path: curry.path.insert(0, dirname)
def imports(repl): '''The modules in scope at the prompt: the loaded module.''' return [] if repl.module is None else [repl.module] def cmdEval(repl): ''' Executes an :eval command. Calls ``repl.action`` for each value. The expression sees the loaded module. A class constraint of the expression is defaulted with the table of the PAKCS REPL; a variable of a trailing ``where x free`` whose type is absent from the result type is reported with its binding, as PAKCS prints it (see curry.typecheck.goals). ''' assert repl.command == ':eval' if repl.args: # The expression goes to curry.eval and is not kept here. A reference # to the root of the goal keeps the history of the search reachable # while the evaluation runs; see curry.typecheck.goals.Goal.evaluate. values = iter(curry.eval( curry.compile(' '.join(repl.args), mode='expr', imports=imports(repl)) )) while True: try: value = next(values) except StopIteration: break except: msg = 'An error occurred during evaluation.' if repl.options['internal-error-details']: msg += ' To suppress details, type ":set -internal-error-details".\n' msg += traceback.format_exc() else: msg += ' For details, rerun with ":set +internal-error-details".' raise RuntimeError(msg) else: repl.action(repl, value) def cmdType(repl): ''' Executes a :type command: prints the type of an expression as the front end infers it, with its class context and before any defaulting, e.g., ``1+2 :: Num a => a``. ''' assert repl.command == ':type' if not repl.args: raise ValueError("No argument provided to ':type'") text = ' '.join(repl.args) scheme = _compile.expression_scheme( curry.getInterpreter(), text, imports=imports(repl) ) module = None if repl.module is None else repl.module.__name__ print('%s :: %s' % (text, curry.typecheck.show_scheme(scheme, module=module))) def cmdSet(repl): '''Executes a :set command. May update ``repl.options``.''' assert repl.command == ':set' try: name,value = repl.args except ValueError: try: name, = repl.args if name.startswith('+'): name = name[1:] value = True elif name.startswith('-'): name = name[1:] value = False else: raise ValueError('Invalid command. Type ":set" for help.') except ValueError: if repl.args: raise ValueError('Invalid command. Type ":set" for help.') print('Usage:', file=sys.stderr) print(' :set <option> <value>', file=sys.stderr) print(' :set [+/-]<option> (Boolean options only)', file=sys.stderr) print(file=sys.stderr) print('Options for ":set" command:', file=sys.stderr) for name in sorted(options.Options.names()): print(options.Options.usage(name, indent=4), file=sys.stderr) print(file=sys.stderr) print('Current settings:', file=sys.stderr) boolopts = [] valueopts = [] for name in sorted(options.Options.names()): name,value,spec = repl.options.state(name) if spec.isbool: boolopts.append(('+' if value else '-') + spec.name) else: valueopts.append((name, value)) print(' '.join(boolopts), file=sys.stderr) w = max((len(name) for name, _ in valueopts), default=0) for name,value in valueopts: print(name.ljust(w), ':', repr(value), file=sys.stderr) return else: if name.startswith('+') or name.startswith('-'): # Handles, e.g., ":set +option True". raise ValueError('Invalid command. Type ":set" for help.') repl.options[name] = value def cmdQuit(repl): '''Executes the :quit command.''' assert repl.command == ':quit' sys.exit(0) COMMANDS = { ':load' : cmdLoad , ':eval' : cmdEval , ':type' : cmdType , ':set' : cmdSet , ':quit' : cmdQuit }
[docs] def eval(name, *args): '''Evaluate a command. The name may be any unambiguous prefix.''' name = resolve(name, COMMANDS.keys(), 'command') if args: # The handlers check the full name of the command. args[0].command = name return COMMANDS[name](*args)