Skip to content
This repository has been archived by the owner on Nov 5, 2023. It is now read-only.

TheEdward162/fup

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FUP - Fuck Un(losed Parens

FUP is a preprocessor for Scheme programming language. It is intended to help avoid the overuse of parens in Scheme in situations when even rainbow colorizers stop helping.

FUP uses pest.rs to parse Scheme and augmented expression. Augmented expressions are then converted to Scheme and valid Scheme code is output.

Usage

The binary currently has two modes - file and repl.

To start the repl, run the binary with no arguments. To read input from file, start the binary with the first cli argument set to the file path.

Currenty, repl has the following meta commands:

Command Description
,exit Exits the repl
,help Prints help with these commands
,mline Toggles multiline and immediate evaluation

Note: By default, repl runs in multiline mode, which means input is buffered until an empty newline is passed.

FUP expressions

Currently, FUP supports these augmented expressions:

Define

Helps the readability of define statements:

define foo(x, y) {
	(display (+ x y))
}
define bar = z;

becomes

(define
	(foo x y)
	(display (+ x y))
)
(define bar z)

Let

Similar to define, helps with readability:

let x = 1 {
	(display x)
}
let* x = foo(1,), y = x {
	y
}

; Can also be implicitly scoped
define foo(x, y) {
	let z = x + y
	(display z)
}

becomes

(let
	(
		(x 1)
	)
	(display x)
)
(let*
	(
		(x (foo 1))
		(y x)
	)
	y
)
(define
	(foo x y)
	(let
		(
			(z (+ x y))
		)
		(display z)
	)
)

Function call

Functions are called in the same way as they are defined:

foo(x, y)

becomes

(foo x y)

However, calling functions with one or zero arguments has a caveat to avoid ambiguity with natural scheme code:

foo (x) ; not a call
foo (x,) ; unambiguous call
foo(,) ; unambiguous call, no arguments
(lambda (x) x)(1,) ; scheme expression call
{ cond {
	#f => foo,
	#t => bar
} }(1,) ; fup expression call

becomes

foo (x) ; not a call
(foo x) ; unambiguous call
(foo) ; unambiguous call, no arguments
((lambda (x) x) 1) ; scheme expression call
(
	(cond
		(#f foo)
		(#t bar)
	) 1
) ; fup expression call

Cond

Cond works similar to switch statemets or the Rust match statement:

cond {
	a => b,
	c => d,
	x => y,
	else => z
}

becomes

(cond
	(a b)
	(c d)
	(x y)
	(else z)
)

List indexing

Lists can be indexed to avoid nested cars and cdrs from making list accesses unreadable:

lst[1] ; one element index
lst[2..] ; slice-after index
lst[..2] ; slice before index
lst[1..1] ; slice between index, empty
lst[1..3] ; slice between index
(list 1 2)[1] ; scheme expression index
{ foo(,) }[0] ; fup expression index

becomes

(car (cdr lst)) ; one element index
(cdr (cdr lst)) ; slice-after index
(list
	(car lst)
	(car (cdr lst))
) ; slice before index
() ; slice between index, empty
(
	list
	(car (cdr lst))
	(car (cdr (cdr lst)))
) ; slice between index
(
	car (cdr (list 1 2))
) ; scheme expression index
(
	car (foo)
) ; fup expression index

Note: Be aware that the ..N and N..M variations evaluate the indexed expression as many times as the the length of the resulting list.

List literal syntax

Lists can be created using [item, item, ..] syntax.

[1,2,3]
[1,2,3,]
[]              ; useful as '()
( foo [1,2] )
( foo [1,2,] )
( foo [1,] )    ; trailing coma disambiguates list literal
( foo [1] )     ; indexing into foo has precedence
( foo [] )      ; with no argument list wins again
{[1,2,3]}[1]    ; indexing possible when wrapped in {}

becomes

(list 1 2 3)
(list 1 2 3)
(list)          ; results in the same thing as '()
(foo (list 1 2))
(foo (list 1 2))
(foo (list 1))
((car (cdr foo)))
(foo (list))
(car (cdr (list 1 2 3)))

Binary operator infixing

Binary operators can be infixed as in other langauges:

1 + 2

a == b ; "==" is an alias for "equal?"

#t is #t ; "is" is an alias for "eq?"

becomes

(+ 1 2)

(equal? a b)

(eq? #t #t)

Note: To avoid ambiguity, infixed operators only work in non-scheme contexts.

Escaping

Some FUP expressions (such as infixed operators) can be ambiguous in Scheme contexts. To explicitly create a FUP context and parse expressions as FUP expressions, use {} blocks:

(define
	(foo x y)
	{x + y}
)

becomes

(define
	(foo x y)
	(+ x y)
)

Releases

No releases published

Packages

No packages published

Languages