Skip to content

Gioyik/Spow

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

#Spow It's a programming language made by people like you. Spow is an experimental mini-language based on the Lisp family of programming languages.

About Spow

Spow starts as a try to create and understand how programming languages are built and how someone could create (or try to create) one. This project has not the vision of be the next big programming languaje in the world and it will not have that goal in the future, but it's a good start to learn about programming languages, interpreters and compilers. The unique profit in this project is more about interpreter design and programming in C.

This is project is intended to be a platform for everybody who wants to try The language programming challenge. Everything borns in base of Build your own Lisp book and the fantastic mpc library, it provides a good start but is not a complete guide, this project will try to expand the limits of the book and let people like you and me start a programming language with all the rules and great things of a professional language.

mpc library (Micro Parser Combinators)

mpc is a lightweight and powerful Parser Combinator library for C.

Using mpc might be of interest to you if you are...

  • Building a new programming language
  • Building a new data format
  • Parsing an existing programming language
  • Parsing an existing data format
  • Embedding a Domain Specific Language
  • Implementing Greenspun's Tenth Rule

Features

Spow is a mini-language that is inspired by the Lisp family of languages. Thus, it shares most of its features with Lisp and Scheme. These include:

Currently, Spow data definition and manipulation capabilities are lacking, but this will hopefully be changed in the future.

Basic features in action

