]> git.vouivredigital.com Git - vouivre.git/commitdiff
Create a development environment for dependent type theories
authoradmin <admin@vouivredigital.com>
Sun, 6 Oct 2024 20:37:32 +0000 (16:37 -0400)
committeradmin <admin@vouivredigital.com>
Sun, 6 Oct 2024 20:37:32 +0000 (16:37 -0400)
.gitignore
Makefile.am
build-aux/test-driver.scm [deleted file]
configure.ac
doc/version.texi [deleted file]
doc/vouivre.texi [deleted file]
guix.scm [deleted file]
hall.scm [deleted file]
pre-inst-env.in [deleted file]
src/v.c [new file with mode: 0644]

index 2506c620d4aae2c53c1a8f93421b136364e222b4..abdb074c5b15cf52ec4c47ceebebf0f3344ef5e3 100644 (file)
@@ -3,13 +3,13 @@
 *.log
 *.pdf
 *.png
-*.tar.xz
 *.tar.gz
+*.tar.xz
 *.tmp
 *~
-.#*
-\#*\#
 ,*
+.#*
+/.version
 /ABOUT-NLS
 /INSTALL
 /aclocal.m4
 /build-aux/missing
 /build-aux/test-driver
 /build-aux/texinfo.tex
+/compile
 /config.status
 /configure
