File cleanup
parent
ac08c5be59
commit
231c873b1c
23
README.md
23
README.md
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|
||||||
Type lambda expressions into the prompt, and Lamb will evaluate them. \
|
Type lambda expressions into the prompt, and Lamb will evaluate them. \
|
||||||
Use your `\` (backslash) key to type a `λ`. \
|
Use your `\` (backslash) key to type a `λ`. \
|
||||||
To define macros, use `=`. For example,
|
To define macros, use `=`. For example,
|
||||||
|
@ -30,22 +31,30 @@ To define macros, use `=`. For example,
|
||||||
|
|
||||||
Note that there are spaces in `λa.a F T`. With no spaces, `aFT` will be parsed as one variable. \
|
Note that there are spaces in `λa.a F T`. With no spaces, `aFT` will be parsed as one variable. \
|
||||||
Lambda functions can only take single-letter, lowercase arguments. `λA.A` is not valid syntax. \
|
Lambda functions can only take single-letter, lowercase arguments. `λA.A` is not valid syntax. \
|
||||||
Unbound variables (upper and lower case) that aren't macros will become free variables. Free variables will be shown with a `'`, like `a'`.
|
Free variables will be shown with a `'`, like `a'`.
|
||||||
|
|
||||||
Be careful, macros are case-sensitive. If you define a macro `MAC` and accidentally write `mac` in the prompt, `mac` will become a free variable.
|
Macros are case-sensitive. If you define a macro `MAC` and accidentally write `mac` in the prompt, `mac` will become a free variable.
|
||||||
|
|
||||||
Numbers will automatically be converted to Church numerals. For example, the following line will reduce to `T`.
|
Numbers will automatically be converted to Church numerals. For example, the following line will reduce to `T`.
|
||||||
```
|
```
|
||||||
==> 3 NOT F
|
==> 3 NOT F
|
||||||
```
|
```
|
||||||
|
|
||||||
If an expression takes too long to evaluate, you may interrupt reduction with `Ctrl-C`.
|
If an expression takes too long to evaluate, you may interrupt reduction with `Ctrl-C`. \
|
||||||
|
Exit the prompt with `Ctrl-C` or `Ctrl-D`.
|
||||||
|
|
||||||
|
There are many useful macros in [macros.lamb](./macros.lamb). Load them with the `:load` command:
|
||||||
|
```
|
||||||
|
==> :load macros.lamb
|
||||||
|
```
|
||||||
|
|
||||||
|
Have fun!
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
Lamb comes with a few commands. Prefix them with a `:`
|
Lamb understands many commands. Prefix them with a `:` in the prompt.
|
||||||
|
|
||||||
`:help` Prints a help message
|
`:help` Prints a help message
|
||||||
|
|
||||||
|
@ -59,8 +68,10 @@ Lamb comes with a few commands. Prefix them with a `:`
|
||||||
|
|
||||||
`:clearmacros` Delete all macros
|
`:clearmacros` Delete all macros
|
||||||
|
|
||||||
`:save [filename]`\
|
`:save [filename]` \
|
||||||
`:load [filename]` Save or load the current environment to a file. The lines in a file look exactly the same as regular entries in the prompt, but must only contain macro definitions.
|
`:load [filename]` \
|
||||||
|
Save or load macros from a file.
|
||||||
|
The lines in a file look exactly the same as regular entries in the prompt, but can only contain macro definitions. See [macros.lamb](./macros.lamb) for an example.
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -1,66 +1,29 @@
|
||||||
|
if __name__ != "__main__":
|
||||||
|
raise ImportError("lamb.__main__ should never be imported. Run it directly.")
|
||||||
|
|
||||||
from prompt_toolkit import PromptSession
|
from prompt_toolkit import PromptSession
|
||||||
from prompt_toolkit.completion import WordCompleter
|
|
||||||
from prompt_toolkit import print_formatted_text as printf
|
from prompt_toolkit import print_formatted_text as printf
|
||||||
from prompt_toolkit.formatted_text import FormattedText
|
from prompt_toolkit.formatted_text import FormattedText
|
||||||
from prompt_toolkit.formatted_text import to_plain_text
|
from prompt_toolkit.formatted_text import to_plain_text
|
||||||
from prompt_toolkit.key_binding import KeyBindings
|
|
||||||
from prompt_toolkit.lexers import Lexer
|
|
||||||
from pyparsing import exceptions as ppx
|
from pyparsing import exceptions as ppx
|
||||||
|
|
||||||
import lamb
|
import lamb
|
||||||
|
|
||||||
|
|
||||||
# Simple lexer for highlighting.
|
|
||||||
# Improve this later.
|
|
||||||
class LambdaLexer(Lexer):
|
|
||||||
def lex_document(self, document):
|
|
||||||
def inner(line_no):
|
|
||||||
return [("class:text", str(document.lines[line_no]))]
|
|
||||||
return inner
|
|
||||||
|
|
||||||
|
|
||||||
lamb.utils.show_greeting()
|
lamb.utils.show_greeting()
|
||||||
|
|
||||||
|
|
||||||
# Replace "\" with pretty "λ"s
|
|
||||||
bindings = KeyBindings()
|
|
||||||
@bindings.add("\\")
|
|
||||||
def _(event):
|
|
||||||
event.current_buffer.insert_text("λ")
|
|
||||||
|
|
||||||
|
|
||||||
r = lamb.Runner(
|
r = lamb.Runner(
|
||||||
prompt_session = PromptSession(
|
prompt_session = PromptSession(
|
||||||
style = lamb.utils.style,
|
style = lamb.utils.style,
|
||||||
lexer = LambdaLexer(),
|
lexer = lamb.utils.LambdaLexer(),
|
||||||
key_bindings = bindings
|
key_bindings = lamb.utils.bindings
|
||||||
),
|
),
|
||||||
prompt_message = FormattedText([
|
prompt_message = FormattedText([
|
||||||
("class:prompt", "==> ")
|
("class:prompt", "==> ")
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
r.run_lines([
|
|
||||||
"T = λab.a",
|
|
||||||
"F = λab.b",
|
|
||||||
"NOT = λa.(a F T)",
|
|
||||||
"AND = λab.(a b F)",
|
|
||||||
"OR = λab.(a T b)",
|
|
||||||
"XOR = λab.(a (NOT b) b)",
|
|
||||||
"M = λx.(x x)",
|
|
||||||
"W = M M",
|
|
||||||
"Y = λf.( (λx.(f (x x))) (λx.(f (x x))) )",
|
|
||||||
"PAIR = λabi.( i a b )",
|
|
||||||
"S = λnfa.(f (n f a))",
|
|
||||||
"Z = λn.n (λa.F) T",
|
|
||||||
"MULT = λnmf.n (m f)",
|
|
||||||
"H = λp.((PAIR (p F)) (S (p F)))",
|
|
||||||
"D = λn.n H (PAIR 0 0) T",
|
|
||||||
"FAC = λyn.(Z n)(1)(MULT n (y (D n)))"
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
i = r.prompt()
|
i = r.prompt()
|
||||||
|
|
|
@ -101,7 +101,14 @@ def cmd_load(command, runner):
|
||||||
lines = [x.strip() for x in f.readlines()]
|
lines = [x.strip() for x in f.readlines()]
|
||||||
|
|
||||||
for i in range(len(lines)):
|
for i in range(len(lines)):
|
||||||
l = lines[i]
|
l = lines[i].strip()
|
||||||
|
|
||||||
|
# Skip comments and empty lines
|
||||||
|
if l.startswith("#"):
|
||||||
|
continue
|
||||||
|
if l == "":
|
||||||
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
x = runner.parse(l)[0]
|
x = runner.parse(l)[0]
|
||||||
except ppx.ParseException as e:
|
except ppx.ParseException as e:
|
||||||
|
|
|
@ -56,7 +56,7 @@ class LambdaParser:
|
||||||
(self.lp + self.pp_history + self.rp)
|
(self.lp + self.pp_history + self.rp)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.pp_command = pp.Suppress(":") + pp.Word(pp.alphas + "_") + pp.Word(pp.alphas + pp.nums + "_")[0, ...]
|
self.pp_command = pp.Suppress(":") + pp.Word(pp.alphas + "_") + pp.Word(pp.alphas + pp.nums + "_.")[0, ...]
|
||||||
|
|
||||||
|
|
||||||
self.pp_all = (
|
self.pp_all = (
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from prompt_toolkit.styles import Style
|
from prompt_toolkit.styles import Style
|
||||||
from prompt_toolkit.formatted_text import HTML
|
from prompt_toolkit.formatted_text import HTML
|
||||||
|
from prompt_toolkit.lexers import Lexer
|
||||||
|
from prompt_toolkit.key_binding import KeyBindings
|
||||||
from prompt_toolkit import print_formatted_text as printf
|
from prompt_toolkit import print_formatted_text as printf
|
||||||
from importlib.metadata import version
|
from importlib.metadata import version
|
||||||
|
|
||||||
|
@ -14,6 +16,11 @@ style = Style.from_dict({ # type: ignore
|
||||||
"code": "#AAAAAA italic",
|
"code": "#AAAAAA italic",
|
||||||
"muted": "#AAAAAA",
|
"muted": "#AAAAAA",
|
||||||
|
|
||||||
|
# Syntax highlighting colors
|
||||||
|
"syn_cmd": "#FFFFFF italic",
|
||||||
|
"syn_lambda": "#AAAAAA",
|
||||||
|
"syn_paren": "#AAAAAA",
|
||||||
|
|
||||||
# Command formatting
|
# Command formatting
|
||||||
# cmd_h: section titles
|
# cmd_h: section titles
|
||||||
# cmd_key: keyboard keys, usually one character
|
# cmd_key: keyboard keys, usually one character
|
||||||
|
@ -28,6 +35,46 @@ style = Style.from_dict({ # type: ignore
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
# Replace "\" with pretty "λ"s
|
||||||
|
bindings = KeyBindings()
|
||||||
|
@bindings.add("\\")
|
||||||
|
def _(event):
|
||||||
|
event.current_buffer.insert_text("λ")
|
||||||
|
|
||||||
|
# Simple lexer for highlighting.
|
||||||
|
# Improve this later.
|
||||||
|
class LambdaLexer(Lexer):
|
||||||
|
def lex_document(self, document):
|
||||||
|
def inner(line_no):
|
||||||
|
out = []
|
||||||
|
tmp_str = []
|
||||||
|
d = str(document.lines[line_no])
|
||||||
|
|
||||||
|
if d.startswith(":"):
|
||||||
|
return [
|
||||||
|
("class:syn_cmd", d)
|
||||||
|
]
|
||||||
|
|
||||||
|
for c in d:
|
||||||
|
if c in "\\λ.":
|
||||||
|
if len(tmp_str) != 0:
|
||||||
|
out.append(("class:text", "".join(tmp_str)))
|
||||||
|
out.append(("class:syn_lambda", c))
|
||||||
|
tmp_str = []
|
||||||
|
elif c in "()":
|
||||||
|
if len(tmp_str) != 0:
|
||||||
|
out.append(("class:text", "".join(tmp_str)))
|
||||||
|
out.append(("class:syn_paren", c))
|
||||||
|
tmp_str = []
|
||||||
|
else:
|
||||||
|
tmp_str.append(c)
|
||||||
|
|
||||||
|
if len(tmp_str) != 0:
|
||||||
|
out.append(("class:text", "".join(tmp_str)))
|
||||||
|
return out
|
||||||
|
return inner
|
||||||
|
|
||||||
|
|
||||||
def show_greeting():
|
def show_greeting():
|
||||||
# | _.._ _.|_
|
# | _.._ _.|_
|
||||||
# |_(_|| | ||_)
|
# |_(_|| | ||_)
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
# How to use exported files in lamb:
|
||||||
|
#
|
||||||
|
# [Syntax Highlighting]
|
||||||
|
# Most languages' syntax highlighters will
|
||||||
|
# highlight this code well. Set it manually
|
||||||
|
# in your editor.
|
||||||
|
#
|
||||||
|
# Don't use a language for which you have a
|
||||||
|
# linter installed, you'll get lots of errors.
|
||||||
|
#
|
||||||
|
# Choose a language you don't have extenstions for,
|
||||||
|
# and a language that uses # comments.
|
||||||
|
#
|
||||||
|
# The following worked well in vscode:
|
||||||
|
# - Julia
|
||||||
|
# - Perl
|
||||||
|
# - Coffeescript
|
||||||
|
# - R
|
||||||
|
|
||||||
|
# [Writing macros]
|
||||||
|
# If you don't have a custom keyboard layout that can
|
||||||
|
# create λs, you may use backslashes instead.
|
||||||
|
# (As in `T = \ab.b`)
|
||||||
|
#
|
||||||
|
# This file must only contain macro definitons. Commands will be ignored.
|
||||||
|
# Statements CANNOT be split among multiple lines.
|
||||||
|
# Comments CANNOT be on the same line as macro defintions.
|
||||||
|
# All leading whitespace is ignored.
|
||||||
|
|
||||||
|
|
||||||
|
# Misc Combinators
|
||||||
|
M = λx.(x x)
|
||||||
|
W = (M M)
|
||||||
|
Y = λf.((λx.(f (x x))) (λx.(f (x x))))
|
||||||
|
|
||||||
|
|
||||||
|
# Booleans
|
||||||
|
T = λab.a
|
||||||
|
F = λab.b
|
||||||
|
NOT = λa.(a F T)
|
||||||
|
AND = λab.(a b F)
|
||||||
|
OR = λab.(a T b)
|
||||||
|
XOR = λab.((a (NOT b)) b)
|
||||||
|
|
||||||
|
|
||||||
|
# Numbers
|
||||||
|
# PAIR: prerequisite for H.
|
||||||
|
# Makes a two-value tuple, indexed with T and F.
|
||||||
|
#
|
||||||
|
# H: shift-and-add, prerequisite for D
|
||||||
|
#
|
||||||
|
# S: successor (adds 1)
|
||||||
|
#
|
||||||
|
# D: predecessor (subtracts 1)
|
||||||
|
#
|
||||||
|
# Z: tests if a number is zero
|
||||||
|
# NZ: equivalent to `NOT Z`
|
||||||
|
#
|
||||||
|
# ADD: adds two numbers
|
||||||
|
#
|
||||||
|
# MULT: multiply two numbers
|
||||||
|
#
|
||||||
|
# FAC:
|
||||||
|
# Recursive factorial. Call with `Y FAC <number>`
|
||||||
|
# Don't call this with numbers bigger than 5 unless you're very patient.
|
||||||
|
#
|
||||||
|
# `Y FAC 6` required 867,920 reductions and took 10 minutes to run.
|
||||||
|
|
||||||
|
PAIR = λabi.(i a b)
|
||||||
|
H = λp.((PAIR (p F)) (S (p F)))
|
||||||
|
S = λnfa.(f (n f a))
|
||||||
|
D = λn.((n H) ((PAIR 0) 0) T)
|
||||||
|
Z = λn.(n (λa.F) T)
|
||||||
|
NZ = λn.(n (λa.T) F)
|
||||||
|
ADD = λmn.(m S n)
|
||||||
|
MULT = λnmf.(n (m f))
|
||||||
|
FAC = λyn.( (Z n)(1)((MULT n) (y (D n))) )
|
|
@ -14,7 +14,7 @@ description = "A lambda calculus engine"
|
||||||
#
|
#
|
||||||
# Patch release:
|
# Patch release:
|
||||||
# Small, compatible fixes.
|
# Small, compatible fixes.
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"prompt-toolkit==3.0.31",
|
"prompt-toolkit==3.0.31",
|
||||||
|
|
Reference in New Issue