README
ΒΆ
π¦ LisGo
LisGo is a minimalist programming language built with Go. It serves as a foundation to explore and experiment with the complexities of functional programming language interpretation. Initially inspired by Lisp, LisGo will gradually diverge as it evolves.
π Try it out in the Web Assembly Playground!
π Table of Contents
- Building the Project
- Building the WebAssembly Binary
- Running the Project
- Running the Playground Locally
- Command-Line Interface (CLI)
- Testing
- Continuous Integration Pipeline
- Design Tradeoffs & Notes
- LisGo Language Documentation
- Updates
- License
π¦ Building the Project
To build the project, simply run:
go build
π Building the WebAssembly Binary
To build the WebAssembly binary, use:
make build
This will execute:
GOOS=js GOARCH=wasm go build -o live/lisgo.wasm wasm/lisgo.go
βΆοΈ Running the Project
-
Execute a script:
lisgo exec [filename]This will execute the script specified by
[filename]. -
Evaluate code directly:
lisgo eval [code]This will execute the code passed as an argument.
π οΈ Running the Playground Locally
WebAssembly's instantiateStreaming method requires CORS to be enabled. To run the playground locally, set up a local server:
npm install http-server
cd live
http-server
π₯οΈ Command-Line Interface (CLI)
LisGo provides a command-line interface for running LisGo scripts and evaluating code directly from your terminal.
Usage
go run lisgo.go
Or, if you have built the binary:
./lisgo
Commands
-
exec [filename]
Executes the LisGo script specified by
[filename].Example:
lisgo exec demo.lisp -
eval [code]
Evaluates the LisGo code passed as an argument.
Example:
lisgo eval "(print (+ 1 2 3))" -
help
Prints the help message with usage instructions.
Example:
lisgo help
β Testing
Run all tests using:
make test
This command will execute all the tests located in the tests folder by running:
go test ./...
π Continuous Integration Pipeline
LisGo's CI pipeline is defined in the .github/workflows/go.yml file and includes the following jobs:
-
Lint:
- Runs
golangci-lintto ensure code quality. - Checks for linting issues across the codebase.
- Runs
-
Format:
- Verifies code formatting using
gofmt. - Ensures consistent formatting across all Go files.
- Verifies code formatting using
-
Build:
- Builds the Go binary for the project.
- Compiles the WebAssembly binary using:
GOOS=js GOARCH=wasm go build -o live/lisgo.wasm wasm/lisgo.go
-
Test:
- Runs all tests in the
testsfolder using:go test ./tests/... -v
- Runs all tests in the
-
Static Analysis:
- Executes
go vetto perform static code analysis and catch potential issues.
- Executes
-
Tidy Check:
- Ensures the
go.modandgo.sumfiles are up-to-date. - Verifies no uncommitted changes exist after running
go mod tidy.
- Ensures the
The pipeline is triggered on:
- Pushes to the
mainbranch. - Pull requests targeting the
mainbranch.
βοΈ Design Tradeoffs & Notes
LisGo is intentionally minimalist and experimental. Some notable tradeoffs and design decisions include:
- Simplicity over Completeness: The language omits many features found in full Lisp implementations to keep the codebase approachable and easy to modify.
- Performance vs. Clarity: The interpreter prioritizes code clarity and educational value over raw execution speed or memory efficiency.
- Error Handling: Error messages are basic and may not always provide detailed diagnostics, in favor of a simpler implementation.
- Type System: LisGo uses dynamic typing with minimal type checking, which can lead to runtime errors but simplifies the interpreter.
- Standard Library: The built-in functions are limited; users are encouraged to extend the language as needed.
- Deviation from Lisp: While inspired by Lisp, LisGo intentionally diverges in syntax and semantics as new features are added or simplified.
- Concurrency: No built-in concurrency primitives are provided, reflecting a focus on core language features first.
These tradeoffs are made to keep LisGo accessible for learning, experimentation, and rapid prototyping.
π LisGo Language Documentation
This section provides an overview of LisGo's syntax and expressions. Each expression is explained with examples.
π¨οΈ Printing Expressions
The print expression outputs a value or result to the console.
Syntax:
(print expression)
Examples:
(print (+ 1 2 3)) ; Outputs the sum of 1, 2, and 3
(print "hello world") ; Outputs "hello world"
(print (print "yes")) ; Prints "yes" and returns nil
β Math Expressions
LisGo supports basic arithmetic operations.
- Addition (
+): Adds multiple numbers. - Subtraction (
-): Subtracts one or more numbers. - Multiplication (
*): Multiplies multiple numbers. - Division (
/): Divides multiple numbers. - Modulo (
%): Computes the remainder of division.
Syntax:
(+ number1 number2 ...)
(- number1 number2 ...)
(* number1 number2 ...)
(/ number1 number2 ...)
(% number1 number2 ...)
Examples:
(+ 1 2 3) ; Returns 6
(- 8 4) ; Returns 4
(* 2 3) ; Returns 6
(/ 8 2) ; Returns 4
(% 10 3) ; Returns 1
π Logical Expressions
LisGo supports logical operations.
- Equality (
==): Checks if all arguments are equal. - Inequality (
!=): Checks if any arguments are not equal. - Negation (
!): Negates a boolean value.
Syntax:
(== value1 value2 ...)
(!= value1 value2 ...)
(! value)
Examples:
(== 1 1 1) ; Returns true
(!= 1 2) ; Returns true
(! true) ; Returns false
π Conditional Expressions
cond
The cond expression executes a specific block of code based on multiple conditions. It checks each condition in sequence and runs the associated code block for the first truthy condition.
Syntax:
(cond
(condition1 expression1)
(condition2 expression2)
...)
Example:
(cond
(0 (print "0")) ; This will not execute as 0 is falsey
(0 (print "1")) ; This will not execute as 0 is falsey
(2 (print "2")) ; This will execute as 2 is truthy
)
if
The if expression evaluates a condition and executes one of two possible blocks based on the result.
Syntax:
(if condition
true_expression
false_expression)
Example:
(if 1
(print "yes") ; Executes if the condition is truthy
(print "no") ; Executes if the condition is falsey
)
π Variable Assignment
The := expression assigns a value to a variable. Once assigned, the variable can be used in subsequent expressions.
Syntax:
(:= variable_name value)
Example:
(:= index 0) ; Assigns 0 to index
(print index) ; Prints the value of index
π Loop Expressions
while
The while expression repeats a block of code as long as the condition remains true.
Syntax:
(while condition expression)
Example:
(:= index 0) ; Initialize index to 0
(while (!= index 10) ; Loop until index equals 10
(print
(:= index (+ index 1))
)
)
π§ Function Definition Expressions
func
The func expression defines a new function with a specified name and parameters. You can then call the function and optionally return a value using return.
Syntax:
(func function_name (param1 param2 ...)
expression
(return return_value))
Examples:
(func function (alpha beta)
(print alpha)
(return "the_return_value")
)
π Function Call Expression
To call a function, use its name followed by its arguments.
Example:
(func addition (a b)
(return (+ a b))
)
(print (addition 100 1)) ; Outputs 101 by calling the addition function
π Return Expressions
return
The return expression exits a function and optionally returns a value.
Syntax:
(return value)
Example:
(func example ()
(return 42)
)
return-from
The return-from expression exits a specific function and optionally returns a value.
Syntax:
(return-from function_name value)
Example:
(func outer ()
(func inner ()
(return-from outer 42)
)
(inner)
)
π Updates
- β Implemented tokenizer (scanner)
- β Implemented parser (s-expression parser)
- β Added rough runtime interpretation schema
- β Added conditional expressions
- β Added loop expressions
- β Added math expressions
- β Added logical expressions
- β Added function definition expression
- β Added return expressions
π License
This project is licensed under the MIT License. See the LICENSE file for details.
Documentation
ΒΆ
There is no documentation for this package.