From e169c6aae292fec27f0f7de6d3a1559835bbd0b9 Mon Sep 17 00:00:00 2001 From: Simon Forman Date: Sun, 15 Jul 2018 11:48:08 -0700 Subject: [PATCH] Some integration with Type Checking. Now the UI highlights commands and numbers as you move the mouse, numbers are blue, commands that type-check are green, commands that fail to type-check are orange and will not be interpreted, and if there is no stack effect information available for a command it is grey but you can still attempt to execute it. You can still evaluate whole expressions by selceting them and right-inter-clicking before you release the left button, or by putting the cursor on a line and typing ctrl-enter, which will run the whole line. These expressions are NOT (yet) type-checked. --- joy/gui/main.py | 4 ++-- joy/gui/mousebindings.py | 4 ++++ joy/gui/textwidget.py | 39 ++++++++++++++++++++++++++++----------- joy/gui/world.py | 18 ++++++++++++++++-- joy/utils/polytypes.py | 26 ++++++++++++++++++++++++++ 5 files changed, 76 insertions(+), 15 deletions(-) diff --git a/joy/gui/main.py b/joy/gui/main.py index bb0e02d..eba355d 100755 --- a/joy/gui/main.py +++ b/joy/gui/main.py @@ -19,10 +19,10 @@ from joy.utils.stack import stack_to_string tb = TEXT_BINDINGS.copy() tb.update({ - '': lambda tv: tv.cut, '': lambda tv: tv.copy_selection_to_stack, + '': lambda tv: tv.cut, # '': lambda tv: tv.pastecut, - '': lambda tv: tv.copyto, + # '': lambda tv: tv.copyto, }) defaults = dict(text_bindings=tb, width=80, height=25) diff --git a/joy/gui/mousebindings.py b/joy/gui/mousebindings.py index 87bc687..3246a03 100644 --- a/joy/gui/mousebindings.py +++ b/joy/gui/mousebindings.py @@ -65,6 +65,7 @@ class MouseBindingsMixin: self.bind("", self.B3r) self.bind("", self.leave) + self.bind("", self.scan_command) def B1d(self, event): '''button one pressed''' @@ -167,6 +168,9 @@ class MouseBindingsMixin: return "break" + def scan_command(self, event): + self.update_command_word(event) + def B1r(self, event): '''button one released''' self.B1_DOWN = False diff --git a/joy/gui/textwidget.py b/joy/gui/textwidget.py index 30a7561..d9cc1f3 100644 --- a/joy/gui/textwidget.py +++ b/joy/gui/textwidget.py @@ -164,10 +164,11 @@ class TextViewerWidget(tk.Text, MouseBindingsMixin, SavingMixin): #These are the config tags for command text when it's highlighted. command_tags = dict( - underline = 1, - bgstipple = "gray50", - borderwidth = "1", - foreground = "orange" + #underline = 1, + #bgstipple = "gray50", + borderwidth = 2, + relief=tk.RIDGE, + foreground = "green" ) def __init__(self, world, master=None, **kw): @@ -196,6 +197,9 @@ class TextViewerWidget(tk.Text, MouseBindingsMixin, SavingMixin): #Add tag config for command highlighting. self.tag_config('command', **self.command_tags) + self.tag_config('bzzt', foreground = "orange") + self.tag_config('huh', foreground = "grey") + self.tag_config('number', foreground = "blue") #Create us a command instance variable self.command = '' @@ -246,18 +250,28 @@ class TextViewerWidget(tk.Text, MouseBindingsMixin, SavingMixin): return cmd, b, e = cmd - if self.world.has(cmd) or is_numerical(cmd): - self.command = cmd - self.highlight_command( - '%d.%d' % (row, b), - '%d.%d' % (row, e), - ) + if is_numerical(cmd): + extra_tags = 'number', + elif self.world.has(cmd): + check = self.world.check(cmd) + if check: extra_tags = () + elif check is None: extra_tags = 'huh', + else: extra_tags = 'bzzt', + else: + return + self.command = cmd + self.highlight_command( + '%d.%d' % (row, b), + '%d.%d' % (row, e), + *extra_tags) - def highlight_command(self, from_, to): + def highlight_command(self, from_, to, *extra_tags): '''Apply command style from from_ to to.''' cmdstart = self.index(from_) cmdend = self.index(to) self.tag_add('command', cmdstart, cmdend) + for tag in extra_tags: + self.tag_add(tag, cmdstart, cmdend) def do_command(self, event): '''Do the currently highlighted command.''' @@ -288,6 +302,9 @@ class TextViewerWidget(tk.Text, MouseBindingsMixin, SavingMixin): def unhighlight_command(self): '''Remove any command highlighting.''' + self.tag_remove('number', 1.0, tk.END) + self.tag_remove('huh', 1.0, tk.END) + self.tag_remove('bzzt', 1.0, tk.END) self.tag_remove('command', 1.0, tk.END) def set_insertion_point(self, event): diff --git a/joy/gui/world.py b/joy/gui/world.py index 1c87106..80a5735 100644 --- a/joy/gui/world.py +++ b/joy/gui/world.py @@ -23,6 +23,7 @@ from inspect import getdoc from joy.joy import run from joy.parser import Symbol from joy.utils.stack import stack_to_string +from joy.utils.polytypes import type_check def is_numerical(s): @@ -40,6 +41,9 @@ class World(object): self.dictionary = dictionary or {} self.text_widget = text_widget + def check(self, name): + return type_check(name, self.stack) + def do_lookup(self, name): if name in self.dictionary: self.stack = (Symbol(name), ()), self.stack @@ -75,6 +79,10 @@ class World(object): return self.stack[0] def interpret(self, command): + if len(command.split()) == 1 and not is_numerical(command): + assert self.has(command), repr(command) + if self.check(command) == False: # not in {True, None}: + return try: self.stack, _, self.dictionary = run( command, @@ -109,8 +117,14 @@ class StackDisplayWorld(World): self.relative_STACK_FN = rel_filename def interpret(self, command): - print '\njoy?', command - super(StackDisplayWorld, self).interpret(command) + if ( + is_numerical(command) + or len(command.split()) > 1 + or self.has(command) + and self.check(command) in {True, None} + ): + print '\njoy?', command + super(StackDisplayWorld, self).interpret(command) def print_stack(self): print '\n%s <-' % stack_to_string(self.stack) diff --git a/joy/utils/polytypes.py b/joy/utils/polytypes.py index f806097..45d2e73 100644 --- a/joy/utils/polytypes.py +++ b/joy/utils/polytypes.py @@ -9,6 +9,7 @@ and we can introduce a kind of Kleene Star or sequence type that can stand for an unbounded sequence of other types. ''' +import sys from inspect import stack as inspect_stack from itertools import chain, product from logging import getLogger @@ -343,6 +344,31 @@ def infer(*expression): return sorted(set(_infer(list_to_stack(expression)))) +def type_check(name, stack): + ''' + Trinary predicate. True if named function type-checks, False if it + fails, None if it's indeterminate (because I haven't entered it into + the FUNCTIONS dict yet.) + ''' + try: + func = FUNCTIONS[name] + except KeyError: + return # None, indicating unknown + + for fi, fo in infer(func): + try: + U = unify(fi, stack) + except JoyTypeError, e: + #print e + continue + except ValueError, e: + #print >> sys.stderr, name, e, stack + continue + #print U + return True + return False + + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 = A b0, b1, b2, b3, b4, b5, b6, b7, b8, b9 = B n0, n1, n2, n3, n4, n5, n6, n7, n8, n9 = N