+/depcomp
 /doc/*.1
-/doc/.dirstamp
-/doc/contributing.*.texi
 /doc/*.aux
 /doc/*.cp
 /doc/*.cps
 /doc/*.info-[0-9]
 /doc/*.ky
 /doc/*.pg
-/doc/*.toc
 /doc/*.t2p
+/doc/*.toc
 /doc/*.tp
 /doc/*.vr
 /doc/*.vrs
+/doc/.dirstamp
+/doc/contributing.*.texi
+/doc/stamp-[0-9]
 /doc/stamp-vti
 /doc/version-*.texi
+/install-sh
 /m4/*
+/missing
 /pre-inst-env
+/src/*.o
+/src/.deps/
+/src/.dirstamp
 /test-env
 /test-tmp
 /tests/*.trs
+/v
 GPATH
 GRTAGS
 GTAGS
 Makefile
 Makefile.in
+\#*\#
 config.cache
 stamp-h[0-9]
 tmp
-/.version
-/doc/stamp-[0-9]
index 3d3a8f656244f511b91a9a7f9e9efeab7b098f86..8ac5d26800254887c0987c7b884ea0d7322afded 100644 (file)
@@ -1,103 +1,4 @@
-bin_SCRIPTS = examples/example.scm \
-              examples/base.scm
-
-# Handle substitution of fully-expanded Autoconf variables.
-do_subst = $(SED)                                      \
-  -e 's,[@]GUILE[@],$(GUILE),g'                                \
-  -e 's,[@]guilemoduledir[@],$(guilemoduledir),g'      \
-  -e 's,[@]guileobjectdir[@],$(guileobjectdir),g'      \
-  -e 's,[@]localedir[@],$(localedir),g'
-
-nodist_noinst_SCRIPTS = pre-inst-env
-
-GOBJECTS = $(SOURCES:%.scm=%.go)
-
-moddir=$(prefix)/share/guile/site/$(GUILE_EFFECTIVE_VERSION)
-godir=$(libdir)/guile/$(GUILE_EFFECTIVE_VERSION)/site-ccache
-ccachedir=$(libdir)/guile/$(GUILE_EFFECTIVE_VERSION)/site-ccache
-
-nobase_dist_mod_DATA = $(filter-out $(BUILT_SOURCES),$(SOURCES)) $(NOCOMP_SOURCES)
-nobase_nodist_mod_DATA = $(BUILT_SOURCES)
-nobase_go_DATA = $(GOBJECTS)
-
-# Make sure source files are installed first, so that the mtime of
-# installed compiled files is greater than that of installed source
-# files.  See
-# <http://lists.gnu.org/archive/html/guile-devel/2010-07/msg00125.html>
-# for details.
-guile_install_go_files = install-nobase_goDATA
-$(guile_install_go_files): install-nobase_dist_modDATA
-
-GUILE_WARNINGS = -Wunbound-variable -Warity-mismatch -Wformat
-SUFFIXES = .scm .go
-.scm.go:
-       $(AM_V_GEN)$(top_builddir)/pre-inst-env $(GUILE_TOOLS) compile $(GUILE_TARGET) $(GUILE_WARNINGS) -o "$@" "$<"
-
-SOURCES = vouivre.scm \
-          language/vouivre/decompile-tree-il.scm \
-          language/vouivre/spec.scm \
-          language/vouivre/compile-tree-il.scm \
-          vouivre/hconfig.scm \
-          vouivre/curry.scm \
-          vouivre/autodiff.scm \
-          vouivre/misc.scm \
-          vouivre/promises.scm \
-          vouivre/mnist.scm
-
-TESTS = tests/curry.scm \
-        tests/autodiff.scm
-
-TEST_EXTENSIONS = .scm
-SCM_LOG_DRIVER =                                \
-  $(top_builddir)/pre-inst-env                  \
-  $(GUILE) --no-auto-compile -e main            \
-      $(top_srcdir)/build-aux/test-driver.scm
-
-# Tell 'build-aux/test-driver.scm' to display only source file names,
-# not indivdual test names.
-AM_SCM_LOG_DRIVER_FLAGS = --brief=yes
-
-AM_SCM_LOG_FLAGS = --no-auto-compile -L "$(top_srcdir)"
-
-AM_TESTS_ENVIRONMENT = abs_top_srcdir="$(abs_top_srcdir)"
-
-info_TEXINFOS = doc/version.texi \
-                doc/vouivre.texi
-
-EXTRA_DIST = README.org \
-             README \
-             HACKING \
-             COPYING \
-             doc/vouivre.info \
-             doc/version.info \
-             doc/.dirstamp \
-             doc/stamp-vti \
-             NEWS \
-             AUTHORS \
-             ChangeLog \
-             guix.scm \
-             .gitignore \
-             hall.scm \
-             build-aux/texinfo.tex \
-             build-aux/mdate-sh \
-             build-aux/test-driver.scm \
-             build-aux/missing \
-             build-aux/install-sh \
-             configure.ac \
-             pre-inst-env.in \
-             Makefile.am \
-             build-aux/test-driver.scm \
-             $(TESTS)
-
-ACLOCAL_AMFLAGS = -I m4
-
-AM_DISTCHECK_DVI_TARGET = info # Disable DVI as part of distcheck
-
-clean-go:
-       -$(RM) $(GOBJECTS)
-.PHONY: clean-go
-
-CLEANFILES =                                   \
-  $(BUILT_SOURCES)                             \
-  $(GOBJECTS)                                  \
-  $(TESTS:tests/%.scm=%.log)
+bin_PROGRAMS = v
+v_SOURCES = src/v.c
+v_CFLAGS = $(GLIB_CFLAGS)
+v_LDADD = $(GLIB_LIBS) -lncursesw
diff --git a/build-aux/test-driver.scm b/build-aux/test-driver.scm
deleted file mode 100644 (file)
index 0c555ea..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-;;;; test-driver.scm - Guile test driver for Automake testsuite harness
-
-(define script-version "2019-01-15.13") ;UTC
-
-;;; Copyright © 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
-;;; Copyright © 2019 Alex Sassmannshausen <alex@pompo.co>
-;;;
-;;; This program is free software; you can redistribute it and/or modify it
-;;; under the terms of the GNU General Public License as published by
-;;; the Free Software Foundation; either version 3 of the License, or (at
-;;; your option) any later version.
-;;;
-;;; This program is distributed in the hope that it will be useful, but
-;;; WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;;; GNU General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU General Public License
-;;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-;;;; Commentary:
-;;;
-;;; This script provides a Guile test driver using the SRFI-64 Scheme API for
-;;; test suites.  SRFI-64 is distributed with Guile since version 2.0.9.
-;;;
-;;; This script is a lightly modified version of the orignal written by
-;;; Matthieu Lirzin.  The changes make it suitable for use as part of the
-;;; guile-hall infrastructure.
-;;;
-;;;; Code:
-
-(use-modules (ice-9 getopt-long)
-             (ice-9 pretty-print)
-             (srfi srfi-26)
-             (srfi srfi-64))
-
-(define (show-help)
-  (display "Usage:
-                         test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
-                         [--expect-failure={yes|no}] [--color-tests={yes|no}]
-                         [--enable-hard-errors={yes|no}] [--brief={yes|no}}] [--]
-               TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS]
-The '--test-name', '--log-file' and '--trs-file' options are mandatory.
-"))
-
-(define %options
-  '((test-name                  (value #t))
-    (log-file                   (value #t))
-    (trs-file                   (value #t))
-    (color-tests                (value #t))
-    (expect-failure             (value #t)) ;XXX: not implemented yet
-    (enable-hard-errors         (value #t)) ;not implemented in SRFI-64
-    (brief                      (value #t))
-    (help    (single-char #\h) (value #f))
-    (version (single-char #\V) (value #f))))
-
-(define (option->boolean options key)
-  "Return #t if the value associated with KEY in OPTIONS is 'yes'."
-  (and=> (option-ref options key #f) (cut string=? <> "yes")))
-
-(define* (test-display field value  #:optional (port (current-output-port))
-                       #:key pretty?)
-  "Display 'FIELD: VALUE\n' on PORT."
-  (if pretty?
-      (begin
-        (format port "~A:~%" field)
-        (pretty-print value port #:per-line-prefix "+ "))
-      (format port "~A: ~S~%" field value)))
-
-(define* (result->string symbol #:key colorize?)
-  "Return SYMBOL as an upper case string.  Use colors when COLORIZE is #t."
-  (let ((result (string-upcase (symbol->string symbol))))
-    (if colorize?
-        (string-append (case symbol
-                         ((pass)       "\e[0;32m")  ;green
-                         ((xfail)      "\e[1;32m")  ;light green
-                         ((skip)       "\e[1;34m")  ;blue
-                         ((fail xpass) "\e[0;31m")  ;red
-                         ((error)      "\e[0;35m")) ;magenta
-                       result
-                       "\e[m")          ;no color
-        result)))
-
-(define* (test-runner-gnu test-name #:key color? brief? out-port trs-port)
-  "Return an custom SRFI-64 test runner.  TEST-NAME is a string specifying the
-file name of the current the test.  COLOR? specifies whether to use colors,
-and BRIEF?, well, you know.  OUT-PORT and TRS-PORT must be output ports.  The
-current output port is supposed to be redirected to a '.log' file."
-
-  (define (test-on-test-begin-gnu runner)
-    ;; Procedure called at the start of an individual test case, before the
-    ;; test expression (and expected value) are evaluated.
-    (let ((result (cute assq-ref (test-result-alist runner) <>)))
-      (format #t "test-name: ~A~%" (result 'test-name))
-      (format #t "location: ~A~%"
-              (string-append (result 'source-file) ":"
-                             (number->string (result 'source-line))))
-      (test-display "source" (result 'source-form) #:pretty? #t)))
-
-  (define (test-on-test-end-gnu runner)
-    ;; Procedure called at the end of an individual test case, when the result
-    ;; of the test is available.
-    (let* ((results (test-result-alist runner))
-           (result? (cut assq <> results))
-           (result  (cut assq-ref results <>)))
-      (unless brief?
-        ;; Display the result of each test case on the console.
-        (format out-port "~A: ~A - ~A~%"
-                (result->string (test-result-kind runner) #:colorize? color?)
-                test-name (test-runner-test-name runner)))
-      (when (result? 'expected-value)
-        (test-display "expected-value" (result 'expected-value)))
-      (when (result? 'expected-error)
-        (test-display "expected-error" (result 'expected-error) #:pretty? #t))
-      (when (result? 'actual-value)
-        (test-display "actual-value" (result 'actual-value)))
-      (when (result? 'actual-error)
-        (test-display "actual-error" (result 'actual-error) #:pretty? #t))
-      (format #t "result: ~a~%" (result->string (result 'result-kind)))
-      (newline)
-      (format trs-port ":test-result: ~A ~A~%"
-              (result->string (test-result-kind runner))
-              (test-runner-test-name runner))))
-
-  (define (test-on-group-end-gnu runner)
-    ;; Procedure called by a 'test-end', including at the end of a test-group.
-    (let ((fail (or (positive? (test-runner-fail-count runner))
-                    (positive? (test-runner-xpass-count runner))))
-          (skip (or (positive? (test-runner-skip-count runner))
-                    (positive? (test-runner-xfail-count runner)))))
-      ;; XXX: The global results need some refinements for XPASS.
-      (format trs-port ":global-test-result: ~A~%"
-              (if fail "FAIL" (if skip "SKIP" "PASS")))
-      (format trs-port ":recheck: ~A~%"
-              (if fail "yes" "no"))
-      (format trs-port ":copy-in-global-log: ~A~%"
-              (if (or fail skip) "yes" "no"))
-      (when brief?
-        ;; Display the global test group result on the console.
-        (format out-port "~A: ~A~%"
-                (result->string (if fail 'fail (if skip 'skip 'pass))
-                                #:colorize? color?)
-                test-name))
-      #f))
-
-  (let ((runner (test-runner-null)))
-    (test-runner-on-test-begin! runner test-on-test-begin-gnu)
-    (test-runner-on-test-end! runner test-on-test-end-gnu)
-    (test-runner-on-group-end! runner test-on-group-end-gnu)
-    (test-runner-on-bad-end-name! runner test-on-bad-end-name-simple)
-    runner))
-
-;;;
-;;; Entry point.
-;;;
-
-(define (main . args)
-  (let* ((opts   (getopt-long (command-line) %options))
-         (option (cut option-ref opts <> <>)))
-    (cond
-     ((option 'help #f)    (show-help))
-     ((option 'version #f) (format #t "test-driver.scm ~A" script-version))
-     (else
-      (let ((log (open-file (option 'log-file "") "w0"))
-            (trs (open-file (option 'trs-file "") "wl"))
-            (out (duplicate-port (current-output-port) "wl")))
-        (redirect-port log (current-output-port))
-        (redirect-port log (current-warning-port))
-        (redirect-port log (current-error-port))
-        (test-with-runner
-            (test-runner-gnu (option 'test-name #f)
-                             #:color? (option->boolean opts 'color-tests)
-                             #:brief? (option->boolean opts 'brief)
-                             #:out-port out #:trs-port trs)
-          (load-from-path (option 'test-name #f)))
-        (close-port log)
-        (close-port trs)
-        (close-port out))))
-    (exit 0)))
index 6f61cc27ed2bb6469e31abd829d1d3d5ca68f121..1c9d824134af02c2375d5f0c129ee5f04fc34690 100644 (file)
@@ -1,39 +1,10 @@
-dnl -*- Autoconf -*-
-
-AC_INIT(vouivre, 0.1.0)
-AC_SUBST(HVERSION, "\"0.1.0\"")
-AC_SUBST(AUTHOR, "\"Vouivre Digital Corporation\"")
-AC_SUBST(COPYRIGHT, "'(2023)")
-AC_SUBST(LICENSE, gpl3+)
-AC_CONFIG_SRCDIR(vouivre.scm)
-AC_CONFIG_AUX_DIR([build-aux])
-AM_INIT_AUTOMAKE([1.12 gnu silent-rules subdir-objects  color-tests parallel-tests -Woverride -Wno-portability])
-AM_SILENT_RULES([yes])
-
+AC_INIT([Vouivre], [0.2.0], [admin@vouivredigital.com])
+AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects])
+AC_PROG_CC
+PKG_PROG_PKG_CONFIG
+PKG_CHECK_MODULES([GLIB], [glib-2.0])
+AC_CHECK_LIB(ncursesw, initscr)
+AC_SUBST(GLIB_CFLAGS)
+AC_SUBST(GLIB_LIBS)
 AC_CONFIG_FILES([Makefile])
-AC_CONFIG_FILES([pre-inst-env], [chmod +x pre-inst-env])
-
-dnl Search for 'guile' and 'guild'.  This macro defines
-dnl 'GUILE_EFFECTIVE_VERSION'.
-GUILE_PKG([3.0 2.2 2.0])
-GUILE_PROGS
-GUILE_SITE_DIR
-if test "x$GUILD" = "x"; then
-   AC_MSG_ERROR(['guild' binary not found; please check your guile-2.x installation.])
-fi
-
-if test "$cross_compiling" != no; then
-   GUILE_TARGET="--target=$host_alias"
-   AC_SUBST([GUILE_TARGET])
-fi
-
-dnl Hall auto-generated guile-module dependencies
-
-
-dnl Installation directories for .scm and .go files.
-guilemoduledir="${datarootdir}/guile/site/$GUILE_EFFECTIVE_VERSION"
-guileobjectdir="${libdir}/guile/$GUILE_EFFECTIVE_VERSION/site-ccache"
-AC_SUBST([guilemoduledir])
-AC_SUBST([guileobjectdir])
-
 AC_OUTPUT
diff --git a/doc/version.texi b/doc/version.texi
deleted file mode 100644 (file)
index 049af9e..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-@set UPDATED 25 November 2023
-@set UPDATED-MONTH November 2023
-@set EDITION 0.2.0
-@set VERSION 0.2.0
diff --git a/doc/vouivre.texi b/doc/vouivre.texi
deleted file mode 100644 (file)
index 099f512..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-\input texinfo
-@c -*-texinfo-*-
-
-@c %**start of header
-@setfilename vouivre.info
-@documentencoding UTF-8
-@settitle Vouivre Reference Manual
-@c %**end of header
-
-@include version.texi
-
-@copying
-Copyright @copyright{} 2023 Vouivre Digital Corporation
-
-Permission is granted to copy, distribute and/or modify this document
-under the terms of the GNU Free Documentation License, Version 1.3 or
-any later version published by the Free Software Foundation; with no
-Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.  A
-copy of the license is included in the section entitled ``GNU Free
-Documentation License''.
-@end copying
-
-@dircategory The Algorithmic Language Scheme
-@direntry
-* Vouivre: (vouivre).   
-@end direntry
-
-@titlepage
-@title The Vouivre Manual
-@author Vouivre Digital Corporation
-
-@page
-@vskip 0pt plus 1filll
-Edition @value{EDITION} @*
-@value{UPDATED} @*
-
-@insertcopying
-@end titlepage
-
-@contents
-
-@c *********************************************************************
-@node Top
-@top Vouivre
-
-This document describes Vouivre version @value{VERSION}.
-
-@menu
-* Introduction::                Why Vouivre?
-@end menu
-
-@c *********************************************************************
-@node Introduction
-@chapter Introduction
-
-INTRODUCTION HERE
-
-This documentation is a stub.
-
-@bye
diff --git a/guix.scm b/guix.scm
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/hall.scm b/hall.scm
deleted file mode 100644 (file)
index e8d74cf..0000000
--- a/hall.scm
+++ /dev/null
@@ -1,69 +0,0 @@
-(hall-description
- (name "vouivre")
- (prefix "")
- (version "0.2.0")
- (author "Vouivre Digital Corporation")
- (email "admin@vouivredigital.com")
- (copyright (2023))
- (synopsis "")
- (description "")
- (home-page "https://vouivredigital.com")
- (license gpl3+)
- (dependencies `())
- (skip ())
- (features ((guix #f) (native-language-support #f) (licensing #f)))
- (files (libraries
-         ((scheme-file "vouivre")
-          (directory
-           "language"
-           ((directory
-             "vouivre"
-             ((scheme-file "decompile-tree-il")
-              (scheme-file "spec")
-              (scheme-file "compile-tree-il")))))
-          (directory
-           "vouivre"
-           ((scheme-file "hconfig")
-            (scheme-file "curry")
-            (scheme-file "autodiff")
-            (scheme-file "misc")
-            (scheme-file "promises")
-            (scheme-file "mnist")))))
-        (tests ((directory
-                 "tests"
-                 ((scheme-file "curry") (scheme-file "autodiff")))))
-        (programs ((directory "scripts" ())
-                  (directory "examples" ((scheme-file "example")
-                                         (scheme-file "base")))))
-        (documentation
-         ((org-file "README")
-          (symlink "README" "README.org")
-          (text-file "HACKING")
-          (text-file "COPYING")
-         (text-file "COPYING.LESSER")
-          (directory
-           "doc"
-           ((info-file "vouivre")
-            (info-file "version")
-            (texi-file "version")
-            (text-file ".dirstamp")
-            (texi-file "vouivre")
-            (text-file "stamp-vti")))
-         (text-file "LICENSE")
-          (text-file "NEWS")
-          (text-file "AUTHORS")
-          (text-file "ChangeLog")))
-        (infrastructure
-         ((scheme-file "guix")
-          (text-file ".gitignore")
-          (scheme-file "hall")
-          (directory
-           "build-aux"
-           ((tex-file "texinfo")
-            (text-file "mdate-sh")
-            (scheme-file "test-driver")
-            (text-file "missing")
-            (text-file "install-sh")))
-          (autoconf-file "configure")
-          (in-file "pre-inst-env")
-          (automake-file "Makefile")))))
diff --git a/pre-inst-env.in b/pre-inst-env.in
deleted file mode 100644 (file)
index 31c499d..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/sh
-
-abs_top_srcdir="`cd "@abs_top_srcdir@" > /dev/null; pwd`"
-abs_top_builddir="`cd "@abs_top_builddir@" > /dev/null; pwd`"
-
-GUILE_LOAD_COMPILED_PATH="$abs_top_builddir${GUILE_LOAD_COMPILED_PATH:+:}$GUILE_LOAD_COMPILED_PATH"
-GUILE_LOAD_PATH="$abs_top_builddir:$abs_top_srcdir${GUILE_LOAD_PATH:+:}:$GUILE_LOAD_PATH"
-export GUILE_LOAD_COMPILED_PATH GUILE_LOAD_PATH
-
-PATH="$abs_top_builddir/scripts:$PATH"
-export PATH
-
-exec "$@"
diff --git a/src/v.c b/src/v.c
new file mode 100644 (file)
index 0000000..6bbb16a
--- /dev/null
+++ b/src/v.c
@@ -0,0 +1,1159 @@
+#include <curses.h>
+#include <locale.h>
+#include <glib.h>
+#include <signal.h>
+
+#define PAIR_BW       1
+#define PAIR_WB       2
+#define PAIR_HL       3
+#define PAIR_SUCCESS  4
+#define PAIR_ERROR    5
+
+#define KEY_ESC 27
+#define KEY_RETURN '\n'
+#define KEY_TAB '\t'
+
+#define NB_COLUMNS 80
+#define HYPOTHESES_SPACING 4
+#define EMPTY_SLOT "█"
+
+enum ui_state
+  {
+    uis_splash,
+    uis_workspace,
+  };
+
+enum slot_type
+  {
+    st_rule_dots,
+    st_ctx_l,
+    st_ctx_v,
+    st_ctx_t,
+    st_ctx_dots,
+    st_ctx_r,
+    st_thesis,
+    st_a,
+    st_b,
+    st_c,
+    st_rule_name,
+  };
+
+enum caret_to_point
+  {
+    ctp_before = -1,
+    ctp_on = 0,
+    ctp_after = +1,
+  };
+
+enum thesis_kind
+  {
+    tk_generic,
+    tk_type,
+    tk_element,
+    tk_type_equal,
+    tk_element_equal,
+    tk_unknown,
+  };
+
+enum scope
+  {
+    s_contextual,
+    s_local,
+    s_global,
+  };
+
+enum name_kind
+  {
+    nk_variable,
+    nk_element,
+    nk_type,
+    nk_generic_context,
+    nk_generic_thesis,
+  };
+
+struct wrap
+{
+  bool is_origin;
+  void *ptr;                   /* name or reference */
+};
+
+struct name
+{
+  enum scope scope;
+  enum name_kind kind;
+  struct wrap *type;
+  struct rule *src;
+  GSList *dependencies;                /* names */
+  char *str;
+};
+
+struct reference
+{
+  struct name *origin;
+  GSList *replacements;                /* wraps */
+};
+
+struct vdecl
+{
+  struct name *elem;
+  struct wrap *type;
+};
+
+struct judgment
+{
+  enum thesis_kind kind;
+  struct wrap *generic_l;
+  struct wrap *generic_r;
+  struct wrap *a;
+  struct wrap *b;
+  GSList *vdecls;
+};
+
+struct rule
+{
+  char *name;
+  GSList *hypotheses;          /* judgments */
+  struct judgment *conclusion;
+};
+
+struct drawing_params
+{
+  bool virtual;
+  bool hide;
+  WINDOW *w;
+  int y;
+  int x;
+  int row;
+  int col;
+  int c;
+  int m;
+  int n;
+  int r;
+  int s;
+  int stacked;
+  int lw;
+  int jw;
+};
+
+/*
+ * As we draw a rule we move the point (pt - an address) from one variable to
+ * another down the rabbit hole of the data structure. When that address equals
+ * the address of the caret, i.e. where we are currently editing, we set the
+ * flag `is_pt_geq_caret', allowing us to know whether or not to draw dots and
+ * conditional slots.
+ */
+struct state
+{
+  bool running;
+  enum ui_state mode;
+  struct rule *rule;
+  struct judgment *judgment;
+  struct name *dependency;
+  WINDOW *workspace_w;
+  WINDOW *support_w;
+  WINDOW *echo_w;
+  GHashTable *rules;           /* str -> rule */
+  GHashTable *decls;           /* str -> name */
+  GHashTable *gctxs;           /* str -> name */
+  GHashTable *gtheses;         /* str -> name */
+  GHashTable *vets;            /* str -> name */
+  GSList *support;             /* rules */
+  int caret;
+  enum slot_type st;
+  GString *input;
+  GString *escaped_input;
+  int status;
+  bool escape;
+  int err;
+};
+
+const char *escaped_strings[] = {
+  "α", "alpha",
+  "β", "beta",
+  "Γ", "Gamma",
+  "γ", "gamma",
+  "Δ", "Delta",
+  "δ", "delta",
+  "ε", "epsilon",
+  "Ζ", "Zeta",
+  "ζ", "zeta",
+  "η", "eta",
+  "Θ", "Theta",
+  "θ", "theta",
+  "Ι", "Iota",
+  "ι", "iota",
+  "κ", "kappa",
+  "Λ", "Lambda",
+  "λ", "lambda",
+  "μ", "mu",
+  "ν", "nu",
+  "Ξ", "Xi",
+  "ξ", "xi",
+  "Ο", "Omicron",
+  "ο", "omicron",
+  "Π", "Pi",
+  "π", "pi",
+  "Ρ", "Rho",
+  "ρ", "rho",
+  "Σ", "Sigma",
+  "σ", "sigma",
+  "υ", "upsilon",
+  "Φ", "Phi",
+  "φ", "phi",
+  "Χ", "Chi",
+  "χ", "chi",
+  "Ψ", "Psi",
+  "ψ", "psi",
+  "Ω", "Omega",
+  "ω", "omega",
+  "𝔸", "AA",
+  "𝔹", "BB",
+  "ℂ", "CC",
+  "𝔻", "DD",
+  "𝔼", "EE",
+  "𝔽", "FF",
+  "𝔾", "GG",
+  "ℍ", "HH",
+  "𝕀", "II",
+  "𝕁", "JJ",
+  "𝕂", "KK",
+  "𝕃", "LL",
+  "𝕄", "MM",
+  "ℕ", "NN",
+  "𝕆", "OO",
+  "ℙ", "PP",
+  "ℚ", "QQ",
+  "ℝ", "RR",
+  "𝕊", "SS",
+  "𝕋", "TT",
+  "𝕌", "UU",
+  "𝕍", "VV",
+  "𝕎", "WW",
+  "𝕏", "XX",
+  "𝕐", "YY",
+  "ℤ", "ZZ",
+  "𝕒", "aa",
+  "𝕓", "bb",
+  "𝕔", "cc",
+  "𝕕", "dd",
+  "𝕖", "ee",
+  "𝕗", "ff",
+  "𝕘", "gg",
+  "𝕙", "hh",
+  "𝕚", "ii",
+  "𝕛", "jj",
+  "𝕜", "kk",
+  "𝕝", "ll",
+  "𝕞", "mm",
+  "𝕟", "nn",
+  "𝕠", "oo",
+  "𝕡", "pp",
+  "𝕢", "qq",
+  "𝕣", "rr",
+  "𝕤", "ss",
+  "𝕥", "tt",
+  "𝕦", "uu",
+  "𝕧", "vv",
+  "𝕨", "ww",
+  "𝕩", "xx",
+  "𝕪", "yy",
+  "𝕫", "zz",
+  "𝟘", "00",
+  "𝟙", "11",
+  "𝟚", "22",
+  "𝟛", "33",
+  "𝟜", "44",
+  "𝟝", "55",
+  "𝟞", "66",
+  "𝟟", "77",
+  "𝟠", "88",
+  "𝟡", "99",  
+};
+
+GHashTable *escaped_table;
+
+struct wrap *
+create_wrap(bool is_origin, void *ptr)
+{
+  struct wrap *w;
+
+  w = malloc(sizeof(struct wrap));
+  w->is_origin = is_origin;
+  w->ptr = ptr;
+  return w;
+}
+
+struct name *
+create_name(enum scope scope, enum name_kind kind, struct wrap *type,
+           struct rule *src, GSList *dependencies, char *str)
+{
+  struct name *name;
+
+  name = malloc(sizeof(struct name));
+  name->scope = scope;
+  name->kind = kind;
+  name->type = type;
+  name->src = src;
+  name->dependencies = dependencies;
+  name->str = str;
+  return name;
+}
+
+struct rule *
+create_rule()
+{
+  struct rule *rule;
+  
+  rule = malloc(sizeof(struct rule));
+  rule->name = NULL;
+  rule->hypotheses = NULL;
+  rule->conclusion = malloc(sizeof(struct judgment));
+  rule->conclusion->kind = tk_unknown;
+  rule->conclusion->generic_l = NULL;
+  rule->conclusion->generic_r = NULL;
+  rule->conclusion->a = NULL;
+  rule->conclusion->b = NULL;
+  rule->conclusion->vdecls = NULL;
+  return rule;
+}
+
+void free_wrap(struct wrap *);
+
+void
+free_name(struct name *name)
+{
+  if (name) {
+    free_wrap(name->type);
+    g_slist_free(name->dependencies);
+    free(name->str);
+    free(name);
+  }
+}
+
+void
+free_reference(struct reference *ref)
+{
+  if (ref) {
+    free_name(ref->origin);
+    g_slist_free_full(ref->replacements, (GDestroyNotify)free_wrap);
+    free(ref);
+  }
+}
+
+
+void
+free_wrap(struct wrap *wrap)
+{
+  if (wrap) {
+    if (wrap->is_origin)
+      free_name(wrap->ptr);
+    else
+      free_reference(wrap->ptr);
+    free(wrap);
+  }
+}
+
+void
+free_vdecl(struct vdecl *decl)
+{
+  if (decl) {
+    free_name(decl->elem);
+    free_wrap(decl->type);
+    free(decl);
+  }
+}
+
+
+void
+free_judgment(struct judgment *judgment)
+{
+  if (judgment) {
+    free_wrap(judgment->generic_l);
+    free_wrap(judgment->generic_r);
+    free_wrap(judgment->a);
+    free_wrap(judgment->b);
+    g_slist_free_full(judgment->vdecls, (GDestroyNotify)free_vdecl);
+    free(judgment);
+  }
+}
+
+void
+free_rule(struct rule *rule)
+{
+  if (rule) {
+    free(rule->name);
+    g_slist_free_full(rule->hypotheses, (GDestroyNotify)free_judgment);
+    free_judgment(rule->conclusion);
+    free(rule);
+  }
+}
+
+int
+greekstrlen(const char *str)
+{
+  return mbstowcs(NULL, str, 0);
+}
+
+void
+draw_slot(WINDOW *w, int *y, int *x, bool virtual, bool optional, int pair, const char *str)
+{
+  if (!virtual) {
+    wattron(w, COLOR_PAIR(pair));
+    wmove(w, *y, *x);
+    if (str && *str != '\0')
+      wprintw(w, str);
+    else {
+      wprintw(w, EMPTY_SLOT);
+      if (optional)
+       wprintw(w, "?");
+    }
+    wattroff(w, COLOR_PAIR(pair));
+  }
+  if (str && *str != '\0')
+    *x += greekstrlen(str);
+  else {
+    if (optional)
+      *x += 2;
+    else
+      *x += 1;
+  }
+}
+
+void
+draw_wrap(WINDOW *w, int *y, int *x, bool virtual, bool optional,
+         int pair, struct wrap *wrap)
+{
+  struct reference *r;
+  GSList *l;
+
+  if (!wrap)
+    draw_slot(w, y, x, virtual, optional, pair, NULL);
+  else {
+    if (wrap->is_origin) {
+      draw_slot(w, y, x, virtual, optional, pair,
+               ((struct name *)wrap->ptr)->str);
+    }
+    else {
+      r = wrap->ptr;
+      draw_slot(w, y, x, virtual, optional, pair, r->origin->str);
+      l = r->replacements;
+      if (l) {
+       draw_slot(w, y, x, virtual, FALSE, PAIR_BW, "[");
+       while (l) {
+         draw_wrap(w, y, x, virtual, FALSE, pair, l->data);
+         l = l->next;
+         if (l)
+           draw_slot(w, y, x, virtual, FALSE, PAIR_BW, ", ");
+       }
+       draw_slot(w, y, x, virtual, FALSE, PAIR_BW, "]");
+      }
+    }
+  }
+}
+
+struct wrap *
+get_wrap_type(struct wrap *wrap)
+{
+  if (wrap) {
+    if (wrap->is_origin)
+      return ((struct name *)wrap->ptr)->type;
+    else
+      return ((struct reference *)wrap->ptr)->origin->type;
+  }
+  else
+    return NULL;
+}
+
+void
+set_ctp_and_pair(enum caret_to_point *ctp, int *pair, int caret, int point)
+{
+  *ctp = caret < point ? ctp_before : (caret > point ? ctp_after : ctp_on);
+  *pair = *ctp == ctp_on ? PAIR_HL : PAIR_BW;
+}
+
+void
+draw_judgment(WINDOW *w, int *y, int *x, bool virtual, bool workplace_mode,
+             int caret, int *point, char *inputstr, struct judgment *judgment)
+{
+  bool v, prev_not_displayed;
+  int pair;
+  GSList *l;
+  struct vdecl *decl;
+  enum caret_to_point ctp;
+
+  v = virtual;
+
+  /* Γ */
+  set_ctp_and_pair(&ctp, &pair, caret, (*point)++);
+  if (judgment->generic_l)
+    draw_wrap(w, y, x, v, TRUE, pair, judgment->generic_l);
+  else if (workplace_mode) {
+    if (ctp == ctp_on)
+      draw_slot(w, y, x, v, FALSE, pair, inputstr);
+    else if (ctp == ctp_before)
+      draw_slot(w, y, x, v, TRUE, pair, NULL);
+    else
+      prev_not_displayed = TRUE;
+  }
+  else
+    prev_not_displayed = TRUE;
+
+  /* vdecls */
+  l = judgment->vdecls;
+  while (l) {
+    set_ctp_and_pair(&ctp, &pair, caret, (*point)++);
+    decl = l->data;
+    if (!(l == judgment->vdecls && prev_not_displayed))
+      draw_slot(w, y, x, v, FALSE, PAIR_BW, ", ");
+    if (ctp == ctp_on)
+      draw_slot(w, y, x, v, FALSE, pair, inputstr);
+    else
+      draw_slot(w, y, x, v, FALSE, pair, decl->elem->str);
+    draw_slot(w, y, x, v, FALSE, PAIR_BW, " : ");
+    set_ctp_and_pair(&ctp, &pair, caret, (*point)++);
+    if (ctp == ctp_on)
+      draw_slot(w, y, x, v, FALSE, pair, inputstr);
+    else
+      draw_wrap(w, y, x, v, FALSE, pair, decl->type);
+    prev_not_displayed = FALSE;
+    l = l->next;
+  }
+
+  /* ctx_dots */
+  set_ctp_and_pair(&ctp, &pair, caret, (*point)++);
+  if (workplace_mode && (ctp == ctp_before || ctp == ctp_on)) {
+    if (!prev_not_displayed)
+      draw_slot(w, y, x, v, FALSE, PAIR_BW, ", ");
+    draw_slot(w, y, x, v, FALSE, pair, "...");
+    prev_not_displayed = FALSE;
+  }
+
+  /* Δ */
+  set_ctp_and_pair(&ctp, &pair, caret, (*point)++);
+  if (judgment->generic_r) {
+    if (!prev_not_displayed)
+      draw_slot(w, y, x, v, FALSE, PAIR_BW, ", ");
+    draw_wrap(w, y, x, v, TRUE, pair, judgment->generic_r);
+  }
+  else if (workplace_mode) {
+    if (ctp == ctp_on) {
+      if (!prev_not_displayed)
+       draw_slot(w, y, x, v, FALSE, PAIR_BW, ", ");
+      draw_slot(w, y, x, v, FALSE, pair, inputstr);
+    }
+    else if (ctp == ctp_before) {
+      if (!prev_not_displayed)
+       draw_slot(w, y, x, v, FALSE, PAIR_BW, ", ");
+      draw_slot(w, y, x, v, TRUE, pair, NULL);
+    }
+  }
+
+  draw_slot(w, y, x, v, FALSE, PAIR_BW, " ⊢ ");
+
+  /* thesis */
+  switch (judgment->kind) {
+  case tk_generic:
+    set_ctp_and_pair(&ctp, &pair, caret, (*point)++);
+    if (ctp == ctp_on)
+      draw_slot(w, y, x, v, FALSE, pair, inputstr);
+    else
+      draw_wrap(w, y, x, v, FALSE, pair, judgment->a);
+    break;
+  case tk_type:
+    set_ctp_and_pair(&ctp, &pair, caret, (*point)++);
+    if (ctp == ctp_on)
+      draw_slot(w, y, x, v, FALSE, pair, inputstr);
+    else
+      draw_wrap(w, y, x, v, FALSE, pair, judgment->a);
+    draw_slot(w, y, x, v, FALSE, PAIR_BW, " type");
+    break;
+  case tk_element:
+    set_ctp_and_pair(&ctp, &pair, caret, (*point)++);
+    if (ctp == ctp_on)
+      draw_slot(w, y, x, v, FALSE, pair, inputstr);
+    else
+      draw_wrap(w, y, x, v, FALSE, pair, judgment->a);
+    draw_slot(w, y, x, v, FALSE, PAIR_BW, " : ");
+    set_ctp_and_pair(&ctp, &pair, caret, (*point)++);
+    if (ctp == ctp_on)
+      draw_slot(w, y, x, v, FALSE, pair, inputstr);
+    else
+      draw_wrap(w, y, x, v, FALSE, pair, get_wrap_type(judgment->a));
+    break;
+  case tk_type_equal:
+    set_ctp_and_pair(&ctp, &pair, caret, (*point)++);
+    if (ctp == ctp_on)
+      draw_slot(w, y, x, v, FALSE, pair, inputstr);
+    else
+      draw_wrap(w, y, x, v, FALSE, pair, judgment->a);
+    draw_slot(w, y, x, v, FALSE, PAIR_BW, " ≐ ");
+    set_ctp_and_pair(&ctp, &pair, caret, (*point)++);
+    if (ctp == ctp_on)
+      draw_slot(w, y, x, v, FALSE, pair, inputstr);
+    else
+      draw_wrap(w, y, x, v, FALSE, pair, judgment->b);
+    draw_slot(w, y, x, v, FALSE, PAIR_BW, " type");
+    break;
+  case tk_element_equal:
+    set_ctp_and_pair(&ctp, &pair, caret, (*point)++);
+    if (ctp == ctp_on)
+      draw_slot(w, y, x, v, FALSE, pair, inputstr);
+    else
+      draw_wrap(w, y, x, v, FALSE, pair, judgment->a);
+    draw_slot(w, y, x, v, FALSE, PAIR_BW, " ≐ ");
+    set_ctp_and_pair(&ctp, &pair, caret, (*point)++);
+    if (ctp == ctp_on)
+      draw_slot(w, y, x, v, FALSE, pair, inputstr);
+    else
+      draw_wrap(w, y, x, v, FALSE, pair, judgment->b);
+    draw_slot(w, y, x, v, FALSE, PAIR_BW, " : ");
+    set_ctp_and_pair(&ctp, &pair, caret, (*point)++);
+    if (ctp == ctp_on)
+      draw_slot(w, y, x, v, FALSE, pair, inputstr);
+    else
+      draw_wrap(w, y, x, v, FALSE, pair, get_wrap_type(judgment->a));
+    break;
+  case tk_unknown:
+    set_ctp_and_pair(&ctp, &pair, caret, (*point)++);
+    draw_slot(w, y, x, v, FALSE, pair, NULL);
+    break;
+  default:
+  }
+}
+
+void
+draw_rule(WINDOW *w, int *y, int *x, bool virtual, bool workspace_mode,
+         int caret, char *inputstr, struct rule *rule)
+{
+  int c, r, lw, row = *y, col = *x;
+  int point, pair;
+  enum caret_to_point ctp;
+
+  point = 0;
+  *y = 0; c = 0; r = 0;
+  draw_judgment(w, y, &c, TRUE, workspace_mode,
+               caret, &point, inputstr, rule->conclusion);
+  set_ctp_and_pair(&ctp, &pair, caret, point++);
+  draw_slot(w, y, &r, TRUE, FALSE, pair, ctp == ctp_on ? inputstr : rule->name);
+  
+  lw = c;
+  if (!virtual) {
+    *y = row + 1; *x = col;
+    wmove(w, *y, *x);
+    for (*x = col ; *x < col + lw ; *x = *x + 1)
+      wprintw(w, "=");
+    point = 0;
+    *y = row + 2; *x = col;
+    draw_judgment(w, y, x, virtual, workspace_mode,
+                 caret, &point, inputstr, rule->conclusion);
+    set_ctp_and_pair(&ctp, &pair, caret, point++);
+    *y = row + 1; *x = col + lw + 1;
+    draw_slot(w, y, x, virtual, FALSE, pair, ctp == ctp_on ? inputstr : rule->name);
+  }
+  *y = row + 2; *x = lw;
+}
+
+void
+render_splash()
+{
+  clear();
+  mvprintw(0, 0, "VOUIVRE version 0.2.0");
+  mvprintw(1, 0, "press any key to continue");
+  refresh();
+}
+
+void
+echo_status(WINDOW *w, int status)
+{
+  int pair;
+  char *str;
+
+  if (status)
+    pair = PAIR_ERROR;
+  else
+    pair = PAIR_SUCCESS;
+  switch (status) {
+  case 0:
+    str = "valid";
+  case 1:
+    str = "error: empty slot";
+    break;
+  case 2:
+    str = "error: rule name already exists";
+    break;
+  case 3:
+    str = "error: unknown name";
+    break;
+  case 4:
+    str = "error: left and right generic contexts must differ";
+    break;
+  case 5:
+    str = "error: type already defined";
+    break;
+  case 6:
+    str = "error: inconsistant kinds"; /* TODO: list the two kinds conflicting */
+    break;
+  default:
+    str = "error: TBD";
+  }
+  wattron(w, COLOR_PAIR(pair));
+  mvwprintw(w, 0, 0, str);
+  wattroff(w, COLOR_PAIR(pair));
+}
+
+void
+render(struct state *state)
+{
+  int y, x, q, i;
+  static const char *str1 = "g: generic  t: type  e: element  "
+    "=t: judgmentally equal types  =e: judgmentally equal elements";
+  static const char *str2 = "=t: judgmentally equal types  "
+    "=e: judgmentally equal elements";
+  GHashTableIter iter;
+  GSList *l;
+  gpointer key, value;
+  struct rule *rule;
+
+  q = (LINES - 3) / 2;
+  clear();
+
+  /* draw debug info */
+  mvwprintw(stdscr, LINES - 3, 0, "mode: %d  st: %d  caret: %d",
+           state->mode, state->st, state->caret);
+
+  /* draw workspace rule */
+  y = 0; x = 0;
+  draw_rule(state->workspace_w, &y, &x, FALSE, TRUE,
+           state->caret, state->input->str, state->rule);
+  
+  /* draw existing rules */
+  i = 0;
+  g_hash_table_iter_init(&iter, state->rules);
+  while (g_hash_table_iter_next(&iter, &key, &value))
+    {
+      rule = value;
+      y = i; x = COLS / 2;
+      draw_rule(state->support_w, &y, &x, FALSE, FALSE, -1, NULL, rule);
+      i = y + 2;
+    }
+
+  /* draw support rules */
+  i = 0;
+  l = state->support;
+  while (l)
+    {
+      rule = l->data;
+      y = i; x = 0;
+      draw_rule(state->support_w, &y, &x, FALSE, FALSE, -1, NULL, rule);
+      i = y + 2;
+      l = l->next;
+    }  
+
+  /* draw echo area */
+  if (state->err) {
+    echo_status(state->echo_w, state->err);
+    state->err = 0;
+  }
+  else {
+    switch (state->mode) {
+    case uis_workspace:
+      if (state->st == st_thesis) {
+       if (state->status == 0)
+         mvwprintw(state->echo_w, 0, 0, str1);
+       else
+         mvwprintw(state->echo_w, 0, 0, str2);
+      }
+      else if (state->escape) {
+       mvwprintw(state->echo_w, 0, 0, "\\");
+       wprintw(state->echo_w, state->escaped_input->str);
+      }
+      break;
+    default:
+    }
+  }
+
+  /* draw window names */
+  attron(COLOR_PAIR(PAIR_WB));
+  move(q, 0);
+  printw("workspace%*s", COLS - 9, "");
+  move(2 * q + 1, 0);
+  printw("support%*s", COLS - 7, "");
+  attroff(COLOR_PAIR(PAIR_WB));
+  
+  refresh();
+}
+
+void
+handle_input(struct state *state, char key)
+{
+  GSList *l;
+  struct name *name;
+  
+  if (key == '\\') {
+    state->escape = TRUE;
+    state->status = 0;
+    g_string_erase(state->escaped_input, 0, -1);
+  }
+  else if (key > 32 && key < 127) {
+    if (!state->escape)
+      state->input = g_string_append_c(state->input, key);
+    else {
+      if ((key > 64 && key < 91) || (key > 96 && key < 123))
+       {
+         state->escaped_input = g_string_append_c(state->escaped_input, key);
+         l = g_hash_table_lookup(escaped_table, state->escaped_input->str);
+         if (!l) {
+           state->escape = FALSE;
+         }
+         else if (!l->next) {
+           state->escape = FALSE;
+           state->input = g_string_append(state->input, l->data);
+         }
+       }
+      else
+       state->escape = FALSE;
+    }
+  }
+  if ((name = g_hash_table_lookup(state->decls, state->input->str))) {
+      if (state->support)
+       state->support->data = name->src;
+      else
+       state->support = g_slist_prepend(state->support, name->src);
+    }
+}
+
+bool
+conclusion_p(struct rule *rule, struct judgment *judgment)
+{
+  return judgment == rule->conclusion ? TRUE : FALSE;
+}
+
+int
+validate_generic_context(struct state *state, bool is_left)
+{
+  struct name *name;
+
+  if (*state->input->str == '\0')
+    return 0;
+  if (conclusion_p(state->rule, state->judgment)) {
+    if (!(name = g_hash_table_lookup(state->gctxs, state->input->str)))
+      return 3;
+    if (is_left) {
+      state->judgment->generic_l = create_wrap(FALSE, name);
+    }
+    else {
+      if (state->judgment->generic_l &&
+         name == state->judgment->generic_l->ptr)
+       return 4;
+      state->judgment->generic_r = create_wrap(FALSE, name);
+    }
+    g_string_erase(state->input, 0, -1);
+    return 0;
+  }
+  else
+    return -1;
+}
+
+GSList *
+list_dependencies(GHashTable *vets)
+{
+  GSList *l;
+  GHashTableIter iter;
+  gpointer key, value;
+
+  l = NULL;
+  g_hash_table_iter_init(&iter, vets);
+  while (g_hash_table_iter_next(&iter, &key, &value))
+    l = g_slist_prepend(l, value); /* value = a name */
+  return l;
+}
+
+int
+validate_type_thesis(struct state *state)
+{
+  struct name *name;
+  
+  if (*state->input->str == '\0')
+    return 1;
+  if (conclusion_p(state->rule, state->judgment)) {
+    if ((name = g_hash_table_lookup(state->decls, state->input->str)))
+      return 5;
+    if ((name = g_hash_table_lookup(state->vets, state->input->str))) {
+      if (name->kind != nk_type)
+       return 6;
+      
+      /* the name exists, is a type, and is local */
+      state->judgment->a = create_wrap(FALSE, name);
+      return -1;               /* TODO: check dependencies */
+    }
+    else {
+      name = create_name(s_global, nk_type, NULL, state->rule,
+                        list_dependencies(state->vets),
+                        strdup(state->input->str));
+      state->judgment->a = create_wrap(TRUE, name);
+      g_string_erase(state->input, 0, -1);
+      return 0;
+    }
+  }
+  return -1;
+}
+
+int
+validate_rule_name(struct state *state)
+{
+  struct rule *rule;
+  struct judgment *c;
+  struct name *name;
+
+  rule = state->rule;
+  if (*state->input->str == '\0')
+    return 1;
+  if (g_hash_table_lookup(state->rules, state->input->str))
+    return 2;
+  rule->name = strdup(state->input->str);
+  g_hash_table_insert(state->rules, state->rule->name, rule);
+  c = rule->conclusion;
+  switch (c->kind) {
+  case tk_type:
+    if (c->a->is_origin) {
+      name = c->a->ptr;
+      g_hash_table_insert(state->decls, name->str, name);
+    }
+    break;
+  default:
+  }
+  g_string_erase(state->input, 0, -1);
+  state->rule = create_rule();
+  state->judgment = state->rule->conclusion;
+  return 0;
+}
+
+int
+main()
+{
+  const char *k, *v;
+  char key, *str;
+  int i, j, q;
+  struct state state;
+  struct judgment *j_p;
+  GSList *l;
+  GHashTableIter iter;
+  gpointer hkey, hvalue;
+  
+  setlocale(LC_ALL, "");
+  initscr();
+  start_color();
+  curs_set(0);
+  cbreak();
+  noecho();
+  ESCDELAY = 0;
+  intrflush(stdscr, FALSE);
+  keypad(stdscr, TRUE);
+  init_pair(PAIR_BW, COLOR_BLACK, COLOR_WHITE);
+  init_pair(PAIR_WB, COLOR_WHITE, COLOR_BLACK);
+  init_pair(PAIR_HL, COLOR_CYAN, COLOR_WHITE);
+  init_pair(PAIR_SUCCESS, COLOR_GREEN, COLOR_WHITE);
+  init_pair(PAIR_ERROR, COLOR_RED, COLOR_WHITE);
+  bkgd(COLOR_PAIR(PAIR_BW));
+  q = (LINES - 3) / 2;
+  state.running = TRUE;
+  state.mode = uis_splash;
+  state.rule = create_rule();
+  state.judgment = state.rule->conclusion;
+  state.dependency = NULL;
+  state.workspace_w = subwin(stdscr, q, COLS, 0, 0);
+  state.support_w = subwin(stdscr, q, COLS, q + 1, 0);
+  state.echo_w = subwin(stdscr, 1, COLS, LINES - 1, 0);
+  state.rules = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                     free, (GDestroyNotify)free_rule);
+  state.decls = g_hash_table_new(g_str_hash, g_str_equal);
+  state.gctxs = g_hash_table_new(g_str_hash, g_str_equal);
+  state.gtheses = g_hash_table_new(g_str_hash, g_str_equal);
+  state.vets = g_hash_table_new(g_str_hash, g_str_equal);
+  state.support = NULL;
+  state.caret = 0;
+  state.st = st_ctx_l;
+  state.input = g_string_new(NULL);
+  state.escaped_input = g_string_new(NULL);
+  state.escape = FALSE;
+  state.err = 0;
+  
+  /* populate escaped strings table */
+  /* NOTE: `g_hash_table_insert' deletes keys and values if they already exist.
+           This is not what we want for the value list. Thus, we will free the
+          values manually before destroying the table.
+  */
+  escaped_table = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL);  
+  for (i = 0 ; i < sizeof(escaped_strings) / sizeof(char *) / 2 ; i++) {
+    k = escaped_strings[2 * i + 1];
+    v = escaped_strings[2 * i + 0];
+    for (j = 1 ; j <= strlen(k) ; j++) {
+      str = strndup(k, j);
+      l = g_hash_table_lookup(escaped_table, str);
+      l = g_slist_prepend(l, (void *)v);
+      g_hash_table_insert(escaped_table, str, l);
+    }
+  }
+
+  if (state.mode == uis_splash) {
+    render_splash();
+    getch();
+    state.mode = uis_workspace;
+    render(&state);
+  }
+  while(state.running) {
+    key = getch();
+    if (key == KEY_ESC) {
+      state.running = FALSE;
+      break;
+    }
+    switch(state.mode) {
+    case uis_workspace:
+      switch (state.st) {
+      case st_rule_dots:
+       break;
+      case st_rule_name:
+       if (key == KEY_TAB) {
+         if (!(state.err = validate_rule_name(&state))) {
+           /* reset the workspace to initial state */
+           state.caret = 0;
+           state.st = st_ctx_l;
+           g_slist_free(g_steal_pointer(&state.support));
+         }
+       }
+       else
+         handle_input(&state, key);
+       break;
+      case st_ctx_l:
+       if (key == KEY_TAB) {
+         if (!(state.err = validate_generic_context(&state, TRUE))) {
+           state.caret += 1;
+           state.st = st_ctx_dots;
+         }
+       }
+       else
+         handle_input(&state, key);
+       break;
+      case st_ctx_v:
+       break;
+      case st_ctx_t:
+       break;
+      case st_ctx_dots:
+       if (key == KEY_TAB) {
+         state.caret += 1;
+         state.st = st_ctx_r;
+       }
+       else if (key == KEY_RETURN) {
+       }
+       break;
+      case st_ctx_r:
+       if (key == KEY_TAB) {
+         if (!(state.err = validate_generic_context(&state, FALSE))) {
+           state.caret += 1;
+           state.st = st_thesis;
+           state.status = 0;
+         }
+       }
+       else
+         handle_input(&state, key);
+       break;
+      case st_thesis:
+       j_p = state.rule->conclusion;
+       if (state.status == 0) {
+         switch (key) {
+         case 'g':
+           state.st = st_a;
+           j_p->kind = tk_generic;
+           break;
+         case 't':
+           state.st = st_a;
+           j_p->kind = tk_type;
+           break;
+         case 'e':
+           state.st = st_a;
+           j_p->kind = tk_element;
+           break;
+         case '=':
+           state.status = 1;
+           break;
+         default:
+         }
+       }
+       else if (state.status == 1) {
+         switch (key) {
+         case 't':
+           state.st = st_a;
+           j_p->kind = tk_type_equal;
+           break;
+         case 'e':
+           state.st = st_a;
+           j_p->kind = tk_element_equal;
+           break;
+         default:
+           state.status = 0;
+         }
+       }
+       break;
+      case st_a:
+       if (key == KEY_TAB) {
+         switch (state.judgment->kind) {
+         case tk_type:
+           if (!(state.err = validate_type_thesis(&state))) {
+             state.caret += 1;
+             state.st = st_rule_name;
+           }
+           break;
+         default:
+           state.err = -1;
+         }
+       }
+       else
+         handle_input(&state, key);
+       break;
+      case st_b:
+       if (key == KEY_TAB) {
+         state.caret += 1;
+         if (j_p->kind == tk_element_equal) {
+           state.st = st_c;
+         }
+         else {
+           state.st = st_rule_name;
+         }
+       }
+       else
+         handle_input(&state, key);
+       break;
+      case st_c:
+       if (key == KEY_TAB) {
+         state.caret += 1;
+         state.st = st_rule_name;
+       }
+       else
+         handle_input(&state, key);
+       break;
+      default:
+      }
+      break;
+    default:
+    }
+    render(&state);
+  }
+  delwin(state.workspace_w);
+  delwin(state.support_w);
+  delwin(state.echo_w);
+  endwin();
+  g_hash_table_iter_init(&iter, escaped_table);
+  while (g_hash_table_iter_next(&iter, &hkey, &hvalue))
+    g_slist_free(hvalue);
+  g_hash_table_destroy(escaped_table);
+  free_rule(state.rule);
+  g_hash_table_destroy(state.rules);
+  g_hash_table_destroy(state.decls);
+  g_hash_table_destroy(state.gctxs);
+  g_hash_table_destroy(state.gtheses);
+  g_hash_table_destroy(state.vets);
+  g_slist_free(state.support);
+  g_string_free(state.input, TRUE);
+  g_string_free(state.escaped_input, TRUE);
+  return 0;
+}