Add syntax help menu and cli option

This commit is contained in:
Alexander Rosenberg 2024-09-16 02:38:09 -07:00
parent 627c62772b
commit 6f8135238b
Signed by: Zander671
GPG Key ID: 5FD0394ADBD72730
5 changed files with 496 additions and 151 deletions

View File

@ -7,6 +7,11 @@
#:delim-p #:delim-p
#:symbol-char-p #:symbol-char-p
#:proposition-parse-error #:proposition-parse-error
#:parse-error-position
#:parse-error-proposition
#:parse-error-message
#:*operator-symbol-table*
#:*operator-descriptions*
#:operator-symbol #:operator-symbol
#:operator-precedence #:operator-precedence
#:interpret-operand #:interpret-operand
@ -45,6 +50,7 @@
#:convert-truth-table-to-html #:convert-truth-table-to-html
#:*table-border-ascii-alist* #:*table-border-ascii-alist*
#:*table-border-unicode-alist* #:*table-border-unicode-alist*
#:with-draw-table
#:typeset-truth-table #:typeset-truth-table
#:*known-formats* #:*known-formats*
#:typeset-table-to-format)) #:typeset-table-to-format))

View File

@ -50,8 +50,80 @@ functions involved in evaluating and typesetting."
:pretty-print pretty-print :pretty-print pretty-print
:latin-truths latin-truths))))) :latin-truths latin-truths)))))
(defun word-wrap-string (string &optional (cols 80))
(with-output-to-string (str)
(loop with word = ()
with word-len = 0
with cur-col = 0
for char across string
when (whitespace-p char) do
(if (>= (+ cur-col word-len 1) cols)
(progn
(terpri str)
(setq cur-col 0))
(unless (zerop cur-col)
(format str " ")
(incf cur-col)))
(format str "~{~c~}" (nreverse word))
(setq word nil
cur-col (+ cur-col word-len)
word-len 0)
else do
(push char word)
(incf word-len)
finally
(if (>= (+ cur-col word-len 1) cols)
(terpri str)
(format str " "))
(format str "~{~c~}" (nreverse word)))))
(defun ascii-string-p (str)
"Return true if STR is only ASCII characters."
(loop for char across str
unless (<= (char-code char) 127)
do (return-from ascii-string-p))
t)
(defun print-syntax-help (ascii-only)
"Print the syntax help message."
(loop
for ((sym (name . nicks) desc examples) . rest-desc)
= *operator-descriptions* then rest-desc
for ((_sym . syntax) . rest-st) = *operator-symbol-table* then rest-st
for syntax-str = (format nil "~{~a~^, ~}"
(sort (copy-list
(if ascii-only
(remove-if-not 'ascii-string-p
syntax)
syntax))
'string<))
while sym
maximize (length name) into name-col-len
maximize (length syntax-str) into syntax-col-len
collect syntax-str into syntax-entries
finally
(let ((col-widths (list name-col-len syntax-col-len))
(box-lookup-table (if ascii-only
*table-border-ascii-alist*
*table-border-unicode-alist*)))
(with-draw-table (t col-widths box-lookup-table
:padding 1 :align :left)
(:row (list "Operator" "Syntax"))
(:seperator)
(loop for (sym (name . nicks) desct) in *operator-descriptions*
for syntax-str in syntax-entries do
(:row (list name syntax-str))))))
(format t "~%~%~a~%Example:~% abc|d = ~a~%"
(word-wrap-string "Two operands next to each other is treated as an
'implicit and' (unless this feature is disabled).")
(typeset-proposition '(or (and "a" "b" "c") "d")
:lookup-table (if ascii-only
*operator-ascii-lookup-alist*
*operator-unicode-lookup-alist*))))
(defparameter *command-line-spec* (defparameter *command-line-spec*
'((#\h "help" help nil "print this message, then exit") '((#\h "help" help nil "print this message, then exit")
(nil "syntax-help" syntax-help nil "print a syntax help message, then exit")
(#\f "format" format t (#\f "format" format t
"specify the output format (*unicode*, ascii, latex, or html)") "specify the output format (*unicode*, ascii, latex, or html)")
(#\s "subexps" subexps nil "include sub-expressions in the output table") (#\s "subexps" subexps nil "include sub-expressions in the output table")
@ -85,6 +157,20 @@ arguments."
(print-usage t *command-line-spec* "truth-table" (print-usage t *command-line-spec* "truth-table"
:general-args "<propositions...>") :general-args "<propositions...>")
(uiop:quit (if cmdline-error 1 0))) (uiop:quit (if cmdline-error 1 0)))
((and (not cmdline-error) ;; if option parsing failed, error out
(option-value 'syntax-help opts))
(let ((format (option-value 'format opts)))
(cond
((or (not format)
(equal format "unicode"))
(print-syntax-help nil))
((equal format "ascii")
(print-syntax-help t))
(t
(cerror "Exit without printing anything" 'command-line-error
:message (format nil "The syntax help table is only ~
available in ASCII or Unicode.")))))
(uiop:quit))
((null prop-strs) ((null prop-strs)
(cerror *cli-parse-continue-string* 'no-input-error)) (cerror *cli-parse-continue-string* 'no-input-error))
(cmdline-error (cmdline-error
@ -96,12 +182,12 @@ arguments."
(setq format "unicode")) (setq format "unicode"))
(princ (eval-and-typeset-propositions (princ (eval-and-typeset-propositions
prop-strs :format format prop-strs :format format
:implicit-and (not (option-value 'no-implicit opts)) :implicit-and (not (option-value 'no-implicit opts))
:multi-char-names (option-value 'multi-char opts) :multi-char-names (option-value 'multi-char opts)
:include-vars (not (option-value 'no-vars opts)) :include-vars (not (option-value 'no-vars opts))
:include-intermediate (option-value 'subexps opts) :include-intermediate (option-value 'subexps opts)
:pretty-print (option-value 'pretty opts) :pretty-print (option-value 'pretty opts)
:latin-truths (option-value 'latin opts))) :latin-truths (option-value 'latin opts)))
(terpri)))))))) (terpri))))))))
(defun toplevel () (defun toplevel ()

View File

@ -86,6 +86,77 @@ proposition."))
(iff "<->" "<>" "<=>" "⇔" "↔" "≡" "iff" "=" "==" "xnor" "⊙")) (iff "<->" "<>" "<=>" "⇔" "↔" "≡" "iff" "=" "==" "xnor" "⊙"))
"Alist table of operator symbols and their possible string representations.") "Alist table of operator symbols and their possible string representations.")
(defparameter *operator-descriptions* ;; noindent 30
`((open-paren ("open parenthesis")
,(format nil "Used in combination with a close parenthesis to denote ~
some terms to be evaluated before the surrounding terms.")
(((and (or true true) false) . false)
((or true (and true false)) . true)))
(close-paren ("close parenthesis")
"Used to close a group started with an open parenthesis."
()) ;; no examples for closed paren
(and ("and" "conjunction")
,(format nil "Evaluate to true only if the expressions to the left and ~
right evaluate to true.")
(((and true true) . true)
((and true false) . false)))
(nand ("nand" "non-conjunction")
,(format nil "Evaluate to true unless the expressions to the left and ~
right are both true. This is the negation of the 'and' operator.")
(((nand true true) . false)
((nand true false) . true)
((nand false false) . true)))
(or ("or" "disjunction")
,(format nil "Evaluate to true if the expression to the left is true, the ~
expression to the right is true, or both the left and right expressions are ~
true.")
(((or true true) . true)
((or true false) . true)
((or false false) . false)))
(nor ("nor" "non-disjunction")
,(format nil "Evaluate to true if the expressions to the left and right ~
are both false. This is the negation of the 'or' operator.")
(((nor true true) . false)
((nor true false) . false)
((nor false false) . true)))
(xor ("exclusive or" "exclusive disjunction") ;; noindent 30
,(format nil "Evaluate to true if the expression to the left is true or ~
if the expression to the right is true, but not if both of them are true.")
(((xor true true) . false)
((xor true false) . true)))
(not ("not" "negation")
,(format nil "Evaluate to false if the expression to the left evaluates ~
to true, and evaluate to true if the expression to the left evaluates to ~
false. This is a unary operator (it only applies to the expression following ~
it).")
(((not true) . false)
((not false) . true)))
(implies ("implies" "conditional")
,(format nil "Evaluate to false if the expression to the left evaluates ~
to true and the expressions to the right evaluates to false. Otherwise, ~
evaluate to true.")
(((implies true true) . true)
((implies true false) . false)
((implies false true) . true)
((implies false false) . true)))
(converse ("converse")
,(format nil "Evaluate to false if the expression to the right evaluates ~
to true and the expression to the left evaluates to false. Otherwise, evaluate ~
to true. This is the 'implies' operator with its arguments flipped.")
(((implies true true) . true)
((implies true false) . true)
((implies false true) . false)
((implies false false) . true)))
(iff ("biconditional" "equivalent")
,(format nil "Evaluate to true if the expressions to the left and rigt ~
evaluate to the same value. That is, they are both true or both false.")
(((iff true true) . true)
((iff true false) . false)
((iff false false) . true))))
"Alist table of operator symbols and their descriptions. The format of this
list is SYMBOL NAMES DESCRIPTION (&rest (EXAMPLE LEFT . EXAMPLE RIGHT)). These
are useful for use in things like syntax explanation messages.")
(defun operator-symbol (oper-str) (defun operator-symbol (oper-str)
"Return the symbol for OPER-STR, or nil if it is not a know operator." "Return the symbol for OPER-STR, or nil if it is not a know operator."
(loop for (oper-sym . strs) in *operator-symbol-table* (loop for (oper-sym . strs) in *operator-symbol-table*

View File

@ -318,7 +318,8 @@ between each row."
(bottom-right . #\┘)) (bottom-right . #\┘))
"Characters used to draw Unicode table borders.") "Characters used to draw Unicode table borders.")
(defun typeset-table-break (stream lengths horiz start column end) (defun typeset-table-break (stream lengths horiz start column end
&key (left-pad-len 0) (right-pad-len 0))
"Typeset the first row, the last row, or a break to STREAM. The proper box "Typeset the first row, the last row, or a break to STREAM. The proper box
character will be placed at each intersection. LENGTHS is a list of column character will be placed at each intersection. LENGTHS is a list of column
lengths. HORIZ, START, COLUMN, and END are the box characters to use when lengths. HORIZ, START, COLUMN, and END are the box characters to use when
@ -328,20 +329,78 @@ drawing."
while length while length
do do
(format stream "~a" (format stream "~a"
(make-string length :initial-element horiz)) (make-string (+ left-pad-len length right-pad-len)
:initial-element horiz))
when rest do when rest do
(format stream "~c" column)) (format stream "~c" column))
(format stream "~c" end)) (format stream "~c" end))
(defun typeset-table-row (stream lengths row vert) (defun typeset-table-row (stream lengths row vert
&key (align :center) (left-pad-str "")
(right-pad-str ""))
"Typeset ROW to STREAM. VERT is the vertical separator. LENGTHS should be the "Typeset ROW to STREAM. VERT is the vertical separator. LENGTHS should be the
length of each column." length of each column."
(loop for col in row (loop with format = (case align
(:right
"~c~a~v:<~a~>~a")
(:left
"~c~a~v@<~a~>~a")
(t ;; :center
"~c~a~v:@<~a~>~a"))
for col in row
for length in lengths for length in lengths
do do
(format stream "~c~v:@<~a~>" vert length col)) (format stream format
vert left-pad-str length col right-pad-str))
(format stream "~c" vert)) (format stream "~c" vert))
(defmacro with-draw-table ((stream col-widths lookup-table
&key (padding 0) (align :center))
&body body)
"Execute BODY with the function \=:seperator and \=:row bound. STREAM is the
stream to write the table to. COL-WIDTHS is a list of column
widths. LOOKUP-TABLE is the table to use to lookup characters for the table
border. PADDING is the number to spaces to both append and prepend to each table
cell. ALIGN is one of \=:right, \=:center, or \=:left."
(let ((pad-str-var (gensym)))
`(let ((,pad-str-var (make-string ,padding :initial-element #\space)))
(truth-table/base::typeset-table-break
,stream ,col-widths
(cdr (assoc 'horizontal ,lookup-table))
(cdr (assoc 'top-left ,lookup-table))
(cdr (assoc 'down ,lookup-table))
(cdr (assoc 'top-right ,lookup-table))
:right-pad-len ,padding
:left-pad-len ,padding)
(format ,stream "~%")
(flet ((:seperator ()
(truth-table/base::typeset-table-break
,stream ,col-widths
(cdr (assoc 'horizontal ,lookup-table))
(cdr (assoc 'right ,lookup-table))
(cdr (assoc 'cross ,lookup-table))
(cdr (assoc 'left ,lookup-table))
:right-pad-len ,padding
:left-pad-len ,padding)
(format ,stream "~%"))
(:row (row)
(truth-table/base::typeset-table-row
,stream ,col-widths row
(cdr (assoc 'vertical ,lookup-table))
:align ,align
:left-pad-str ,pad-str-var
:right-pad-str ,pad-str-var)
(format ,stream "~%")))
,@body)
(truth-table/base::typeset-table-break
,stream ,col-widths
(cdr (assoc 'horizontal ,lookup-table))
(cdr (assoc 'bottom-left ,lookup-table))
(cdr (assoc 'up ,lookup-table))
(cdr (assoc 'bottom-right ,lookup-table))
:right-pad-len ,padding
:left-pad-len ,padding))))
(defun typeset-truth-table (table &optional (defun typeset-truth-table (table &optional
(expr-lookup-table (expr-lookup-table
*operator-ascii-lookup-alist*) *operator-ascii-lookup-alist*)
@ -355,47 +414,28 @@ between each row."
(let* ((typeset-exprs (mapcar (lambda (expr) (let* ((typeset-exprs (mapcar (lambda (expr)
(typeset-proposition (typeset-proposition
expr :lookup-table expr-lookup-table expr :lookup-table expr-lookup-table
:latin-truths latin-truths)) :latin-truths latin-truths))
(extract-truth-table-expressions table))) (extract-truth-table-expressions table)))
(col-widths (mapcar (lambda (expr) (col-widths (mapcar (lambda (expr)
(+ (length expr) 2)) (+ (length expr) 2))
typeset-exprs))) typeset-exprs)))
(with-output-to-string (str) (with-output-to-string (str)
(typeset-table-break str col-widths (with-draw-table (str col-widths box-lookup-table)
(cdr (assoc 'horizontal box-lookup-table)) (:row typeset-exprs)
(cdr (assoc 'top-left box-lookup-table)) (:seperator)
(cdr (assoc 'down box-lookup-table)) (dolist (row (extract-truth-table-values table))
(cdr (assoc 'top-right box-lookup-table))) (:row (mapcar
(terpri str) (lambda (entry)
(typeset-table-row str col-widths typeset-exprs (cdr (assoc
(cdr (assoc 'vertical box-lookup-table))) (if entry
(terpri str) (if latin-truths
(typeset-table-break str col-widths 'latin-true
(cdr (assoc 'horizontal box-lookup-table)) 'true)
(cdr (assoc 'right box-lookup-table)) (if latin-truths
(cdr (assoc 'cross box-lookup-table)) 'latin-false
(cdr (assoc 'left box-lookup-table))) 'false))
(terpri str) expr-lookup-table)))
(dolist (row (extract-truth-table-values table)) row)))))))
(typeset-table-row str col-widths
;; convert t or nil to strings
(mapcar (lambda (entry)
(cdr (assoc (if entry
(if latin-truths
'latin-true
'true)
(if latin-truths
'latin-false
'false))
expr-lookup-table)))
row)
(cdr (assoc 'vertical box-lookup-table)))
(terpri str))
(typeset-table-break str col-widths
(cdr (assoc 'horizontal box-lookup-table))
(cdr (assoc 'bottom-left box-lookup-table))
(cdr (assoc 'up box-lookup-table))
(cdr (assoc 'bottom-right box-lookup-table))))))
(defparameter *known-formats* (defparameter *known-formats*
'("unicode" "ascii" "latex" "html") '("unicode" "ascii" "latex" "html")

350
web.lisp
View File

@ -38,6 +38,94 @@
(defparameter *default-address* "127.0.0.1") (defparameter *default-address* "127.0.0.1")
(defparameter *default-prefix* "/") (defparameter *default-prefix* "/")
(defwidget help-overlay ()
()
(:documentation "Simple class to handle holding the help overlay."))
(defmethod render ((overlay help-overlay))
(with-html
(:div :id "help-table-wrapper"
(:div :id "help-header-wrapper"
(:span :id "help-header" "Help")
(:span :id "help-close-button"
:onclick "document.querySelector(\".help-overlay\").style.display = \"none\""
"Close"))
(:table
(:tr (:th "Operator") (:th "Syntax"))
(loop for ((sym (name . nics) desc (examples)) . rest-desc)
= *operator-descriptions* then rest-desc
for ((_sym . syntax) . rest-st)
= *operator-symbol-table* then rest-st
while sym
do
(:tr
(:td name)
(:td (format nil "~{~a~^, ~}" (sort (copy-list syntax)
'string<))))))
(:p "You can input multiple propositions by separating them with"
"commas (,):"
(:br)
(:code "ab,cd"))
(:p "Two operands next to each other is treated as an 'implicit and'"
"(unless this feature is disabled):"
(:br)
(:code (:raw "abc|d = a &and; b &and; c &or; d"))))))
(defmethod get-dependencies ((overlay help-overlay))
(append
(list
(reblocks-lass:make-dependency
'(.help-overlay
:display "none"
:position "fixed"
:top "0px"
:left "0px"
:width "100%"
:height "100%"
:z-index "100" ;; be above EVERYTHING
(|#help-table-wrapper|
:background "#ffffff"
:border-width "2px"
:border-style "solid"
:border-color "black"
:padding "10px"
:width "fit-content"
:height "fit-content"
:position "fixed"
:top "40%"
:left "50%"
:transform translate "-50%" "-50%"
(|#help-header-wrapper|
:margin-bottom "3px"
:position "relative"
(|#help-header|
:font-size "x-large"
:font-weight "bold"
:display "block"
:text-align "center")
(|#help-close-button|
:user-select "none"
:text-decoration-line "underline"
:cursor "pointer"
:position "absolute"
:top "0"
:right "0"))
(table
:border-collapse "collapse"
:border-spacing "0px"
:margin "auto"
((:or th td)
:padding "3px"
:padding-left "10px"
:padding-right "10px"
:text-align "left"
:border-style "solid"
:border-width "1px"
:border-color "black"))
(code
:padding-left "1em")))))
(call-next-method)))
(defwidget truth-table () (defwidget truth-table ()
((data :initform nil ((data :initform nil
:accessor truth-table-data) :accessor truth-table-data)
@ -77,24 +165,24 @@ reblocks bug.")
(with-html (with-html
(:div :class "label" "Output:") (:div :class "label" "Output:")
(:span :id "output-span" (:span :id "output-span"
;; there seems to be a bug in reblocks that means you have to pass ;; there seems to be a bug in reblocks that means you have to pass
;; the second argument to `make-js-action' ;; the second argument to `make-js-action'
(:button :onclick (make-js-action (:button :onclick (make-js-action
(lambda (&key &allow-other-keys) (lambda (&key &allow-other-keys)
(truth-table-toggle-output table) (truth-table-toggle-output table)
(update table)) (update table))
:args *blank-hash-table*) :args *blank-hash-table*)
:id "output-expander-button" :id "output-expander-button"
(if output-visible (if output-visible
"⏷" "⏷"
"⏵")) "⏵"))
(if (or pretty-print (if (or pretty-print
(equal format "ascii") (equal format "ascii")
(equal format "unicode")) (equal format "unicode"))
(:pre :id "output-area" :hidden (not output-visible) (:pre :id "output-area" :hidden (not output-visible)
other-text) other-text)
(:code :id "output-area" :hidden (not output-visible) (:code :id "output-area" :hidden (not output-visible)
other-text))) other-text)))
(:div :class "label" "HTML Preview:")) (:div :class "label" "HTML Preview:"))
(princ html-text reblocks/html:*stream*)))) (princ html-text reblocks/html:*stream*))))
nil) nil)
@ -175,9 +263,53 @@ reblocks bug.")
((table :initform (make-instance 'truth-table) ((table :initform (make-instance 'truth-table)
:accessor page-table) :accessor page-table)
(error-box :initform (make-instance 'error-box) (error-box :initform (make-instance 'error-box)
:accessor page-error-box)) :accessor page-error-box)
(help-overlay :initform (make-instance 'help-overlay)
:accessor page-help-overlay))
(:documentation "The root of the whole page")) (:documentation "The root of the whole page"))
(defun parse-and-eval-propositions (input-str &key implicit-and
multi-char-names
include-vars
include-intermediate)
"Parse and then eval all of comma separated props in INPUT-STR."
(let ((prop-start 0))
(handler-case
(loop
for prop-str in (uiop:split-string input-str :separator '(#\,))
for (parsed-exp vars) = (multiple-value-list
(parse-proposition-string
prop-str
:implicit-and implicit-and
:multi-char-names multi-char-names))
when parsed-exp
append vars into all-vars
and
collect parsed-exp into parsed-exps
and
do (incf prop-start (1+ (length prop-str)))
finally
(return (create-combined-truth-table
parsed-exps
(remove-duplicates all-vars :test 'equal
:from-end t)
:include-intermediate include-intermediate
:include-vars include-vars)))
(proposition-parse-error (e)
;; adjust the position and proposition string
(error 'proposition-parse-error
:message (parse-error-message e)
:proposition input-str
:position (+ (parse-error-position e)
prop-start))))))
(defun blank-prop-string-p (str)
"Return t if STR would produce a blank proposition table."
(not (find-if-not (lambda (c)
(or (eq c #\,)
(whitespace-p c)))
str)))
(defmethod handle-generate-request ((page page) (defmethod handle-generate-request ((page page)
&key prop-str implicit-and multi-char-names &key prop-str implicit-and multi-char-names
format include-vars subexps latin pretty) format include-vars subexps latin pretty)
@ -185,19 +317,17 @@ reblocks bug.")
(with-slots (table error-box) page (with-slots (table error-box) page
(setf (truth-table-format table) format (setf (truth-table-format table) format
(error-box-message error-box) nil) (error-box-message error-box) nil)
(if (not (zerop (length prop-str))) (if (not (blank-prop-string-p prop-str))
(handler-case (handler-case
(multiple-value-bind (parsed-exp vars) (setf (truth-table-data table)
(parse-proposition-string (parse-and-eval-propositions
prop-str prop-str
:implicit-and implicit-and :implicit-and implicit-and
:multi-char-names multi-char-names) :multi-char-names multi-char-names
(setf (truth-table-data table) (create-truth-table parsed-exp :include-vars include-vars
:vars vars :include-intermediate subexps)
:include-vars include-vars (truth-table-latin-truths table) latin
:include-intermediate subexps) (truth-table-pretty-print table) pretty)
(truth-table-latin-truths table) latin
(truth-table-pretty-print table) pretty))
((or proposition-parse-error proposition-eval-error) (e) ((or proposition-parse-error proposition-eval-error) (e)
(setf (error-box-message error-box) (princ-to-string e)))) (setf (error-box-message error-box) (princ-to-string e))))
(setf (truth-table-data table) nil)) (setf (truth-table-data table) nil))
@ -207,76 +337,86 @@ reblocks bug.")
(defmethod render ((page page)) (defmethod render ((page page))
"Render PAGE." "Render PAGE."
(with-html (with-html
(with-slots (table error-box) page (:doctype)
(:h1 "Truth Table Generator") (:html
(with-html-form (:POST (lambda (&key prop-str implicit-and (:head
multi-char-names format (:title "Truth Table Generator"))
include-vars subexps latin (:body
pretty (with-slots (table error-box help-overlay) page
&allow-other-keys) (render help-overlay)
(handle-generate-request (:h1 "Truth Table Generator")
page :prop-str prop-str (with-html-form (:POST (lambda (&key prop-str implicit-and
:implicit-and implicit-and multi-char-names format
:multi-char-names multi-char-names include-vars subexps latin
:format format pretty
:include-vars include-vars &allow-other-keys)
:subexps subexps (handle-generate-request
:pretty pretty page :prop-str prop-str
:latin latin))) :implicit-and implicit-and
(:div :id "main-controls-wrapper" :multi-char-names multi-char-names
(:input :id "prop-input-field" :format format
:type "text" :include-vars include-vars
:name "prop-str" :subexps subexps
:placeholder "Proposition string...") :pretty pretty
(:input :id "submit-button" :latin latin)))
:type "submit" (:div :id "main-controls-wrapper"
:value "Generate")) (:input :id "prop-input-field"
(:div :id "extra-controls-wrapper" :type "text"
(:input :type "checkbox" :name "prop-str"
:name "implicit-and" :placeholder "Proposition string...")
:checked t) (:input :id "submit-button"
(:label :for "implicit-and" "Implicit And") :type "submit"
(:input :type "checkbox" :value "Generate")
:name "multi-char-names" (:button :id "help-button"
:style "margin-left: 10px;") :onclick
(:label :for "multi-char-names" "Multi-character Variables") "document.querySelector(\".help-overlay\").style.display = \"initial\""
(:input :type "checkbox" "Help"))
:name "include-vars" (:div :id "extra-controls-wrapper"
:checked t (:input :type "checkbox"
:style "margin-left: 10px;") :name "implicit-and"
(:label :for "include-vars" "Include Variables") :checked t)
(:input :type "checkbox" (:label :for "implicit-and" "Implicit And")
:name "subexps" (:input :type "checkbox"
:checked t :name "multi-char-names"
:style "margin-left: 10px;") :style "margin-left: 10px;")
(:label :for "subexps" "Include Sub-expressions") (:label :for "multi-char-names" "Multi-character Variables")
(:input :type "checkbox" (:input :type "checkbox"
:name "pretty" :name "include-vars"
:checked t :checked t
:style "margin-left: 10px;") :style "margin-left: 10px;")
(:label :for "pretty" "Pretty Print") (:label :for "include-vars" "Include Variables")
(:input :type "checkbox" (:input :type "checkbox"
:name "latin" :name "subexps"
:checked nil :checked t
:style "margin-left: 10px;") :style "margin-left: 10px;")
(:label :for "latin" "Latin Truth Values") (:label :for "subexps" "Include Sub-expressions")
(:select :name "format" :style "margin-left: 10px;" (:input :type "checkbox"
(:option :value "html" "HTML") :name "pretty"
(:option :value "latex" "LaTeX") :checked t
(:option :value "ascii" "ASCII") :style "margin-left: 10px;")
(:option :value "unicode" "Unicode")))) (:label :for "pretty" "Pretty Print")
(render error-box) (:input :type "checkbox"
(render table) :name "latin"
(:div :id "info-text" :checked nil
(:span :style "margin-left: 10px;")
"This website is free software under the terms of the AGPL" (:label :for "latin" "Latin Truth Values")
"license version 3. You can find a copy of the license ") (:select :name "format" :style "margin-left: 10px;"
(:a :href "https://www.gnu.org/licenses/agpl-3.0.html" (:option :value "html" "HTML")
"here") (:option :value "latex" "LaTeX")
(:span ". You can find the source of this website ") (:option :value "ascii" "ASCII")
(:a :href "https://git.zander.im/Zander671/truth-table" (:option :value "unicode" "Unicode"))))
"here") (render error-box)
(:span "."))))) (render table)
(:div :id "info-text"
(:span
"This website is free software under the terms of the AGPL"
"license version 3. You can find a copy of the license ")
(:a :href "https://www.gnu.org/licenses/agpl-3.0.html"
"here")
(:span ". You can find the source of this website ")
(:a :href "https://git.zander.im/Zander671/truth-table"
"here")
(:span ".")))))))
(defmethod get-dependencies ((page page)) (defmethod get-dependencies ((page page))
(append (append
@ -290,13 +430,15 @@ reblocks bug.")
:margin-bottom "5px" :margin-bottom "5px"
(|#main-controls-wrapper| (|#main-controls-wrapper|
:display flex :display flex
:margin-bottom "5px" :margin-bottom "5px"
(|#prop-input-field| (|#prop-input-field|
:flex-grow "1" :flex-grow "1"
:margin-right "5px" :margin-right "5px"
:font-size "large") :font-size "large")
(|#submit-button| ((:or |#submit-button| |#help-button|)
:font-size "large")) :font-size "large")
(|#help-button|
:margin-left "5px"))
(|#extra-controls-wrapper| (|#extra-controls-wrapper|
:display "flex" :display "flex"
:justify-content "center" :justify-content "center"