Spow supports inline comments using hash (#):

# Comment here
(print 'hello') # More comments here

Printing to standard output can be done using print and println:

spow> (println 'Hello world')
Hello world!

Variables are created with define (which affects the local environment) and global (which, as the name suggests, affects the global environment):

spow> (global foo 'bar')
spow> (println foo)
bar

Requeriments:

  • gcc or clang
  • You'll need make
  • git to clone the source

Build and run

Most of Spow dependencies are included in the repository, so you shouldn't need to install anything other than the build tools, so you will need a fairly recent C compiler. First, clone the repository, and then compile using make:

$ git clone https://github.com/Gioyik/spow
$ cd spow
$ make

This will create an spow binary under out/bin/ directory.

Clean up if you want to start over:

$ make clean

Usage

The spow binary can take a single argument - a path to a file to execute.

$ ./out/bin/spow [file].spow

If no argument is given, then it will drop into the REPL (interpreter):

$ ./out/bin/spow
 ______                         
 |__  / | __ _ _ __   __ _  ___  
   / /| |/ _` | '_ \ / _` |/ _ \ 
  / /_| | (_| | | | | (_| | (_) |
 /____|_|\__,_|_| |_|\__, |\___/ 
                     |___/       

Spow repl - vX.X.X (Press Ctrl+c or type exit to finish)
spow> 

Docs

TBD

Primitive Data Types

Type Example Description
Integer 5, -9 A standard integer (long)
Floating point -5., 3.14 A standard floating point (double)
Boolean true, false A standard... boolean
String "\"escapes\" OK", 'foobar' A string type - either single or double quotes
Q-Symbol :like-in-ruby, :'foo' A quoted symbol (identifier), can also be written similar to strings
Q-Expr {1 'b' (+ 1 2) x y} A quoted expression. The basic data structure - acts like a list
Dictionary [:x 23 :y 'hello' :z {a b c}] A key-value store. Keys are Q-Symbols, values can be anything
Function (fn (x) (/ 1 x)) An anonymous function. The basic mechanism of function definition
Error (error 'somebody set up us the bomb') An error. Stops evaluation

Expressions

Function calls in Spow are defined as S-Expressions (symbolic expressions). They are syntactically enclosed in parentheses (). The first argument of the expression must be a callable, and is evaluated in the current environment with any following arguments as parameters (this is the iconic "Polish notation" of Lisp).

spow> (+ 5 6)
11
spow> (println 'foo')
foo

When evaluating user-defined functions, partial application is done automatically for any unfilled arguments (this is currently not done for builtins). This makes it easy to use higher-order functions quickly:

spow> (define xs {1 2 3 4})
spow> (define square (map (fn (x) (* x x))))
spow> (square xs)
{1 4 9 16}

Variable and function identifiers, called "symbols," are evaluated to the values that they map to, except in certain special forms (e.g. when they are being defined):

spow> (define x 5)
spow> (+ x 6)
11

The primitive types evaluate to themselves.

Q-Expressions (quoted expressions, often referred to simply as 'lists') are particularly important. They are enclosed inside curly braces {}. They are a collection type and behave similar to lists in other languages. They can store any number and mixture of primitive types. And they have one more important ability: expressions that they contain which would normally be evaluated, such as symbols and S-Expressions, are left unevaluated (i.e. they are "quoted"). This allows them to contain arbitrary code, and then be converted and evaluated as S-Expressions:

spow> (head {1 2 3})
1
spow> (tail {1 2 3})
{2 3}
spow> (define x {* 3 (+ 2 2)})
spow> x
{* 3 (+ 2 2)}
spow> (eval x)
12

There are a few more expression types that are useful in special cases.

E-Expressions (escaped expressions) are denoted with a preceding backslash \, and can be used to specifically evaluate a section within a Q-Expression literal:

spow> {1 2 (+ 2 1)}
{1 2 (+ 2 1)}
spow> {1 2 \(+ 2 1)}
{1 2 3}

C-Expressions (concatenating expressions) are denoted with a preceding at-sign @. They behave similarly to E-Expressions, with the exception that, when given a list (Q-Expression), they "extract" the contents and include it directly in the outer list:

spow> {1 2 \{3 4}}
{1 2 {3 4}}
spow> {1 2 @{3 4}}
{1 2 3 4}

Finally, there is another collection type that is slightly more mundane than Q-Expressions and their ilk: Dictionaries. Dictionaries act as simple key-value stores, and are similar to the dictionaries in other languages. They are delimited with square brackets [], use Q-Symbols as their keys, and can store any normal value:

spow> (dict-get [:foo 12 :bar 43] :foo)
12
spow> (dict-set [:x 1 :y 2] :z 3)
[:'x' 1 :'y' 2 :'z' 3]

Builtins

Builtins usually behave like normal functions, but they also have the special role of enabling some of Spow's basic features, since they are written in C (for example, the fn builtin creates a new anonymous function).

Spow makes no distinction between "operators" (+, -, *) and other kinds of builtins - they are simply named differently.

Builtin Signature Description
+ (+ [args...]) Addition. Takes 2 or more arguments
- (- [args...]) Subtraction. Takes 2 or more arguments
* (* [args...]) Multiplication. Takes 2 or more arguments
/ (/ [args...]) Division. Promotes integers to floats if necessary. Takes 2 or more arguments
// (// [args...]) Truncating division. Removes decimal remainder. Takes 2 or more arguments
% (% [args...]) Modulo. Takes 2 or more arguments
^ (^ [args...]) Power operator. Takes 2 or more arguments
> (> [arg1] [arg2]) Greater than. Takes 2 arguments
>= (>= [arg1] [arg2]) Greater than or equal to. Takes 2 arguments
< (< [arg1] [arg2]) Less than. Takes 2 arguments
<= (<= [arg1] [arg2]) Less than or equal to. Takes 2 arguments
== (== [arg1] [arg2]) Equal to. Tests deep equality. Takes 2 arguments
!= (!= [arg1] [arg2]) Unequal to. Tests deep equality. Takes 2 arguments
and (and [arg1] [arg2]) Logical 'and'. Short circuiting. Takes 2 arguments
or (or [arg1] [arg2]) Logical 'or'. Short circuiting. Takes 2 arguments
not (not [arg1]) Logical 'not'. Takes 1 argument
head (head [arg1]) Returns the extracted first element (head) of a list
qhead (qhead [arg1]) Like head, except quotes symbols and S-Exprs
tail (tail [arg1]) Returns the tail of a list, excluding the first element
first (first [arg1]) Similar to head, but doesn't extract
last (last [arg1]) Returns the last element of a list, unextracted
except-last (except-last [arg1]) Returns the first section of a list, excluding the last element
list (list [args...]) Returns a list containing the evaluated arguments
eval (eval [arg1]) Evaluates a list as if it were an S-Expression
append (append [args...]) Concatenates two or more lists
cons (cons [arg1] [arg2]) Attaches a primitive type to the head of a list
dict-get (dict-get [dict] [key]) Retrieves a value from a dict using a key
dict-set (dict-set [dict] [key] [val]) Returns a new dict with a value set
dict-del (dict-del [dict] [key]) Returns a new dict with a value deleted
dict-haskey? (dict-haskey? [dict] [key]) Checks if a dict has a key set
dict-keys (dict-keys [dict]) Returns a list of keys in the dictionary
dict-vals (dict-vals [dict]) Returns a list of values in the dictionary
len (len [arg1]) Returns the length of a collection
reverse (reverse [arg1]) Reverses a collection
slice (slice [c] [start] [end] [step]) Returns a slice of a collection based on start, stop, and step numbers
if (if [pred] [then-branch] [else-branch]) If expression. Evaluates a predicate, and one of two branches based on the result
define (define [sym] [value]) Defines a variable in the local environment
global (global [sym] [value]) Defines a variable in the global environment
let (let (([sym1] [val1])...) [expr]) Creates a local environment and defines variables within
fn (fn ([args...]) [body]) Defines an anonymous function with the specified arguments and body. The function also retains the current environment as a closure
macro (macro [name] ([args...]) [body]) Defines a macro that can operate on code before it is evaluated
typeof (typeof [arg1]) Returns a string representing the type of the argument
convert (convert [type] [value]) Converts a value to type, which is represented by a qsym, as returned by typeof
import (import [path]) Attempts to import the spow file at the given path
print (print [arg1]) Prints to standard output
println (println [arg1]) Prints to standard output, adding a newline
random (random) Returns a floating point random number between 0 and 1
error (error [arg1])
exit (exit [arg1]) Exits the interactive REPL

Core Library

In addition to builtins, there exists a core library that Spow imports on startup. Among other things, this library aims to exercise some of Spow's features, as well as provide some basic functional tools.

Symbol Signature Description
nil Alias for {}
func (func ([name] [args]) [body]) Macro that defines a named function
int? (int? [arg1]) Checks that argument is an integer
float? (float? [arg1]) Checks that argument is a floating point
str? (str? [arg1]) Checks that argument is a string
builtin? (builtin? [arg1]) Checks that argument is a builtin
fn? (fn? [arg1]) Checks that argument is a user-defined function
macro? (macro? [arg1]) Checks that argument is a macro
bool? (bool? [arg1]) Checks that argument is a boolean
qexpr? (qexpr? [arg1]) Checks that argument is a Q-Expression
dict? (dict? [arg1]) Checks that argument is a Dictionary
list? (list? [arg1]) Alias for qexpr?
nil? (nil? [arg1]) Checks that argument is nil
to-str (to-str [arg1]) Converts argument to a string
do (do [expr1] [expr2] ... [exprn]) Evaluates its arguments one by one, and returns the result of the last argument
compose (compose [f] [g] [xs...]) Composes two functions
flip (flip [f] [x] [y]) Takes a function and two argument, and flip the ordering of the arguments
id (id [x]) The identity function, returns whatever is passed
reduce (reduce [f] [l] [acc]) Reduces a list to a single value using a reducer function
reduce-left (reduce-left [f] [l] [acc]) Like reduce, but traverses the list in the opposite direction
map (map [f] [l]) Applies a function to each element of a list
filter (filter [f] [l]) Uses a predicate function to filter out elements from a list
any (any [f] [l]) Checks whether any value in list l satisfies f
all (all [f] [l]) Checks whether all values in list l satisfy f
sum (sum [l]) Sums elements of a list
product (product [l]) Multiplies together elements of a list
pack (pack [f] [args...]) Takes multiple argument and feeds it to a function as a single list argument
unpack (unpack [f] [l]) Evaluates a function using a list of arguments
nth (nth [n] [l]) Returns the nth element of a list
zip (zip [lists...]) Returns a list of lists, each containing the i-th element of the argument lists
take (take [n] [l]) Takes the first n elements of a list
drop (drop [n] [l]) Drops the first n elements of a list, returning what's left
member? (member? [x] [l]) Checks if an element is a member of a list
range (range [s] [e]) Returns a list of integers starting with s and going up to e
dict-items (dict-items [dict]) Returns a list of key-value pairs from the given dict
random-between (random-between [s] [e]) Returns a random floating point between s and e

TODO

This a list of a non-detailed ToDo list:

Sintax

  • Work on a custom sintax different to lisp

Language features

  • string manipulation functions
    • index-of
    • concat
  • proper error handling? Propagation and try/catch
  • precision decimal (and fraction) types?
  • complex number type?
  • bignum integers?
  • modules
  • user defined types (algebraic data types?)
  • pattern matching on user defined types?
  • memory pool allocation
  • closures that only add free variables?

Interpreter features

  • coloring, completion, etc
  • test cases

Low level APIs

  • file system interface

Standard library

  • mathematical functions

Contribute

Credits

Many thanks goes to the following awesome libraries and open source projects, and their creators:

  • mpc.c
  • linenoise
  • clang / LLVM

Also, thanks goes to the creator of the free "Build Your Own Lisp" online book, which is what Spow was inspired from.

License

Legally, nobody can copyright a programming language (it's like try to copyright spanish, english or any other spoken language). So, Spow as a programming language has a BDFL (Benevolent Dictator for Life) who determines what goes into the language and what doesn't. The BDFL is Giovanny Andres Gongora Granada who was the person who started spow as programming language. However, the code of the interpreter, compiler and files in this repository are licesed under MIT License.

About

😎 Spow programming language

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published