Build Your Own Lisp

Published on March 2017 | Categories: Documents | Downloads: 289 | Comments: 0 | Views: 810
of 205
Download PDF   Embed   Report

Comments

Content

Build Your Own Lisp
Learn C and build your own programming language in under 1000 lines of code!
If you're looking to learn C, or you've ever wondered how to build your own programming language, this is the book for you. In just a few lines of code, I'll teach you how to effectively use C, and what it takes to start building your own language. Along the way we'll learn about the weird and wonderful nature of Lisps, and what really makes a programming language. y building a real world C program we'll learn implicit things that conventional books cannot teach. !ow to develop a project, how to make life easy for your users, and how to write beautiful code. "his book is free to read online. #et started now$

%

Introduction • Chapter 1

About
In this book you'll learn the C programming language, and at the same time learn how to build your very own programming language, a minimal Lisp, in under %&&& lines of code$ 'e'll be using a library to do some of the initial work, so I'm cheating a little bit on the line count, but the rest of the code will be completely original, and you really will create a powerful little Lisp by the end. "his book is somewhat inspired by online tutorials such as 'rite (ourself a )cheme in *+ !ours which go through the steps of building a programming language from scratch. I wrote this book to show that this kind of fun and creative project is a great way to learn a language, and not limited to abstract high,level languages, or e-perienced programmers. .any people are keen to learn C, but have nowhere to start. 'ell here is your e-cuse. If you follow this book I can promise that, in the worst case, you'll get a cool new programming language to play with, and hopefully you'll become an e-perienced C programmer too$

Who this is for
"his book is for anyone wanting to learn C, or who has once wondered how to build their own programming language. "his book is not suitable as a first programming language book, but anyone with at least some minimal programming e-perience, in any language, should find something and new and interesting inside.

Ada Lovelace / (our typical brogrammer.

0

I've tried to make this book as friendly as possible to beginners. I welcome beginners the most because they have so much to discover$ ut beginners will also find this book hard. 'e will be covering lots of new concepts, and essentially learning two new programming languages at once. If you look for help you may find people are not patient with you. (ou may find that, rather than help, they take the time to e-press how much they know about the subject. 1steemed people with lots of e-perience might tell you that you are wrong. "he subte-t to their tone might be that you should stop now, rather than inflict your bad code on the world. After a couple of engagements like this you may decide that you are not a programmer, or don't really like programming, or that you just don't get it. (ou may have thought that you once enjoyed the idea of building your own programming language, but now you have reali2ed that it is too abstract and you don't care any more. 3ow you are concerned with your other passion, and any insight that may have been playful, joyful or interesting will now have become an obstacle. 4or this I can only apologi2e. 5rogrammers can be hostile, macho, arrogant, insecure, and aggressive. "here is no e-cuse for this behaviour. 6now that I am on your side. 3o one gets it at first. 1veryone struggles and doubts their abilities. 5lease don't give up or let the joy be sucked out of the creative e-perience. e proud of what you create no matter what it is. 5eople like me don't want you to stop programming. 'e want to hear your voice, and what you have to say, even if you do not shout as loud as others.

Why learn C
C is one of the most popular and influential programming languages in the world. It is the language of choice for development on Linu-, and has been used e-tensively in the creation of 7) 8 and to some e-tent .icrosoft 'indows. It is used on micro,computers too. (our fridge and car probably run on it. In modern software development, the use of C may be escapable, but its legacy is not. Anyone wanting to make a career out of software development would be smart to learn C.

9

A fridge / (our typical C user

ut C is not about software development and careers. C is about freedom. It rose to fame on the back of technologies of collaboration and freedom , :ni-, Linu-, and "he Libre )oftware .ovement. It personifies the idea of personal liberty within computing. It wills you to take control of the technology affecting your life. In this day and age, when technology is more powerful than ever, this could not be more important. "he ideology of freedom is reflected in the nature of C itself. "here is little C hides from you, including its warts and flaws. "here is little C stops you from doing, including breaking your programs in horrible ways. 'hen programming in C you do not stand on a path, but a plane of decision, and C dares you to decide what to do. C is also the language of fun and learning. efore the mainstream media got hold of it we had a word for this. Hacking. "he philosophy that glorifies what is fun and clever. 3othing to do with the illegal unauthorised access of other peoples' computers. !acking is the philosophy of e-ploration, personal e-pression, pushing boundaries, and breaking the rules. It stands against hierarchy and bureaucracy. It celebrates the individual. !acking baits you with fun, learning, and glory. !acking is the promise that with a computer and access to the internet, you have the agency to change the world. "o want to master C, is to care about what is powerful, fun, clever, and free. "o become a programmer with all the vast powers of technology at his or her fingertips. And the responsibility to say "I will do good with this".

ow to learn C
"here is no way around the fact that C is a difficult language. It has many concepts that are unfamiliar, and it makes no attempts to help a new user. In this book I am not going to cover in detail things like the synta- of the language, or how to write loops and conditional statements. I will, on the other hand, show you how to build a real world program in C. "his approach is always more difficult for the reader, but hopefully will teach you many implicit things a traditional approach cannot. I can't make any promise that this book will make you a confident user of C. 'hat I can promise, is that those %&&& lines of code are going to be packed with content , and you will learn something worthwhile. "his book consists of %; short chapters. !ow you complete these is up to you. It may well be possible to blast through this book over a weekend, or to take it more slowly, and do a chapter or two each evening over a week. It shouldn't take very long to complete, and will hopefully leave you with a taste for developing your language further.

Why build a Lisp
*

"he language we are going to be building in this book is a Lisp. "his is a family of programming languages characteri2ed by the fact that all their computation is represented by lists. "his may sound scarier than it is. Lisps are actually very easy, distinctive, and powerful languages.

.ike "yson / (our typical Lisp user

uilding a Lisp is a great project for so many reasons. It puts you in the shoes of language designers, and gives you an appreciation for the whole process of programming, from language all the way down to machine. It teaches you about functional programming, and different ways to view computation out of the norm. "he final product you are rewarded with provides a template for future thoughts and developments, giving you a starting ground for trying new things. It simply isn't possible to comprehend the creativity and cleverness that goes into programming and computer science until you e-plore languages themselves. "he type of Lisp we'll be building is one I've invented for the purposes of this book. I've designed it for minimalism, simplicity and clarity, and I've become <uite fond of it along the way. I hope you come to like it too. Conceptually, syntactically, and in implementation this brand of Lisp has a number of serious differences to other major brands of Lisp. )o much so that I'm sure I will be getting e,mails from Lisp programmers telling me it isn't a Lisp because it doesn't do/have/look-like this or that. I've not made this Lisp different to confuse beginners or to spread untruths. I've made it different because different is good. If you are looking to learn about the semantics and behaviours of conventional Lisps, and how to program them, this book may not be for you. 'hat this book offers instead is new and uni<ue concepts, self e-pression, creativity, and fun. 'hatever your motivation, heed this disclaimer now. 3ot everything I say will be objectively correct or true$ (ou will have to decide that for yourselves.

Your own Lisp
=

"he best way to follow this book is to, as the title says, write your own Lisp. If you are feeling confident enough I want you to add your own features, modifications and changes. (our Lisp should suit you and your own philosophy on what is right or true. "hroughout the book I'll be giving description and insight, but with it I'll be providing a lot of code. "his will make it easy to follow along by copy and pasting each section into your program without really understanding. Please do not do this!. "ype out each piece of sample code yourself. "his is called he Hard !ay. 3ot because it is hard technically, but because it re<uires discipline. y doing things he Hard !ay you will come to understand the reasoning behind what you are typing. Ideally things will click as you follow it along character by character. 'hen reading you may have an intuition as to why it looks right, or what may be going on, but this will not always translate to a real understanding unless you do the writing yourself$ In a perfect world you would use my code as a reference , an instruction booklet and guide as to building the programming language you always dreamed of. In reality this isn't practical or viable. ut the base philosophy remains. If you want to change something, do it.

;

Installation • Chapter 2

!etup

Cat / Install at own risk.

efore we can start programming in C we'll need to install a couple of things, and set up our environment so that we have everything we need. ecause C is such a universal language this should hopefully be fairly simple. 1ssentially we need to install two main things. A te"t editor and a compiler.

"e#t $ditor
A te-t editor is a program that allows you to edit te-t files in a way suitable for programming. 7n Linu# the te-t editor I recommend is gedit. 'hatever other basic te-t editor comes installed with your distribution will also work well. If you are a >im or 1macs user these are fine to use. 5lease don't use an I?1. It isn't re<uired for such a small project and won't help in understanding what is going on.

@

7n %ac A simple te-t editor that can be used is "e-t'rangler. If you have a different preference this is fine, but please don't use 8Code for te-t editing. "his is a small project and using an I?1 won't help you understand what is going on. 7n Windows my te-t editor of choice is 3otepadAA. If you have another preference this is fine. 5lease don't use #isual $tudio as it does not have proper support for C programming. It you attempt to use it you will run into many problems.

Compiler
"he compiler is a program that transforms the C source code into a program your computer can run. "he installation process for these is different depending on what operating system you are running. Compiling and running C programs is also going to re<uire really basic usage of the command line. "his I will not cover, so I am going to assume you have at least some familiarity with using the command line. If you are are worried about this then search online for information on using it, relevant to your operating system. 7n Linu# you can install a compiler by downloading some packages. If you are running :buntu or ?ebian you can install everything you need with the following command sudo apt-get install build-essential. If you are running 4edora or a similar Linu- variant you can use this command su -c "yum groupinstall development-tools". 7n %ac you can install a compiler by downloading and installing the latest version of 8Code from Apple. If you are unsure of how to do this you can search online for Binstalling -codeB and follow any advice shown. (ou will then need to install the %ommand Line ools. 7n .ac 7) 8 %&.C this can be done by running the command xcode-select --install from the command line. 7n versions of .ac 7) 8 prior to %&.C this can be done by going to 8Code 5references, ?ownloads, and selecting %ommand Line ools for Installation. 7n Windows you can install a compiler by downloading and installing .in#'. If you use the installer at some point it may present you with a list of possible packages. .ake sure you pick at least mingw32-base and msys-base. 7nce installed you need to add the compiler and other programs to your system PATH variable. "o do this follow these instructions appending the directory ;C !"in#$!bin to the variable called PATH. (ou can create this variable if it doesn't e-ist. (ou may need to restart cmd%exe for the changes to take effect. "his will allow you to run a compiler from the command line cmd%exe. It will also install other programs which make cmd%exe act like a :ni- command line.

Testing the Compiler
"o test if your C compiler is installed correctly type the following into the command line.
cc --version

If you get some information about the compiler version echoed back then it should be installed correctly. (ou are ready to go$ If you get any sort of error about an unrecognised or

+

not found command, then it is not ready. (ou may need to restart the command line or your computer for changes to take effect.

ello World
3ow that your environment is set up, start by opening your te-t editor and inputting the following program. Create a directory where you are going to put your work for this book, and save this file as &ello'world%c. "his is your first C program$
(include )stdio%&* int main+int argc, c&ar-- argv. / puts+"Hello, world0".; return 1; 2

"his may look like a lot of cra2y symbols that make very little sense. I'll try to e-plain it step by step. In the first line we include what is called a header. "his statement allows us to use the functions from stdio%&, the standard input and output library which comes included with C. 7ne of the functions from this library is the puts function you see later on in the program. 3e-t we declare a function called main. "his function is declared to output an int, and take as input an int called argc and a c&ar-- called argv. All C programs must contain this function. All programs start running from this function. Inside main the puts function is called with the argument "Hello, world0". "his outputs the message Hello, world0 to the command line. "he function puts is short for put string. "he second statement inside the function is return 1;. "his tells the main function to finish and return 1. 'hen a C program returns 1 this indicates there have been no errors running the program.

Compilation
efore we can run this program we need to compile it. "his will produce the actual e"ecuta&le we can run on our computer. 7pen up the command line and browse to the directory that &ello'world%c is saved in. (ou can then compile your program using the following command.
cc -std3c44 -$all &ello'world%c -o &ello'world

"his compiles the code in &ello'world%c, reporting any warnings, and outputs the program to a new file called &ello'world. 'e use the -std3c44 flag to tell the compiler which version or standard of C we are programming with. "his lets the compiler ensure our code is standardi2ed, so that people with different operating systems or compilers will be able to use our code.

C

If successful you should see the output file in the current directory. "his can be run by typing %5&ello'world Dor just &ello'world on 'indowsE. If everything is correct you should see a friendly Hello, world0 message appear. Congratulations! (ou've just compiled and run your first C program.

$rrors
If there are some problems with your C program the compilation process may fail. "hese issues can range from simple synta- errors, to other complicated problems that are hard to understand. )ometimes the error message from the compiler will make sense, but if you are having trouble understanding it try searching online for it. (ou should see if you can find a concise e-planation of what it means, and work out how to correct it. Femember thisG there are many people before you who have struggled with e-actly the same problems.

Fage / A poor debugging techni<ue

)ometimes there will be many compiler errors stemming from one source. Always work through compiler errors from first to last. )ometimes the compiler will compile a program, but when you run it it will crash. ?ebugging C programs in this situation is hard. It can be an art far beyond the scope of this book. .y first port of call for debugging a crashing C program is to print out lots of information as the program is running. :sing this method I can try to isolate e-actly what part of the code is incorrect and what, if anything, is going wrong up until the crash. 4or beginners I would recommend this techni<ue. It is a debugging techni<ue which is active. "his is the important thing. As long as you are doing something, and not just staring at the code, the process is less painful and the temptation to give up is lessened. 4or people feeling more confident a program called gdb can be used to debug your C programs. "his can be difficult and complicated to use, but it is also very powerful and can

%&

give you e-tremely valuable information and what went wrong and where. Information on how to use gdb can be found online. 7n %ac the most recent versions of 7) 8 don't come with gdb. Instead you can use lldb which does largely the same job. 7n Linu# or %ac valgrind can be used to aid the debugging of memory leaks and other more nasty errors. >algrind is a tool that can save you hours, or even days, of debugging. It does not take much to get proficient at it, so investigating it is highly recommended. Information on how to use it can be found online.

&ocumentation
"hrough this book you may come across functions in some e-ample code that you don't recogni2e. (ou might wonder what it does. In this case you will want to look toward the online documentation of the standard library. "his will e-plain all the functions included in the standard library, what they do, and how to use them.

'eference
What is this section for( In this section I'll link to the code I've written for this particular chapter of the book. 'hen finishing with a chapter your code should probably look similar to mine. "his code can be used for reference if the e-planation has been unclear. If you encounter a bug please do not copy and paste my code into your project. "ry to track down the bug yourself and use my code as a reference to highlight what may be wrong, or where the error may lie.

hello)world*c
(include )stdio%&* int main+int argc, c&ar-- argv. / puts+"Hello, world0".; return 1; 2

Bonus %ar+s
What is this section for( %%

In this section I'll list some things to try for fun, and learning. It is good if you can attempt to do some of these challenges. )ome will be easy, while some will be very difficult. 4or this reason don't worry if you can't figure them all out. )ome might not even be possible$ .any will re<uire some research on the internet. "his is an integral part of learning a new language so should not be avoided. "he ability to teach yourself things is one of the most valuable skills in programming. )ee how many you can complete for each chapter. .ove on when you get bored.
• • • •

H Change the Hello $orld0 greeting given by your program to something different. H 'hat happens when no main function is givenI H :se the online documentation to lookup the puts function. H Look up how to use gdb and run it with your program.

%0

Basics • Chapter 3

O,er,iew

5rograms / :seful for the theatre.

In this chapter I've prepared a <uick overview of the basic features of C. "here are very few 'eatures in C, and the synta- is relatively simple. ut this doesn't mean it is easy. All the depth hides below the surface. ecause of this we're going to cover the 'eatures and synta" fairly <uickly now, and see them in greater depth as we continue. "he goal of this chapter is get everyone on the same page. 5eople totally new to C should therefore read it in some consideration, and take some time over it, while those with some e-isting e-perience may find it easier to skim and return to later as re<uired.

-rograms
%9

A program in C consists of only 'unction de'initions and structure de'initions. "herefore a source file is simply a list of 'unctions and types. "hese functions can call each other or themselves, and can use any data types that have been declared or are built into the language. It is possible to call functions in other libraries, or to use their data types. "his is how layers of comple-ity are accumulated in C programming. As we saw in the previous chapter, the e-ecution of a C program always starts in the function called main. 4rom here it calls more and more functions, to perform all the actions it re<uires.

.ariables
4unctions in C consists of manipulating #aria&les. "hese are items of data which we give a name to. 1very variable in C has an e-plicit type. "hese types are declared by ourselves or built into the language. 'e can declare a new variable by writing the name of its type, followed by its name, and optionally setting it to some value using 3. "his declaration is called a statement, and we terminate all statements in C with a semicolon ;. "o create a new int called count we could write the following...
int count;

7r to declare it and set the value...
int count 3 61;

!ere are some descriptions and e-amples of some of the built in types. 1mpty "ype c&ar c&ar last'initial 3 7H7; )ingle CharacterJ yte int int age 3 23; Integer long long age'o8'universe 3 6394:111111; Integer that can hold larger values 8loat ?ecimal 3umber 8loat liters'per'pint 3 1%;<:8; double ?ecimal 3umber with more precision double speed'o8'swallow 3 1%16192:4<;
void

/unction &eclarations
A 4unction is a computation that manipulates variables, and optionally changes the state of the program. It takes as input some variables and returns some single variable as output. "o declare a function we write the type of the variable it returns, the name of the function, and then in parenthesis a list of the variables it takes as input , separated by commas. "he

%*

contents of the function is put inside curly brackets /2, and lists all of the statements the function e-ecutes, terminated by semicolons ;. A return statement is used to let the function finish and output a variable. 4or e-ample a function that takes two int variables called x and y and adds them together could look like this.
int add'toget&er+int x, int y. / int result 3 x = y; return result; 2

'e call functions by writting their name and putting the arguments to the function in parenthesis, separated by commas. 4or e-ample to call the above function and store the result in a variable added we would write the following.
int added 3 add'toget&er+61, 6:.;

!tructure &eclarations
)tructures are used to declare new types. )tructures are several variables bundled together into a single package. 'e can use structure to represent more comple- data types. 4or e-ample to represent a point in 0? space we could create a structure called point that packs together two 8loat DdecimalE values called x and y. "o declare structures we can use the struct keyword in conjunction with the typede8 keyword. 7ur declaration would look like this.
typede8 struct / 8loat x; 8loat y; 2 point;

'e should place this definition above any functions that wish to use it. "his type is no different to the built in types, and we can use it in all the same ways. "o access an individual field we use a dot %, followed by the name of the field, such as x.
point p; p%x 3 1%6; p%y 3 61%1; 8loat lengt& 3 s>rt+p%x - p%x = p%y - p%y.;

-ointers

%=

5ointer / A short haired one

A pointer is a variation on a normal type where the type name is suffi-ed with an asterisk. 4or e-ample we could declare a pointer to an integer by writing int-. 'e already saw a pointer type c&ar-- argv. "his is a pointer to pointers to characters, and is used as input to main function. 5ointers are used for a whole number of different things such as for strings or lists. "hese are a difficult part of C and will be e-plained in much greater detail in later chapters. 'e won't make use of them for a while, so for now it is good to simply know they e-ist, and how to spot them. ?on't let them scare you off$

!trings
In C strings are represented by the pointer type c&ar-. :nder the hood they are stored as a list of characters, where the final character is a special character called the null terminator. )trings are a complicated, and important part of C, which we'll learn to use effectively in the ne-t few chapters. )trings can also be declared literally by putting te-t between <uotation marks. 'e used this in the previous chapter with our string "Hello, $orld0". 4or now, remember that if you see c&ar-, you can read it as a string.

Conditionals

%;

Conditional statements let the program perform some code only if certain conditions are met. "o perform code under some condition we use the i8 statement. "his is written as i8 followed by some condition in parenthesis, followed by the code to e-ecute in curly brackets. An i8 statement can be followed by an optional else statement, followed by other statements in curly brackets. "he code in these brackets will be performed in the case the conditional is false. 'e can test for multiple conditions using the logical operators ?? for or, and @@ for and. Inside a conditional statement's parenthesis any value that is not 1 will evaluate to true. "his is important to remember as many conditions use this to check things implicitly. If we wished to check if an int called x was greater than 61 and less then 611, we would write the following.
i8 +x * 61 @@ x ) 611. / puts+"x is greater t&an ten and less t&an one &undred0".; 2 else / puts+"x is eit&er less t&an eleven or greater t&an ninety-nine0".; 2

Loops
Loops allow for some code to be repeated until some condition becomes false, or some counter elapses. "here are two main loops in C. "he first is a w&ile loop. "his loop repeatedly e-ecutes a block of code until some condition becomes false. It is written as w&ile followed by some condition in parenthesis, followed by the code to e-ecute in curly brackets. 4or e-ample a loop that counts downward from 61 to 6 could be written as follows.
int i 3 61; w&ile +i * 1. / puts+"Aoop Bteration".; i 3 i - 6; 2

"he second kind of loop is a 8or loop. Father than a condition, this loop re<uires three e-pressions separated by semicolons ;. "hese are an initialiser, a condition and an incrementer. "he initialiser is performed before the loop starts, the condition is checked at the end of each iteration of the loop, and if false the loop is e-ited. "he incrementer is performed before the ne-t iteration of the loop. "hese loops are often used for counting as they are more compact than the w&ile loop. 4or e-ample to write a loop that counts up from 1 to 4 we might write the following. In this case the == operator increments the variable i.
8or +int i 3 1; i ) 61; i==. / puts+"Aoop Bteration".;

%@

2

Bonus %ar+s
• • • • • • • • • • • •

H :se a 8or loop to print out Hello $orld0 five times. H :se a w&ile loop to print out Hello $orld0 five times. H ?eclare a function that outputs Hello $orld0 n number of times. Call this from main. H 'hat built in types are there other than the ones listedI H 'hat other conditional operators are there other than greater than *, and less than )I H 'hat other mathematical operators are there other than add =, and su&tract -I H 'hat is the =3 operator, and how does it workI H 'hat is the do loop, and how does it workI H 'hat is the switc& statement and how does it workI H 'hat is the breaC keyword and what does it doI H 'hat is the continue keyword and what does it doI H 'hat does the typede8 keyword do e-actlyI

%+

An Interactive Prompt • Chapter 4

'ead0 $,aluate0 -rint

Feptile / )ort of like F15L

As we build our programming language we'll need some way to interact with it. C uses a compiler, where you can change the program, recompile and run it. It'd be good if we could do something better, and interact with the language dynamically. "hen we test its behaviour under a number of conditions very <uickly. 4or this we can built something called an interactive prompt. "his is a program that prompts the user for some input, and when supplied with it, replies back with some message. :sing this will be the easiest way to test our programming language and see how it acts. "his system is also called a ()PL, which stands for read,evaluate,print loop. It is a common way of interacting with a programming language which you may have used before in languages such as Python. efore building a full ()PL we'll start with something simpler. 'e are going to make a system that prompts the user, and echoes any input straight back. If we make this we can later e-tend it to parse the user input and evaluate it, as if it were an actual Lisp program.

An 1nteracti,e -rompt
4or the basic setup we want to write a loop which repeatedly writes out a message, and then waits for some input. "o get user input we can use a function called 8gets, which reads any input up until a newline. 'e need somewhere to store this user input. 4or this we can declare a constantly si2ed input buffer. 7nce we have this user input stored we can then print it back to the user using a function called print8. %C

(include )stdio%&* 5- Declare a static bu88er 8or user input o8 maximum siEe 21F: -5 static c&ar inputG21F:H; int main+int argc, c&ar-- argv. / 5- Print Iersion and Jxit Bn8ormation -5 puts+"Aispy Iersion 1%1%1%1%6".; puts+"Press Ctrl=c to Jxit!n".; 5- Bn a never ending loop -5 w&ile +6. / 5- Kutput our prompt -5 8puts+"lispy* ", stdout.; 5- Lead a line o8 user input o8 maximum siEe 21F: -5 8gets+input, 21F:, stdin.; 5- Jc&o input bacC to user -5 print8+"Mo you7re a Ns", input.; 2 2 return 1;

What is that te#t in light green( "he above code contains comments. "hese are sections of the code between 5- -5 symbols, which are ignored by the compiler, but are used to inform the person reading what is going on. "ake notice of them$ Lets go over this program in a little more depth. "he line static c&ar inputG21F:H; declares a global array of 0&*+ characters. "his is a reserved block of data we can access anywhere from our program. In it we are going to store the user input which is typed into the command line. "he static keyword make this variable local to this file, and the G21F:H section is what declares the si2e. 'e write an infinite loop using w&ile +6.. In a conditional block 6 always evaluates to true. "herefore commands inside this loop will run forever. "o output our prompt we use the function 8puts. "his is a slight variation on puts which does not append a newline character. 'e use the 8gets function for getting user input from the command line. oth of these functions re<uire some file to write to, or read from. 4or this we supply the special variables stdin and stdout. "hese are declared in )stdio%&* and are special file variables representing input to, and output from, the command line. 'hen passed this variable the 8gets function will wait for a user to input a line of te-t, and when it has it will store it into the input buffer, including the newline character. )o that 8gets does not read in too much data we also must supply the si2e of the buffer 21F:. "o echo the message back to the user we use the function print8. "his is a function that provides a way of printing messages consisting of several elements. It matches arguments to

0&

patterns in the given string. 4or e-ample in our case we can see the Ns pattern in the given string. "his means that it will be replaced by whatever argument is passed in ne-t, interpreted as a string. 4or more information on these different patterns please see the documentation on print8. ow am 1 meant to +now about functions li+e fgets and printf( It isn't immediately obvious how to know about these standard functions, and when to use them. 'hen faced with a problem it takes e-perience to know when it has been solved for you by library functions. Luckily C has a very small standard library and almost all of it can be learnt in practice. If you want to do something that seems <uite basic, or fundamental, it is worth looking at the reference documentation for the standard library and checking if there are any functions included that do what you want.

Compilation
(ou can compile this with the same command as was used in the second chapter.
cc -std3c44 -$all prompt%c -o prompt

After compiling this you should try to run it. (ou can use Ctrl=c to <uit the program when you are done. If everything is correct your program should run something like this.
Aispy Iersion 1%1%1%1%6 Press Ctrl=c to Jxit lispy* &ello Mo Oou7re a &ello lispy* my name is Dan Mo Oou7re a my name is Dan lispy* Ptop being so rude0 Mo Oou7re a Ptop being so rude0 lispy*

$diting input
If you're working on Linu- or .ac you'll notice some weird behaviour when you use the arrow keys to attempt to edit your input.
Aispy Iersion 1%1%1%1%3 Press Ctrl=c to Jxit lispy* &elQGGDQGGC

0%

:sing the arrow keys is creating these weird characters QGGD or QGGC, rather than moving the cursor around in the input. 'hat we really want is to be able to move around on the line, deleting and editing the input in case we make a mistake. 7n 'indows this behaviour is the default. 7n Linu- and .ac it is provided by a library called editline. 7n Linu- and .ac we need to replace our calls to 8puts and 8gets with calls to functions this library provides. If you're developing on 'indows and just want to get going, feel free to skip to the end of this chapter as the ne-t few sections may not be relevant.

Using Editline
"he library editline provides two functions we are going to use called readline and add'&istory. "his first function, readline is used to read input from some prompt, while allowing for editing of that input. "he second function add'&istory lets us record the history of inputs so that they can be retrieved with the up and down arrows. 'e replace 8puts and 8gets with calls to these functions to get the following.
(include )stdio%&* (include )stdlib%&* (include )editline5readline%&* (include )editline5&istory%&* int main+int argc, c&ar-- argv. / 5- Print Iersion and Jxit Bn8ormation -5 puts+"Aispy Iersion 1%1%1%1%6".; puts+"Press Ctrl=c to Jxit!n".; 5- Bn a never ending loop -5 w&ile +6. / 5- Kutput our prompt and get input -5 c&ar- input 3 readline+"lispy* ".; 5- Add input to &istory -5 add'&istory+input.; 5- Jc&o input bacC to user -5 print8+"Mo you7re a Ns!n", input.; 5- Rree retrived input -5 8ree+input.; 2 return 1; 2

'e have included a few new headers. "here is (include )stdlib%&*, which gives us access to the 8ree function used later on in the code. 'e have also added (include

00

)editline5readline%&* and (include )editline5&istory%&* the editline functions, readline and add'&istory.

which give us access to

Instead of prompting, and getting input with 8gets, we do it in one go using readline. "he result of this we pass to add'&istory to record it. 4inally we print it out as before using print8. :nlike 8gets, the readline function strips the trailing newline character from the input, so we need to add this to our print8 function. 'e also need to delete the input given to us by the readline function using 8ree. "his is because unlike 8gets, which writes to some e-isting buffer, the readline function allocates new memory when it is called. 'hen to free memory is something we cover in depth in later chapters.

Compiling

ith Editline

If you try to compile this right away with the previous command you'll get an error. "his is because you first need to install the editline library on your computer.
8atal error editline5readline%& )editline5readline%&* Mo suc& 8ile or directory (include

7n Linu# this can be done using the command sudo apt-get install libedit-dev. 7n 4edora or similar you can use the command su -c "yum install libedit-dev-" 7n %ac the editline library should have been installed alongside the %ommand Line ools but if not you can install the readline library as a drop,in replacement using !omebrew or .ac5orts. 7nce you have installed this you can try to compile it again. "his time you'll get a different error.
unde8ined re8erence to Sreadline7 unde8ined re8erence to Sadd'&istory7

"his means that you haven't linked your program to editline. "his linking process allows the compiler to directly embed calls to editline in your program. (ou can make it link by adding the flag -ledit to your compile command, just before the output flag.
cc -std3c44 -$all prompt%c -ledit -o prompt

!opefully now you should be able to compile and link your program with editline. Fun it and check that now you can edit inputs as you type them in. 1t2s still not wor+ing! )ome systems might have slight variations on how to install, include, and link to editline. 4or e-ample if you are on Arch linu- the editline history header is &istedit%&. 7n some systems there is no editline history header at all. If you are having trouble search online and see if you can find distribution specific instructions on how to use editline or readline, an e<uivalent library.

09

"he C -reprocessor
4or such a small project it might be okay that we have to program differently depending on what operating system we are using, but if I want to send my source code to a friend on different operating system to give me a hand with the programming, it is going to cause problem. In an ideal world I'd wish for my source code to be able to compile no matter where, or on what computer, it is being compiled. "his is a general problem in C, and it is called porta&ility. "here is not always an easy or correct solution.

7ctopus / )ort of like 7ctothorpe

ut C does provide a mechanism to help, called the preprocessor. "he preprocessor is a program that runs before the compiler. It has a number of purposes, and we've been actually using it already without knowing. Any line that starts with a octothorpe ( character Dhash to you and meE is a preprocessor command. 'e've been using it to include header files, giving us access to functions from the standard library and others. Another use of the preprocessor is to detect which operating system the code is being compiled on, and to use this to emit different code. "his is e-actly how we are going to use it. If we are running 'indows we're going to let the preprocessor emit code with some fake readline and add'&istory functions I've prepared, otherwise we are going to include the headers from editline and use these. "o declare what code the compiler should emit we can wrap it in (i8de8, (else, and (endi8 preprocessor statements. "hese are like an i8 function that happens before the code is compiled. All the contents of the file from the first (i8de8 to the ne-t (else are used if the condition is true, otherwise all the contents from the (else to the final (endi8 are used instead. y putting these around our fake functions, and our editline headers, the code that is emitted should compile on 'indows, Linu- or .ac$
(include )stdio%&*

0*

(include )stdlib%&* 5- B8 we are compiling on $indows compile t&ese 8unctions -5 (i8de8 '$BM32 (include )string%&* static c&ar bu88erG21F:H; 5- RaCe readline 8unction -5 c&ar- readline+c&ar- prompt. / 8puts+"lispy* ", stdout.; 8gets+bu88er, 21F:, stdin.; c&ar- cpy 3 malloc+strlen+bu88er.=6.; strcpy+cpy, bu88er.; cpyGstrlen+cpy.-6H 3 7!17; return cpy; 2 5- RaCe add'&istory 8unction -5 void add'&istory+c&ar- unused. /2 5- Kt&erwise include t&e editline &eaders -5 (else (include )editline5readline%&* (include )editline5&istory%&* (endi8 int main+int argc, c&ar-- argv. / puts+"Aispy Iersion 1%1%1%1%6".; puts+"Press Ctrl=c to Jxit!n".; w&ile +6. / 5- Mow in eit&er case readline will be correctly de8ined -5 c&ar- input 3 readline+"lispy* ".; add'&istory+input.; print8+"Mo you7re a Ns!n", input.; 8ree+input.; 2 2 return 1;

'eference

0=

prompt)windows*c
(include )stdio%&* 5- Declare a static bu88er 8or user input o8 maximum siEe 21F: -5 static c&ar inputG21F:H; int main+int argc, c&ar-- argv. / 5- Print Iersion and Jxit Bn8ormation -5 puts+"Aispy Iersion 1%1%1%1%6".; puts+"Press Ctrl=c to Jxit!n".; 5- Bn a never ending loop -5 w&ile +6. / 5- Kutput our prompt -5 8puts+"lispy* ", stdout.; 5- Lead a line o8 user input o8 maximum siEe 21F: -5 8gets+input, 21F:, stdin.; 5- Jc&o input bacC to user -5 print8+"Mo you7re a Ns", input.; 2 2 return 1;

prompt)uni#*c
(include )stdio%&* (include )stdlib%&* (include )editline5readline%&* (include )editline5&istory%&* int main+int argc, c&ar-- argv. / 5- Print Iersion and Jxit Bn8ormation -5 puts+"Aispy Iersion 1%1%1%1%6".; puts+"Press Ctrl=c to Jxit!n".; 5- Bn a never ending loop -5 w&ile +6. / 5- Kutput our prompt and get input -5 c&ar- input 3 readline+"lispy* ".; 5- Add input to &istory -5 add'&istory+input.; 5- Jc&o input bacC to user -5 print8+"Mo you7re a Ns!n", input.; 5- Rree retrived input -5 8ree+input.; 2

0;

2

return 1;

prompt*c
(include )stdio%&* (include )stdlib%&* 5- B8 we are compiling on $indows compile t&ese 8unctions -5 (i8de8 '$BM32 (include )string%&* static c&ar bu88erG21F:H; 5- RaCe readline 8unction -5 c&ar- readline+c&ar- prompt. / 8puts+"lispy* ", stdout.; 8gets+bu88er, 21F:, stdin.; c&ar- cpy 3 malloc+strlen+bu88er.=6.; strcpy+cpy, bu88er.; cpyGstrlen+cpy.-6H 3 7!17; return cpy; 2 5- RaCe add'&istory 8unction -5 void add'&istory+c&ar- unused. /2 5- Kt&erwise include t&e editline &eaders -5 (else (include )editline5readline%&* (include )editline5&istory%&* (endi8 int main+int argc, c&ar-- argv. / puts+"Aispy Iersion 1%1%1%1%6".; puts+"Press Ctrl=c to Jxit!n".; w&ile +6. / 5- Mow in eit&er case readline will be correctly de8ined -5 c&ar- input 3 readline+"lispy* ".; add'&istory+input.; print8+"Mo you7re a Ns!n", input.; 8ree+input.; 2 return 1; 2

Bonus %ar+s

0@

• • • • • • • • •

H Change the prompt from lispy* to something of your choice. H Change what is echoed back to the user. H Add an e-tra message to the #ersion and )"it Information. H 'hat does the !n mean in those stringsI H 'hat other patterns can be used with print8. H 'hat happens when you pass print8 a variable does not match the patternI H 'hat does the preprocessor command (i8nde8 doI H 'hat does the preprocessor command (de8ine doI H If '$BM32 is defined on windows, what is defined for Linu- or .acI

0+

!anguages • Chapter "

What is a -rogramming Language(
A programming language is very similar to a real language. "here is a structure behind it, and some rules which dictate what is, and isn't, a valid thing to say. 'hen we read and write natural language, we are unconsciously learning these rules, and the same is true for programming languages. 'e can utilise these rules to understand others, and generate our own speech, or code. In the %C=&s the linguist *oam %homsky formalised a number of important observations about languages. .any of these form the basis of our understanding of language today. 7ne of these was the observation that natural languages are built up of recursive and repeated substructures.

Cat / cannot speak or program

As an e-ample of this, we can e-amine the phrase. H T&e cat walCed on t&e carpet% :sing the rules of 1nglish, the noun cat can be replaced by two nouns separated by and. H T&e cat and dog walCed on t&e carpet% 1ach of these new nouns could in turn be replaced again. 'e could use the same rule as before, and replace cat with two new nouns joined with and. 7r we could use a different rule and replace each of the nouns with an adjective and a noun, to add description to them. H T&e cat and mouse and dog walCed on t&e carpet% H T&e white cat and black dog walCed on t&e carpet%

0C

"hese are just two e-amples, but 1nglish has many different rules for how types of words can be changed, manipulated and replaced. 'e notice this e-act behaviour in programming languages too. In C, the body of an i8 statement contains a list of new statements. 1ach of these new statements, could themselves be another i8 statement. "hese repeated structures and replacements are reflected in all parts of the language. "hese are sometimes called re-write rules because they tell you how one thing can be re-written as something else. H i8 +x * ;. / return x; 2 H i8 +x * ;. / if (x > 10) { return x; } 2 "he conse<uence of this observation by *oam %homsky was important. It meant that although there is an infinite number of different things that can be said, or written down, in a particular languageK it is still possible to process and understand all of them, with a finite number of these re,write rules. "he name given to a set of these re,write rules is a grammar. 'e can describe re,write rules in a number of ways. 7ne way is te-tual. 'e could say something like, Ba sentence must contain a ver& phraseB, or Ba ver& phrase can be either a ver& or, an adver& and a ver&B. "his method is good for humans but it is too vague for computers to understand. 'hen programming we need to write down a more formal description of a grammar. "o write a programming language such as our Lisp we are going to need to understand grammars. 4or reading in the user input we need to write a grammar which describes it. "hen we can use it along with our user input, to decide if the input is valid. 'e can also use it to build a structured internal representation, which will make the job of understanding it, and then evaluating it, performing the computations encoded within, much easier. "his is where a library called mpc comes in.

-arser Combinators
is a Parser %om&inator library written by yours truly. "his means it is a library that allows you to build programs that understand and process particular languages. "hese are known as parsers. "here are many different ways of building parsers, but the nice thing about using a Parser %om&inator library is that it lets you build parsers easily, just by specifying the grammar ... sort of.
mpc

.any Parser %om&inator libraries actually work by letting you write normal code that looks a &it like a grammar, not by actually specifying a grammar directly. In many situations this is fine, but sometimes it can get clunky and complicated. Luckily for us mpc allows for us to write normal code that just looks like a grammar, or we can use special notation to write a grammar directly$

Coding 3rammars
9&

)o what does code that looks like a grammar..look likeI Let us take a look at mpc by trying to write code for a grammar that recogni2es the language of )hiba Inu. .ore collo<uially know as +oge. "his language we are going to define as follows. H An ,djective is either "wow", "many", "so" or "such". H A *oun is either "lisp", "language", "c", "&ook" or "&uild". H A Phrase is an ,djective followed by a *oun. H A +oge is 2ero or more Phrases. 'e can start by trying to define ,djective and *oun. "o do this we create two new parsers, represented by the type mpc'parser't-, and we store them in the variables AdTective and Moun. 'e use the function mpc'or to create a parser where one of several options should be used, and the function mpc'sym to wrap our initial strings. If you s<uint your eyes you could attempt to read the code as if it were the rules we specified above.
5- Uuild a new parser 7AdTective7 to recogniEe descriptions -5 mpc'parser't- AdTective 3 mpc'or+F, mpc'sym+"wow"., mpc'sym+"many"., mpc'sym+"so"., mpc'sym+"suc&". .; 5- Uuild a new parser 7Moun7 to recogniEe t&ings -5 mpc'parser't- Moun 3 mpc'or+;, mpc'sym+"lisp"., mpc'sym+"language"., mpc'sym+"c"., mpc'sym+"booC"., mpc'sym+"build". .;

ow can 1 access these mpc functions( 4or now don't worry about compiling or running any of the sample code in this chapter. Lust read it and see if you can understand the theory behind grammars. In the ne-t chapter we'll get setup with mpc and use it for a language closer to our Lisp. "o define P&rase we can reference our e-isting parsers. 'e need to use the function mpc'and, that specifies one thing is re<uired then another. As input we pass it AdTective and Moun, our previously defined parsers. "his function also takes the arguments mpc8'str8old and 8ree, which say how to join or delete the results of these parsers. Ignore these arguments for now.
mpc'parser't- P&rase 3 mpc'and+2, mpc8'str8old, AdTective, Moun, 8ree.;

"o define +oge we must specify that -ero or more of some parser is re<uired. 4or this we need to use the function mpc'many. As before, this function re<uires the special variable mpc8'str8old to say how the results are joined together, which we can ignore.

9%

mpc'parser't- Doge 3 mpc'many+mpc8'str8old, P&rase.;

y creating a parser that looks for -ero or more occurrences of another parser a cool thing has happened. 7ur Doge parser accepts inputs of any length. "his means its language is in'inite. !ere are just some e-amples of possible strings Doge could accept. Lust as we discovered in the first section of this chapter we have used a finite number of re,write rules to create an infinite language.
"wow booC suc& language many lisp" "so c suc& build suc& language" "many build wow c" "" "wow lisp wow c many language" "so c"

If we use more mpc functions, we can slowly build up parsers that parse more and more complicated languages. "he code we use sort o' reads like a grammar, but becomes much more messy with added comple-ity. ?ue to this, taking this approach isn't always an easy task. A whole set of helper functions that build on simple constructs to make fre<uent tasks easy are all documented on the mpc repository. "his is a good approach for complicated languages, as it allows for fine,grained control, but won't be re<uired for our needs.

4atural 3rammars
lets us write grammars in a more natural form too. Father than using C functions that look less like a grammar, we can specify the whole thing in one long string. 'hen using this method we don't have to worry about how to join or discard inputs, with functions such as mpc8'str8old, or 8ree. All of that is done automatically for us$
mpc

!ere is how we would recreate the previous e-amples using this method.
mpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tAdTective Moun P&rase Doge 3 3 3 3 mpc'new+"adTective".; mpc'new+"noun".; mpc'new+"p&rase".; mpc'new+"doge".;

mpca'lang+"PC'AAM#'DJRAVAT, " ! adTective !"wow!" ? !"many!" ? !"so!" ? !"suc&!"; ! noun !"lisp!" ? !"language!" ? !"c!" ? !"booC!" ? !"build!"; ! p&rase )adTective* )noun*; ! doge )p&rase*-; ! ", AdTective, Moun, P&rase, Doge.; 5- Do some parsing &ere%%% -5 mpc'cleanup+F, AdTective, Moun, P&rase, Doge.;

'ithout having an e-actly understanding of the synta- for that long string, it should be obvious how much clearer the grammar is in this format. If we learn what all the special symbols mean we barely have to s<uint our eyes to read it as one.

90

Another thing to notice is that the process is now in two steps. 4irst we create and name several rules using mpc'new and then we define them using mpca'lang. "he first argument to mpca'lang are the options flags. 4or this we just use the defaults. "he second is a long multi,line string in C. "his is the grammar specification. It consists of a number of re-write rules. 1ach rule has the name of the rule on the left, a colon , and on the right it's definition terminated with a semicolon ;. "he special symbols used to define the rules on the right hand side work as follows. "he string ab is re<uired. "he character a is re<uired. 7a7 7b7 4irst 7a7 is re<uired, then 7b7 is re<uired.. 7a7 ? 7b7 1ither 7a7 is re<uired, or 7b7 is re<uired. 7a7Mero or more 7a7 are re<uired. 7a7= 7ne or more 7a7 are re<uired. )abba* "he rule called abba is re<uired.
"ab" 7a7

!ounds familiar*** ?id you notice that the description of what the input string to mpca'lang should look like sort of sounded like I was specifying a grammarI "hat's because it was$ mpc uses itself internally to parse the input you give it to mpca'lang. It does it by specifying a grammar in code using the previous method. !ow neat is that.. :sing what is described above verify that what I've written above is e<ual to what we specified in code. "his method of specifying a grammar is what we are going to use in the following chapters. It might seem overwhelming at first. #rammars can be difficult to understand right away. ut as we continue you will become much more familiar with how to create and edit them. "his chapter is about theory, so if you are going to try some of the bonus marks, don't worry too much about correctness. "hinking in the right mindset is more important. 4eel free to invent symbols and notation for certain concepts to make them simpler to write down. )ome of the bonus marks also might re<uire cyclic or recursive grammar stuctures, so don't worry if you have to use these$

'eference
doge)code*c
(include "mpc%&" int main+int argc, c&ar-- argv. / 5- Uuild a new parser 7AdTective7 to recogniEe descriptions -5 mpc'parser't- AdTective 3 mpc'or+F, mpc'sym+"wow"., mpc'sym+"many".,

99

.;

mpc'sym+"so".,

mpc'sym+"suc&".

5- Uuild a new parser 7Moun7 to recogniEe t&ings -5 mpc'parser't- Moun 3 mpc'or+;, mpc'sym+"lisp"., mpc'sym+"language"., mpc'sym+"c"., mpc'sym+"booC"., mpc'sym+"build". .; mpc'parser't- P&rase 3 mpc'and+2, mpc8'str8old, AdTective, Moun, 8ree.; mpc'parser't- Doge 3 mpc'many+mpc8'str8old, P&rase.; 5- Do some parsing &ere%%% -5 mpc'delete+Doge.; return 1; 2

doge)grammar*c
(include "mpc%&" int main+int argc, c&ar-- argv. / mpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tAdTective Moun P&rase Doge 3 3 3 3 mpc'new+"adTective".; mpc'new+"noun".; mpc'new+"p&rase".; mpc'new+"doge".;

mpca'lang+"PC'AAM#'DJRAVAT, " ! adTective !"wow!" ? !"many!" ? !"so!" ? !"suc&!"; ! noun !"lisp!" ? !"language!" ? !"c!" ? !"booC!" ? !"build!"; ! p&rase )adTective* )noun*; ! doge )p&rase*-; ! ", AdTective, Moun, P&rase, Doge.; 5- Do some parsing &ere%%% -5 mpc'cleanup+F, AdTective, Moun, P&rase, Doge.; return 1; 2

Bonus %ar+s
9*

• • • • • • • •

H 'rite down some more e-amples of strings the Doge language contains. H 'hy are there back slashes ! in front of the <uote marks " in the grammarI H 'hy are there back slashes ! at the end of the line in the grammarI H ?escribe te-tually a grammar for decimal numbers such as 1%16 or ;2%226. H ?escribe te-tually a grammar for web :FLs such as &ttp 55www%buildyourownlisp%com. H ?escribe te-tually a grammar for simple 1nglish sentences such as t&e cat sat on t&e mat. H ?escribe more formally the above grammars. :se ?, -, or any symbols of your own invention. H If you are familiar with L)73, te-tually describe a grammar for it.

9=

Parsing • Chapter #

A 5olish 3obleman / A typical 5olish 3otation user

-olish 4otation
"o try out mpc we're going to implement a simple grammar that resembles a mathematical subset of our Lisp. It's called 5olish 3otation and is a notation for arithmetic where the operator comes before the operands. 4or e-ample... is = 6 2 < is = < +- 2 4. +61 - 2. 5 +F = 2. is 5 +- 61 2. += F 2.
6 = 2 = < < = +2 - 4.

'e need to work out a grammar which describes this notation. 'e can begin by describing it te"tually and then later formalise our thoughts. "o start, we observe that in polish notation the operator always comes first in an e-pression, followed by either numbers or other e-pressions in parenthesis. "his means we can say Ba program is an operator followed by one or more e"pressions,B where Ban e"pression is either a num&er, or, in parenthesis, an operator followed by one or more e"pressionsB. .ore formally...

9;

Program Jxpression Kperator Mumber

the start o' input, an Kperator, one or more Jxpression, and the end o' input. either a Mumber or 7+7, an Kperator, one or more Jxpression, and an 7.7. 7=7, 7-7, 7-7, or 757. an optional -, and one or more characters between 1 and 4

'egular $#pressions
'e should be able to encode most of the above rules using things we know already, but *um&er and Program might pose some trouble. "hey contain a couple of constructs we've not learnt how to e-press yet. 'e don't know how to e-press the start or end of input, optional characters, or ranges of characters. "hese can be e-pressed, but they re<uire something called a (egular )"pression. Fegular e-pressions are a way of writing grammars for small sections of te-t such as words or numbers. #rammars written using regular e-pressions can't consist of multiple rules, but they do give precise, and concise, control over what is matched and what isn't. !ere are some basic rules for writing regular e-pressions. "he character a is re<uired. Gabcde8H Any character in the set abcde8 is re<uired. Ga-8H Any character in the range a to 8 is re<uired. aW "he character a is optional. aMero or more of the character a are re<uired. a= 7ne or more of the character a are re<uired. Q "he start of input is re<uired. X "he end of input is re<uired.
a

"hese are all the regular e-pression rules we need for now. 'hole books have been written on learning regular e-pressions. 4or those curious much more information can be found online or from these sources. 'e will be using them in later chapters, so some basic knowledge will be re<uired, but you won't need to master them for now. In an mpc grammar we write regular e-pressions by putting them between forward slashes 5. :sing the above guide our *um&er rule can be e-pressed as a regular e-pression using the string 5-WG1-4H=5.

1nstalling mpc
efore we work on writing this grammar we first need to include the mpc headers, and then link to the mpc library, just as we did for editline on Linu- and .ac. )tarting with your code from chapter *, you can rename the file to parsing%c and download mpc%& and mpc%c from the mpc repo. 5ut these in the same directory as your source file.

9@

"o include mpc put (include "mpc%&" at the top of the file. "o link to mpc put mpc%c directly into the compile command. 7n linu- you will also have to link to the maths library by adding the flag -lm. 7n Linu# and %ac
cc -std3c44 -$all parsing%c mpc%c -ledit -lm -o parsing

7n Windows
cc -std3c44 -$all parsing%c mpc%c -o parsing

old on0 don2t you mean #include

mpc!h>(

"here are actually two ways to include files in C. 7ne is using angular brackets )* as we've seen so far, and the other is with <uotation marks "". "he only difference between the two is that using angular brackets searches the system locations for headers first, while <uotation marks searches the current directory first. ecause of this system headers such as )stdio%&* are typically put in angular brackets, while local headers such as "mpc%&" are typically put in <uotation marks.

-olish 4otation 3rammar
4ormalising the above rules further, and using some regular e-pressions, we can write a final grammar for the language of polish notation as follows. "ake a read over the below code and verify that it matches what we had written te-tually, and our ideas of polish notation.
5- Create Pome Parsers mpc'parser't- Mumber mpc'parser't- Kperator mpc'parser't- Jxpr mpc'parser't- Aispy -5 3 mpc'new+"number".; 3 mpc'new+"operator".; 3 mpc'new+"expr".; 3 mpc'new+"lispy".;

5- De8ine t&em wit& t&e 8ollowing Aanguage -5 mpca'lang+"PC'AAM#'DJRAVAT, " number 5-WG1-4H=5 ; operator 7=7 ? 7-7 ? 7-7 ? 757 ; expr )number* ? 7+7 )operator* )expr*= 7.7 ; lispy 5Q5 )operator* )expr*= 5X5 ; ", Mumber, Kperator, Jxpr, Aispy.;

! ! ! ! !

'e need to add this to the interactive prompt we started on in chapter *. 5ut this code right at the beginning of the main function before we print the #ersion and )"it information. At the end of our program we also need to delete the parsers when we are done with them. Fight before main returns we should place the following clean,up code.
5- Vnde8ine and Delete our Parsers -5 mpc'cleanup+F, Mumber, Kperator, Jxpr, Aispy.;

9+

12m getting an error undefined reference to "mpc#lang$ "hat should be mpca'lang, with an a at the end$

-arsing 5ser 1nput
7ur new code creates a mpc parser for our Polish *otation language, but we still need to actually use it on the user input supplied each time from the prompt. 'e need to edit our w&ile loop so that rather than just echoing user input back, it actually attempts to parse the input using our parser. 'e can do this by replacing the call to print8 with the following mpc code, that makes use of our program parser Aispy.
5- Attempt to Parse t&e user Bnput -5 mpc'result't r; i8 +mpc'parse+")stdin*", input, Aispy, @r.. / 5- Kn Puccess Print t&e APT -5 mpc'ast'print+r%output.; mpc'ast'delete+r%output.; 2 else / 5- Kt&erwise Print t&e Jrror -5 mpc'err'print+r%error.; mpc'err'delete+r%error.; 2

"his code calls the mpc'parse function with our parser Aispy, and the input string input. It copies the result of the parse into r and returns 6 on success and 1 on failure. 'e use the address of operator @ on r when we pass it to the function. "his operator will be e-plained in more detail in later chapters. 7n success a internal structure is copied into r, in the field output. 'e can print out this structure using mpc'ast'print and delete it using mpc'ast'delete. 7therwise there has been an error, which is copied into r in the field error. 'e can print it out using mpc'err'print and delete it using mpc'err'delete. Compile these updates, and take this program for a spin. "ry out different inputs and see how the system reacts. Correct behaviour should look like the following.
Aispy Iersion 1%1%1%1%2 Press Ctrl=c to Jxit lispy* = ; +- 2 2. * regex operator?c&ar 7=7 expr?number?regex 7;7 expr?* c&ar 7+7 operator?c&ar 7-7 expr?number?regex 727 expr?number?regex 727 c&ar 7.7 regex

9C

lispy* &ello )stdin* 1 1 error expected 7=7, 7-7, 7-7 or 757 at 7&7 lispy* 5 6dog @ cat )stdin* 1 3 error expected end o8 input at 7d7 lispy*

12m getting an error stdin>%0%0% error% &arser 'ndefined(* "his error is due to the synta- for your grammar supplied to mpca'lang being incorrect. )ee if you can work out what part of the grammar is incorrect. (ou can use the reference code for this chapter to help you find this, and verify how the grammar should look.

'eference
parsing*c
(include "mpc%&" (i8de8 '$BM32 static c&ar bu88erG21F:H; c&ar- readline+c&ar- prompt. / 8puts+"lispy* ", stdout.; 8gets+bu88er, 21F:, stdin.; c&ar- cpy 3 malloc+strlen+bu88er.=6.; strcpy+cpy, bu88er.; cpyGstrlen+cpy.-6H 3 7!17; return cpy; 2 void add'&istory+c&ar- unused. /2 (else (include )editline5readline%&* (include )editline5&istory%&* (endi8 int main+int argc, c&ar-- argv. / 5- Create Pome Parsers mpc'parser't- Mumber mpc'parser't- Kperator mpc'parser't- Jxpr mpc'parser't- Aispy -5 3 mpc'new+"number".; 3 mpc'new+"operator".; 3 mpc'new+"expr".; 3 mpc'new+"lispy".;

5- De8ine t&em wit& t&e 8ollowing Aanguage -5 mpca'lang+"PC'AAM#'DJRAVAT, " number 5-WG1-4H=5 ; operator 7=7 ? 7-7 ? 7-7 ? 757 ; expr )number* ? 7+7 )operator* )expr*= 7.7 ; lispy 5Q5 )operator* )expr*= 5X5 ; ", Mumber, Kperator, Jxpr, Aispy.;

! ! ! ! !

*&

puts+"Aispy Iersion 1%1%1%1%2".; puts+"Press Ctrl=c to Jxit!n".; w&ile +6. / c&ar- input 3 readline+"lispy* ".; add'&istory+input.; 5- Attempt to parse t&e user input -5 mpc'result't r; i8 +mpc'parse+")stdin*", input, Aispy, @r.. / 5- Kn success print and delete t&e APT -5 mpc'ast'print+r%output.; mpc'ast'delete+r%output.; 2 else / 5- Kt&erwise print and delete t&e Jrror -5 mpc'err'print+r%error.; mpc'err'delete+r%error.; 2 8ree+input.; 2 5- Vnde8ine and delete our parsers -5 mpc'cleanup+F, Mumber, Kperator, Jxpr, Aispy.; return 1; 2

Bonus %ar+s
• • • • • • • •

H 'rite a regular e-pression matching strings of all a or b such as aababa or bbaa. H 'rite a regular e-pression matching strings of consecutive a and b such as ababab or aba. H 'rite a regular e-pression matching pit, pot and respite but not peat, spit, or part. H Change the grammar to add a new operator such as N. H Change the grammar to recogni2e operators written in te-tual format add, sub, mul, div. H Change the grammar to recogni2e decimal numbers such as 1%16, ;%26, or 61%2. H Change the grammar to make the operators written conventionally, between two e-pressions. H :se the grammar from the previous chapter to parse Doge. (ou must add start and end of input$

*%

Evaluation • Chapter $

"rees
3ow we can read input, and we have it structured internally, but we are still unable to evaluate it. In this chapter we add the code that evaluates this structure and actually performs the computations encoded within. "his internal structure is what we saw printed out by the program in the previous chapter. It is called an ,&stract $ynta" ree, and it represents the structure of the program based on the input entered by the user. At the leaves of this tree are numbers and operators , the actual data to be processed. At the branches are the rules used to produce this part of the tree , the information on how to traverse and evaluate it.

Abstract Christmas "ree / A seasonal variation

*0

efore working out e-actly how we are going to do this traversal, lets see e-actly how this structure is defined internally. If we peek inside mpc%& we can have a look at the definition of mpc'ast't, which is the data structure we got from the parse.
typede8 struct mpc'ast't / c&ar- tag; c&ar- contents; int c&ildren'num; struct mpc'ast't-c&ildren; 2 mpc'ast't;

"his struct has a number of fields we can access. Lets take a look at them one by one. "he first field is tag. 'hen we printed out the tree this was the information that precluded the contents of the node. It was a string containing a list of all the rules used to parse that particular item. 4or e-ample expr?number?regex. "his tag field is going to be important as it lets us see what type of thing the node is. "he second field is contents. "his will contain the actual contents of the node such as 7-7, 7+7 or 7;7. (ou'll notice for branches this is empty, but for leaves we can use it to find the operator or number to use. 4inally we see two fields that are going to help us traverse the tree. "hese are c&ildren'num and c&ildren. "he first field tells us how many children a node has, and the second is an array of these children. "he type of the c&ildren field is mpc'ast't--. "his is a double pointer type we've not seen before. "his will be e-plained in greater detail in later chapters. 4or now you can think of it as a list of the child nodes of the this tree. 'e can access a child node, by accessing this field using array notation. "his is done by writing the field name c&ildren and suffi-ing it with s<uare brackets containing the inde- of the child to access. 4or e-ample to access the first child of the node we can use c&ildrenG1H. 3otice that C counts its array indices from 1. ecause the type mpc'ast't- is a pointer to a struct, there is a slightly different synta- to access its fields. 'e need to use an arrow -* instead of a dot %. "here is no fundamental reason for this switch in operators, so for now just remember that field access of pointer types uses an arrow.
5- Aoad APT 8rom output -5 mpc'ast't- a 3 r%output; print8+"Tag Ns!n", a-*tag.; print8+"Contents Ns!n", a-*contents.; print8+"Mumber o8 c&ildren Ni!n", a-*c&ildren'num.; 5- #et Rirst C&ild -5 mpc'ast't- c1 3 a-*c&ildrenG1H; print8+"Rirst C&ild Tag Ns!n", c1-*tag.; print8+"Rirst C&ild Contents Ns!n", c1-*contents.; print8+"Rirst C&ild Mumber o8 c&ildren Ni!n", c1-*c&ildren'num.;

*9

'ecursion
"here is a funny thing about this tree structure. It refers to itself. 1ach of its children are themselves trees again, and the children of those children are trees yet again. Lust like our languages, and re,write rules, data in this structure contains repeated substructures, that resemble their parents.

Fecursion / ?angerous in a fire.

"his pattern of repeated substructures could go on and on. Clearly if we want a function which can work on all possible trees we can't look just a couple of nodes down, we have to define it to work on trees of any depth. Luckily we can do this, by e-ploiting the nature of how these substructures repeat, and using a techni<ue called recursion. 5ut simply a recursive 'unction is one that calls itself as some part of its calculation. It sounds weird for a function to be defined in terms of itself. ut consider that functions can give different outputs when supplied with different inputs. If we give some changed, or different inputs to a recursive call to the same function, and provide a way for this function to not call itself again under certain conditions, we can be more confident this recursive 'unction is doing something useful. As an e-ample we can write a recursive function which will count the number of nodes in our tree structure.

**

"o begin we work out how it will act in the most simple case, if the input tree has no children. In this case we know the result is simply one. 3ow we can go on to define the more comple- case, if the tree has one or more children. In this case the result will be one Dfor the node itselfE, plus the number of nodes in all of those children. ut how do we find the number of nodes in all of the childrenI 'ell we can use the function we are in the process defining$ .eah/ (ecursion0 In C we might write it something like this.
int number'o8'nodes+mpc'ast't- t. / i8 +t-*c&ildren'num 33 1. / return 6; 2 i8 +t-*c&ildren'num *3 6. / int total 3 6; 8or +int i 3 1; i ) t-*c&ildren'num; i==. / total 3 total = number'o8'nodes+t-*c&ildrenGiH.; 2 return total; 2 2

Fecursive functions are weird because they re<uire an odd leap of faith. 4irst we have to assume we have some function which does something correctly already, and then we have to go about using this function, to write the initial function we assumed we had$ Like most things, recursive functions almost always end up following a similar pattern. 4irst a &ase case is defined. "his is the case that ends the recursion, such as t-*c&ildren'num 33 1 in our previous e-ample. After this the recursive case is defined, such as t-*c&ildren'num *3 6 in our previous e-ample, which breaks down a computation into smaller parts, and calls itself recursively to compute those parts, before combining them together. Fecursive functions can take some thought, so pause now and ensure you understand them before continuing onto other chapters because we'll be making good use of them in the rest of the book. If you are still uncertain, you can check out some of the bonus marks for this chapter to practice.

$,aluation
"o evaluate the parse tree we are going to write a recursive function. ut before we get started, let us try and see what observations we can make about the structure of the tree we get as input. "ry printing out some e-pressions using your program from the previous chapter. )ee what you can notice.
lispy* = 6 +- ; F. * regex operator?c&ar 7=7 expr?number?regex 767 expr?* c&ar 7+7 operator?c&ar 7-7 expr?number?regex 7;7

*=

expr?number?regex c&ar 7.7 regex

7F7

7ne observation is that if a node is tagged with number it is always a number, has no children, and we can just convert the contents to an integer. "his will act as the &ase case in our recursion. If a node is tagged with expr, and is not a number, we need to look at its second child Dthe first child is always 7+7E and see which operator it is. "hen we need to apply this operator to the evaluation of the remaining children, e-cluding the final child which is always 7.7. "his is our recursive case. "his also needs to be done for the root node. 'hen we evaluate our tree, just like when counting the nodes, we'll need to accumulate the result. "o represent this result we'll use the C type long which means a long integer. "o detect the tag of a node, or to get a number from a node, we will need to make use of the tag and contents fields. "hese are string fields, so we are going to have to learn a couple of string functions first. Converts a c&ar- to a long. "akes as input two c&ar- and if they are e<ual it returns 1. "akes as input two c&ar- and returns a pointer to the location of the second in the strstr first, or 1 if the second is not a sub,string of the first.
atoi strcmp

'e can use strcmp to check which operator to use, and strstr to check if a tag contains some substring. Altogether our recursive evaluation function looks like this.
long eval+mpc'ast't- t. / 5- B8 tagged as number return it directly, ot&erwise expression% -5 i8 +strstr+t-*tag, "number".. / return atoi+t-*contents.; 2 5- T&e operator is always second c&ild% -5 c&ar- op 3 t-*c&ildrenG6H-*contents; 5- $e store t&e t&ird c&ild in SxS -5 long x 3 eval+t-*c&ildrenG2H.; 5- Bterate t&e remaining c&ildren, combining using our operator -5 int i 3 3; w&ile +strstr+t-*c&ildrenGiH-*tag, "expr".. / x 3 eval'op+x, op, eval+t-*c&ildrenGiH..; i==; 2 return x; 2

'e can define the eval'op function as follows. It takes in a number, an operator string, and another number. It tests for which operator is passed in, an performs the corresponding C operation on the inputs.
5- Vse operator string to see w&ic& operation to per8orm -5

*;

long eval'op+long x, c&ar- op, long y. i8 +strcmp+op, "=". 33 1. / return x i8 +strcmp+op, "-". 33 1. / return x i8 +strcmp+op, "-". 33 1. / return x i8 +strcmp+op, "5". 33 1. / return x return 1; 2

/ = 5

y; y; y; y;

2 2 2 2

-rinting
Instead of printing the tree we now want to print the result of the evaluation. "herefore we need to pass the tree into our eval function, and print the result we get using print8 and the specifier Nli, which is used for long type. 'e also need to remember to delete the output tree after we are done evaluating it.
long result 3 eval+r%output.; print8+"Nli!n", result.; mpc'ast'delete+r%output.;

If all of this is successful we should be able to do some basic maths with our new programming language$
Aispy Iersion 1%1%1%1%3 Press Ctrl=c to Jxit lispy* = ; < 66 lispy* - +- 61 61. += 6 6 6. 49 lispy* - +5 61 2. 21 -6; lispy*

'eference
e,aluation*c
(include "mpc%&" (i8de8 '$BM32 static c&ar bu88erG21F:H; c&ar- readline+c&ar- prompt. / 8puts+"lispy* ", stdout.; 8gets+bu88er, 21F:, stdin.; c&ar- cpy 3 malloc+strlen+bu88er.=6.; strcpy+cpy, bu88er.; cpyGstrlen+cpy.-6H 3 7!17; return cpy; 2 void add'&istory+c&ar- unused. /2

*@

(else (include )editline5readline%&* (include )editline5&istory%&* (endi8 5- Vse operator string to see w&ic& operation to per8orm -5 long eval'op+long x, c&ar- op, long y. / i8 +strcmp+op, "=". 33 1. / return x = y; 2 i8 +strcmp+op, "-". 33 1. / return x - y; 2 i8 +strcmp+op, "-". 33 1. / return x - y; 2 i8 +strcmp+op, "5". 33 1. / return x 5 y; 2 return 1; 2 long eval+mpc'ast't- t. / 5- B8 tagged as number return it directly, ot&erwise expression% -5 i8 +strstr+t-*tag, "number".. / return atoi+t-*contents.; 2 5- T&e operator is always second c&ild% -5 c&ar- op 3 t-*c&ildrenG6H-*contents; 5- $e store t&e t&ird c&ild in SxS -5 long x 3 eval+t-*c&ildrenG2H.; 5- Bterate t&e remaining c&ildren, combining using our operator -5 int i 3 3; w&ile +strstr+t-*c&ildrenGiH-*tag, "expr".. / x 3 eval'op+x, op, eval+t-*c&ildrenGiH..; i==; 2 return x; 2 int main+int argc, c&ar-- argv. / mpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tMumber 3 mpc'new+"number".; Kperator 3 mpc'new+"operator".; Jxpr 3 mpc'new+"expr".; Aispy 3 mpc'new+"lispy".; ! ! ! ! !

mpca'lang+"PC'AAM#'DJRAVAT, " number 5-WG1-4H=5 ; operator 7=7 ? 7-7 ? 7-7 ? 757 ; expr )number* ? 7+7 )operator* )expr*= 7.7 ; lispy 5Q5 )operator* )expr*= 5X5 ; ", Mumber, Kperator, Jxpr, Aispy.; puts+"Aispy Iersion 1%1%1%1%3".; puts+"Press Ctrl=c to Jxit!n".; w&ile +6. / c&ar- input 3 readline+"lispy* ".; add'&istory+input.;

*+

mpc'result't r; i8 +mpc'parse+")stdin*", input, Aispy, @r.. / long result 3 eval+r%output.; print8+"Nli!n", result.; mpc'ast'delete+r%output.; 2 else / mpc'err'print+r%error.; mpc'err'delete+r%error.; 2 8ree+input.; 2 mpc'cleanup+F, Mumber, Kperator, Jxpr, Aispy.; return 1; 2

Bonus %ar+s
• • • • • • • • • •

H 'rite a recursive function to compute the number of leaves of a tree. H 'rite a recursive function to compute the number of branches of a tree. H 'rite a recursive function to compute the most number of children spanning from one branch of a tree. H !ow would you use strstr to see if a node was tagged as an expr. H !ow would you use strcmp to see if a node had the contents 7+7 or 7.7. H Add the operator N, which returns the remainder of division. 4or e-ample N 61 < is F. H Add the operator Q, which raises one number to another. 4or e-ample Q F 2 is 6<. H Add the function min, which returns the smallest number. 4or e-ample min 6 ; 3 is 6. H Add the function max, which returns the biggest number. 4or e-ample max 6 ; 3 is ;. H Change the minus operator - so that when it receives one argument it negates it.

*C

Error %andling • Chapter &

Crashes
)ome of you may have noticed a problem with the previous chapter's program. "ry entering this into the prompt and see what happens.
Aispy Iersion 1%1%1%1%3 Press Ctrl=c to Jxit lispy* 5 61 1

7uch. "he program crashed upon trying to divide by 2ero. It's okay if a program crashes during development, but our final program would hopefully never crash, and always e-plain to the user what went wrong. At the moment our program can produce synta- errors but it still has no functionality for reporting errors in the evaluation of e-pressions. 'e need to build in some kind of error handling functionality to do this. It can be awkward in C, but if we start off on the right track, it will pay off later on when our system gets more complicated.

Lisp .alue
"here are several ways to deal with errors in C, but in this conte-t my preferred method is to make errors a possible result of evaluating an e-pression. "hen we can say that, in Lispy, an e-pression will evaluate to either a num&er, or an error. 4or e-ample = 6 2 will evaluate to a number, but 5 61 1 will evaluate to an error. 4or this we need a data structure that can act as either one thing or anything. 4or simplicity sake we are just going to use a struct with fields specific to each thing that can be represented, and a special field type to tell us e-actly what fields are meaningful to access. "his we are going to call an lval, which stands for Lisp #alue.
5- Declare Mew lval Ptruct -5 typede8 struct / int type; long num; int err; 2 lval;

$numerations
(ou'll notice the type of the fields type, and err, is int. "his means they are represented by a single integer number.

=&

"he reason we pick int is because we will assign meaning to each integer value, to encode what we re<uire. 4or e-ample we can make a rule "I' type is 0 then the structure is a *um&er0", or "I' type is 1 then the structure is an )rror0" "his is a good way of doing things. It is simple and effective. ut if we litter our code with stray 1 and 6 then it is going to become increasingly unclear as to what is happening. Instead we can use named constants that have been assigned these integer values. "his gives the reader an indication as to why one might be comparing a number to 1 or 6 and what is meant in this conte-t. In C this is supported using something called an enum.
5- Create Jnumeration o8 Possible lval Types -5 enum / AIAA'MV", AIAA'JLL 2;

An enum is a declaration of variables which under the hood are automatically assigned integer constant values. Above is how we would declare some enumerated values for the type field. 'e also want to declare an enumeration for the error field. 'e have three error cases in our particular program. "here is division by 2ero, an unknown operator, or being passed a number that is too large to represented internally using a long. "hese can be enumerated as follows.
5- Create Jnumeration o8 Possible Jrror Types -5 enum / AJLL'DBI'YJLK, AJLL'UAD'KP, AJLL'UAD'MV" 2;

Lisp .alue /unctions
7ur lval type is almost ready to go. :nlike the previous long type we have no current method for creating new instances of it. "o do this we can declare two functions that construct an lval of either an error type or a num&er type.
5- Create a new number type lval -5 lval lval'num+long x. / lval v; v%type 3 AIAA'MV"; v%num 3 x; return v; 2 5- Create a new error type lval -5 lval lval'err+int x. / lval v; v%type 3 AIAA'JLL; v%err 3 x; return v; 2

"hese functions first create an lval called v, and assign the fields before returning it.

=%

ecause our lval function can now be one of two things we can no longer just use print8 to output it. 'e will want to do different behaviour depending upon the type of the lval that is given. "here is a concise way to do this in C using the switc& statement. "his takes some value as input and compares it to other known values, known as cases. 'hen the values are e<ual it e-ecutes the code that follows up until the ne-t breaC statement. :sing this we can build a function that can print an lval of any type like this.
5- Print an "lval" -5 void lval'print+lval v. / switc& +v%type. / 5- Bn t&e case t&e type is a number print it, t&en 7breaC7 out o8 t&e switc&% -5 case AIAA'MV" print8+"Nli", v%num.; breaC; 5- Bn t&e case t&e type is an case AIAA'JLL 5- C&ecC $&at exact type o8 i8 +v%err 33 AJLL'DBI'YJLK. i8 +v%err 33 AJLL'UAD'KP. i8 +v%err 33 AJLL'UAD'MV". breaC; 2 2 error -5 error it is and print it -5 / print8+"Jrror Division Uy Yero0".; 2 / print8+"Jrror Bnvalid Kperator0".; 2 / print8+"Jrror Bnvalid Mumber0".; 2

5- Print an "lval" 8ollowed by a newline -5 void lval'println+lval v. / lval'print+v.; putc&ar+7!n7.; 2

$,aluating $rrors
3ow that we know how to work with the lval type, we need to change our evaluation functions to use it instead of long. As well as changing the type signatures we need to change the functions such that they work correctly upon encountering either an error as input, or a num&er as input. In our eval'op function, if we encounter an error we should return it right away, and only do computation if both the arguments are numbers. 'e also should add our code to return an error rather than attempt to divide by 2ero. "o fi- the crash from right at the beginning of this chapter.
lval eval'op+lval x, c&ar- op, lval y. / 5- B8 eit&er value is an error return it -5 i8 +x%type 33 AIAA'JLL. / return x; 2 i8 +y%type 33 AIAA'JLL. / return y; 2 5i8 i8 i8 i8 Kt&erwise do mat&s on t&e number values -5 +strcmp+op, "=". 33 1. / return lval'num+x%num = y%num.; 2 +strcmp+op, "-". 33 1. / return lval'num+x%num - y%num.; 2 +strcmp+op, "-". 33 1. / return lval'num+x%num - y%num.; 2 +strcmp+op, "5". 33 1. / 5- B8 second operand is Eero return error instead o8 result -5 return y%num 33 1 W lval'err+AJLL'DBI'YJLK. lval'num+x%num 5 y%num.;

=0

2 2 return lval'err+AJLL'UAD'KP.;

What is that ) doing there( (ou'll notice that for division to check if the second argument is 2ero we use a <uestion mark symbol W, followed by a colon . "his is called the ternary operator, and it allows you to write conditional e-pressions on one line. It works something like this. )condition* W )t&en* )else*. In other words, if the condition is true it returns what follows the W, otherwise it returns what follows . )ome people dislike this operator because they believe it makes code unclear. If you feel unfamiliar with it it can seem awkward, but once you get to know it there are rarely problems. 'e need to give a similar treatment to our eval function. In this case because we've defined eval'op to robustly handle errors we just need to add the error conditions to our number conversion function. In this case we us the strtol function to convert from string to long. "his lets use check a special variable errno to ensure the conversion goes correctly. "his is a more robust way to convert numbers than our previous method using atoi.
lval eval+mpc'ast't- t. / i8 +strstr+t-*tag, "number".. / 5- C&ecC i8 t&ere is some error in conversion -5 long x 3 strtol+t-*contents, MVAA, 61.; return errno 03 JLAM#J W lval'num+x. lval'err+AJLL'UAD'MV".; 2 c&ar- op 3 t-*c&ildrenG6H-*contents; lval x 3 eval+t-*c&ildrenG2H.; int i 3 3; w&ile +strstr+t-*c&ildrenGiH-*tag, "expr".. / x 3 eval'op+x, op, eval+t-*c&ildrenGiH..; i==; 2 return x; 2

"he final small step is to change how we print the result found by our evaluation to use our newly defined printing function which can print any type of lval.
lval result 3 eval+r%output.; lval'println+result.; mpc'ast'delete+r%output.;

=9

And we are done$ "ry running this new program and make sure there are no crashes when dividing by 2ero$
lispy* 5 61 1 Jrror Division Uy Yero0 lispy* 5 61 2 ;

-lumbing

5lumbing / !arder than you think

)ome of you who have gotten this far in the book may feel uncomfortable with how it is progressing. (ou may feel you've managed to follow instructions well enough, but don't have a clear understanding of all of the underlying mechanisms going on behind the scenes. If this is the case I want to reassure you that you are doing well. If you don't understand the internals it's because I have not e-plained everything in sufficient depth. "his is okay. "o be able to progress and get code to work under these conditions is a really great skill in programming, and if you've made it this far it shows you have it. In programming we call this plum&ing. Foughly speaking this is following instructions to try and tie together a bunch libraries or components, without fully understanding how they work internally. It re<uires 'aith and intuition. 1aith is re<uired to believe that if the stars align, and every incantation is correctly performed for this magical machine, the right thing really will happen. And intuition is re<uired to work out what has gone wrong, and how to fi- things when they don't go as planned. :nfortunately these can't be taught directly, so if you've made it this far then congratulations! (ou've made it over a difficult hump, and in the following chapters I =*

promise we'll finish up with the plumbing, and actually start to programming that feels fresh and wholesome.

'eference
error)handling*c
(include "mpc%&" (i8de8 '$BM32 static c&ar bu88erG21F:H; c&ar- readline+c&ar- prompt. / 8puts+"lispy* ", stdout.; 8gets+bu88er, 21F:, stdin.; c&ar- cpy 3 malloc+strlen+bu88er.=6.; strcpy+cpy, bu88er.; cpyGstrlen+cpy.-6H 3 7!17; return cpy; 2 void add'&istory+c&ar- unused. /2 (else (include )editline5readline%&* (include )editline5&istory%&* (endi8 5- Create Jnumeration o8 Possible Jrror Types -5 enum / AJLL'DBI'YJLK, AJLL'UAD'KP, AJLL'UAD'MV" 2; 5- Create Jnumeration o8 Possible lval Types -5 enum / AIAA'MV", AIAA'JLL 2; 5- Declare Mew lval Ptruct -5 typede8 struct / int type; long num; int err; 2 lval; 5- Create a new number type lval -5 lval lval'num+long x. / lval v; v%type 3 AIAA'MV"; v%num 3 x; return v; 2 5- Create a new error type lval -5 lval lval'err+int x. / lval v; v%type 3 AIAA'JLL; v%err 3 x; return v;

==

2 5- Print an "lval" -5 void lval'print+lval v. / switc& +v%type. / 5- Bn t&e case t&e type is a number print it, t&en 7breaC7 out o8 t&e switc&% -5 case AIAA'MV" print8+"Nli", v%num.; breaC; 5- Bn t&e case t&e type is an case AIAA'JLL 5- C&ecC $&at exact type o8 i8 +v%err 33 AJLL'DBI'YJLK. i8 +v%err 33 AJLL'UAD'KP. i8 +v%err 33 AJLL'UAD'MV". breaC; 2 2 error -5 error it is and print it -5 / print8+"Jrror Division Uy Yero0".; 2 / print8+"Jrror Bnvalid Kperator0".; 2 / print8+"Jrror Bnvalid Mumber0".; 2

5- Print an "lval" 8ollowed by a newline -5 void lval'println+lval v. / lval'print+v.; putc&ar+7!n7.; 2 lval eval'op+lval x, c&ar- op, lval y. / 5- B8 eit&er value is an error return it -5 i8 +x%type 33 AIAA'JLL. / return x; 2 i8 +y%type 33 AIAA'JLL. / return y; 2 5i8 i8 i8 i8 2 2 lval eval+mpc'ast't- t. / i8 +strstr+t-*tag, "number".. / 5- C&ecC i8 t&ere is some error in conversion -5 long x 3 strtol+t-*contents, MVAA, 61.; return errno 03 JLAM#J W lval'num+x. lval'err+AJLL'UAD'MV".; 2 c&ar- op 3 t-*c&ildrenG6H-*contents; lval x 3 eval+t-*c&ildrenG2H.; int i 3 3; w&ile +strstr+t-*c&ildrenGiH-*tag, "expr".. / x 3 eval'op+x, op, eval+t-*c&ildrenGiH..; i==; 2 return x; 2 int main+int argc, c&ar-- argv. / Kt&erwise do mat&s on t&e number values -5 +strcmp+op, "=". 33 1. / return lval'num+x%num = y%num.; 2 +strcmp+op, "-". 33 1. / return lval'num+x%num - y%num.; 2 +strcmp+op, "-". 33 1. / return lval'num+x%num - y%num.; 2 +strcmp+op, "5". 33 1. / 5- B8 second operand is Eero return error instead o8 result -5 return y%num 33 1 W lval'err+AJLL'DBI'YJLK. lval'num+x%num 5 y%num.;

return lval'err+AJLL'UAD'KP.;

=;

mpc'parser'tmpc'parser'tmpc'parser'tmpc'parser't-

Mumber 3 mpc'new+"number".; Kperator 3 mpc'new+"operator".; Jxpr 3 mpc'new+"expr".; Aispy 3 mpc'new+"lispy".; ! ! ! ! !

mpca'lang+"PC'AAM#'DJRAVAT, " number 5-WG1-4H=5 ; operator 7=7 ? 7-7 ? 7-7 ? 757 ; expr )number* ? 7+7 )operator* )expr*= 7.7 ; lispy 5Q5 )operator* )expr*= 5X5 ; ", Mumber, Kperator, Jxpr, Aispy.; puts+"Aispy Iersion 1%1%1%1%F".; puts+"Press Ctrl=c to Jxit!n".; w&ile +6. / c&ar- input 3 readline+"lispy* ".; add'&istory+input.; mpc'result't r; i8 +mpc'parse+")stdin*", input, Aispy, @r.. / lval result 3 eval+r%output.; lval'println+result.; mpc'ast'delete+r%output.; 2 else / mpc'err'print+r%error.; mpc'err'delete+r%error.; 2 8ree+input.; 2 mpc'cleanup+F, Mumber, Kperator, Jxpr, Aispy.; return 1; 2

Bonus %ar+s
• • • • • •

H !ow do you give an enum a nameI H 'hat are union data types and how do they workI H Can you change how lval is defined to use union instead of structI H 'hat are the advantages over using a union instead of structI H 1-tend parsing and evaluation to support the remainder operator N. H 1-tend parsing and evaluation to support decimal types using a double field.

=@

'(E)pressions • Chapter *

Lists and Lisps

ALL CA5) / )7 FI#!" (1" )7 'F73#.

Lisps are famous for having little distinction between data and code. "hey use the same structures to represent both. "his allows them to do many powerful things other languages cannot. If we want this power for our programming language we're going to have to separate out the process of reading in input, and evaluating that input we have stored. "he final result of this chapter will only differ slightly in behaviour from the previous chapter. "his is because we are going to spend time changing how things work internally. "his is called re-'actoring and it will make our life a lot easier later on. Like preparation for a meal, just because we're not putting food onto plates it doesn't mean we're wasting time. )ometimes the anticipation is even better than eating$ "o store the program we will need to create an internal list structure that is built up recursively of numbers, symbols, and other lists. In Lisp, this structure is commonly called an ),1-pression standing for $ym&olic )"pression. 'e will e-tend our lval structure to be able to represent it. "he evaluation behaviour of ),1-pressions is the behaviour typical of Lisps, that we are used to so far. "o evaluate an ),1-pression we look at the first item in the list, and take this to be the operator. 'e then look at all the other items in the list, and take these as operands to get the result. y introducing ),1-pressions we'll finally be entering the world of Lisp.

-ointers

=+

In C no concept of lists can be e-plored without dealing properly with pointers. 5ointers are a famously misunderstood aspect of C. "hey are difficult to teach because while being conceptually very simple, they come with a lot of new terminology, and often no clear use, case. "his makes them appear far more monstrous than they are. Luckily for us, we have a couple ideal use,cases, both of which are e-tremely typical in C, and will likely end up being how you use pointers C&N of the time. "he reason we need pointers in C is because of how function calling works. 'hen you call a function in C the arguments are always passed &y value. "his means a copy of them is passed to the function you call. "his is true for int, long, c&ar, and user defined struct types such as lval. .ost of the time this is great but occasionally it can cause issues. A common problem is if we have a large struct containing many other sub structs we wish to pass around. 1very time we call a function we must create another copy of it. )uddenly the amount of data that needs to be copied around just to call a function can become huge$ A second problem is this. 'hen we define a struct, it is always a fi-ed si2e. It has a limited number of fields, and each of these fields must be a struct which itself is limited in si2e. If I want to call a function with just a list o' things, where the number of things varies from call to call, clearly I can't use a struct to do this. "o get around these issues the developers of C Dor y'know...someoneE came up with a clever idea. "hey imagined computer memory as a single huge list of bytes. In this list each byte can be given a global inde-, or position. A bit like a house number. "he first byte is numbered 1, the second is 6, etc. In this case, all the data in the computer, including the structs and variables used in the currently running program, start at some inde- in this huge list. If, rather than copying the data itself to a function, we instead copy a number representing the inde" at where this data starts, the function being called can look up any amount of data it wants. y using addresses instead of the actual data, we can allow a function to access and modify some location in memory without having to copy any data. 4unctions can also use pointers to do other cool stuff, like output data to some address given as input. ecause the total si2e of computer memory is fi-ed, the number of bytes needed to represent an address is always the same. ut if we keep track of it, the number of bytes the address points to can grown and shrink. "his means we can create a variable si2ed data,structure and still sort of pass it to a function, which can inspect and modify it. )o a pointer is just a number. A number representing the starting inde- of some data in memory. "he type of the pointer hints to us, and the compiler, what type of data might be accessible at this location. 'e can declare pointer types by suffi-ing e-isting ones with the the - character. 'e've seen some e-amples of this already with mpc'parser't-, mpc'ast't-, or c&ar-. "o create a pointer to some data, we need to get its inde-, or address. "o get the address of a some data we use the address o' operator @. Again you've seen this before when we passed in a pointer to mpc'parse so it would output into our mpc'result't. =C

4inally to get the data at an address, called dere'erencing, we use the - operator on the left hand si2e of a variable. "o get the data at the field of a pointer to a struct we use the arrow -*. "his you saw in chapter @.

"he !tac+ 6 "he

eap

I said that memory can be visuali2ed of as one long list of bytes. Actually it is better to imagine it split into two sections. "hese sections are called he $tack and he Heap. )ome of you may have heard tales of these mysterious locations, such as "the stack grows down &ut the heap grows up", or "there can &e many stacks/ &ut only one heap". "hese sorts of things don't matter much. ?ealing with the stack and the heap in C can be comple-, but it doesn't have to be a mystery. In essence they are just two sections of memory used for two different tasks.

The 'tac+

"he )tack / Like what you do with bricks.

"he )tack is the memory where your program lives. It is where all of your temporary variables and data structures e-ist as you manipulate and edit them. 1ach time you call a function a new area of the stack is put aside for it to use. Into this area are put local variables, copies of any arguments passed to the function, as well as some bookkeeping data such as who the caller was, and what to do when finished. 'hen the function is done the area it used is unallocated, ready for use again by someone else. I like to think of the stack as a building site. 1ach time we need to do something new we corner off a section of space, enough for our tools and materials, and set to work. 'e can still go to other parts of the site, or go off,site, if we need certain things, but all our work is done in this section. 7nce we are done with some task, we take what we've constructed to a new place and clean up that section of the space we've been using to make it. ;&

The %eap

"he !eap / : L7C6. 6115 61(.

"he !eap is a section of memory put aside for storage of objects with a longer lifespan. .emory in this area has to be manually allocated and deallocated. "o allocate new memory the malloc function is used. "his function takes as input the number of bytes re<uired, and returns back a pointer to a new block of memory with that many bytes set aside. 'hen done with the memory at that location it must be released again. "o do this the pointer received from malloc should be passed to the 8ree function. :sing the !eap is trickier than the )tack because it re<uires the programmer to remember to call 8ree and to call it correctly. If he or she doesn't, the program may continuously allocate more and more memory. "his is called a memory leak. A easy rule to avoid this is to ensure for each malloc there is a corresponding Dand only one correspondingE 8ree. If this can always be ensured the program should be handling "he !eap correctly. I Imagine the !eap like a huge :,)tore,It. 'e can call up the reception with malloc and re<uest a number of bo-es. 'ith these bo-es we can do what we want, and we know they will persist no matter how messy the building site gets. 'e can take things to and from the :, )tore,It and the building site. It is useful to store materials and large objects which we only need to retrieve once in a while. "he only problem is we need to remember to call the receptionist again with 8ree when we are done. 7therwise soon we'll have re<uested all the bo-es, have no space, and run up a huge bill.

-arsing $#pressions
ecause we're now thinking in ),1-pressions, and not 5olish 3otation we need to update our parser. "he synta- for ),1-pressions is simple. It is just a number of other 1-pressions between parenthesis, where an 1-pression can be a 3umber, 7perator, or other ), 1-pression . 'e can modify our e-isting parse rules to reflect this. 'e also are going to ;%

rename our operator rule to symbol. "his is in anticipation of adding more operators, variables and functions later.
mpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tMumber Pymbol Pexpr Jxpr Aispy 3 3 3 3 3 mpc'new+"number".; mpc'new+"symbol".; mpc'new+"sexpr".; mpc'new+"expr".; mpc'new+"lispy".;

mpca'lang+"PC'AAM#'DJRAVAT, " ! number 5-WG1-4H=5 ; ! symbol 7=7 ? 7-7 ? 7-7 ? 757 ; ! sexpr 7+7 )expr*- 7.7 ; ! expr )number* ? )symbol* ? )sexpr* ; ! lispy 5Q5 )expr*- 5X5 ; ! ", Mumber, Pymbol, Pexpr, Jxpr, Aispy.;

'e should also remember to cleanup these rules on e-it.
mpc'cleanup+;, Mumber, Pymbol, Pexpr, Jxpr, Aispy.;

$#pression !tructure
'e need a way to store ),1-pressions internally as lval. "his means we'll also need to store internally $ym&ols and *um&ers. 'e're going to add two new lval types to the enumeration. "he first new type is AIAA'PO", which we're going to use to represent operators such as =. "he second new type is AIAA'PJZPL which we're going to use to represent ),1-pressions.
5- Add PO" and PJZPL as possible lval types% -5 enum / AIAA'JLL, AIAA'MV", AIAA'PO", AIAA'PJZPL 2;

),1-pressions are variable length lists of other ),1-pressions. As we learnt at the beginning of this chapter we can't create variable length structs, so we are going to need to use a pointer. 'e are going to create a pointer field cell which points to a location where we store a list of lval-. "hese are pointers to the other individual lval. 7ur field should therefore be a double pointer type lval--. A pointer to lval pointers. 'e will also need to keep track of how many lval- are in this list, so we add an e-tra field count to record this. Are there e,er pointers to pointers to pointers( "here is an old programming joke Dand by programming joke I mean silly anecdoteE, which says you can rate C programmers by how many stars are on their pointers. eginners program might only use c&ar- or the odd int-, so they were called one star programmer. .ost intermediate programs contain double pointer types such as lval--. "hese programmers are therefore called two star programmers. "o spot a triple pointer is something special. (ou would be viewing the work of someone grand and terrible, writing

;0

code not meant to be read with mortal eyes. As such being called a three star programmer is rarely a compliment. As far as I know, a <uadruple pointer has never been seen in the wild. "o represent symbols we're going to use a string. 'e're also going to change the representation of errors to a string. "his means we can store a uni<ue error message rather than just an error code. "his will make our error reporting better and more fle-ible, and we can get rid of the original error enum. 7ur updated lval struct looks like this.
typede8 struct lval / int type; long num; 5- Jrror and Pymbol types &ave some string data -5 c&ar- err; c&ar- sym; 5- Count and Pointer to a list o8 "lval-" -5 int count; struct lval-- cell; 2 lval;

What is that struct +eyword doing there( 7ur new definition of lval needs to contain a reference to itself. "his means we have to slightly change how it is defined. efore we open the curly brackets we can put the name of the struct, and then refer to this inside using struct lval. 1ven though a struct can refer to its own type, it must only contain pointers to its own type, not its own type directly. 7therwise the si2e of the struct would refer to itself, and grow infinite in si2e when you tried to calculate it$

Constructors 6 &estructors
'e can change our lval construction functions to return pointers to an lval, rather than one directly. "his will make keeping track of lval variables easier. 4or this we need to use malloc with the siEeo8 function to allocate enough space for the lval struct, and then to fill in the fields with the relevant information using the arrow operator -*. 'hen we construct an lval its fields may contain pointers to other things that have been allocated on the heap. "his means we need to be careful. 'henever we are done with an lval we also need to delete the things it points to on the heap. 'e will have to make a rule to ourselves. 'henever we free the memory allocated for an lval, we also free all the things it points to.
5- Construct a pointer to a new Mumber lval -5 lval- lval'num+long x. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'MV";

;9

v-*num 3 x; return v; 2 5- Construct a pointer to a new Jrror lval -5 lval- lval'err+c&ar- m. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'JLL; v-*err 3 malloc+strlen+m. = 6.; strcpy+v-*err, m.; return v; 2 5- Construct a pointer to a new Pymbol lval -5 lval- lval'sym+c&ar- s. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'PO"; v-*sym 3 malloc+strlen+s. = 6.; strcpy+v-*sym, s.; return v; 2 5- A pointer to a new empty Pexpr lval -5 lval- lval'sexpr+void. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'PJZPL; v-*count 3 1; v-*cell 3 MVAA; return v; 2

What is *'++( is a special constant that points to memory location 1. In many places it is used as a convention to signify some non,value or empty data. Above we use it to specify that we have a data pointer, but that it doesn't point to any number of data items. (ou will see MVAA crop up a lot more later on so always remember to look out for it.
MVAA

Why are you using strlen(s) , 1( In C strings are null terminated. "his means that the final character of them is always the 2ero character !1. "his is a convention in C to signal the end of a string. It is important that all strings are stored this way otherwise programs will break in nasty ways. "he strlen function only returns the number of bytes in a string e"cluding the null terminator. "his is why we need to add one, to ensure there is enough allocated space for it all$ 'e now need a special function to delete lval-. "his should call 8ree on the pointer itself to release the memory ac<uired from malloc, but more importantly it should inspect the type of the lval, and release any memory pointed to by its fields. If we match every call to one of the above construction functions with lval'del we can ensure we will get no memory leaks.
void lval'del+lval- v. / switc& +v-*type. /

;*

5- Do not&ing special 8or number type -5 case AIAA'MV" breaC; 5- Ror Jrr or Pym 8ree t&e string data -5 case AIAA'JLL 8ree+v-*err.; breaC; case AIAA'PO" 8ree+v-*sym.; breaC; 5- B8 Pexpr t&en delete all elements inside -5 case AIAA'PJZPL 8or +int i 3 1; i ) v-*count; i==. / lval'del+v-*cellGiH.; 2 5- Also 8ree t&e memory allocated to contain t&e pointers -5 8ree+v-*cell.; breaC;

2

2

5- Rinally 8ree t&e memory allocated 8or t&e "lval" struct itsel8 -5 8ree+v.;

'eading $#pressions
4irst we are going to read in the program and construct an lval- that represents it all. "hen we are going to evaluate this lval- to get the result of our program. "his first stage should convert the a&stract synta" tree into an ),1-pression, and the second stage should evaluate this ),1-pression using our normal Lisp rules. "o complete the first stage we can recursively look at each node of the tree, and construct different lval- types depending on the tag and contents fields of the node. If the given node is tagged as a number or symbol, then we use our constructors to return an lval- directly for those types. If the given node is the root, or an sexpr, then we create an empty ),1-pression lval and slowly add each valid sub,e-pression contained in the tree. "o add an element to an ),1-pression we can create a function lval'add. "his function increases the count of the 1-pression list by one, and then uses realloc to reallocate the amount of space re<uired by v-*cell. "his new space can be used to store the e-tra lvalre<uired. :sing this new space it sets the final value of the list with v-*cellGv-*count-6H to the value lval- x passed in. &on2t Lisps use Cons cells( 7ther Lisps have a slightly different definition of what an ),1-pression is. In most other Lisps ),1-pressions are defined inductively as either an atom such as a symbol of number, or two other ),1-pressions joined, or cons, together. "his naturally leads to an implementation using linked lists, a different data structure to the one we are using. I choose to represent ),1-pressions as a variable si2ed array in this book for the purposes of simplicity, but it is important to be aware that the official definition, and typical implementation are both subtly different.

;=

lval- lval'add+lval- v, lval- x. / v-*count==; v-*cell 3 realloc+v-*cell, siEeo8+lval-. - v-*count.; v-*cellGv-*count-6H 3 x; return v; 2 lval- lval'read'num+mpc'ast't- t. / long x 3 strtol+t-*contents, MVAA, 61.; return errno 03 JLAM#J W lval'num+x. lval'err+"invalid number".; 2 lval- lval'read+mpc'ast't- t. / 5- B8 Pymbol or Mumber return conversion to t&at type -5 i8 +strstr+t-*tag, "number".. / return lval'read'num+t.; 2 i8 +strstr+t-*tag, "symbol".. / return lval'sym+t-*contents.; 2 5- B8 root +*. or sexpr t&en create empty list -5 lval- x 3 MVAA; i8 +strcmp+t-*tag, "*". 33 1. / x 3 lval'sexpr+.; 2 i8 +strstr+t-*tag, "sexpr".. / x 3 lval'sexpr+.; 2 5- Rill t&is list wit& any valid expression contained wit&in -5 8or +int i 3 1; i ) t-*c&ildren'num; i==. / i8 +strcmp+t-*c&ildrenGiH-*contents, "+". 33 1. / continue; 2 i8 +strcmp+t-*c&ildrenGiH-*contents, ".". 33 1. / continue; 2 i8 +strcmp+t-*c&ildrenGiH-*contents, "2". 33 1. / continue; 2 i8 +strcmp+t-*c&ildrenGiH-*contents, "/". 33 1. / continue; 2 i8 +strcmp+t-*c&ildrenGiH-*tag, "regex". 33 1. / continue; 2 x 3 lval'add+x, lval'read+t-*c&ildrenGiH..; 2 return x; 2

-rinting $#pressions
'e are so close to trying out all of our new changes$ 'e need to modify our print function to print out ),1-pressions types. :sing this we can double check that the reading phase is working correctly by printing out the ),1-pressions we read in and verifying they match those we input. "o print out ),1-pressions we can create another function that loops over all the sub, e-pressions of an e-pression and prints these individually separated by spaces, just like how they are input.
void lval'expr'print+lval- v, c&ar open, c&ar close. / putc&ar+open.; 8or +int i 3 1; i ) v-*count; i==. / 5- Print Ialue contained wit&in -5 lval'print+v-*cellGiH.; 5- Don7t print trailing space i8 last element -5 i8 +i 03 +v-*count-6.. /

;;

2 2

putc&ar+7 7.;

2 putc&ar+close.; void lval'print+lval- v. / switc& +v-*type. / case AIAA'MV" print8+"Nli", v-*num.; breaC; case AIAA'JLL print8+"Jrror Ns", v-*err.; breaC; case AIAA'PO" print8+"Ns", v-*sym.; breaC; case AIAA'PJZPL lval'expr'print+v, 7+7, 7.7.; breaC; 2 2 void lval'println+lval- v. / lval'print+v.; putc&ar+7!n7.; 2

1 can2t declare these functions because they call each other* "he lval'expr'print function calls the lval'print function and vice,versa. "here is no way we can order them in the source file to resolve this dependency. Instead we need to 'orward declare one of them. "his is declaring a function without giving it a body. It lets other functions call it, while allowing you to define it properly later on. "o write a forward declaration write the function definition but instead of the body put a semicolon ;. In this e-ample we should put void lval'print+lval- v.; somewhere in the source file before lval'expr'print. (ou'll definitely run into this later, and I won't always alert you about it, so try to remember how to fi- it$ In our main loop, we can remove the evaluation for now, and instead try reading in the result and printing out what we have read.
lval- x 3 lval'read+r%output.; lval'println+x.; lval'del+x.;

If this is successful you should see something like the following when entering input to your program.
lispy* = 2 2 += 2 2. lispy* = 2 +- 9 <. +- 2 ;. += 2 +- 9 <. +- 2 ;.. lispy* ;; 616 += 1 1 1. +- ;; 616 += 1 1 1.. lispy*

$,aluating $#pressions
"he behaviour of our evaluation function is largely the same as before. 'e need to adapt it to deal with lval- and our more rela-ed definition of what constitutes an e-pression. 'e can think of our evaluation function as a kind of transformer. It takes in some lval- and ;@

transforms it in some way to some new lval-. In some cases it can just return e-actly the same thing. In other cases it may modify the input lval- and the return it. In many cases it will delete the input, and return something completely different. If we are going to return something new we must always remember to delete the lval- we get as input. 4or ),1-pressions we first evaluate all the children of the ),1-pression. If any of these children are errors we return the first error we encounter using a function we'll define later called lval'taCe. If the ),1-pression has no children we just return it directly. "his corresponds to the empty e-pression, denoted by +.. 'e also check for single e-pressions. "hese are e-pressions with only one child such as +;.. In this case we return the single e-pression contained within the parenthesis. If neither of these are the case we know we have a valid e-pression with more than one child. In this case we separate the first element of the e-pression using a function we'll define later called lval'pop. 'e then check this is a sym&ol and not anything else. If it is a symbol we check what symbol it is, and pass it, and the arguments, to a function builtin'op which does our calculations. If the first element is not a symbol we delete it, and the values passed into the evaluation function, returning an error. "o evaluate all other types we just return them directly back.
lval- lval'eval'sexpr+lval- v. / 5- Jvaluate C&ildren -5 8or +int i 3 1; i ) v-*count; i==. / v-*cellGiH 3 lval'eval+v-*cellGiH.; 2 5- Jrror C&ecCing -5 8or +int i 3 1; i ) v-*count; i==. / i8 +v-*cellGiH-*type 33 AIAA'JLL. / return lval'taCe+v, i.; 2 2 5- Jmpty Jxpression -5 i8 +v-*count 33 1. / return v; 2 5- Pingle Jxpression -5 i8 +v-*count 33 6. / return lval'taCe+v, 1.; 2 5- Jnsure Rirst Jlement is Pymbol -5 lval- 8 3 lval'pop+v, 1.; i8 +8-*type 03 AIAA'PO". / lval'del+8.; lval'del+v.; return lval'err+"P-expression Does not start wit& symbol0".; 2 5- Call builtin wit& operator -5 lval- result 3 builtin'op+v, 8-*sym.; lval'del+8.; return result;

2

lval- lval'eval+lval- v. / 5- Jvaluate Pexpressions -5

;+

2

i8 +v-*type 33 AIAA'PJZPL. / return lval'eval'sexpr+v.; 2 5- All ot&er lval types remain t&e same -5 return v;

"here are two functions we've used and not defined in the above code. "hese are lval'pop and lval'taCe. "hese are general purpose functions for manipulating ),1-pression lval types which we'll make use of here and in the future. "he lval'pop function e-tracts a single element from an ),1-pression at inde- i and shifts the rest of the list backward so that it no longer contains that lval-. It then returns the e-tracted value. 3otice that it doesn't delete the input list. It is like taking an element from a list and popping it out, leaving what remains. "his means both the element popped and the old list need to be deleted at some point with lval'del. "he lval'taCe function is similar to lval'pop but it deletes the list it has e-tracted the element from. "his is like taking an element from the list and deleting the rest. It is a slight variation on lval'pop but it makes our code easier to read in some places. :nlike lval'pop, only the e-pression you take from the list needs to be deleted by lval'del.
lval- lval'pop+lval- v, int i. / 5- Rind t&e item at "i" -5 lval- x 3 v-*cellGiH; 5- P&i8t t&e memory 8ollowing t&e item at "i" over t&e top o8 it -5 memmove+@v-*cellGiH, @v-*cellGi=6H, siEeo8+lval-. - +v-*count-i-6..; 5- Decrease t&e count o8 items in t&e list -5 v-*count--; 5- Leallocate t&e memory used -5 v-*cell 3 realloc+v-*cell, siEeo8+lval-. - v-*count.; return x;

2

lval- lval'taCe+lval- v, int i. / lval- x 3 lval'pop+v, i.; lval'del+v.; return x; 2

'e also need to define the evaluation function builtin'op. "his is like the eval'op function used in our previous chapter but modified to take a single lval- representing a list of all the arguments to operate on. It needs to do some more rigorous error checking. If any of the inputs are a non,number lval- we need to return an error. 4irst it checks that all the arguments input are numbers. It then pops the first argument to start. If there are no more sub,e-pressions and the operator is subtraction it performs unary negation on this first number. "his makes e-pressions such as +- ;. evaluate correctly. If there are more arguments it constantly pops the ne-t one from the list and performs arithmetic depending on which operator we're meant to be using. If a 2ero is encountered on division it deletes the temporary x, y, and the argument list a, and returns an error.

;C

If there have been no errors the input arguments are deleted and the new e-pression returned.
lval- builtin'op+lval- a, c&ar- op. / 5- Jnsure all arguments are numbers -5 8or +int i 3 1; i ) a-*count; i==. / i8 +a-*cellGiH-*type 03 AIAA'MV". / lval'del+a.; return lval'err+"Cannot operator on non number0".; 2 2 5- Pop t&e 8irst element -5 lval- x 3 lval'pop+a, 1.; 5- B8 no arguments and sub t&en per8orm unary negation -5 i8 ++strcmp+op, "-". 33 1. @@ a-*count 33 1. / x-*num 3 -x-*num; 2 5- $&ile t&ere are still elements remaining -5 w&ile +a-*count * 1. / 5- Pop t&e next element -5 lval- y 3 lval'pop+a, 1.; 5i8 i8 i8 i8 Per8orm operation -5 +strcmp+op, "=". 33 1. / x-*num =3 y-*num; 2 +strcmp+op, "-". 33 1. / x-*num -3 y-*num; 2 +strcmp+op, "-". 33 1. / x-*num -3 y-*num; 2 +strcmp+op, "5". 33 1. / i8 +y-*num 33 1. / lval'del+x.; lval'del+y.; lval'del+a.; x 3 lval'err+"Division Uy Yero0".; breaC; 2 else / x-*num 53 y-*num; 2

2

2

5- Delete element now 8inis&ed wit& -5 lval'del+y.;

5- Delete input expression and return result -5 lval'del+a.; return x; 2

"his completes our evaluation functions. 'e just need to change main again so it passes the input through this evaluation before printing it.
lval- x 3 lval'eval+lval'read+r%output..; lval'println+x.; lval'del+x.;

3ow you should now be able to evaluate e-pressions correctly in the same way as in the previous chapter. "ake a little breather and have a play around with the new evaluation. .ake sure everything is working correctly, and the behaviour is as e-pected. In the ne-t chapter we're going to make great use of these changes to implement some cool new features.

@&

lispy* 34 lispy* -611 lispy* +. lispy* 5 lispy* Jrror lispy*

= 6 +- 9 ;. 3 +- 611.

5 +5 +.. Cannot operator on non number0

'eference
s)e#pressions*c
(include "mpc%&" (i8de8 '$BM32 static c&ar bu88erG21F:H; c&ar- readline+c&ar- prompt. / 8puts+"lispy* ", stdout.; 8gets+bu88er, 21F:, stdin.; c&ar- cpy 3 malloc+strlen+bu88er.=6.; strcpy+cpy, bu88er.; cpyGstrlen+cpy.-6H 3 7!17; return cpy; 2 void add'&istory+c&ar- unused. /2 (else (include )editline5readline%&* (include )editline5&istory%&* (endi8 5- Add PO" and PJZPL as possible lval types -5 enum / AIAA'JLL, AIAA'MV", AIAA'PO", AIAA'PJZPL 2; typede8 struct lval / int type; long num; 5- Jrror and Pymbol types &ave some string data -5 c&ar- err; c&ar- sym; 5- Count and Pointer to a list o8 "lval-" -5 int count; struct lval-- cell; 2 lval; 5- Construct a pointer to a new Mumber lval -5

@%

lval- lval'num+long x. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'MV"; v-*num 3 x; return v; 2 5- Construct a pointer to a new Jrror lval -5 lval- lval'err+c&ar- m. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'JLL; v-*err 3 malloc+strlen+m. = 6.; strcpy+v-*err, m.; return v; 2 5- Construct a pointer to a new Pymbol lval -5 lval- lval'sym+c&ar- s. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'PO"; v-*sym 3 malloc+strlen+s. = 6.; strcpy+v-*sym, s.; return v; 2 5- A pointer to a new empty Pexpr lval -5 lval- lval'sexpr+void. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'PJZPL; v-*count 3 1; v-*cell 3 MVAA; return v; 2 void lval'del+lval- v. / switc& +v-*type. / 5- Do not&ing special 8or number type -5 case AIAA'MV" breaC; 5- Ror Jrr or Pym 8ree t&e string data -5 case AIAA'JLL 8ree+v-*err.; breaC; case AIAA'PO" 8ree+v-*sym.; breaC; 5- B8 Pexpr t&en delete all elements inside -5 case AIAA'PJZPL 8or +int i 3 1; i ) v-*count; i==. / lval'del+v-*cellGiH.; 2 5- Also 8ree t&e memory allocated to contain t&e pointers -5 8ree+v-*cell.; breaC; 2 5- Rinally 8ree t&e memory allocated 8or t&e "lval" struct itsel8 -5 8ree+v.; 2 lval- lval'add+lval- v, lval- x. / v-*count==; v-*cell 3 realloc+v-*cell, siEeo8+lval-. - v-*count.;

@0

v-*cellGv-*count-6H 3 x; return v; 2 lval- lval'pop+lval- v, int i. / 5- Rind t&e item at "i" -5 lval- x 3 v-*cellGiH; 5- P&i8t t&e memory 8ollowing t&e item at "i" over t&e top o8 it -5 memmove+@v-*cellGiH, @v-*cellGi=6H, siEeo8+lval-. - +v-*count-i-6..; 5- Decrease t&e count o8 items in t&e list -5 v-*count--; 5- Leallocate t&e memory used -5 v-*cell 3 realloc+v-*cell, siEeo8+lval-. - v-*count.; return x;

2

lval- lval'taCe+lval- v, int i. / lval- x 3 lval'pop+v, i.; lval'del+v.; return x; 2 void lval'print+lval- v.; void lval'expr'print+lval- v, c&ar open, c&ar close. / putc&ar+open.; 8or +int i 3 1; i ) v-*count; i==. / 5- Print Ialue contained wit&in -5 lval'print+v-*cellGiH.; 5- Don7t print trailing space i8 last element -5 i8 +i 03 +v-*count-6.. / putc&ar+7 7.; 2 2 putc&ar+close.; 2 void lval'print+lval- v. / switc& +v-*type. / case AIAA'MV" print8+"Nli", v-*num.; breaC; case AIAA'JLL print8+"Jrror Ns", v-*err.; breaC; case AIAA'PO" print8+"Ns", v-*sym.; breaC; case AIAA'PJZPL lval'expr'print+v, 7+7, 7.7.; breaC; 2 2 void lval'println+lval- v. / lval'print+v.; putc&ar+7!n7.; 2 lval- builtin'op+lval- a, c&ar- op. / 5- Jnsure all arguments are numbers -5 8or +int i 3 1; i ) a-*count; i==. / i8 +a-*cellGiH-*type 03 AIAA'MV". / lval'del+a.; return lval'err+"Cannot operator on non number0".; 2

@9

2 5- Pop t&e 8irst element -5 lval- x 3 lval'pop+a, 1.; 5- B8 no arguments and sub t&en per8orm unary negation -5 i8 ++strcmp+op, "-". 33 1. @@ a-*count 33 1. / x-*num 3 -x-*num; 2 5- $&ile t&ere are still elements remaining -5 w&ile +a-*count * 1. / 5- Pop t&e next element -5 lval- y 3 lval'pop+a, 1.; 5i8 i8 i8 i8 Per8orm operation -5 +strcmp+op, "=". 33 1. / x-*num =3 y-*num; 2 +strcmp+op, "-". 33 1. / x-*num -3 y-*num; 2 +strcmp+op, "-". 33 1. / x-*num -3 y-*num; 2 +strcmp+op, "5". 33 1. / i8 +y-*num 33 1. / lval'del+x.; lval'del+y.; lval'del+a.; x 3 lval'err+"Division Uy Yero%".; breaC; 2 else / x-*num 53 y-*num; 2

2 5- Delete element now 8inis&ed wit& -5 lval'del+y.; 2 5- Delete input expression and return result -5 lval'del+a.; return x;

2

lval- lval'eval+lval- v.; lval- lval'eval'sexpr+lval- v. / 5- Jvaluate C&ildren -5 8or +int i 3 1; i ) v-*count; i==. / v-*cellGiH 3 lval'eval+v-*cellGiH.; 2 5- Jrror C&ecCing -5 8or +int i 3 1; i ) v-*count; i==. / i8 +v-*cellGiH-*type 33 AIAA'JLL. / return lval'taCe+v, i.; 2 2 5- Jmpty Jxpression -5 i8 +v-*count 33 1. / return v; 2 5- Pingle Jxpression -5 i8 +v-*count 33 6. / return lval'taCe+v, 1.; 2 5- Jnsure Rirst Jlement is Pymbol -5 lval- 8 3 lval'pop+v, 1.; i8 +8-*type 03 AIAA'PO". / lval'del+8.; lval'del+v.;

@*

2

return lval'err+"P-expression Does not start wit& symbol%".;

2

5- Call builtin wit& operator -5 lval- result 3 builtin'op+v, 8-*sym.; lval'del+8.; return result;

lval- lval'eval+lval- v. / 5- Jvaluate Pexpressions -5 i8 +v-*type 33 AIAA'PJZPL. / return lval'eval'sexpr+v.; 2 5- All ot&er lval types remain t&e same -5 return v; 2 lval- lval'read'num+mpc'ast't- t. / long x 3 strtol+t-*contents, MVAA, 61.; return errno 03 JLAM#J W lval'num+x. lval'err+"invalid number".; 2 lval- lval'read+mpc'ast't- t. / 5- B8 Pymbol or Mumber return conversion to t&at type -5 i8 +strstr+t-*tag, "number".. / return lval'read'num+t.; 2 i8 +strstr+t-*tag, "symbol".. / return lval'sym+t-*contents.; 2 5- B8 root +*. or sexpr t&en create empty list -5 lval- x 3 MVAA; i8 +strcmp+t-*tag, "*". 33 1. / x 3 lval'sexpr+.; 2 i8 +strstr+t-*tag, "sexpr".. / x 3 lval'sexpr+.; 2 5- Rill t&is list wit& any valid expression contained wit&in -5 8or +int i 3 1; i ) t-*c&ildren'num; i==. / i8 +strcmp+t-*c&ildrenGiH-*contents, "+". 33 1. / continue; 2 i8 +strcmp+t-*c&ildrenGiH-*contents, ".". 33 1. / continue; 2 i8 +strcmp+t-*c&ildrenGiH-*contents, "2". 33 1. / continue; 2 i8 +strcmp+t-*c&ildrenGiH-*contents, "/". 33 1. / continue; 2 i8 +strcmp+t-*c&ildrenGiH-*tag, "regex". 33 1. / continue; 2 x 3 lval'add+x, lval'read+t-*c&ildrenGiH..; 2 2 return x;

int main+int argc, c&ar-- argv. / mpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tMumber Pymbol Pexpr Jxpr Aispy 3 3 3 3 3 mpc'new+"number".; mpc'new+"symbol".; mpc'new+"sexpr".; mpc'new+"expr".; mpc'new+"lispy".;

mpca'lang+"PC'AAM#'DJRAVAT, " ! number 5-WG1-4H=5 ; ! symbol 7=7 ? 7-7 ? 7-7 ? 757 ; ! sexpr 7+7 )expr*- 7.7 ; ! expr )number* ? )symbol* ? )sexpr* ; ! lispy 5Q5 )expr*- 5X5 ; ! ",

@=

Mumber, Pymbol, Pexpr, Jxpr, Aispy.; puts+"Aispy Iersion 1%1%1%1%;".; puts+"Press Ctrl=c to Jxit!n".; w&ile +6. / c&ar- input 3 readline+"lispy* ".; add'&istory+input.; mpc'result't r; i8 +mpc'parse+")stdin*", input, Aispy, @r.. / lval- x 3 lval'eval+lval'read+r%output..; lval'println+x.; lval'del+x.; mpc'ast'delete+r%output.; 2 else / mpc'err'print+r%error.; mpc'err'delete+r%error.; 2 8ree+input.; 2 mpc'cleanup+;, Mumber, Pymbol, Pexpr, Jxpr, Aispy.; return 1; 2

Bonus %ar+s
• • • • • • • •

H #ive an e-ample of a variable in our program that lives on he $tack. H #ive an e-ample of a variable in our program that points to he Heap. H 'hat does the strcpy function doI H 'hat does the realloc function doI H 'hat does the memmove function doI H !ow does memmove differ from memcpyI H 1-tend parsing and evaluation to support the remainder operator N. H 1-tend parsing and evaluation to support decimal types using a double field.

,(E)pressions • Chapter 1-

Adding /eatures
(ou'll notice that the following chapters will all follow a similar pattern. "his pattern is the typical approach used to add new features to a language. It consists of a number of steps that bring a feature from start to finish. "hese are listed below, and are e-actly what we're going to do in this chapter, to introduce a new feature called a O,1-pression.

@;

!tep 0 7 !ynta# !tep 1 7 'epresentation !tep 8 7 -arsing !tep 9 7 !emantics

Add new rule to the language grammar for this feature. Add new data type variation to represent this feature. Add new functions for reading this feature from the a&stract synta" tree. Add new functions for evaluating and manipulating this feature.

:uoted $#pressions
In this chapter we'll implement a new type of Lisp >alue called a O,1-pression. "his stands for 2uoted e"pression, and is a type of Lisp 1-pression that is not evaluated by the standard Lisp mechanics. 'hen encountered by the evaluation function O,e-pressions are left e-actly as they are. "his makes them ideal for a number of purposes. 'e can use them to store and manipulate other Lisp values such as numbers, symbols, or other ),1-pressions themselves$ After we've added O,1-pressions we are going to implement a concise set of operators to manipulate them. Like the arithmetic operators these will prove fundamental in how we think about and play with e-pressions. "he synta- for O,1-pressions is very similar to that of ),1-pressions. "he only difference is that instead of parenthesis +. O,1-pressions are surrounded by curly brackets /2. 'e can add this to our grammar as follows. 12,e ne,er heard of :;$#pressions* O,1-pressions don't e-ist in other Lisps. 7ther Lisps use 3acros to stop evaluation. "hese look like normal functions, but they do not evaluate their arguments. A special .acro called <uote 7 e-ists, which can be used to stop the evaluation of almost anything. "his is the inspiration for O,1-pressions, which are uni<ue to our Lisp, and will be used instead of .acros for doing all the same tasks and more. "he way I've used $-)"pression and 4-)"pression in this book is a slight abuse of terminology, but I hope this misdemeanor makes the behaviour of our Lisp clearer.
mpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tMumber Pymbol Pexpr [expr Jxpr Aispy 3 3 3 3 3 3 mpc'new+"number".; mpc'new+"symbol".; mpc'new+"sexpr".; mpc'new+">expr".; mpc'new+"expr".; mpc'new+"lispy".; ! ! ! ! ! !

mpca'lang+"PC'AAM#'DJRAVAT, " number 5-WG1-4H=5 ; symbol 7=7 ? 7-7 ? 7-7 ? 757 ; sexpr 7+7 )expr*- 7.7 ; >expr 7/7 )expr*- 727 ; expr )number* ? )symbol* ? )sexpr* ? )>expr* ;

@@

lispy 5Q5 )expr*- 5X5 ; ", Mumber, Pymbol, Pexpr, [expr, Jxpr, Aispy.;

!

'e also must remember to update our cleanup function to deal with the new rule we've added.
mpc'cleanup+<, Mumber, Pymbol, Pexpr, [expr, Jxpr, Aispy.;

'eading :;$#pressions
ecause O,1-pressions are so similar ),1-pressions much of their internal behaviour is going to be the same. 'e're going to reuse our ),1-pression data fields to represent O, 1-pressions, but we still need to add a separate type to the enumeration.
enum / AIAA'JLL, AIAA'MV", AIAA'PO", AIAA'PJZPL, AIAA'[JZPL 2;

'e can also add a constructor for this variation.
5- A pointer to a new empty [expr lval -5 lval- lval'>expr+void. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'[JZPL; v-*count 3 1; v-*cell 3 MVAA; return v; 2

"o print and delete O,1-pressions we do essentially the same thing as with ),1-pressions. 'e can add the relevant lines to our functions for printing and deletion as follows.
void lval'del+lval- v. / switc& case case case +v-*type. AIAA'MV" AIAA'JLL AIAA'PO" / breaC; 8ree+v-*err.; breaC; 8ree+v-*sym.; breaC;

5- B8 [expr or Pexpr t&en delete all elements inside -5 case AIAA'[JZPL case AIAA'PJZPL 8or +int i 3 1; i ) v-*count; i==. / lval'del+v-*cellGiH.; 2 5- Also 8ree t&e memory allocated to contain t&e pointers -5 8ree+v-*cell.; breaC; 2 8ree+v.; 2 void lval'print+lval- v. / switc& +v-*type. / case AIAA'MV" print8+"Nli", v-*num.; breaC; case AIAA'JLL print8+"Jrror Ns", v-*err.; breaC;

@+

2 2

case AIAA'PO" case AIAA'PJZPL case AIAA'[JZPL

print8+"Ns", v-*sym.; breaC; lval'expr'print+v, 7+7, 7.7.; breaC; lval'expr'print+v, 7/7, 727.; breaC;

:sing these simple changes we can update our reading function lval'read to be able to read in in O,1-pressions. ecause we reused all the ),1-pression data fields for our O,1-pression type, we can also reuse all of the functions for ),1-pressions such as lval'add. "herefore to read in O,1-pressions we just need to add a special case for constructing an empty O, 1-pression to lval'read just below where we detect and create empty ),1-pressions from the a&stract synta" tree.
i8 +strstr+t-*tag, ">expr".. / x 3 lval'>expr+.; 2

ecause there is no special method of evaluating O,1-pressions, we don't need to edit any of the evaluation functions. 7ur O,1-pressions should be ready to try. Compile and run the program. )ee if you can use them as a new data type. 1nsure they are not evaluated.
lispy* /6 2 3 F2 /6 2 3 F2 lispy* /6 2 += ; <. F2 /6 2 += ; <. F2 lispy* //2 3 F2 /622 //2 3 F2 /622 lispy*

Builtin /unctions
'e can read in O,1-pressions but they are still somewhat useless. 'e need some way to manipulate them. 4or this we can define some built,in operators to work on our list type. Choosing a concise set of these is important. If we implement a few fundamental operations then we can use these to define new operations without adding e-tra C code. "here are a few ways to pick these fundamental operators but I've chosen a set that will allow us to do everything we need. "hey are defined as follows. "akes one or more arguments and returns a new O,1-pression containing the arguments "akes a O,1-pression and returns a O,1-pression with only of the first element tail "akes a O,1-pression and returns a O,1-pression with the first element removed "akes one or more O,1-pressions and returns a O,1-pression of them conjoined Toin together eval "akes a O,1-pression and evaluates it as if it were a ),1-pression
list &ead

Like with our mathematical operators we should add these functions as possible valid symbols. Afterward we can go about trying to define their behaviour in a similar way to builtin'op.
mpca'lang+"PC'AAM#'DJRAVAT,

@C

! !

" number 5-WG1-4H=5 ;

symbol !"list!" ? !"&ead!" ? !"tail!" ? !"Toin!" ? !"eval!" ? 7=7 ? 7-7 ? 7-7 ? 757 ; ! sexpr 7+7 )expr*- 7.7 ; ! >expr 7/7 )expr*- 727 ; ! expr )number* ? )symbol* ? )sexpr* ? )>expr* ; ! lispy 5Q5 )expr*- 5X5 ; ! ", Mumber, Pymbol, Pexpr, [expr, Jxpr, Aispy.

/irst Attempt
7ur builtin functions should have the same interface as builtin'op. "hat means the arguments should be bundled into an ),1-pression which the function must use and then delete. "hey should return a new lval- as a result of the evaluation. "he actual functionality of taking the head or tail of an O,1-pression shouldn't be too hard for us. 'e can make use of the e-isting functions we've defined for ),1-pressions such as lval'taCe and lval'pop. ut like builtin'op we also need to check that the inputs we get are valid. Lets take a look at &ead and tail first. "hese functions have a number of conditions under which they can't act. 4irst of all we must ensure they are only passed a single argument, and that that argument is a O,1-pression. "hen we need to ensure that this O,1-pression isn't empty and actually has some elements. "he &ead function can repeatedly pop and delete the item at inde- 6 until there is nothing else left in the list. "he tail function is even more simple. It can pop and delete the item at inde- 1, leaving the tail remaining. An initial attempt at these functions might look like this.
lval- builtin'&ead+lval- a. / 5- C&ecC Jrror Conditions -5 i8 +a-*count 03 6. / lval'del+a.; return lval'err+"Runction 7&ead7 passed too many arguments0".; 2 i8 +a-*cellG1H-*type 03 AIAA'[JZPL. / lval'del+a.; return lval'err+"Runction 7&ead7 passed incorrect types0".; 2 i8 +a-*cellG1H-*count 33 1. / lval'del+a.; return lval'err+"Runction 7&ead7 passed /20".;

+&

2 5- Kt&erwise taCe 8irst argument -5 lval- v 3 lval'taCe+a, 1.; 5- Delete all elements t&at are not &ead and return -5 w&ile +v-*count * 6. / lval'del+lval'pop+v, 6..; 2 return v; 2 lval- builtin'tail+lval- a. / 5- C&ecC Jrror Conditions -5 i8 +a-*count 03 6. / lval'del+a.; return lval'err+"Runction 7tail7 passed too many arguments0".; 2 i8 +a-*cellG1H-*type 03 AIAA'[JZPL. / lval'del+a.; return lval'err+"Runction 7tail7 passed incorrect types0".; 2 i8 +a-*cellG1H-*count 33 1. / lval'del+a.; return lval'err+"Runction 7tail7 passed /20".; 2 5- TaCe 8irst argument -5 lval- v 3 lval'taCe+a, 1.; 5- Delete 8irst element and return -5 lval'del+lval'pop+v, 1..; return v;

2

%acros

)trawberry / A delicious macro.

"hese &ead and tail functions do the correct thing, but the code pretty unclear, and long. "here is so much error checking that the functionality is hard to see. 7ne method we can use to clean it up is to use a 3acro.

+%

A .acro is a preprocessor statement for creating function,like,things that are evaluated before the program is compiled. It can be used for many different things, one of which is what we need to do here, clean up code. .acros work by taking some arguments Dwhich can be almost anythingE, and copying and pasting them into some given pattern. y changing the pattern or the arguments we can change what different code is generated by the .acro. "o define macros we use the (de8ine preprocessor directive. After this we write the name of the macro, followed by the argument names in parenthesis. After this the pattern is specified, to declare what code should be generated for the given arguments. 'e can design a macro to help with our error conditions called AAPPJLT. .acros are typically given names in all caps to help distinguish them from normal C functions. "his macro take in three arguments args, cond and err. It then generates code as shown on the right hand side, but with these variables pasted in at the locations where they are named. "his pattern is a good fit for all of our error conditions.
(de8ine AAPPJLT+args, cond, err. i8 +0+cond.. / lval'del+args.; return lval'err+err.; 2

'e can use this to change how our above functions are written, without actually changing what code is generated by the compiler. "his makes it much easier to read for the programmer, and saves a bit of typing. "he rest of the error conditions for our functions should become a piece of cake to write too$

%ead . Tail
:sing this our &ead and tail functions are defined as follows. 3otice how much clearer their real functionality is.
lval- builtin'&ead+lval- a. / AAPPJLT+a, +a-*count 33 6 ., "Runction 7&ead7 passed too many arguments0".; AAPPJLT+a, +a-*cellG1H-*type 33 AIAA'[JZPL., "Runction 7&ead7 passed incorrect type0".; AAPPJLT+a, +a-*cellG1H-*count 03 1 ., "Runction 7&ead7 passed /20".; lval- v 3 lval'taCe+a, 1.; w&ile +v-*count * 6. / lval'del+lval'pop+v, 6..; 2 return v;

2

lval- builtin'tail+lval- a. / AAPPJLT+a, +a-*count 33 6 ., "Runction 7tail7 passed too many arguments0".; AAPPJLT+a, +a-*cellG1H-*type 33 AIAA'[JZPL., "Runction 7tail7 passed incorrect type0".; AAPPJLT+a, +a-*cellG1H-*count 03 1 ., "Runction 7tail7 passed /20".; lval- v 3 lval'taCe+a, 1.; lval'del+lval'pop+v, 1..; return v; 2

+0

!ist . Eval
"he list function is dead simple. It just converts the input ),1-pression to a O,1-pression and returns it. "he eval function is somewhat like the opposite. It takes as input some single O,1-pression, which it converts to an ),1-pression, and evaluates using lval'eval.
lval- builtin'list+lval- a. / a-*type 3 AIAA'[JZPL; return a; 2 lval- builtin'eval+lval- a. / AAPPJLT+a, +a-*count 33 6 ., "Runction 7eval7 passed too many arguments0".; AAPPJLT+a, +a-*cellG1H-*type 33 AIAA'[JZPL., "Runction 7eval7 passed incorrect type0".; lval- x 3 lval'taCe+a, 1.; x-*type 3 AIAA'PJZPL; return lval'eval+x.; 2

/oin
"he Toin function is our final function to define. :nlike the others it can take multiple arguments, so its structure looks somewhat more like that of builtin'op. 4irst we check that all of the arguments are O,1-pressions and then we join them together one,by,one. "o do this we use the function lval'Toin. "his works by repeatedly popping each item from y and adding it to x until y is empty. It then deletes y and returns x.
lval- builtin'Toin+lval- a. / 8or +int i 3 1; i ) a-*count; i==. / AAPPJLT+a, +a-*cellGiH-*type 33 AIAA'[JZPL., "Runction 7Toin7 passed incorrect type%".; 2 lval- x 3 lval'pop+a, 1.; w&ile +a-*count. / x 3 lval'Toin+x, lval'pop+a, 1..; 2 lval'del+a.; return x; 2 lval- lval'Toin+lval- x, lval- y. / 5- Ror eac& cell in 7y7 add it to 7x7 -5 w&ile +y-*count. / x 3 lval'add+x, lval'pop+y, 1..;

+9

2 5- Delete t&e empty 7y7 and return 7x7 -5 lval'del+y.; return x;

2

Builtins Loo+up
'e've now got all of our builtin functions defined. 'e need to make a function that can call the correct one depending on what symbol it encounters in evaluation. 'e can do this using strcmp and strstr.
lval- builtin+lval- a, c&ar- 8unc. / i8 +strcmp+"list", 8unc. 33 1. / return builtin'list+a.; 2 i8 +strcmp+"&ead", 8unc. 33 1. / return builtin'&ead+a.; 2 i8 +strcmp+"tail", 8unc. 33 1. / return builtin'tail+a.; 2 i8 +strcmp+"Toin", 8unc. 33 1. / return builtin'Toin+a.; 2 i8 +strcmp+"eval", 8unc. 33 1. / return builtin'eval+a.; 2 i8 +strstr+"=-5-", 8unc.. / return builtin'op+a, 8unc.; 2 lval'del+a.; return lval'err+"VnCnown Runction0".; 2

"hen we can change our evaluation line in lval'eval'sexpr to call builtin rather than builtin'op.
5- Call builtin wit& operator -5 lval- result 3 builtin+v, 8unc-*sym.; lval'del+8unc.; return result;

4inally O,1-pressions should be fully supported in our language$ Compile and run the latest version and see what you can do with the new list operators. "ry putting code and symbols into our lists and evaluating them in different ways. "he ability to put ),1-pressions inside a list using O,1-pressions is pretty awesome. It means we can treat code like data itself. "his is a flagship feature of Lisps, and something that really cannot be done in languages such as C$
lispy* list 6 2 3 F /6 2 3 F2 lispy* /&ead +list 6 2 3 F.2 /&ead +list 6 2 3 F.2 lispy* eval /&ead +list 6 2 3 F.2 /62 lispy* tail /tail tail tail2 /tail tail2 lispy* eval +tail /tail tail /; < 922. /< 92 lispy* eval +&ead /+= 6 2. += 61 21.2. 3

'eference

+*

<)e#pressions*c
(include "mpc%&" (i8de8 '$BM32 static c&ar bu88erG21F:H; c&ar- readline+c&ar- prompt. / 8puts+"lispy* ", stdout.; 8gets+bu88er, 21F:, stdin.; c&ar- cpy 3 malloc+strlen+bu88er.=6.; strcpy+cpy, bu88er.; cpyGstrlen+cpy.-6H 3 7!17; return cpy; 2 void add'&istory+c&ar- unused. /2 (else (include )editline5readline%&* (include )editline5&istory%&* (endi8 5- Add [JZPL as possible lval type -5 enum / AIAA'JLL, AIAA'MV", AIAA'PO", AIAA'PJZPL, AIAA'[JZPL 2; typede8 struct lval / int type; long num; c&ar- err; c&ar- sym; int count; struct lval-- cell; 2 lval; lval- lval'num+long x. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'MV"; v-*num 3 x; return v; 2 lval- lval'err+c&ar- m. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'JLL; v-*err 3 malloc+strlen+m. = 6.; strcpy+v-*err, m.; return v; 2 lval- lval'sym+c&ar- s. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'PO"; v-*sym 3 malloc+strlen+s. = 6.; strcpy+v-*sym, s.;

+=

2

return v;

lval- lval'sexpr+void. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'PJZPL; v-*count 3 1; v-*cell 3 MVAA; return v; 2 5- A pointer to a new empty [expr lval -5 lval- lval'>expr+void. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'[JZPL; v-*count 3 1; v-*cell 3 MVAA; return v; 2 void lval'del+lval- v. / switc& case case case +v-*type. AIAA'MV" AIAA'JLL AIAA'PO" / breaC; 8ree+v-*err.; breaC; 8ree+v-*sym.; breaC;

5- B8 [expr or Pexpr t&en delete all elements inside -5 case AIAA'[JZPL case AIAA'PJZPL 8or +int i 3 1; i ) v-*count; i==. / lval'del+v-*cellGiH.; 2 5- Also 8ree t&e memory allocated to contain t&e pointers -5 8ree+v-*cell.; breaC; 2 2 8ree+v.;

lval- lval'add+lval- v, lval- x. / v-*count==; v-*cell 3 realloc+v-*cell, siEeo8+lval-. - v-*count.; v-*cellGv-*count-6H 3 x; return v; 2 lval- lval'pop+lval- v, int i. / lval- x 3 v-*cellGiH; memmove+@v-*cellGiH, @v-*cellGi=6H, siEeo8+lval-. - +v-*count-i-6..; v-*count--; v-*cell 3 realloc+v-*cell, siEeo8+lval-. - v-*count.; return x; 2 lval- lval'Toin+lval- x, lval- y. / w&ile +y-*count. / x 3 lval'add+x, lval'pop+y, 1..; 2

+;

2

lval'del+y.; return x;

lval- lval'taCe+lval- v, int i. / lval- x 3 lval'pop+v, i.; lval'del+v.; return x; 2 void lval'print+lval- v.; void lval'expr'print+lval- v, c&ar open, c&ar close. / putc&ar+open.; 8or +int i 3 1; i ) v-*count; i==. / lval'print+v-*cellGiH.; i8 +i 03 +v-*count-6.. / putc&ar+7 7.; 2 2 putc&ar+close.; 2 void lval'print+lval- v. / switc& +v-*type. / case AIAA'MV" print8+"Nli", v-*num.; breaC; case AIAA'JLL print8+"Jrror Ns", v-*err.; breaC; case AIAA'PO" print8+"Ns", v-*sym.; breaC; case AIAA'PJZPL lval'expr'print+v, 7+7, 7.7.; breaC; case AIAA'[JZPL lval'expr'print+v, 7/7, 727.; breaC; 2 2 void lval'println+lval- v. / lval'print+v.; putc&ar+7!n7.; 2 (de8ine AAPPJLT+args, cond, err. i8 +0+cond.. / lval'del+args.; return lval'err+err.; 2 lval- lval'eval+lval- v.; lval- builtin'list+lval- a. / a-*type 3 AIAA'[JZPL; return a; 2 lval- builtin'&ead+lval- a. / AAPPJLT+a, +a-*count 33 6 ., "Runction 7&ead7 passed too many arguments%".; AAPPJLT+a, +a-*cellG1H-*type 33 AIAA'[JZPL., "Runction 7&ead7 passed incorrect type%".; AAPPJLT+a, +a-*cellG1H-*count 03 1 ., "Runction 7&ead7 passed /2%".; lval- v 3 lval'taCe+a, 1.; w&ile +v-*count * 6. / lval'del+lval'pop+v, 6..; 2 return v;

2

+@

lval- builtin'tail+lval- a. / AAPPJLT+a, +a-*count 33 6 ., "Runction 7tail7 passed too many arguments%".; AAPPJLT+a, +a-*cellG1H-*type 33 AIAA'[JZPL., "Runction 7tail7 passed incorrect type%".; AAPPJLT+a, +a-*cellG1H-*count 03 1 ., "Runction 7tail7 passed /2%".; lval- v 3 lval'taCe+a, 1.; lval'del+lval'pop+v, 1..; return v;

2

lval- builtin'eval+lval- a. / AAPPJLT+a, +a-*count 33 6 ., "Runction 7eval7 passed too many arguments%".; AAPPJLT+a, +a-*cellG1H-*type 33 AIAA'[JZPL., "Runction 7eval7 passed incorrect type%".; lval- x 3 lval'taCe+a, 1.; x-*type 3 AIAA'PJZPL; return lval'eval+x.; 2 lval- builtin'Toin+lval- a. / 8or +int i 3 1; i ) a-*count; i==. / AAPPJLT+a, +a-*cellGiH-*type 33 AIAA'[JZPL., "Runction 7Toin7 passed incorrect type%".; 2 lval- x 3 lval'pop+a, 1.; w&ile +a-*count. / x 3 lval'Toin+x, lval'pop+a, 1..; 2 lval'del+a.; return x;

2

lval- builtin'op+lval- a, c&ar- op. / 8or +int i 3 1; i ) a-*count; i==. / i8 +a-*cellGiH-*type 03 AIAA'MV". / lval'del+a.; return lval'err+"Cannot operator on non number0".; 2 2 lval- x 3 lval'pop+a, 1.; i8 ++strcmp+op, "-". 33 1. @@ a-*count 33 1. / x-*num 3 -x-*num; 2 w&ile +a-*count * 1. / lval- y 3 lval'pop+a, 1.; i8 i8 i8 i8 +strcmp+op, +strcmp+op, +strcmp+op, +strcmp+op, "=". "-". "-". "5". 33 33 33 33 1. 1. 1. 1. / x-*num =3 y-*num; 2 / x-*num -3 y-*num; 2 / x-*num -3 y-*num; 2 /

++

2 2

i8 +y-*num 33 1. / lval'del+x.; lval'del+y.; lval'del+a.; x 3 lval'err+"Division Uy Yero%".; breaC; 2 else / x-*num 53 y-*num; 2

lval'del+y.; lval'del+a.; return x; 2 lval- builtin+lval- a, c&ar- 8unc. / i8 +strcmp+"list", 8unc. 33 1. / return builtin'list+a.; 2 i8 +strcmp+"&ead", 8unc. 33 1. / return builtin'&ead+a.; 2 i8 +strcmp+"tail", 8unc. 33 1. / return builtin'tail+a.; 2 i8 +strcmp+"Toin", 8unc. 33 1. / return builtin'Toin+a.; 2 i8 +strcmp+"eval", 8unc. 33 1. / return builtin'eval+a.; 2 i8 +strstr+"=-5-", 8unc.. / return builtin'op+a, 8unc.; 2 lval'del+a.; return lval'err+"VnCnown Runction0".; 2 lval- lval'eval'sexpr+lval- v. / 8or +int i 3 1; i ) v-*count; i==. / v-*cellGiH 3 lval'eval+v-*cellGiH.; 2 8or +int i 3 1; i ) v-*count; i==. / i8 +v-*cellGiH-*type 33 AIAA'JLL. / return lval'taCe+v, i.; 2 2 i8 +v-*count 33 1. / return v; 2 i8 +v-*count 33 6. / return lval'taCe+v, 1.; 2 lval- 8 3 lval'pop+v, 1.; i8 +8-*type 03 AIAA'PO". / lval'del+8.; lval'del+v.; return lval'err+"P-expression Does not start wit& symbol%".; 2 5- Call builtin wit& operator -5 lval- result 3 builtin+v, 8-*sym.; lval'del+8.; return result;

2

lval- lval'eval+lval- v. / i8 +v-*type 33 AIAA'PJZPL. / return lval'eval'sexpr+v.; 2 return v; 2 lval- lval'read'num+mpc'ast't- t. / long x 3 strtol+t-*contents, MVAA, 61.; return errno 03 JLAM#J W lval'num+x. lval'err+"invalid number".;

+C

2 lval- lval'read+mpc'ast't- t. / i8 +strstr+t-*tag, "number".. / return lval'read'num+t.; 2 i8 +strstr+t-*tag, "symbol".. / return lval'sym+t-*contents.; 2 lval- x 3 MVAA; i8 +strcmp+t-*tag, "*". 33 1. / x 3 lval'sexpr+.; 2 i8 +strstr+t-*tag, "sexpr".. / x 3 lval'sexpr+.; 2 i8 +strstr+t-*tag, ">expr".. / x 3 lval'>expr+.; 2 8or +int i 3 1; i ) t-*c&ildren'num; i==. / i8 +strcmp+t-*c&ildrenGiH-*contents, "+". 33 i8 +strcmp+t-*c&ildrenGiH-*contents, ".". 33 i8 +strcmp+t-*c&ildrenGiH-*contents, "2". 33 i8 +strcmp+t-*c&ildrenGiH-*contents, "/". 33 i8 +strcmp+t-*c&ildrenGiH-*tag, "regex". 33 x 3 lval'add+x, lval'read+t-*c&ildrenGiH..; 2 return x; 2 int main+int argc, c&ar-- argv. / mpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tMumber Pymbol Pexpr [expr Jxpr Aispy 3 3 3 3 3 3 mpc'new+"number".; mpc'new+"symbol".; mpc'new+"sexpr".; mpc'new+">expr".; mpc'new+"expr".; mpc'new+"lispy".; 1. 1. 1. 1. 1. / / / / / continue; continue; continue; continue; continue; 2 2 2 2 2

! !

mpca'lang+"PC'AAM#'DJRAVAT, " number 5-WG1-4H=5 ;

symbol !"list!" ? !"&ead!" ? !"tail!" ? !"eval!" ? !"Toin!" ? 7=7 ? 7-7 ? 7-7 ? 757 ; ! sexpr 7+7 )expr*- 7.7 ; ! >expr 7/7 )expr*- 727 ; ! expr )number* ? )symbol* ? )sexpr* ? )>expr* ; ! lispy 5Q5 )expr*- 5X5 ; ! ", Mumber, Pymbol, Pexpr, [expr, Jxpr, Aispy.; puts+"Aispy Iersion 1%1%1%1%<".; puts+"Press Ctrl=c to Jxit!n".; w&ile +6. / c&ar- input 3 readline+"lispy* ".; add'&istory+input.; mpc'result't r; i8 +mpc'parse+")stdin*", input, Aispy, @r.. /

C&

lval- x 3 lval'eval+lval'read+r%output..; lval'println+x.; lval'del+x.; mpc'ast'delete+r%output.; 2 else / mpc'err'print+r%error.; mpc'err'delete+r%error.; 2 8ree+input.; 2 mpc'cleanup+<, Mumber, Pymbol, Pexpr, [expr, Jxpr, Aispy.; return 1; 2

Bonus %ar+s
• • • • • •

H 'hat are the four typical steps for adding new language featuresI H Create a .acro specifically for testing for the incorrect number of arguments. H Create a .acro specifically for testing for being called with the empty list. H Add a builtin function cons that takes a value and a O,1-pression and appends it to the front. H Add a builtin function len that returns the number of elements in a O,1-pression. H Add a builtin function init that returns all of a O,1-pression e-cept the final element.

C%

0aria1les • Chapter 11

1mmutability

"eenage 3inja "urtle / 3ot Immutable.

In the previous chapters we've learnt a lot, and made great progress on the infrastructure of our language. Already we can do a number of cool things that other languages can't, such as putting code inside of lists. 3ow is the time to start adding in the 'eatures which will make our language practical. "he first one of these is going to be varia&les. "hey're called variables, but it's a misleading name, because our variables won't vary. 7ur variables are immuta&le meaning they cannot change. 1verything in our language so far has acted like it is immuta&le. 'hen we evaluate an e-pression we have imagined that the old thing has been deleted and a new thing returned. In implementation often it is easier for us to reuse the data from the old thing to build the new thing, but conceptually it is a good way to think about how our language works. )o actually our variables are simply a way of naming values. "hey let us assign a name to a value, and then let us get a copy of that value later on when we need it. "o allow for naming values we need to create a structure which stores the name and value of everything named in our program. 'e call this the environment. 'hen we start a new interactive prompt we want to create a new environment to go along with it, in which each new bit of input is evaluated. "hen we can store and recall variables as we program.

C0

What happens when we re;assign a name to something new( 1sn2t this li+e mutability( In our Lisp, when we re,assign a name we're going to delete the old association and create a new one. "his gives the illusion that the thing assigned to that name has changed, and is mutable, but in fact we have deleted the old thing and assigned it a new thing. "his is different to C where we really can change the data pointed to by a pointer, or stored in a struct, without deleting it and creating a new one.

!ymbol !ynta#
3ow that we're going to allow for user defined variables we need to update the grammar for symbols to be more fle-ible. Father than just our builtin functions it should match any possible valid symbol. :nlike in C, where the name a variable can be given is fairly restrictive, we're going to allow for all sorts of characters in the name of a variable. 'e can create a regular e-pression that e-presses the range of characters available as follows.
5Ga-EA-Y1-4'=!!--!!5!!!!3)*0@H=5

7n first glace this looks like we've just bashed our hands in the keyboard. Actually it is a regular e-pression using a big range specifier GH. Inside range specifiers special characters lose their meaning, but some of these characters still need to be escaped with backslashes. ecause this is part of a C string we need to put two back slashes to represent a single backslash character in the input. "his rule lets symbols be any of, the standard C identifier characters a-EA-Y1-4', the arithmetic operator characters =!!--!!5, the backslash character !!!!, the comparison operator characters 3)*0, or an ampersands @. "his will give us all the fle-ibility we need for defining new and e-isting symbols.
mpca'lang+"PC'AAM#'DJRAVAT, " number 5-WG1-4H=5 ; symbol 5Ga-EA-Y1-4'=!!--!!5!!!!3)*0@H=5 ; sexpr 7+7 )expr*- 7.7 ; >expr 7/7 )expr*- 727 ; expr )number* ? )symbol* ? )sexpr* ? )>expr* ; lispy 5Q5 )expr*- 5X5 ; ", Mumber, Pymbol, Pexpr, [expr, Jxpr, Aispy.; ! ! ! ! ! ! !

/unction -ointers
7nce we introduce variables, symbols will no longer represent functions in our language, but rather they will represent a name for us to lookup into our environment and get some new value back from.

C9

"herefore we need a new value to represent functions in our language, which we can return once one of the builtin symbols is encountered. "o create this new type of lval we are going to use something called a 'unction pointer. 4unction pointers are a great feature of C that lets you store and pass around pointers to functions. It doesn't make sense to edit the data pointed to by these pointers. Instead we use them to call the function they point to, as if it were a normal function. Like normal pointers, function pointers have some type associated with them. "his type specifies the type of the function pointed to, not the type of the data pointed to. "his lets the compiler work out if it has been called correctly. In the previous chapter our builtin functions took a lval- as input and returned a lval- as output. In this chapter our builtin functions will take an e-tra pointer to the environment lenv- as input. 'e can declare a new function pointer type called lbuiltin, for this type of function, like this.
typede8 lval-+-lbuiltin.+lenv-, lval-.;

Why is that synta# so odd( In some places the synta- of C can look particularly weird. It can help if we understand e-actly why the synta- is like this. Let us de,construct the above synta- part by part. 4irst the typede8. "his can be put before any standard variable declaration. It results in the name of the variable, being declared a new type, matching what would be the inferred type of that variable. "his is why in the above declaration what looks like the function name becomes the new type name. 3e-t all those -. 5ointer types in C are actually meant to be written with the star - on the left hand side of the variable name, not the right hand side of the type int -x;. "his is because C type synta- works by a kind of weird inference. Instead of reading "%reate a new int pointer x". It is meant to read "%reate a new varia&le x where to dere'erence x results in an int0" "herefore x is inferred to be a pointer to an int. "his idea is e-tended to function pointers. 'e can read the above declaration as follows. " o get an lval* we dere'erence lbuiltin and call it with a lenv* and a lval*0" "herefore lbuiltin must be a function pointer that takes an lenv- and a lval- and returns a lval-.

Cyclic "ypes
"he lbuiltin type references the lval type and the lenv type. "his means that they should be declared first in the source file. ut we want to make a lbuiltin field in our lval struct so we can create function values. )o therefore our lbuiltin declaration must go before our lval declaration. "his leads to what is called a cyclic type dependency, where two types depend on each other.

C*

'e've come across this problem before with functions which depend on each other. "he solution was to create a 'orward declaration which declared a function but left the body of it empty. In C we can do e-actly the same with types. 4irst we declare two struct types without a body. )econdly we typedef these to the names lval and lenv. "hen we can define our lbuiltin function pointer type. And finally we can define the body of our lval struct. 3ow all our type issues are resolved and the compiler won't complain any more.
5- Rorward Declarations -5 struct lval; struct lenv; typede8 struct lval lval; typede8 struct lenv lenv; 5- Aisp Ialue -5 enum / AIAA'JLL, AIAA'MV", AIAA'PO", AIAA'RVM, AIAA'PJZPL, AIAA'[JZPL 2; typede8 lval-+-lbuiltin.+lenv-, lval-.; struct lval / int type; long num; c&ar- err; c&ar- sym; lbuiltin 8un; int count; lval-- cell;

2;

/unction "ype
As we've added a new possible lval type with the enumeration AIAA'RVM. 'e should update all our relevant functions that work on lvals to deal correctly with this update. In most cases this just means inserting new cases into switch statements. 'e can start by making a new constructor function for this type.
lval- lval'8un+lbuiltin 8unc. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'RVM; v-*8un 3 8unc; return v; 2

7n deletion we don't need to do anything special for function pointers.
case AIAA'RVM breaC;

7n printing we can just print out some nominal string. C=

case AIAA'RVM

print8+")8unction*".; breaC;

'e're also going to add a new function for copying an lval. "his is going to come in useful when we put things into, and take things out of, the environment. 4or numbers and functions we can just copy the relevant fields directly. 4or strings we need to copy using malloc and strcpy. "o copy lists we need to allocate the correct amount of space and then copy each element individually.
lval- lval'copy+lval- v. / lval- x 3 malloc+siEeo8+lval..; x-*type 3 v-*type; switc& +v-*type. / 5- Copy Runctions and Mumbers Directly -5 case AIAA'RVM x-*8un 3 v-*8un; breaC; case AIAA'MV" x-*num 3 v-*num; breaC; 5- Copy Ptrings using malloc and strcpy -5 case AIAA'JLL x-*err 3 malloc+strlen+v-*err. = 6.; strcpy+x-*err, v*err.; breaC; case AIAA'PO" x-*sym 3 malloc+strlen+v-*sym. = 6.; strcpy+x-*sym, v*sym.; breaC; 5- Copy Aists by copying eac& sub-expression -5 case AIAA'PJZPL case AIAA'[JZPL x-*count 3 v-*count; x-*cell 3 malloc+siEeo8+lval-. - x-*count.; 8or +int i 3 1; i ) x-*count; i==. / x-*cellGiH 3 lval'copy+v-*cellGiH.; 2 breaC;

2 2

return x;

$n,ironment
7ur environment structure must encode a list of relationships between names and values. "here are many ways to build a structure that can do this sort of thing. 'e are going to go for the simplest possible method that works well. "his is to use two lists of e<ual length. 7ne is a list of lval-, and the other is a list of c&ar-. 1ach entry in one list has a corresponding entry in the other list at the same position. 'e've already forward declared our lenv struct, so we can define it as follows.
struct lenv / int count; c&ar-- syms; lval-- vals; 2;

C;

'e need some functions to create and delete this structure. "hese are pretty simple. Creation initialises the struct fields, while deletion iterates over the items in both lists and deletes or frees them.
lenv- lenv'new+void. / lenv- e 3 malloc+siEeo8+lenv..; e-*count 3 1; e-*syms 3 MVAA; e-*vals 3 MVAA; return e; 2 void lenv'del+lenv- e. / 8or +int i 3 1; i ) e-*count; i==. / 8ree+e-*symsGiH.; lval'del+e-*valsGiH.; 2 8ree+e-*syms.; 8ree+e-*vals.; 8ree+e.; 2

3e-t we can create two functions that either get values from the environment or put values into it. "o get a value from the environment we loop over all the items in the environment and check if the given symbol matches any of the stored strings. If we find a match we can return a copy of the stored value. If no match is found we should return an error. "he function for putting new variables into the environment is a little bit more comple-. 4irst we want to check if a variable with the same name already e-ists. If this is the case we should replace its value with the new one. "o do this we loop over all the e-isting variables in the environment and check their name. If a match is found we delete the value stored at that location, and store there a copy of the input value. If no e-isting value is found with that name, we need to allocate some more space to put it in. 4or this we can use realloc, and store a copy of the lval and its name at the newly allocated locations.
lval- lenv'get+lenv- e, lval- C. / 5- Bterate over all items in environment -5 8or +int i 3 1; i ) e-*count; i==. / 5- C&ecC i8 t&e stored string matc&es t&e symbol string -5 5- B8 it does, return a copy o8 t&e value -5 i8 +strcmp+e-*symsGiH, C-*sym. 33 1. / return lval'copy+e-*valsGiH.; 2 2 5- B8 no symbol 8ound return error -5 return lval'err+"unbound symbol0",.;

2

void lenv'put+lenv- e, lval- C, lval- v. / 5- Bterate over all items in environment -5 5- T&is is to see i8 variable already exists -5 8or +int i 3 1; i ) e-*count; i==. /

C@

5- B8 variable is 8ound delete item at t&at position -5 5- And replace wit& variable supplied by user -5 i8 +strcmp+e-*symsGiH, C-*sym. 33 1. / lval'del+e-*valsGiH.; e-*valsGiH 3 lval'copy+v.; e-*symsGiH 3 realloc+e-*symsGiH, strlen+C-*sym.=6.; strcpy+e-*symsGiH, C-*sym.; return; 2 2 5- B8 no existing entry 8ound t&en allocate space 8or new entry -5 e-*count==; e-*vals 3 realloc+e-*vals, siEeo8+lval-. - e-*count.; e-*syms 3 realloc+e-*syms, siEeo8+c&ar-. - e-*count.; 5- Copy contents o8 lval and symbol string into new location -5 e-*valsGe-*count-6H 3 lval'copy+v.; e-*symsGe-*count-6H 3 malloc+strlen+C-*sym.=6.; strcpy+e-*symsGe-*count-6H, C-*sym.;

2

.ariable $,aluation
7ur evaluation function now depends on the some environment. 'e should pass this in as an argument and use it to e-tract get a value if we encounter a symbol type.
lval- lval'eval+lenv- e, lval- v. / i8 +v-*type 33 AIAA'PO". / return lenv'get+e, v.; 2 i8 +v-*type 33 AIAA'PJZPL. / return lval'eval'sexpr+e, v.; 2 return v; 2

ecause we've added a function type, our evaluation of ),1-pressions also needs to change. Instead of checking for a symbol type we want to ensure it is a function type. If this condition holds we can call the 8un field of the lval using the same notation as standard function calls.
lval- lval'eval'sexpr+lenv- e, lval- v. / 8or +int i 3 1; i ) v-*count; i==. / v-*cellGiH 3 lval'eval+e, v*cellGiH.; 2 8or +int i 3 1; i ) v-*count; i==. / i8 +v-*cellGiH-*type 33 AIAA'JLL. / return lval'taCe+v, i.; 2 2 i8 +v-*count 33 1. / return v; 2 i8 +v-*count 33 6. / return lval'taCe+v, 1.; 2 5- Jnsure 8irst element is a 8unction a8ter evaluation -5 lval- 8 3 lval'pop+v, 1.; i8 +8-*type 03 AIAA'RVM. / lval'del+v.; lval'del+8.; return lval'err+"8irst element is not a 8unction".; 2 5- B8 so call 8unction to get result -5 lval- result 3 8-*8un+e, v.;

C+

lval'del+8.; return result; 2

Builtins
3ow that our evaluation relies on the new function type we need to make sure we can register all of our builtin functions with the environment before we start the interactive prompt. At the moment our builtin functions might not be the correct type. 'e need to change their type signature such that they take in some environment, and where appropriate change them to pass this environment into other calls that re<uire it. 7nce we've changed them to the correct type we can create a function that registers all of our builtins into an environment. 4or each builtin we want to create a function lval and symbol lval with the given name. 'e then register these with the environment using lenv'put. "he environment always takes or returns copies of a values, so we need to remember to delete these two lval after registration as we won't need them anymore. If we split this task into two functions we can neatly register all of our builtins with some environment.
void lenv'add'builtin+lenv- e, c&ar- name, lbuiltin 8unc. / lval- C 3 lval'sym+name.; lval- v 3 lval'8un+8unc.; lenv'put+e, C, v.; lval'del+C.; lval'del+v.; 2 void lenv'add'builtins+lenv- e. / 5- Aist Runctions -5 lenv'add'builtin+e, "list", builtin'list.; lenv'add'builtin+e, "&ead", builtin'&ead.; lenv'add'builtin+e, "tail", builtin'tail.; lenv'add'builtin+e, "eval", builtin'eval.; lenv'add'builtin+e, "Toin", builtin'Toin.; 5- "at&ematical Runctions -5 lenv'add'builtin+e, "=", builtin'add.; lenv'add'builtin+e, "-", builtin'sub.; lenv'add'builtin+e, "-", builtin'mul.; lenv'add'builtin+e, "5", builtin'div.; 2

"he final step is to call this function before we create the interactive prompt. 'e also need to remember to delete the environment once we are finished.
lenv- e 3 lenv'new+.; lenv'add'builtins+e.; w&ile +6. / c&ar- input 3 readline+"lispy* ".;

CC

add'&istory+input.; mpc'result't r; i8 +mpc'parse+")stdin*", input, Aispy, @r.. / lval- x 3 lval'eval+e, lval'read+r%output..; lval'println+x.; lval'del+x.; mpc'ast'delete+r%output.; 2 else / mpc'err'print+r%error.; mpc'err'delete+r%error.; 2 8ree+input.; 2 lenv'del+e.;

If everything is working correctly we should have a play around in the prompt and verify that functions are actually a new type of value now, not symbols.
lispy* = )8unction* lispy* eval +&ead /; 61 66 6;2. ; lispy* eval +&ead /= - = - - 52. )8unction* lispy* +eval +&ead /= - = - - 52.. 61 21 31 lispy* &ello Jrror unbound symbol0 lispy*

&efine /unction
'e've managed to register our builtins as variables but we still don't have a way for users to define their own variables. "his is actually a bit awkward. 'e need to get the user to pass in a symbol to name, as well as the value to assign to it. ut symbols can't appear on their own. 7therwise the evaluation function will attempt to retrieve a value for them from the environment. "he only way we can pass around symbols without them being evaluated is to put them between /2 in a <uoted e-pression. )o we're going to use this techni<ue for our define function. It will take as input a list of symbols, and a number of other values. It will then assign each of the values to each of the symbols. "his function should act like any other builtin. It first checks for error conditions and then performs some command and return a value. In this case it first checks that the input arguments are the correct types. It then iterates over each symbol and value and puts them

%&&

into the environment. If these is an error we can return this, but on success we will return the empty e-pression +..
lval- builtin'de8+lenv- e, lval- a. / AAPPJLT+a, +a-*cellG1H-*type 33 AIAA'[JZPL., "Runction 7de87 passed incorrect type0".; 5- Rirst argument is symbol list -5 lval- syms 3 a-*cellG1H; 5- Jnsure all elements o8 8irst list are symbols -5 8or +int i 3 1; i ) syms-*count; i==. / AAPPJLT+a, +syms-*cellGiH-*type 33 AIAA'PO"., "Runction 7de87 cannot de8ine non-symbol".; 2 5- C&ecC correct number o8 symbols and values -5 AAPPJLT+a, +syms-*count 33 a-*count-6., "Runction 7de87 cannot de8ine incorrect number o8 values to symbols".; 5- Assign copies o8 values to symbols -5 8or +int i 3 1; i ) syms-*count; i==. / lenv'put+e, syms-*cellGiH, a-*cellGi=6H.; 2 lval'del+a.; return lval'sexpr+.; 2

'e need to register this new builtin using our builtin function lenv'add'builtins.
5- Iariable Runctions -5 lenv'add'builtin+e, "de8", builtin'de8.;

3ow we should be able to support user defined variables$ ecause our de8 function takes in a list of symbols we can do some cool things storing and manipulating symbols in lists before passing them to be defined. !ave a play around in the prompt and ensure everything is working correctly. (ou should get behaviour as follows. )ee what other comple- methods are possible for the definition and evaluation of variables. 7nce we get to defining functions we'll really see some of the neat things that can be done with this approach.
lispy* +. lispy* +. lispy* 611 lispy* 211 lispy* 311 lispy* +. lispy* 66 lispy* +. lispy* de8 /x2 611 de8 /y2 211 x y = x y de8 /a b2 ; < = a b de8 /arglist2 /a b x y2 arglist

%&%

/a b x lispy* +. lispy* /6 2 3 lispy*

y2 de8 arglist 6 2 3 F list a b x y F2

$rror 'eporting
)o far our error reporting kind of sucks. 'e can report when an error occurs, and give a vague notion of what was the problem was, but we don't give the user much information about what e-actly has gone wrong. 4or e-ample if there is an unbound symbol we should be able to report e-actly which symbol was unbound. "his can help the user track down errors, typos, and other trivial problems.

1clipses / Like ellipsis.

'ouldn't it be great if we could write a function that can report errors in a similar way to how print8 works. It would be ideal if we could pass in strings, integers, and other data to make our error messages richer. "he print8 function is a special function in C because it takes a variable number of arguments. 'e can create our own varia&le argument functions, which is what we're going to do to make our error reporting better. 'e'll modify lval'err to act in the same way as print8, taking in a format string, and after that a variable number of arguments to match into this string. "o declare that a function takes variables arguments in the type signature you use the special synta- of ellipsis %%%, which represent the rest of the arguments.
lval- lval'err+c&ar- 8mt, %%%.;

"hen, inside the function there are some standard library functions we can use to e-amine what the caller has passed in.

%&0

"he first step is to create a va'list struct and initialise it with va'start, passing in the last named argument. 4or other purposes it is possible to e-amine each argument passed in using va'arg, but we are going to pass our whole variable argument list directly to the vsnprint8 function. "his function acts like print8 but instead writes to a string and takes in a va'list. 7nce we are done with our variable arguments, we shoulder call va'end to cleanup any resources used. "he vsnprint8 function outputs to a string, which we need to allocate some first. ecause we don't know the si2e of this string until we've run the function we first allocate a buffer ;62 characters big and then reallocate to a smaller buffer once we've output to it. If an error message is going to be longer than =%0 character it will just get cut off, but hopefully this won't happen. 5utting it all together our new error function looks like this.
lval- lval'err+c&ar- 8mt, %%%. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'JLL; 5- Create a va list and initialiEe it -5 va'list va; va'start+va, 8mt.; 5- Allocate ;62 bytes o8 space -5 v-*err 3 malloc+;62.; 5- print8 into t&e error string wit& a maximum o8 ;66 c&aracters -5 vsnprint8+v-*err, ;66, 8mt, va.; 5- Leallocate to number o8 bytes actually used -5 v-*err 3 realloc+v-*err, strlen+v-*err.=6.; 5- Cleanup our va list -5 va'end+va.; 2 return v;

:sing this we can then start adding in some better error messages to our functions. As an e-ample we can look at lenv'get. 'hen a symbol can't be found, rather than reporting a generic error, we can actually report the name that was not found.
return lval'err+"Vnbound Pymbol 7Ns7", C-*sym.;

'e can also adapt our AAPPJLT macro such that it can take variable arguments too. ecause this is a macro and not a standard function the synta- is slightly different. 7n the left hand side of the definition we use the ellipses notation again, but on the right hand side we use a special variable ''IA'AL#P'' to paste in the contents of all the other arguments. 'e need to prefi- this special variable with two hash signs ((. "his ensure that it is pasted correctly when the macro is passed no e-tra arguments. In essence what this does is make sure to remove the leading comma , to appear as if no e-tra arguments were passed in.

%&9

ecause we might use args in the construction of the error message we need to make sure we don't delete it until we've created the error value.
(de8ine AAPPJLT+args, cond, 8mt, %%%. ! i8 +0+cond.. / lval- err 3 lval'err+8mt, ((''IA'AL#P''.; lval'del+args.; return err; 2

3ow we can update some of our error messages to make them more informative. 4or e-ample if the incorrect number of arguments were passed we can specify how many were re<uired and how many were given.
AAPPJLT+a, +a-*count 33 6., "Runction 7&ead7 passed too many arguments% #ot Ni, Jxpected Ni%", a-*count, 6.;

'e can also improve our error reporting for type errors. 'e should attempt to report what type was e-pected by a function and what type it actually got. efore we can do this it would be useful to have a function that took as input some type enumeration and returned a string representation of that type.
c&ar- ltype'name+int t. / switc&+t. / case AIAA'RVM return "Runction"; case AIAA'MV" return "Mumber"; case AIAA'JLL return "Jrror"; case AIAA'PO" return "Pymbol"; case AIAA'PJZPL return "P-Jxpression"; case AIAA'[JZPL return "[-Jxpression"; de8ault return "VnCnown"; 2 2

"hen we can use this function in our AAPPJLT functions to report what was retrieved and what was e-pected in a useful way.
AAPPJLT+a, +a-*cellG1H-*type 33 AIAA'[JZPL., "Runction 7&ead7 passed incorrect type 8or argument 1% #ot Ns, Jxpected Ns%", ltype'name+a-*cellG1H-*type., type'name+AIAA'[JZPL..;

#o ahead and improve the error reporting in all situations where we return an error in the code. "his should make debugging many of the ne-t stages much easier as we begin to write real, and complicated code using our new language$ )ee if you can use macros to save on typing and automatically generate code for common methods of error reporting.
lispy* = 6 /; < 92 Jrror Runction 7=7 passed incorrect type 8or argument 6% #ot [-Jxpression, Jxpected Mumber% lispy* &ead /6 2 32 /F ; <2 Jrror Runction 7&ead7 passed incorrect number o8 arguments% #ot 2, Jxpected 6% lispy*

'eference

%&*

,ariables*c
(include "mpc%&" (i8de8 '$BM32 static c&ar bu88erG21F:H; c&ar- readline+c&ar- prompt. / 8puts+"lispy* ", stdout.; 8gets+bu88er, 21F:, stdin.; c&ar- cpy 3 malloc+strlen+bu88er.=6.; strcpy+cpy, bu88er.; cpyGstrlen+cpy.-6H 3 7!17; return cpy; 2 void add'&istory+c&ar- unused. /2 (else (include )editline5readline%&* (include )editline5&istory%&* (endi8 5- Rorward Declarations -5 struct lval; struct lenv; typede8 struct lval lval; typede8 struct lenv lenv; 5- Aisp Ialue -5 enum / AIAA'JLL, AIAA'MV", AIAA'PO", AIAA'RVM, AIAA'PJZPL, AIAA'[JZPL 2; typede8 lval-+-lbuiltin.+lenv-, lval-.; struct lval / int type; long num; c&ar- err; c&ar- sym; lbuiltin 8un; int count; lval-- cell;

2;

lval- lval'num+long x. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'MV"; v-*num 3 x; return v; 2 lval- lval'err+c&ar- 8mt, %%%. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'JLL;

%&=

5- Create a va list and initialiEe it -5 va'list va; va'start+va, 8mt.; 5- Allocate ;62 bytes o8 space -5 v-*err 3 malloc+;62.; 5- print8 into t&e error string wit& a maximum o8 ;66 c&aracters -5 vsnprint8+v-*err, ;66, 8mt, va.; 5- Leallocate to number o8 bytes actually used -5 v-*err 3 realloc+v-*err, strlen+v-*err.=6.; 5- Cleanup our va list -5 va'end+va.; return v; 2 lval- lval'sym+c&ar- s. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'PO"; v-*sym 3 malloc+strlen+s. = 6.; strcpy+v-*sym, s.; return v; 2 lval- lval'8un+lbuiltin 8unc. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'RVM; v-*8un 3 8unc; return v; 2 lval- lval'sexpr+void. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'PJZPL; v-*count 3 1; v-*cell 3 MVAA; return v; 2 lval- lval'>expr+void. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'[JZPL; v-*count 3 1; v-*cell 3 MVAA; return v; 2 void lval'del+lval- v. / switc& +v-*type. / case AIAA'MV" breaC; case AIAA'RVM breaC; case AIAA'JLL 8ree+v-*err.; breaC; case AIAA'PO" 8ree+v-*sym.; breaC; case AIAA'[JZPL case AIAA'PJZPL 8or +int i 3 1; i ) v-*count; i==. /

%&;

lval'del+v-*cellGiH.; 2 8ree+v-*cell.; breaC; 2 2 8ree+v.;

lval- lval'copy+lval- v. / lval- x 3 malloc+siEeo8+lval..; x-*type 3 v-*type; switc& +v-*type. / 5- Copy Runctions and Mumbers Directly -5 case AIAA'RVM x-*8un 3 v-*8un; breaC; case AIAA'MV" x-*num 3 v-*num; breaC; 5- Copy Ptrings using malloc and strcpy -5 case AIAA'JLL x-*err 3 malloc+strlen+v-*err. = 6.; strcpy+x-*err, v*err.; breaC; case AIAA'PO" x-*sym 3 malloc+strlen+v-*sym. = 6.; strcpy+x-*sym, v*sym.; breaC; 5- Copy Aists by copying eac& sub-expression -5 case AIAA'PJZPL case AIAA'[JZPL x-*count 3 v-*count; x-*cell 3 malloc+siEeo8+lval-. - x-*count.; 8or +int i 3 1; i ) x-*count; i==. / x-*cellGiH 3 lval'copy+v-*cellGiH.; 2 breaC;

2 2

return x; lval- lval'add+lval- v, lval- x. / v-*count==; v-*cell 3 realloc+v-*cell, siEeo8+lval-. - v-*count.; v-*cellGv-*count-6H 3 x; return v; 2 lval- lval'Toin+lval- x, lval- y. / 8or +int i 3 1; i ) y-*count; i==. / x 3 lval'add+x, y-*cellGiH.; 2 8ree+y-*cell.; 8ree+y.; return x; 2 lval- lval'pop+lval- v, int i. / lval- x 3 v-*cellGiH; memmove+@v-*cellGiH, @v-*cellGi=6H, siEeo8+lval-. - +v-*count-i-6..; v-*count--; v-*cell 3 realloc+v-*cell, siEeo8+lval-. - v-*count.;

%&@

2

return x;

lval- lval'taCe+lval- v, int i. / lval- x 3 lval'pop+v, i.; lval'del+v.; return x; 2 void lval'print+lval- v.; void lval'print'expr+lval- v, c&ar open, c&ar close. / putc&ar+open.; 8or +int i 3 1; i ) v-*count; i==. / lval'print+v-*cellGiH.; i8 +i 03 +v-*count-6.. / putc&ar+7 7.; 2 2 putc&ar+close.; 2 void lval'print+lval- v. / switc& +v-*type. / case AIAA'RVM print8+")8unction*".; breaC; case AIAA'MV" print8+"Nli", v-*num.; breaC; case AIAA'JLL print8+"Jrror Ns", v-*err.; breaC; case AIAA'PO" print8+"Ns", v-*sym.; breaC; case AIAA'PJZPL lval'print'expr+v, 7+7, 7.7.; breaC; case AIAA'[JZPL lval'print'expr+v, 7/7, 727.; breaC; 2 2 void lval'println+lval- v. / lval'print+v.; putc&ar+7!n7.; 2 c&ar- ltype'name+int t. / switc&+t. / case AIAA'RVM return "Runction"; case AIAA'MV" return "Mumber"; case AIAA'JLL return "Jrror"; case AIAA'PO" return "Pymbol"; case AIAA'PJZPL return "P-Jxpression"; case AIAA'[JZPL return "[-Jxpression"; de8ault return "VnCnown"; 2 2 5- Aisp Jnvironment -5 struct lenv / int count; c&ar-- syms; lval-- vals; 2; lenv- lenv'new+void. / 5- BnitialiEe struct -5 lenv- e 3 malloc+siEeo8+lenv..; e-*count 3 1; e-*syms 3 MVAA;

%&+

e-*vals 3 MVAA; return e; 2 void lenv'del+lenv- e. / 5- Bterate over all items in environment deleting t&em -5 8or +int i 3 1; i ) e-*count; i==. / 8ree+e-*symsGiH.; lval'del+e-*valsGiH.; 2 5- Rree allocated memory 8or lists -5 8ree+e-*syms.; 8ree+e-*vals.; 8ree+e.;

2

lval- lenv'get+lenv- e, lval- C. / 5- Bterate over all items in environment -5 8or +int i 3 1; i ) e-*count; i==. / 5- C&ecC i8 t&e stored string matc&es t&e symbol string -5 5- B8 it does, return a copy o8 t&e value -5 i8 +strcmp+e-*symsGiH, C-*sym. 33 1. / return lval'copy+e-*valsGiH.; 2 2 5- B8 no symbol 8ound return error -5 return lval'err+"Vnbound Pymbol 7Ns7", C-*sym.;

2

void lenv'put+lenv- e, lval- C, lval- v. / 5- Bterate over all items in environment -5 5- T&is is to see i8 variable already exists -5 8or +int i 3 1; i ) e-*count; i==. / 5- B8 variable is 8ound delete item at t&at position -5 5- And replace wit& variable supplied by user -5 i8 +strcmp+e-*symsGiH, C-*sym. 33 1. / lval'del+e-*valsGiH.; e-*valsGiH 3 lval'copy+v.; e-*symsGiH 3 realloc+e-*symsGiH, strlen+C-*sym.=6.; strcpy+e-*symsGiH, C-*sym.; return; 2 2 5- B8 no existing entry 8ound t&en allocate space 8or new entry -5 e-*count==; e-*vals 3 realloc+e-*vals, siEeo8+lval-. - e-*count.; e-*syms 3 realloc+e-*syms, siEeo8+c&ar-. - e-*count.; 5- Copy contents o8 lval and symbol string into new location -5 e-*valsGe-*count-6H 3 lval'copy+v.; e-*symsGe-*count-6H 3 malloc+strlen+C-*sym.=6.; strcpy+e-*symsGe-*count-6H, C-*sym.;

2

5- Uuiltins -5

%&C

(de8ine AAPPJLT+args, cond, 8mt, %%%. ! i8 +0+cond.. / lval- err 3 lval'err+8mt, ((''IA'AL#P''.; lval'del+args.; return err; 2 (de8ine AAPPJLT'TOPJ+8unc, args, index, expect. ! AAPPJLT+args, args-*cellGindexH-*type 33 expect, ! "Runction 7Ns7 passed incorrect type 8or argument Ni% #ot Ns, Jxpected Ns%", ! 8unc, index, ltype'name+args-*cellGindexH-*type., ltype'name+expect.. (de8ine AAPPJLT'MV"+8unc, args, num. ! AAPPJLT+args, args-*count 33 num, ! "Runction 7Ns7 passed incorrect number o8 arguments% #ot Ni, Jxpected Ni%", ! 8unc, args-*count, num. (de8ine AAPPJLT'MKT'J"PTO+8unc, args, index. ! AAPPJLT+args, args-*cellGindexH-*count 03 1, ! "Runction 7Ns7 passed /2 8or argument Ni%", 8unc, index.; lval- lval'eval+lenv- e, lval- v.; lval- builtin'list+lenv- e, lval- a. / a-*type 3 AIAA'[JZPL; return a; 2 lval- builtin'&ead+lenv- e, lval- a. / AAPPJLT'MV"+"&ead", a, 6.; AAPPJLT'TOPJ+"&ead", a, 1, AIAA'[JZPL.; AAPPJLT'MKT'J"PTO+"&ead", a, 1.; lval- v 3 lval'taCe+a, 1.; w&ile +v-*count * 6. / lval'del+lval'pop+v, 6..; 2 return v; 2 lval- builtin'tail+lenv- e, lval- a. / AAPPJLT'MV"+"tail", a, 6.; AAPPJLT'TOPJ+"tail", a, 1, AIAA'[JZPL.; AAPPJLT'MKT'J"PTO+"tail", a, 1.; lval- v 3 lval'taCe+a, 1.; lval'del+lval'pop+v, 1..; return v; 2 lval- builtin'eval+lenv- e, lval- a. / AAPPJLT'MV"+"eval", a, 6.; AAPPJLT'TOPJ+"tail", a, 1, AIAA'[JZPL.; lval- x 3 lval'taCe+a, 1.; x-*type 3 AIAA'PJZPL; return lval'eval+e, x.;

2

lval- builtin'Toin+lenv- e, lval- a. / 8or +int i 3 1; i ) a-*count; i==. / AAPPJLT'TOPJ+"Toin", a, i, AIAA'[JZPL.; 2

%%&

lval- x 3 lval'pop+a, 1.; w&ile +a-*count. / lval- y 3 lval'pop+a, 1.; x 3 lval'Toin+x, y.; 2 lval'del+a.; return x; 2 lval- builtin'op+lenv- e, lval- a, c&ar- op. / 8or +int i 3 1; i ) a-*count; i==. / AAPPJLT'TOPJ+op, a, i, AIAA'MV".; 2 lval- x 3 lval'pop+a, 1.; i8 ++strcmp+op, "-". 33 1. @@ a-*count 33 1. / x-*num 3 -x-*num; 2 w&ile +a-*count * 1. / lval- y 3 lval'pop+a, 1.; i8 i8 i8 i8 +strcmp+op, "=". 33 1. / x-*num =3 y-*num; 2 +strcmp+op, "-". 33 1. / x-*num -3 y-*num; 2 +strcmp+op, "-". 33 1. / x-*num -3 y-*num; 2 +strcmp+op, "5". 33 1. / i8 +y-*num 03 1. / lval'del+x.; lval'del+y.; lval'del+a.; return lval'err+"Division Uy Yero%".; 2 x-*num 53 y-*num;

2 2 lval'del+y.;

2

lval'del+a.; return x; builtin'add+lenvbuiltin'sub+lenvbuiltin'mul+lenvbuiltin'div+lenve, e, e, e, lvallvallvallvala. a. a. a. / / / / return return return return builtin'op+e, builtin'op+e, builtin'op+e, builtin'op+e, a, a, a, a, "=".; "-".; "-".; "5".; 2 2 2 2

lvallvallvallval-

lval- builtin'de8+lenv- e, lval- a. / AAPPJLT'TOPJ+"de8", a, 1, AIAA'[JZPL.; 5- Rirst argument is symbol list -5 lval- syms 3 a-*cellG1H; 5- Jnsure all elements o8 8irst list are symbols -5 8or +int i 3 1; i ) syms-*count; i==. / AAPPJLT+a, +syms-*cellGiH-*type 33 AIAA'PO"., "Runction 7de87 cannot de8ine non-symbol% #ot Ns, Jxpected Ns%", ltype'name+syms-*cellGiH-*type., ltype'name+AIAA'PO"..; 2 5- C&ecC correct number o8 symbols and values -5

%%%

AAPPJLT+a, +syms-*count 33 a-*count-6., "Runction 7de87 passed too many arguments 8or symbols% #ot Ni, Jxpected Ni%", syms-*count, a-*count-6.; 5- Assign copies o8 values to symbols -5 8or +int i 3 1; i ) syms-*count; i==. / lenv'put+e, syms-*cellGiH, a-*cellGi=6H.; 2 lval'del+a.; return lval'sexpr+.; 2 void lenv'add'builtin+lenv- e, c&ar- name, lbuiltin 8unc. / lval- C 3 lval'sym+name.; lval- v 3 lval'8un+8unc.; lenv'put+e, C, v.; lval'del+C.; lval'del+v.; 2 void lenv'add'builtins+lenv- e. / 5- Iariable Runctions -5 lenv'add'builtin+e, "de8", builtin'de8.; 5- Aist Runctions -5 lenv'add'builtin+e, "list", builtin'list.; lenv'add'builtin+e, "&ead", builtin'&ead.; lenv'add'builtin+e, "tail", builtin'tail.; lenv'add'builtin+e, "eval", builtin'eval.; lenv'add'builtin+e, "Toin", builtin'Toin.; 5- "at&ematical Runctions -5 lenv'add'builtin+e, "=", builtin'add.; lenv'add'builtin+e, "-", builtin'sub.; lenv'add'builtin+e, "-", builtin'mul.; lenv'add'builtin+e, "5", builtin'div.; 2 5- Jvaluation -5 lval- lval'eval'sexpr+lenv- e, lval- v. / 8or +int i 3 1; i ) v-*count; i==. / v-*cellGiH 3 lval'eval+e, v*cellGiH.; 2 8or +int i 3 1; i ) v-*count; i==. / i8 +v-*cellGiH-*type 33 AIAA'JLL. / return lval'taCe+v, i.; 2 2 i8 +v-*count 33 1. / return v; 2 i8 +v-*count 33 6. / return lval'taCe+v, 1.; 2 5- Jnsure 8irst element is a 8unction a8ter evaluation -5 lval- 8 3 lval'pop+v, 1.; i8 +8-*type 03 AIAA'RVM. / lval- err 3 lval'err+ "P-Jxpression starts wit& incorrect type% #ot Ns, Jxpected Ns%", ltype'name+8-*type., ltype'name+AIAA'RVM..; lval'del+8.; lval'del+v.; return err; 2

%%0

5- B8 so call 8unction to get result -5 lval- result 3 8-*8un+e, v.; lval'del+8.; return result; 2 lval- lval'eval+lenv- e, lval- v. / i8 +v-*type 33 AIAA'PO". / return lenv'get+e, v.; 2 i8 +v-*type 33 AIAA'PJZPL. / return lval'eval'sexpr+e, v.; 2 return v; 2 5- Leading -5 lval- lval'read'num+mpc'ast't- t. / long x 3 strtol+t-*contents, MVAA, 61.; return errno 03 JLAM#J W lval'num+x. lval'err+"Bnvalid Mumber%".; 2 lval- lval'read+mpc'ast't- t. / i8 +strstr+t-*tag, "number".. / return lval'read'num+t.; 2 i8 +strstr+t-*tag, "symbol".. / return lval'sym+t-*contents.; 2 lval- x 3 MVAA; i8 +strcmp+t-*tag, "*". 33 1. / x 3 lval'sexpr+.; 2 i8 +strstr+t-*tag, "sexpr".. / x 3 lval'sexpr+.; 2 i8 +strstr+t-*tag, ">expr".. / x 3 lval'>expr+.; 2 8or +int i 3 1; i ) t-*c&ildren'num; i==. / i8 +strcmp+t-*c&ildrenGiH-*contents, "+". 33 i8 +strcmp+t-*c&ildrenGiH-*contents, ".". 33 i8 +strcmp+t-*c&ildrenGiH-*contents, "2". 33 i8 +strcmp+t-*c&ildrenGiH-*contents, "/". 33 i8 +strcmp+t-*c&ildrenGiH-*tag, "regex". 33 x 3 lval'add+x, lval'read+t-*c&ildrenGiH..; 2 2 return x; 1. 1. 1. 1. 1. / / / / / continue; continue; continue; continue; continue; 2 2 2 2 2

5- "ain -5 int main+int argc, c&ar-- argv. / mpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tMumber Pymbol Pexpr [expr Jxpr Aispy 3 3 3 3 3 3 mpc'new+"number".; mpc'new+"symbol".; mpc'new+"sexpr".; mpc'new+">expr".; mpc'new+"expr".; mpc'new+"lispy".; ! ! ! ! ! ! !

mpca'lang+"PC'AAM#'DJRAVAT, " number 5-WG1-4H=5 ; symbol 5Ga-EA-Y1-4'=!!--!!5!!!!3)*0@H=5 ; sexpr 7+7 )expr*- 7.7 ; >expr 7/7 )expr*- 727 ; expr )number* ? )symbol* ? )sexpr* ? )>expr* ; lispy 5Q5 )expr*- 5X5 ; ",

%%9

Mumber, Pymbol, Pexpr, [expr, Jxpr, Aispy.; puts+"Aispy Iersion 1%1%1%1%9".; puts+"Press Ctrl=c to Jxit!n".; lenv- e 3 lenv'new+.; lenv'add'builtins+e.; w&ile +6. / c&ar- input 3 readline+"lispy* ".; add'&istory+input.; mpc'result't r; i8 +mpc'parse+")stdin*", input, Aispy, @r.. / lval- x 3 lval'eval+e, lval'read+r%output..; lval'println+x.; lval'del+x.; mpc'ast'delete+r%output.; 2 else / mpc'err'print+r%error.; mpc'err'delete+r%error.; 2 8ree+input.; 2 lenv'del+e.; mpc'cleanup+<, Mumber, Pymbol, Pexpr, [expr, Jxpr, Aispy.; 2 return 1;

Bonus %ar+s
• • • • • • • •

H Create a .acro to aid specifically with reporting type errors. H Create a .acro to aid specifically with reporting argument count errors. H Create a .acro to aid specifically with reporting empty list errors. H Change printing a builtin function print its name. H 'rite a function for printing out all the named values in an environment. H Fedefine one of the builtin variables to something different. H Change redefinition of one of the builtin variables to something different an error. H Create an exit function for stopping the prompt and e-iting.

%%*

2unctions • Chapter 12

What is a /unction(
4unctions are the essence of all programming. ack in the early days of computer science they represented a naive dream. "he idea was that we could reduce computation into these smaller and smaller bits of re,usable code. #iven enough time, and a proper structure for libraries, eventually we would have written code re<uired for all computational needs. 3o longer would people have to write their own functions, and programming would consist of an easy job of stitching together components. "his dream hasn't come true yet, but it persists, no matter how flawed. 1ach new programming techni<ue, or paradigm, that comes along shakes up this idea a little. "hey promise better re,use of code. etter abstractions, and an easier life for all.

ananaphone / Another naive dream.

In reality what each paradigm delivers is simply di''erent abstractions. "here has always been a trade,off. 4or each higher level of thinking about programming, some piece is thrown away. And this means, no matter how well you decide what to keep and what to leave, occasionally someone will need that piece that has been lost. ut through all of this, one way or the other, functions have always persisted, and continually proven to be effective. 'e've used functions in C, we know what they look like, but we don't know e-actly what they are. !ere are a few ways to think about them. 7ne way to think about functions is as description of some computation you want to be performed later. 'hen you define a function it is like saying Bwhen I use this name I want that sort of thing to happenB. "his is a very practical idea of a function. It is very intuitive, and metaphorical to language. "his is the way you would command a human, or animal. Another thing I like about this is that it captures the delayed nature of functions. 4unctions are defined once, but can be called on repeatedly after.

%%=

Another way to think about functions is as a black bo- that takes some input and produces some output. "his idea is subtly different from the former. It is more algebraic, and doesn't talk about computation or commands. "his idea is a mathematical concept, and is not tied to some particular machine, or language. In some situations this idea is e-ceptionally useful. It allows us to think about functions without worrying about their internals, or how they are computed e-actly. 'e can then combine and compose them together without worry of something subtle going wrong. "his is the core idea behind an abstraction, and is what allows layers of comple-ity to work together with each other rather than conflict. "his idea's strength can also be its downfall. ecause it does not mention anything about computation it does not deal with a number of real world concerns. "How long will this 'unction take to run5", "Is this 'unction e''icient5", "!ill it modi'y the state o' my program5 I' so how5".

lack o- / (our typical function.

A third method is to think of functions as partial computations. Like the .athematical model they can take some inputs. "hese values are re<uired before it can complete the computation. "his is why it is called partial. ut like the computational model, the body of the function consists of a computation specified in some language of commands. "hese inputs are called un&ound varia&les, and to finish the computation one simply supplies them. Like fitting a cog into a machine which was before spinning aimlessly, this completes all that is needed for the computation to run, and the machine runs. "he output of these partial computations is itself a variable with an unknown value. "his output can be placed as input to a new function, and so one function relies on another. An advantage of this idea over the .athematical .odel is that we recogni2e that functions contain computation. 'e see that when the computation runs, some physical process is going on in the machine. "his means we recogni2e the fact that certain things take time to elapse, or that a function might change the program state, or do anything else we're not sure about$ %%;

All these ideas are e-plored in the study of functions, Lam&da calculus. "his is a field that combines logic, maths, and computer science. "he name comes from the #reek letter Lambda, which is used in the representation of &inding varia&les. :sing Lambda calculus gives a way of defining, composing and building 'unctions using a simple mathematical notation. 'e are going to use all of the previous ideas to add user defined functions to our language. Lisp is already well suited to this sort of playing around, and using these concepts it won't take much work for us to implement functions. "he first step will be to write a builtin function that can create user defined functions. !ere is one idea as to how it can be specified. "he first argument could be a list of symbols, just like our de8 function. "hese symbols we call the 'ormal arguments, also known as the un&ound varia&les. "hey act as the inputs to our partial computation. "he second argument could be another list. 'hen running the function this is going to be evaluated with our builtin eval function. "his function we'll call just !, Da homage to "he Lambda Calculus as the ! character looks a little bit like a lambdaE. "o create a function which takes two inputs and adds them together, we would then write something like this.
! /x y2 /= x y2

'e can call the function by putting it as the first argument in a normal ),1-pression
+! /x y2 /= x y2. 61 21

If we want to name this function we can pass it to our e-isting builtin de8 like any other value and store it in the environment.
de8 /add-toget&er2 +! /x y2 /= x y2.

"hen we can call it by refering to it by name.
add-toget&er 61 21

/unction "ype
"o store a function as an lval we need to think e-actly what it consists of. :sing the previous definition, a function should consists of three parts. 4irst is the list of 'ormal arguments, which we must bind before we can evaluate the function. "he second part is a O,1-pression that represents the body of the function. 4inally we re<uire a location to store the values assigned to the 'ormal arguments. Luckily we already have a structure for storing variables, an environment. 'e will store our builtin functions and user defined functions under the same type AIAA'RVM. "his means we need a way internally to differentiate between them. "o do this we can check

%%@

if the lbuiltin function pointer is MVAA or not. If it is not MVAA we know the lval is some builtin function, otherwise we know it is a user function.
struct lval / int type; 5- Uasic -5 long num; c&ar- err; c&ar- sym; 5- Runction -5 lbuiltin builtin; lenv- env; lval- 8ormals; lval- body; 5- Jxpression -5 int count; lval-- cell;

2;

'e've renamed the lbuiltin field from 8unc to builtin. 'e should make sure to change this in all the places it is used in our code. 'e also need to create a constructor for user defined lval functions. !ere we build a new environment for the function, and assign the 8ormals and body values to those passed in.
lval- lval'lambda+lval- 8ormals, lval- body. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'RVM; 5- Pet Uuiltin to Mull -5 v-*builtin 3 MVAA; 5- Uuilt new environment -5 v-*env 3 lenv'new+.; 5- Pet Rormals and Uody -5 v-*8ormals 3 8ormals; v-*body 3 body; return v; 2

As with whenever we change our lval type we need to update the functions for deletion, copying, and printing to deal with the changes. 4or evaluation we'll need to look in greater depth. 4or &eletion...
case AIAA'RVM i8 +v-*builtin 03 MVAA. / lenv'del+v-*env.; lval'del+v-*8ormals.; lval'del+v-*body.; 2 breaC;

%%+

4or Copying...
case AIAA'RVM x-*builtin 3 v-*builtin; i8 +x-*builtin 03 MVAA. / x-*env 3 lenv'copy+v-*env.; x-*8ormals 3 lval'copy+v-*8ormals.; x-*body 3 lval'copy+v-*body.; 2 breaC;

4or -rinting...
case AIAA'RVM i8 +v-*builtin. / print8+")builtin*".; 2 else / print8+"+!! ".; lval'print+v-*8ormals.; putc&ar+7 7.; lval'print+v*body.; putc&ar+7.7.; 2 breaC;

Lambda /unction
'e can now add a builtin for our lambda function. 'e want it to take as input some list of symbols, and a list that represents the code. After that it should return a function lval. 'e've defined a few of builtins now, and this one will follow the same format. Like in de8 we do some error checking to ensure the argument types and count are correct Dusing some newly defined .acrosE. "hen we just pop the first two arguments from the list and pass them to our previously defined function lval'lambda.
lval- builtin'lambda+lenv- e, lval- a. / 5- C&ecC Two arguments, eac& o8 w&ic& are [-Jxpressions -5 AAPPJLT'MV"+"!!", a, 2.; AAPPJLT'TOPJ+"!!", a, 1, AIAA'[JZPL.; AAPPJLT'TOPJ+"!!", a, 6, AIAA'[JZPL.; 5- C&ecC 8irst [-Jxpression contains only Pymbols -5 8or +int i 3 1; i ) a-*cellG1H-*count; i==. / AAPPJLT+a, +a-*cellG1H-*cellGiH-*type 33 AIAA'PO"., "Cannot de8ine non-symbol% #ot Ns, Jxpected Ns%", ltype'name+a-*cellG1H-*cellGiH-*type., ltype'name+AIAA'PO"..; 2 5- Pop 8irst two arguments and pass t&em to lval'lambda -5 lval- 8ormals 3 lval'pop+a, 1.; lval- body 3 lval'pop+a, 1.; lval'del+a.; return lval'lambda+8ormals, body.; 2

%%C

5laygroup / (our typical parent environment.

-arent $n,ironment
'e've given functions their own environment. In this environment we will place the values that their formal arguments are set to. 'hen we come to evaluate the body of the function we can do it in this environment and know that those variables will have the correct values. ut ideally we also want these functions to be able to access variables which are in the global environment, such as our builtin functions. 'e can solve this problem by changing the definition of our environment to contain a reference to some parent environment. "hen, when we want to evaluate a function, we can set this parent environment to our global environment, which has all of our builtins defined within. 'hen we add this to our lenv struct, conceptually it will be a re'erence to a parent environment, not some sub,environment or anything like that. ecause of this we shouldn't delete it when our lenv gets deleted, or copy it when our lenv gets copied. "he way the parent environment works is simple. If someone calls lenv'get on the environment, and the symbol cannot be found. It will look then in any parent environment to see if the named value e-ists there, and repeat the process till either the variable is found or there are no more parents. "o signify that an environment has no parent we set the reference to MVAA. "he constructor function only re<uire basic changes to allow for this.
struct lenv / lenv- par;

%0&

2;

int count; c&ar-- syms; lval-- vals;

lenv- lenv'new+void. / lenv- e 3 malloc+siEeo8+lenv..; e-*par 3 MVAA; e-*count 3 1; e-*syms 3 MVAA; e-*vals 3 MVAA; return e; 2

"o get a value from an environment we need to add in the search of the parent environment in the case that a symbol is not found.
lval- lenv'get+lenv- e, lval- C. / 8or +int i 3 1; i ) e-*count; i==. / i8 +strcmp+e-*symsGiH, C-*sym. 33 1. / return lval'copy+e-*valsGiH.; 2 2 5- B8 no symbol c&ecC in parent ot&erwise error -5 i8 +e-*par. / return lenv'get+e-*par, C.; 2 else / return lval'err+"Vnbound Pymbol 7Ns7", C-*sym.; 2

2

ecause we have a new lval type that has its own environment we need a function for copying environments, to use for when we copy lval structs.
lenv- lenv'copy+lenv- e. / lenv- n 3 malloc+siEeo8+lenv..; n-*par 3 e-*par; n-*count 3 e-*count; n-*syms 3 malloc+siEeo8+c&ar-. - n-*count.; n-*vals 3 malloc+siEeo8+lval-. - n-*count.; 8or +int i 3 1; i ) e-*count; i==. / n-*symsGiH 3 malloc+strlen+e-*symsGiH. = 6.; strcpy+n-*symsGiH, e-*symsGiH.; n-*valsGiH 3 lval'copy+e-*valsGiH.; 2 return n; 2

!aving parent environments also changes our concept of de'ining a variable. "here are two ways we could define a variable now. 1ither we could define it in the local, innermost environment, or we could define it in the global, outermost environment. 'e will add functions to do both. 'e'll leave the lenv'put method the same. It can be used for definition in the local environment. ut we'll add a new function lenv'de8 for definition in the global environment. "his works by simply following the parent chain up before using lval'put to define locally.

%0%

void lenv'de8+lenv- e, lval- C, lval- v. / 5- Bterate till e &as no parent -5 w&ile +e-*par. / e 3 e-*par; 2 5- Put value in e -5 lenv'put+e, C, v.; 2

At the moment this distinction may seem useless, but later on we will use it to write partial results of calculations to local variables inside a function. 'e should add another builtin for local assignment. 'e'll call this put in C, but give it the 3 symbol in Lisp. 'e can adapt our builtin'de8 function and re,use the common code, just like we do with our mathematical operators.
lval- builtin'var+lenv- e, lval- a, c&ar- 8unc. / AAPPJLT'TOPJ+8unc, a, 1, AIAA'[JZPL.; lval- syms 3 a-*cellG1H; 8or +int i 3 1; i ) syms-*count; i==. / AAPPJLT+a, +syms-*cellGiH-*type 33 AIAA'PO"., "Runction 7Ns7 cannot de8ine non-symbol% #ot Ns, Jxpected Ns%", 8unc, ltype'name+syms-*cellGiH-*type., ltype'name+AIAA'PO"..; 2 AAPPJLT+a, +syms-*count 33 a-*count-6., "Runction 7Ns7 passed too many arguments 8or symbols% #ot Ni, Jxpected Ni%", 8unc, syms-*count, a-*count-6.; 8or +int i 3 1; i ) syms-*count; i==. / 5- B8 7de87 de8ine in global scope% B8 7put7 de8ine in local scope -5 i8 +strcmp+8unc, "de8". 33 1. / lenv'de8+e, syms-*cellGiH, a*cellGi=6H.; 2 i8 +strcmp+8unc, "3". 33 1. / lenv'put+e, syms-*cellGiH, a*cellGi=6H.; 2 2 lval'del+a.; return lval'sexpr+.;

2

lval- builtin'de8+lenv- e, lval- a. / return builtin'var+e, a, "de8".; 2 lval- builtin'put+lenv- e, lval- a. / return builtin'var+e, a, "3".; 2

"hen we need to register these as a builtins.
lenv'add'builtin+e, "de8", builtin'de8.; lenv'add'builtin+e, "3", builtin'put.;

/unction Calling
'e need to write the code that runs when an e-pression gets evaluated and an function lval is called. 'hen this function type is a builtin we can call it as before, using the function pointer, but we need to do something separate for our user defined functions. 'e need to bind each of the %00

arguments passed in, to each of the symbols in the 8ormals field. 7nce this is done we need to evaluate the body field, using the env field as an environment, and the calling environment as a parent. A first attempt, without error checking, might look like thisG
lval- lval'call+lenv- e, lval- 8, lval- a. / 5- B8 Uuiltin t&en simply call t&at -5 i8 +8-*builtin. / return 8-*builtin+e, a.; 2 5- Assign eac& argument to eac& 8ormal in order -5 8or +int i 3 1; i ) a-*count; i==. / lenv'put+8-*env, 8-*8ormals-*cellGiH, a-*cellGiH.; 2 lval'del+a.; 5- Pet t&e parent environment -5 8-*env-*par 3 e; 5- Jvaluate t&e body -5 return builtin'eval+8-*env, lval'add+lval'sexpr+., lval'copy+8-*body...;

2

"his works fine providing all error conditions are met, but it doesn't deal correctly with the case where the number of arguments supplied, and the number of formal arguments re<uired, differ. In this situation it will just crash. Actually this is an interesting case, and leaves us a couple of options. 'e could just throw an error when the argument count supplied is incorrect, but we can do something more fun. 'hen too few arguments are supplied we could instead bind the first few formal arguments of the function and then return it, leaving the rest unbound. "his creates a function that has been partially evaluated and reflects our previous idea of a function being some kind of partial computation. If we start with a function that takes two arguments, and pass in a single argument, we can bind this first argument and return a new function with its first formal argument bound, and its second remaining empty. 'e can use this metaphor to create a cute image of how functions work. 'e can imagine each e-pression consisting of a function at the front which repeatedly consumes the input directly to its right. 7nce it has consumed the input to its right, if it is full Dre<uires no more inputsE it evaluates and replaces itself with some value. If it needs more it replaced itself with another new, fuller function, with one of its variables bound and repeats the process. )o you can imagine functions like a little 5ac,.an, not consuming all inputs at once, but iteratively eating inputs to the right and getting bigger and bigger until it is full. "his isn't actually how we're going to implement it in code, but either way it is 'un to imagine.
lval- lval'call+lenv- e, lval- 8, lval- a. / 5- B8 Uuiltin t&en simply apply t&at -5 i8 +8-*builtin. / return 8-*builtin+e, a.; 2

%09

5- Lecord Argument Counts -5 int given 3 a-*count; int total 3 8-*8ormals-*count; 5- $&ile arguments still remain to be processed -5 w&ile +a-*count. / 5- B8 we7ve ran out o8 8ormal arguments to bind -5 i8 +8-*8ormals-*count 33 1. / lval'del+a.; return lval'err+"Runction passed too many arguments% #ot Ni, Jxpected Ni%", given, total.; 2 5- Pop t&e 8irst symbol 8rom t&e 8ormals -5 lval- sym 3 lval'pop+8-*8ormals, 1.; 5- Pop t&e next argument 8rom t&e list -5 lval- val 3 lval'pop+a, 1.; 5- Uind a copy into t&e 8unction7s environment -5 lenv'put+8-*env, sym, val.; 5- Delete symbol and value -5 lval'del+sym.; lval'del+val.; 2 5- Argument list is now bound so can be cleaned up -5 lval'del+a.; 5- B8 all 8ormals &ave been bound evaluate -5 i8 +8-*8ormals-*count 33 1. / 5- Pet Runction Jnvironment parent to current evaluation Jnvironment -5 8-*env-*par 3 e; 5- Jvaluate and return -5 return builtin'eval+8-*env, lval'add+lval'sexpr+., lval'copy+8*body...; 2 else / 5- Kt&erwise return partially evaluated 8unction -5 return lval'copy+8.; 2 2

"he above function does e-actly as we e-plained above, with correct error handling added in too. 4irst it iterates over the passed in arguments attempting to place each one in the environment. "hen it checks if the environment is full, and if so evaluates, otherwise returns a copy of itself with some arguments filled. If we update our evaluation function lval'eval'sexpr to call lval'call, we can give our new system a spin.
lval- 8 3 lval'pop+v, 1.; i8 +8-*type 03 AIAA'RVM. / lval- err 3 lval'err+ "P-Jxpression starts wit& incorrect type% #ot Ns, Jxpected Ns%", ltype'name+8-*type., ltype'name+AIAA'RVM..; lval'del+8.; lval'del+v.;

%0*

2

return err;

lval- result 3 lval'call+e, 8, v.;

"ry defining some functions and test out how partial evaluation works.
lispy* ! /x y2 /= x y2 +! /x y2 /= x y2. lispy* +! /x y2 /= x y2. 61 21 31 lispy* de8 /add-mul2 +! /x y2 /= x +- x y.2. +. lispy* add-mul 61 21 261 lispy* add-mul 61 +! /y2 /= x +- x y.2. lispy* de8 /add-mul-612 +add-mul 61. +. lispy* add-mul-61 ;1 ;61 lispy*

.ariable Arguments
'e've defined some of our builtin functions so they can take in a variable number of arguments. 4unctions like = and Toin can take any number of arguments, and operator on them logically. 'e should find a way to let user defined functions work on multiple arguments also. :nfortunately there isn't an elegant way for us to allow for this, without adding in some special synta-. )o we're going to hard,code some system into our language using a special symbol @. 'e are going to let users define formal arguments that look like /x @ xs2, which means that a function will take in a single argument x, followed by 2ero or more other arguments, joined together into a list called xs. "his is a bit like the ellipsis we used to declare variable arguments in C. 'hen assigning our formal arguments we're going look for a @ symbol and if it e-ists take the ne-t formal argument and assign it any remaining supplied arguments we've been passed. It's important we convert this argument list to a O,1-pression. 'e need to also remember to check that @ is followed by a real symbol, and if it isn't we should throw an error. Lust after the first symbol is popped from the formals in the w&ile loop of lval'call we can add this special case.
5- Ppecial Case to deal wit& 7@7 -5 i8 +strcmp+sym-*sym, "@". 33 1. / 5- Jnsure 7@7 is 8ollowed by anot&er symbol -5 i8 +8-*8ormals-*count 03 6. / lval'del+a.;

%0=

return lval'err+"Runction 8ormat invalid% Pymbol 7@7 not 8ollowed by single symbol%".; 2 5- Mext 8ormal s&ould be bound to remaining arguments -5 lval- nsym 3 lval'pop+8-*8ormals, 1.; lenv'put+8-*env, nsym, builtin'list+e, a..; lval'del+sym.; lval'del+nsym.; breaC;

2

)uppose when calling the function the user doesn't supply any variable arguments, but only the first named ones. In this case we need to set the symbol following @ to the empty list. Lust after we delete the argument list, and before we check to see if all the formals have been evaluated add in this special case.
5- B8 7@7 remains in 8ormal list it s&ould be bound to empty list -5 i8 +8-*8ormals-*count * 1 @@ strcmp+8-*8ormals-*cellG1H-*sym, "@". 33 1. / 5- C&ecC to ensure t&at @ is not passed invalidly% -5 i8 +8-*8ormals-*count 03 2. / return lval'err+"Runction 8ormat invalid% Pymbol 7@7 not 8ollowed by single symbol%".; 2 5- Pop and delete 7@7 symbol -5 lval'del+lval'pop+8-*8ormals, 1..; 5- Pop next symbol and create empty list -5 lval- sym 3 lval'pop+8-*8ormals, 1.; lval- val 3 lval'>expr+.; 5- Uind to environment and delete -5 lenv'put+8-*env, sym, val.; lval'del+sym.; lval'del+val.; 2

1nteresting /unctions
2unction 3e4inition
Lambdas are clearly a simple and powerful way of defining functions. ut the synta- is a little clumsy. "here are a lot of brackets and symbols involved. !ere is an interesting idea. 'e can try to write a function that defines a function itself, using in some way nicer synta-. 1ssentially what we want is a function that can perform two steps at once. 4irst creating a new function and then defining it some name. 'e could let the user supply the name and the formal arguments altogether and then separate these out for them and use them in the definition. !ere is a function that does that. It takes as input some arguments and some body. It takes the head of the arguments to be the function name and the rest to be the formals to the function body.
! /args body2 /de8 +&ead args. +! +tail args. body.2

%0;

'e can name this function something like 8un by passing it to de8 as usual.
de8 /8un2 +! /args body2 /de8 +&ead args. +! +tail args. body.2.

"his means that we can now define functions in a much simpler and nicer way. "o define our previously mentioned add-toget&er we can do the following. 4unctions that can define functions. "hat is certainly something we could never do in C. !ow cool is that$
8un /add-toget&er x y2 /= x y2

Currying / 3ot as good as it sounds.

Curr5ing
At the moment functions like = take a variable number of arguments. In some situations that's great, but what if we instead had a list of arguments we wished to pass it. In this situation it is rendered somewhat useless. Again we can try to create a function to solve this problem. If we can create a list in the format we wish to use for our e-pression we can use eval to treat it as such. In the situation of = we could append this function to the front of the list and then perform the evaluation. 'e can define a function unpacC that does this. It takes as input some function and some list and appends the function to the front of the list, before evaluating it.
8un /unpacC 8 xs2 /eval +Toin +list 8. xs.2

In some situations we might be faced with the opposite dilemma. 'e may have a function that takes as input some list, but we wish to call it using variable arguments. In this case the solution is even simpler. 'e use the fact that our @ synta- for variable arguments packs up variable arguments into a list for us.
8un /pacC 8 @ xs2 /8 xs2

In some languages this is called currying and uncurrying respectively. "his is named after Haskell %urry and unfortunately has nothing to do with our favourite spicy food.
lispy* uncurry &ead ; < 9

%0@

/;2 lispy* curry = /; < 92 6:

ecause of the way our partial evaluation works we don't need to think of currying with a specific set of arguments. 'e can think of functions themselves being in curried or uncurried form.
lispy* de8 /add-unpacCed2 +curry =. +. lispy* add-unpacCed /; < 92 6:

!ave a play around and see what other interesting and powerful functions you can try to come up with. (ou might be a bit limited for now. In the ne-t chapter well add conditionals which will really start to make our language more complete. ut that doesn't mean you won't be able to come up with some other interesting ideas now. 7ur Lisp really is getting richer$

'eference
functions*c
(include "mpc%&" (i8de8 '$BM32 static c&ar bu88erG21F:H; c&ar- readline+c&ar- prompt. / 8puts+"lispy* ", stdout.; 8gets+bu88er, 21F:, stdin.; c&ar- cpy 3 malloc+strlen+bu88er.=6.; strcpy+cpy, bu88er.; cpyGstrlen+cpy.-6H 3 7!17; return cpy; 2 void add'&istory+c&ar- unused. /2 (else (include )editline5readline%&* (include )editline5&istory%&* (endi8 5- Rorward Declarations -5 struct lval; struct lenv; typede8 struct lval lval; typede8 struct lenv lenv; 5- Aisp Ialue -5 enum / AIAA'JLL, AIAA'MV", AIAA'PO", AIAA'RVM, AIAA'PJZPL, AIAA'[JZPL 2;

%0+

typede8 lval-+-lbuiltin.+lenv-, lval-.; struct lval / int type; 5- Uasic -5 long num; c&ar- err; c&ar- sym; 5- Runction -5 lbuiltin builtin; lenv- env; lval- 8ormals; lval- body; 5- Jxpression -5 int count; lval-- cell;

2;

lval- lval'num+long x. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'MV"; v-*num 3 x; return v; 2 lval- lval'err+c&ar- 8mt, %%%. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'JLL; va'list va; va'start+va, 8mt.; v-*err 3 malloc+;62.; vsnprint8+v-*err, ;66, 8mt, va.; v-*err 3 realloc+v-*err, strlen+v-*err.=6.; va'end+va.; return v; 2 lval- lval'sym+c&ar- s. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'PO"; v-*sym 3 malloc+strlen+s. = 6.; strcpy+v-*sym, s.; return v; 2 lval- lval'builtin+lbuiltin 8unc. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'RVM; v-*builtin 3 8unc; return v; 2 lenv- lenv'new+void.; lval- lval'lambda+lval- 8ormals, lval- body. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'RVM;

%0C

5- Pet Uuiltin to Mull -5 v-*builtin 3 MVAA; 5- Uuilt new environment -5 v-*env 3 lenv'new+.; 5- Pet Rormals and Uody -5 v-*8ormals 3 8ormals; v-*body 3 body; return v; 2 lval- lval'sexpr+void. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'PJZPL; v-*count 3 1; v-*cell 3 MVAA; return v; 2 lval- lval'>expr+void. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'[JZPL; v-*count 3 1; v-*cell 3 MVAA; return v; 2 void lenv'del+lenv- e.; void lval'del+lval- v. / switc& case case i8 +v-*type. / AIAA'MV" breaC; AIAA'RVM +0v-*builtin. / lenv'del+v-*env.; lval'del+v-*8ormals.; lval'del+v-*body.;

2 breaC; case AIAA'JLL 8ree+v-*err.; breaC; case AIAA'PO" 8ree+v-*sym.; breaC; case AIAA'[JZPL case AIAA'PJZPL 8or +int i 3 1; i ) v-*count; i==. / lval'del+v-*cellGiH.; 2 8ree+v-*cell.; breaC; 2 2 8ree+v.;

lenv- lenv'copy+lenv- e.; lval- lval'copy+lval- v. / lval- x 3 malloc+siEeo8+lval..; x-*type 3 v-*type; switc& +v-*type. /

%9&

case AIAA'RVM i8 +v-*builtin. / x-*builtin 3 v-*builtin; 2 else / x-*builtin 3 MVAA; x-*env 3 lenv'copy+v-*env.; x-*8ormals 3 lval'copy+v-*8ormals.; x-*body 3 lval'copy+v-*body.; 2 breaC; case AIAA'MV" x-*num 3 v-*num; breaC; case AIAA'JLL x-*err 3 malloc+strlen+v-*err. = 6.; strcpy+x-*err, v*err.; breaC; case AIAA'PO" x-*sym 3 malloc+strlen+v-*sym. = 6.; strcpy+x-*sym, v*sym.; breaC; case AIAA'PJZPL case AIAA'[JZPL x-*count 3 v-*count; x-*cell 3 malloc+siEeo8+lval-. - x-*count.; 8or +int i 3 1; i ) x-*count; i==. / x-*cellGiH 3 lval'copy+v-*cellGiH.; 2 breaC; 2 return x; 2 lval- lval'add+lval- v, lval- x. / v-*count==; v-*cell 3 realloc+v-*cell, siEeo8+lval-. - v-*count.; v-*cellGv-*count-6H 3 x; return v; 2 lval- lval'Toin+lval- x, lval- y. / 8or +int i 3 1; i ) y-*count; i==. / x 3 lval'add+x, y-*cellGiH.; 2 8ree+y-*cell.; 8ree+y.; return x; 2 lval- lval'pop+lval- v, int i. / lval- x 3 v-*cellGiH; memmove+@v-*cellGiH, @v-*cellGi=6H, siEeo8+lval-. - +v-*count-i-6..; v-*count--; v-*cell 3 realloc+v-*cell, siEeo8+lval-. - v-*count.; return x; 2 lval- lval'taCe+lval- v, int i. / lval- x 3 lval'pop+v, i.; lval'del+v.; return x; 2 void lval'print+lval- v.; void lval'print'expr+lval- v, c&ar open, c&ar close. / putc&ar+open.;

%9%

2

8or +int i 3 1; i ) v-*count; i==. / lval'print+v-*cellGiH.; i8 +i 03 +v-*count-6.. / putc&ar+7 7.; 2 2 putc&ar+close.;

void lval'print+lval- v. / switc& +v-*type. / case AIAA'RVM i8 +v-*builtin. / print8+")builtin*".; 2 else / print8+"+!! ".; lval'print+v-*8ormals.; putc&ar+7 7.; lval'print+v*body.; putc&ar+7.7.; 2 breaC; case AIAA'MV" print8+"Nli", v-*num.; breaC; case AIAA'JLL print8+"Jrror Ns", v-*err.; breaC; case AIAA'PO" print8+"Ns", v-*sym.; breaC; case AIAA'PJZPL lval'print'expr+v, 7+7, 7.7.; breaC; case AIAA'[JZPL lval'print'expr+v, 7/7, 727.; breaC; 2 2 void lval'println+lval- v. / lval'print+v.; putc&ar+7!n7.; 2 c&ar- ltype'name+int t. / switc&+t. / case AIAA'RVM return "Runction"; case AIAA'MV" return "Mumber"; case AIAA'JLL return "Jrror"; case AIAA'PO" return "Pymbol"; case AIAA'PJZPL return "P-Jxpression"; case AIAA'[JZPL return "[-Jxpression"; de8ault return "VnCnown"; 2 2 5- Aisp Jnvironment -5 struct lenv / lenv- par; int count; c&ar-- syms; lval-- vals; 2; lenv- lenv'new+void. / lenv- e 3 malloc+siEeo8+lenv..; e-*par 3 MVAA; e-*count 3 1; e-*syms 3 MVAA; e-*vals 3 MVAA; return e; 2 void lenv'del+lenv- e. / 8or +int i 3 1; i ) e-*count; i==. /

%90

8ree+e-*symsGiH.; lval'del+e-*valsGiH.; 2 8ree+e-*syms.; 8ree+e-*vals.; 8ree+e.; 2 lenv- lenv'copy+lenv- e. / lenv- n 3 malloc+siEeo8+lenv..; n-*par 3 e-*par; n-*count 3 e-*count; n-*syms 3 malloc+siEeo8+c&ar-. - n-*count.; n-*vals 3 malloc+siEeo8+lval-. - n-*count.; 8or +int i 3 1; i ) e-*count; i==. / n-*symsGiH 3 malloc+strlen+e-*symsGiH. = 6.; strcpy+n-*symsGiH, e-*symsGiH.; n-*valsGiH 3 lval'copy+e-*valsGiH.; 2 return n; 2 lval- lenv'get+lenv- e, lval- C. / 8or +int i 3 1; i ) e-*count; i==. / i8 +strcmp+e-*symsGiH, C-*sym. 33 1. / return lval'copy+e-*valsGiH.; 2 2 5- B8 no symbol c&ecC in parent ot&erwise error -5 i8 +e-*par. / return lenv'get+e-*par, C.; 2 else / return lval'err+"Vnbound Pymbol 7Ns7", C-*sym.; 2 2 void lenv'put+lenv- e, lval- C, lval- v. / 8or +int i 3 1; i ) e-*count; i==. / i8 +strcmp+e-*symsGiH, C-*sym. 33 1. / lval'del+e-*valsGiH.; e-*valsGiH 3 lval'copy+v.; e-*symsGiH 3 realloc+e-*symsGiH, strlen+C-*sym.=6.; strcpy+e-*symsGiH, C-*sym.; return; 2 2 e-*count==; e-*vals 3 realloc+e-*vals, siEeo8+lval-. - e-*count.; e-*syms 3 realloc+e-*syms, siEeo8+c&ar-. - e-*count.; e-*valsGe-*count-6H 3 lval'copy+v.; e-*symsGe-*count-6H 3 malloc+strlen+C-*sym.=6.; strcpy+e-*symsGe-*count-6H, C-*sym.;

2

void lenv'de8+lenv- e, lval- C, lval- v. / 5- Bterate till e &as no parent -5 w&ile +e-*par. / e 3 e-*par; 2 5- Put value in e -5

%99

2

lenv'put+e, C, v.;

5- Uuiltins -5 (de8ine AAPPJLT+args, cond, 8mt, %%%. ! i8 +0+cond.. / lval- err 3 lval'err+8mt, ((''IA'AL#P''.; lval'del+args.; return err; 2 (de8ine AAPPJLT'TOPJ+8unc, args, index, expect. ! AAPPJLT+args, args-*cellGindexH-*type 33 expect, ! "Runction 7Ns7 passed incorrect type 8or argument Ni% #ot Ns, Jxpected Ns%", ! 8unc, index, ltype'name+args-*cellGindexH-*type., ltype'name+expect.. (de8ine AAPPJLT'MV"+8unc, args, num. ! AAPPJLT+args, args-*count 33 num, ! "Runction 7Ns7 passed incorrect number o8 arguments% #ot Ni, Jxpected Ni%", ! 8unc, args-*count, num. (de8ine AAPPJLT'MKT'J"PTO+8unc, args, index. ! AAPPJLT+args, args-*cellGindexH-*count 03 1, ! "Runction 7Ns7 passed /2 8or argument Ni%", 8unc, index.; lval- lval'eval+lenv- e, lval- v.; lval- builtin'lambda+lenv- e, lval- a. / 5- C&ecC Two arguments, eac& o8 w&ic& are [-Jxpressions -5 AAPPJLT'MV"+"!!", a, 2.; AAPPJLT'TOPJ+"!!", a, 1, AIAA'[JZPL.; AAPPJLT'TOPJ+"!!", a, 6, AIAA'[JZPL.; 5- C&ecC 8irst [-Jxpression contains only Pymbols -5 8or +int i 3 1; i ) a-*cellG1H-*count; i==. / AAPPJLT+a, +a-*cellG1H-*cellGiH-*type 33 AIAA'PO"., "Cannot de8ine non-symbol% #ot Ns, Jxpected Ns%", ltype'name+a-*cellG1H-*cellGiH-*type., ltype'name+AIAA'PO"..; 2 5- Pop 8irst two arguments and pass t&em to lval'lambda -5 lval- 8ormals 3 lval'pop+a, 1.; lval- body 3 lval'pop+a, 1.; lval'del+a.; return lval'lambda+8ormals, body.; 2 lval- builtin'list+lenv- e, lval- a. / a-*type 3 AIAA'[JZPL; return a; 2 lval- builtin'&ead+lenv- e, lval- a. / AAPPJLT'MV"+"&ead", a, 6.; AAPPJLT'TOPJ+"&ead", a, 1, AIAA'[JZPL.; AAPPJLT'MKT'J"PTO+"&ead", a, 1.; lval- v 3 lval'taCe+a, 1.; w&ile +v-*count * 6. / lval'del+lval'pop+v, 6..; 2 return v;

%9*

2 lval- builtin'tail+lenv- e, lval- a. / AAPPJLT'MV"+"tail", a, 6.; AAPPJLT'TOPJ+"tail", a, 1, AIAA'[JZPL.; AAPPJLT'MKT'J"PTO+"tail", a, 1.; lval- v 3 lval'taCe+a, 1.; lval'del+lval'pop+v, 1..; return v; 2 lval- builtin'eval+lenv- e, lval- a. / AAPPJLT'MV"+"eval", a, 6.; AAPPJLT'TOPJ+"tail", a, 1, AIAA'[JZPL.; lval- x 3 lval'taCe+a, 1.; x-*type 3 AIAA'PJZPL; return lval'eval+e, x.;

2

lval- builtin'Toin+lenv- e, lval- a. / 8or +int i 3 1; i ) a-*count; i==. / AAPPJLT'TOPJ+"Toin", a, i, AIAA'[JZPL.; 2 lval- x 3 lval'pop+a, 1.; w&ile +a-*count. / lval- y 3 lval'pop+a, 1.; x 3 lval'Toin+x, y.; 2 lval'del+a.; return x;

2

lval- builtin'op+lenv- e, lval- a, c&ar- op. / 8or +int i 3 1; i ) a-*count; i==. / AAPPJLT'TOPJ+op, a, i, AIAA'MV".; 2 lval- x 3 lval'pop+a, 1.; i8 ++strcmp+op, "-". 33 1. @@ a-*count 33 1. / x-*num 3 -x-*num; 2 w&ile +a-*count * 1. / lval- y 3 lval'pop+a, 1.; i8 i8 i8 i8 +strcmp+op, "=". 33 1. / x-*num =3 y-*num; 2 +strcmp+op, "-". 33 1. / x-*num -3 y-*num; 2 +strcmp+op, "-". 33 1. / x-*num -3 y-*num; 2 +strcmp+op, "5". 33 1. / i8 +y-*num 03 1. / lval'del+x.; lval'del+y.; lval'del+a.; return lval'err+"Division Uy Yero%".; 2 x-*num 53 y-*num;

2 2

lval'del+y.;

%9=

2

lval'del+a.; return x; builtin'add+lenvbuiltin'sub+lenvbuiltin'mul+lenvbuiltin'div+lenve, e, e, e, lvallvallvallvala. a. a. a. / / / / return return return return builtin'op+e, builtin'op+e, builtin'op+e, builtin'op+e, a, a, a, a, "=".; "-".; "-".; "5".; 2 2 2 2

lvallvallvallval-

lval- builtin'var+lenv- e, lval- a, c&ar- 8unc. / AAPPJLT'TOPJ+8unc, a, 1, AIAA'[JZPL.; lval- syms 3 a-*cellG1H; 8or +int i 3 1; i ) syms-*count; i==. / AAPPJLT+a, +syms-*cellGiH-*type 33 AIAA'PO"., "Runction 7Ns7 cannot de8ine non-symbol% #ot Ns, Jxpected Ns%", 8unc, ltype'name+syms-*cellGiH-*type., ltype'name+AIAA'PO"..; 2 AAPPJLT+a, +syms-*count 33 a-*count-6., "Runction 7Ns7 passed too many arguments 8or symbols% #ot Ni, Jxpected Ni%", 8unc, syms-*count, a-*count-6.; 8or +int i 3 1; i ) syms-*count; i==. / 5- B8 7de87 de8ine in global scope% B8 7put7 de8ine in local scope -5 i8 +strcmp+8unc, "de8". 33 1. / lenv'de8+e, syms-*cellGiH, a*cellGi=6H.; 2 i8 +strcmp+8unc, "3". 33 1. / lenv'put+e, syms-*cellGiH, a*cellGi=6H.; 2 2 lval'del+a.; return lval'sexpr+.;

2

lval- builtin'de8+lenv- e, lval- a. / return builtin'var+e, a, "de8".; 2 lval- builtin'put+lenv- e, lval- a. / return builtin'var+e, a, "3".; 2 void lenv'add'builtin+lenv- e, c&ar- name, lbuiltin 8unc. / lval- C 3 lval'sym+name.; lval- v 3 lval'builtin+8unc.; lenv'put+e, C, v.; lval'del+C.; lval'del+v.; 2 void lenv'add'builtins+lenv- e. / 5- Iariable Runctions -5 lenv'add'builtin+e, "!!", builtin'lambda.; lenv'add'builtin+e, "de8", builtin'de8.; lenv'add'builtin+e, "3", builtin'put.; 5- Aist Runctions -5 lenv'add'builtin+e, "list", builtin'list.; lenv'add'builtin+e, "&ead", builtin'&ead.; lenv'add'builtin+e, "tail", builtin'tail.; lenv'add'builtin+e, "eval", builtin'eval.; lenv'add'builtin+e, "Toin", builtin'Toin.; 5- "at&ematical Runctions -5

%9;

lenv'add'builtin+e, "=", builtin'sub.; lenv'add'builtin+e, "-", builtin'div.; 2 5- Jvaluation -5

builtin'add.; lenv'add'builtin+e, "-", builtin'mul.; lenv'add'builtin+e, "5",

lval- lval'call+lenv- e, lval- 8, lval- a. / 5- B8 Uuiltin t&en simply apply t&at -5 i8 +8-*builtin. / return 8-*builtin+e, a.; 2 5- Lecord Argument Counts -5 int given 3 a-*count; int total 3 8-*8ormals-*count; 5- $&ile arguments still remain to be processed -5 w&ile +a-*count. / 5- B8 we7ve ran out o8 8ormal arguments to bind -5 i8 +8-*8ormals-*count 33 1. / lval'del+a.; return lval'err+"Runction passed too many arguments% #ot Ni, Jxpected Ni%", given, total.; 2 5- Pop t&e 8irst symbol 8rom t&e 8ormals -5 lval- sym 3 lval'pop+8-*8ormals, 1.; 5- Ppecial Case to deal wit& 7@7 -5 i8 +strcmp+sym-*sym, "@". 33 1. / 5- Jnsure 7@7 is 8ollowed by anot&er symbol -5 i8 +8-*8ormals-*count 03 6. / lval'del+a.; return lval'err+"Runction 8ormat invalid% Pymbol 7@7 not 8ollowed by single symbol%".; 2 5- Mext 8ormal s&ould be bound to remaining arguments -5 lval- nsym 3 lval'pop+8-*8ormals, 1.; lenv'put+8-*env, nsym, builtin'list+e, a..; lval'del+sym.; lval'del+nsym.; breaC;

2

5- Pop t&e next argument 8rom t&e list -5 lval- val 3 lval'pop+a, 1.; 5- Uind a copy into t&e 8unction7s environment -5 lenv'put+8-*env, sym, val.; 5- Delete symbol and value -5 lval'del+sym.; lval'del+val.;

2

5- Argument list is now bound so can be cleaned up -5 lval'del+a.; 5- B8 7@7 remains in 8ormal list it s&ould be bound to empty list -5

%9@

i8 +8-*8ormals-*count * 1 @@ strcmp+8-*8ormals-*cellG1H-*sym, "@". 33 1. / 5- C&ecC to ensure t&at @ is not passed invalidly% -5 i8 +8-*8ormals-*count 03 2. / return lval'err+"Runction 8ormat invalid% Pymbol 7@7 not 8ollowed by single symbol%".; 2 5- Pop and delete 7@7 symbol -5 lval'del+lval'pop+8-*8ormals, 1..; 5- Pop next symbol and create empty list -5 lval- sym 3 lval'pop+8-*8ormals, 1.; lval- val 3 lval'>expr+.; 5- Uind to environment and delete -5 lenv'put+8-*env, sym, val.; lval'del+sym.; lval'del+val.;

2

5- B8 all 8ormals &ave been bound evaluate -5 i8 +8-*8ormals-*count 33 1. / 5- Pet Runction Jnvironment parent to current evaluation Jnvironment -5 8-*env-*par 3 e; 5- Jvaluate and return -5 return builtin'eval+8-*env, lval'add+lval'sexpr+., lval'copy+8*body...; 2 else / 5- Kt&erwise return partially evaluated 8unction -5 return lval'copy+8.; 2 2 lval- lval'eval'sexpr+lenv- e, lval- v. / 8or +int i 3 1; i ) v-*count; i==. / v-*cellGiH 3 lval'eval+e, v*cellGiH.; 2 8or +int i 3 1; i ) v-*count; i==. / i8 +v-*cellGiH-*type 33 AIAA'JLL. / return lval'taCe+v, i.; 2 2 i8 +v-*count 33 1. / return v; 2 i8 +v-*count 33 6. / return lval'eval+e, lval'taCe+v, 1..; 2 lval- 8 3 lval'pop+v, 1.; i8 +8-*type 03 AIAA'RVM. / lval- err 3 lval'err+ "P-Jxpression starts wit& incorrect type% #ot Ns, Jxpected Ns%", ltype'name+8-*type., ltype'name+AIAA'RVM..; lval'del+8.; lval'del+v.; return err; 2 lval- result 3 lval'call+e, 8, v.; lval'del+8.; return result;

2

%9+

lval- lval'eval+lenv- e, lval- v. / i8 +v-*type 33 AIAA'PO". / return lenv'get+e, v.; 2 i8 +v-*type 33 AIAA'PJZPL. / return lval'eval'sexpr+e, v.; 2 return v; 2 5- Leading -5 lval- lval'read'num+mpc'ast't- t. / long x 3 strtol+t-*contents, MVAA, 61.; return errno 03 JLAM#J W lval'num+x. lval'err+"Bnvalid Mumber%".; 2 lval- lval'read+mpc'ast't- t. / i8 +strstr+t-*tag, "number".. / return lval'read'num+t.; 2 i8 +strstr+t-*tag, "symbol".. / return lval'sym+t-*contents.; 2 lval- x 3 MVAA; i8 +strcmp+t-*tag, "*". 33 1. / x 3 lval'sexpr+.; 2 i8 +strstr+t-*tag, "sexpr".. / x 3 lval'sexpr+.; 2 i8 +strstr+t-*tag, ">expr".. / x 3 lval'>expr+.; 2 8or +int i 3 1; i ) t-*c&ildren'num; i==. / i8 +strcmp+t-*c&ildrenGiH-*contents, "+". 33 i8 +strcmp+t-*c&ildrenGiH-*contents, ".". 33 i8 +strcmp+t-*c&ildrenGiH-*contents, "2". 33 i8 +strcmp+t-*c&ildrenGiH-*contents, "/". 33 i8 +strcmp+t-*c&ildrenGiH-*tag, "regex". 33 x 3 lval'add+x, lval'read+t-*c&ildrenGiH..; 2 2 return x; 1. 1. 1. 1. 1. / / / / / continue; continue; continue; continue; continue; 2 2 2 2 2

5- "ain -5 int main+int argc, c&ar-- argv. / mpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tMumber Pymbol Pexpr [expr Jxpr Aispy 3 3 3 3 3 3 mpc'new+"number".; mpc'new+"symbol".; mpc'new+"sexpr".; mpc'new+">expr".; mpc'new+"expr".; mpc'new+"lispy".; ! ! ! ! ! ! !

mpca'lang+"PC'AAM#'DJRAVAT, " number 5-WG1-4H=5 ; symbol 5Ga-EA-Y1-4'=!!--!!5!!!!3)*0@H=5 ; sexpr 7+7 )expr*- 7.7 ; >expr 7/7 )expr*- 727 ; expr )number* ? )symbol* ? )sexpr* ? )>expr* ; lispy 5Q5 )expr*- 5X5 ; ", Mumber, Pymbol, Pexpr, [expr, Jxpr, Aispy.; 8puts+"Aispy Iersion 1%1%1%1%:".; 8puts+"Press Ctrl=c to Jxit!n".; lenv- e 3 lenv'new+.;

%9C

lenv'add'builtins+e.; w&ile +6. / c&ar- input 3 readline+"lispy* ".; add'&istory+input.; mpc'result't r; i8 +mpc'parse+")stdin*", input, Aispy, @r.. / lval- x 3 lval'eval+e, lval'read+r%output..; lval'println+x.; lval'del+x.; mpc'ast'delete+r%output.; 2 else / mpc'err'print+r%error.; mpc'err'delete+r%error.; 2 8ree+input.; 2 lenv'del+e.; mpc'cleanup+<, Mumber, Pymbol, Pexpr, [expr, Jxpr, Aispy.; 2 return 1;

Bonus %ar+s
• • • • •

H ?efine a Lisp function that returns the first element from a list. H ?efine a Lisp function that returns the second element from a list. H ?efine a Lisp function that calls a function with two arguments in reverse order. H ?efine a Lisp function that calls a function with arguments, then passes the result to another function. H Change variable arguments so at least one e-tra argument must be supplied before it is evaluated.

%*&

Conditionals • Chapter 13

&oing it yourself
'e've come <uite far now. (our knowledge of C should be good enough for you to stand on your own feet a little more. If you're feeling confident, this chapter is a perfect opportunity to stretch your wings out, and attempt something on your own. It is a fairly short chapter and essentially consists of adding a couple of new builtin functions to deal with comparison and ordering.

5ug / if pug is asleep then pug is cute.

If you're feeling positive, go ahead and try to implement comparison and ordering into your language now. ?efine some new builtin functions for greater than, less than, e2ual to, and all the other comparison operators we use in C. "ry to define an i8 function that tests for some condition and then either evaluate some code, or some other code, depending on the result. 7nce you've finished come back and compare your work to mine. 7bserve the differences and decide which parts you prefer. If you still feel uncertain don't worry. 4ollow along and I'll e-plain my approach.

Ordering
4or simplicity's sake I'm going to re,use our number data type to represent the result of comparisons. I'll make a rule similar to C, to say that any number that isn't 1 evaluates to true in an i8 statement, while 1 always evaluates to false.

%*%

"herefore our ordering functions are a little like a simplified version of our arithmetic functions. "hey'll only work on numbers, and we only want them to work on two arguments. If these error conditions are met the maths is simple, we want to return an number lval either 1 or 6 depending on the e<uality comparison between the two input lval. 'e can use C's comparison operators to do this. Like our arithmetic functions we'll make use of a single function to do all of the comparisons. 4irst we check the error conditions, then we compare the numbers in each of the arguments to get some result. 4inally we return this result as a number value.
lval- builtin'ord+lenv- e, lval- a, c&ar- op. / AAPPJLT'MV"+op, a, 2.; AAPPJLT'TOPJ+op, a, 1, AIAA'MV".; AAPPJLT'TOPJ+op, a, 6, AIAA'MV".; int r; i8 +strcmp+op, "*". i8 +strcmp+op, ")". i8 +strcmp+op, "*3". i8 +strcmp+op, ")3". lval'del+a.; return lval'num+r.; 2 lvallvallvallvalbuiltin'gt+lenvbuiltin'lt+lenvbuiltin'ge+lenvbuiltin'le+lenve, e, e, e, lvallvallvallvala. a. a. a. / / / / return return return return builtin'ord+e, builtin'ord+e, builtin'ord+e, builtin'ord+e, a, a, a, a, "*".; ")".; "*3".; ")3".; 2 2 2 2 33 33 33 33 1. 1. 1. 1. / / / / r r r r 3 3 3 3 +a-*cellG1H-*num +a-*cellG1H-*num +a-*cellG1H-*num +a-*cellG1H-*num * ) *3 )3 a-*cellG6H-*num.; a-*cellG6H-*num.; a-*cellG6H-*num.; a-*cellG6H-*num.; 2 2 2 2

$<uality
1<uality is going to be different to ordering because we want it to work on more than number types. It will be useful to see if an input is e<ual to an empty list, or to see if two functions passed in are the same. "herefore we need to define a function which can test for e<uality between two different types of lval. "his function essentially checks that all the fields which make up the data for a particular lval type are e<ual. If all the fields are e<ual, the whole thing is considered e<ual. 7therwise if there are any differences the whole thing is considered une<ual.
int lval'e>+lval- x, lval- y. / 5- Di88erent Types are always une>ual -5 i8 +x-*type 03 y-*type. / return 1; 2 5- Compare Uased upon type -5 switc& +x-*type. / 5- Compare Mumber Ialue -5 case AIAA'MV" return +x-*num 33 y-*num.; 5- Compare Ptring Ialues -5 case AIAA'JLL return +strcmp+x-*err, y-*err. 33 1.; case AIAA'PO" return +strcmp+x-*sym, y-*sym. 33 1.;

%*0

5- B8 Uuiltin compare 8unctions, ot&erwise compare 8ormals and body -5 case AIAA'RVM i8 +x-*builtin. / return x-*builtin 33 y-*builtin; 2 else / return lval'e>+x-*8ormals, y-*8ormals. @@ lval'e>+x-*body, y*body.; 2 5- B8 list compare every individual element -5 case AIAA'[JZPL case AIAA'PJZPL i8 +x-*count 03 y-*count. / return 1; 2 8or +int i 3 1; i ) x-*count; i==. / 5- B8 any element not e>ual t&en w&ole list not e>ual -5 i8 +0lval'e>+x-*cellG1H, y-*cellG1H.. / return 1; 2 2 5- Kt&erwise lists must be e>ual -5 return 6; breaC;

2

2 return 1;

:sing this function the new builtin function for e<uality comparison is very simple to add. 'e simply ensure two arguments are input, and that they are e<ual. 'e store the result of the comparison into a new lval and return it.
lval- builtin'cmp+lenv- e, lval- a, c&ar- op. / AAPPJLT'MV"+op, a, 2.; int r; i8 +strcmp+op, "33". 33 1. / r 3 lval'e>+a-*cellG1H, a-*cellG6H.; 2 i8 +strcmp+op, "03". 33 1. / r 3 0lval'e>+a-*cellG1H, a-*cellG6H.; 2 lval'del+a.; return lval'num+r.; 2 lval- builtin'e>+lenv- e, lval- a. / return builtin'cmp+e, a, "33".; 2 lval- builtin'ne+lenv- e, lval- a. / return builtin'cmp+e, a, "03".; 2

1f /unction
"o make our comparison operators useful well need an i8 function. "his function is a little like the ternary operation in C. :pon some condition being true it evaluates to one thing, otherwise it evaluates to another. 'e can again make use of O,1-pressions to encode a computation. 4irst we get the user to pass in the result of a comparison, then we get the user to pass in two O,1-pressions representing the code to be evaluated upon a condition being either true or false.
lval- builtin'i8+lenv- e, lval- a. / AAPPJLT'MV"+"i8", a, 3.; AAPPJLT'TOPJ+"i8", a, 1, AIAA'MV".; AAPPJLT'TOPJ+"i8", a, 6, AIAA'[JZPL.;

%*9

AAPPJLT'TOPJ+"i8", a, 2, AIAA'[JZPL.; 5- "arC Uot& Jxpressions as evaluable -5 lval- x; a-*cellG6H-*type 3 AIAA'PJZPL; a-*cellG2H-*type 3 AIAA'PJZPL; i8 +a-*cellG1H-*num. / 5- B8 condition is true evaluate 8irst expression -5 x 3 lval'eval+e, lval'pop+a, 6..; 2 else / 5- Kt&erwise evaluate second expression -5 x 3 lval'eval+e, lval'pop+a, 2..; 2 5- Delete argument list and return -5 lval'del+a.; return x; 2

All that remains is for us to register all of these new builtins and we are again ready to go$
5- Comparison Runctions -5 lenv'add'builtin+e, "i8", lenv'add'builtin+e, "33", builtin'ne.; lenv'add'builtin+e, "*", builtin'lt.; lenv'add'builtin+e, "*3", builtin'le.; builtin'i8.; builtin'e>.; lenv'add'builtin+e, "03", builtin'gt.; lenv'add'builtin+e, ")", builtin'ge.; lenv'add'builtin+e, ")3",

!ave a <uick mess around to check that everything is working correctly.
lispy* 6 lispy* 1 lispy* 1 lispy* 1 lispy* 6 lispy* 6 lispy* 6 lispy* +. lispy* -611 * 61 ; )3 :: ; 33 ; < 33 ; /2 33 6 6 03 /2 ;< 33 /6 2 3 /; <22 /6 de8 /x y2 611 211 i8 +33 x y. /= x y2 /- x y2 2 3 /; <22

'ecursi,e /unctions
y Introducing conditionals we've actually made our language a lot more powerful. "his is because they effectively let us implement recursive functions.

%**

Fecursive functions are those which call themselves. 'e've used these already in C to perform reading in and evaluation of e-pressions. "he reason we re<uire conditionals for these is because they let us test for the situation where we wish to terminate the recursion. 4or e-ample we can use conditionals to implement a function len which tells us the number of items in a list. If we encounter the empty list we just return 1. 7therwise we return the length of the tail of the input list, plus 6. "hink about why this works. It repeatedly uses the len function until it reaches the empty list. At this point it returns 1 and adds all the other partial results together.
+8un /len l2 / i8 +33 l /2. /12 /= 6 +len +tail l..2 2.

"here is a pleasant symmetry to this sort of recursive function. 4irst we do something for the empty list Dthis is often called the &ase caseE. "hen if we get something bigger, we take off a chunk such as the head of the list, and do something to it, before combining it with the rest of the thing to which the function has been already applied. !ere is another function for reversing a list. Like before it checks for the empty list, but this time it returns the empty list back. "his makes sense. "he reverse of the empty list is just the empty list. ut if it gets something bigger than the empty list, it reverses the tail, and stick this in front of the head.
+8un /reverse l2 / i8 +33 l /2. //22 /Toin +reverse +tail l.. +&ead l.2 2.

'e're going to use this techni<ue to build lots functions like this, this is because it is going to be the primary way to achieve looping in our language.

'eference
conditionals*c
(include "mpc%&" (i8de8 '$BM32 static c&ar bu88erG21F:H; c&ar- readline+c&ar- prompt. / 8puts+"lispy* ", stdout.; 8gets+bu88er, 21F:, stdin.; c&ar- cpy 3 malloc+strlen+bu88er.=6.; strcpy+cpy, bu88er.; cpyGstrlen+cpy.-6H 3 7!17; return cpy; 2

%*=

void add'&istory+c&ar- unused. /2 (else (include )editline5readline%&* (include )editline5&istory%&* (endi8 5- Rorward Declarations -5 struct lval; struct lenv; typede8 struct lval lval; typede8 struct lenv lenv; 5- Aisp Ialue -5 enum / AIAA'JLL, AIAA'MV", AIAA'PO", AIAA'RVM, AIAA'PJZPL, AIAA'[JZPL 2; typede8 lval-+-lbuiltin.+lenv-, lval-.; struct lval / int type; 5- Uasic -5 long num; c&ar- err; c&ar- sym; 5- Runction -5 lbuiltin builtin; lenv- env; lval- 8ormals; lval- body; 5- Jxpression -5 int count; lval-- cell; 2; lval- lval'num+long x. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'MV"; v-*num 3 x; return v; 2 lval- lval'err+c&ar- 8mt, %%%. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'JLL; va'list va; va'start+va, 8mt.; v-*err 3 malloc+;62.; vsnprint8+v-*err, ;66, 8mt, va.; v-*err 3 realloc+v-*err, strlen+v-*err.=6.; va'end+va.; return v; 2

%*;

lval- lval'sym+c&ar- s. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'PO"; v-*sym 3 malloc+strlen+s. = 6.; strcpy+v-*sym, s.; return v; 2 lval- lval'builtin+lbuiltin 8unc. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'RVM; v-*builtin 3 8unc; return v; 2 lenv- lenv'new+void.; lval- lval'lambda+lval- 8ormals, lval- body. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'RVM; v-*builtin 3 MVAA; v-*env 3 lenv'new+.; v-*8ormals 3 8ormals; v-*body 3 body; return v; 2 lval- lval'sexpr+void. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'PJZPL; v-*count 3 1; v-*cell 3 MVAA; return v; 2 lval- lval'>expr+void. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'[JZPL; v-*count 3 1; v-*cell 3 MVAA; return v; 2 void lenv'del+lenv- e.; void lval'del+lval- v. / switc& case case i8 +v-*type. / AIAA'MV" breaC; AIAA'RVM +0v-*builtin. / lenv'del+v-*env.; lval'del+v-*8ormals.; lval'del+v-*body.;

2 breaC; case AIAA'JLL 8ree+v-*err.; breaC; case AIAA'PO" 8ree+v-*sym.; breaC; case AIAA'[JZPL case AIAA'PJZPL 8or +int i 3 1; i ) v-*count; i==. /

%*@

lval'del+v-*cellGiH.; 2 8ree+v-*cell.; breaC; 2 2 8ree+v.;

lenv- lenv'copy+lenv- e.; lval- lval'copy+lval- v. / lval- x 3 malloc+siEeo8+lval..; x-*type 3 v-*type; switc& +v-*type. / case AIAA'RVM i8 +v-*builtin. / x-*builtin 3 v-*builtin; 2 else / x-*builtin 3 MVAA; x-*env 3 lenv'copy+v-*env.; x-*8ormals 3 lval'copy+v-*8ormals.; x-*body 3 lval'copy+v-*body.; 2 breaC; case AIAA'MV" x-*num 3 v-*num; breaC; case AIAA'JLL x-*err 3 malloc+strlen+v-*err. = 6.; strcpy+x-*err, v*err.; breaC; case AIAA'PO" x-*sym 3 malloc+strlen+v-*sym. = 6.; strcpy+x-*sym, v*sym.; breaC; case AIAA'PJZPL case AIAA'[JZPL x-*count 3 v-*count; x-*cell 3 malloc+siEeo8+lval-. - x-*count.; 8or +int i 3 1; i ) x-*count; i==. / x-*cellGiH 3 lval'copy+v-*cellGiH.; 2 breaC; 2 return x; 2 lval- lval'add+lval- v, lval- x. / v-*count==; v-*cell 3 realloc+v-*cell, siEeo8+lval-. - v-*count.; v-*cellGv-*count-6H 3 x; return v; 2 lval- lval'Toin+lval- x, lval- y. / 8or +int i 3 1; i ) y-*count; i==. / x 3 lval'add+x, y-*cellGiH.; 2 8ree+y-*cell.; 8ree+y.; return x; 2 lval- lval'pop+lval- v, int i. / lval- x 3 v-*cellGiH; memmove+@v-*cellGiH, @v-*cellGi=6H, siEeo8+lval-. - +v-*count-i-6..;

%*+

2

v-*count--; v-*cell 3 realloc+v-*cell, siEeo8+lval-. - v-*count.; return x;

lval- lval'taCe+lval- v, int i. / lval- x 3 lval'pop+v, i.; lval'del+v.; return x; 2 void lval'print+lval- v.; void lval'print'expr+lval- v, c&ar open, c&ar close. / putc&ar+open.; 8or +int i 3 1; i ) v-*count; i==. / lval'print+v-*cellGiH.; i8 +i 03 +v-*count-6.. / putc&ar+7 7.; 2 2 putc&ar+close.; 2 void lval'print+lval- v. / switc& +v-*type. / case AIAA'RVM i8 +v-*builtin. / print8+")builtin*".; 2 else / print8+"+!! ".; lval'print+v-*8ormals.; putc&ar+7 7.; lval'print+v*body.; putc&ar+7.7.; 2 breaC; case AIAA'MV" print8+"Nli", v-*num.; breaC; case AIAA'JLL print8+"Jrror Ns", v-*err.; breaC; case AIAA'PO" print8+"Ns", v-*sym.; breaC; case AIAA'PJZPL lval'print'expr+v, 7+7, 7.7.; breaC; case AIAA'[JZPL lval'print'expr+v, 7/7, 727.; breaC; 2 2 void lval'println+lval- v. / lval'print+v.; putc&ar+7!n7.; 2 int lval'e>+lval- x, lval- y. / 5- Di88erent Types are always une>ual -5 i8 +x-*type 03 y-*type. / return 1; 2 5- Compare Uased upon type -5 switc& +x-*type. / 5- Compare Mumber Ialue -5 case AIAA'MV" return +x-*num 33 y-*num.; 5- Compare Ptring Ialues -5 case AIAA'JLL return +strcmp+x-*err, y-*err. 33 1.; case AIAA'PO" return +strcmp+x-*sym, y-*sym. 33 1.; 5- B8 Uuiltin compare 8unctions, ot&erwise compare 8ormals and body -5 case AIAA'RVM i8 +x-*builtin. /

%*C

return x-*builtin 33 y-*builtin; 2 else / return lval'e>+x-*8ormals, y-*8ormals. @@ lval'e>+x-*body, y*body.; 2 5- B8 list compare every individual element -5 case AIAA'[JZPL case AIAA'PJZPL i8 +x-*count 03 y-*count. / return 1; 2 8or +int i 3 1; i ) x-*count; i==. / 5- B8 any element not e>ual t&en w&ole list not e>ual -5 i8 +0lval'e>+x-*cellG1H, y-*cellG1H.. / return 1; 2 2 5- Kt&erwise lists must be e>ual -5 return 6; breaC;

2

2 return 1;

c&ar- ltype'name+int t. / switc&+t. / case AIAA'RVM return "Runction"; case AIAA'MV" return "Mumber"; case AIAA'JLL return "Jrror"; case AIAA'PO" return "Pymbol"; case AIAA'PJZPL return "P-Jxpression"; case AIAA'[JZPL return "[-Jxpression"; de8ault return "VnCnown"; 2 2 5- Aisp Jnvironment -5 struct lenv / lenv- par; int count; c&ar-- syms; lval-- vals; 2; lenv- lenv'new+void. / lenv- e 3 malloc+siEeo8+lenv..; e-*par 3 MVAA; e-*count 3 1; e-*syms 3 MVAA; e-*vals 3 MVAA; return e; 2 void lenv'del+lenv- e. / 8or +int i 3 1; i ) e-*count; i==. / 8ree+e-*symsGiH.; lval'del+e-*valsGiH.; 2 8ree+e-*syms.; 8ree+e-*vals.; 8ree+e.; 2

%=&

lenv- lenv'copy+lenv- e. / lenv- n 3 malloc+siEeo8+lenv..; n-*par 3 e-*par; n-*count 3 e-*count; n-*syms 3 malloc+siEeo8+c&ar-. - n-*count.; n-*vals 3 malloc+siEeo8+lval-. - n-*count.; 8or +int i 3 1; i ) e-*count; i==. / n-*symsGiH 3 malloc+strlen+e-*symsGiH. = 6.; strcpy+n-*symsGiH, e-*symsGiH.; n-*valsGiH 3 lval'copy+e-*valsGiH.; 2 return n; 2 lval- lenv'get+lenv- e, lval- C. / 8or +int i 3 1; i ) e-*count; i==. / i8 +strcmp+e-*symsGiH, C-*sym. 33 1. / return lval'copy+e-*valsGiH.; 2 2 i8 +e-*par. / return lenv'get+e-*par, C.; 2 else / return lval'err+"Vnbound Pymbol 7Ns7", C-*sym.; 2

2

void lenv'put+lenv- e, lval- C, lval- v. / 8or +int i 3 1; i ) e-*count; i==. / i8 +strcmp+e-*symsGiH, C-*sym. 33 1. / lval'del+e-*valsGiH.; e-*valsGiH 3 lval'copy+v.; e-*symsGiH 3 realloc+e-*symsGiH, strlen+C-*sym.=6.; strcpy+e-*symsGiH, C-*sym.; return; 2 2 e-*count==; e-*vals 3 realloc+e-*vals, siEeo8+lval-. - e-*count.; e-*syms 3 realloc+e-*syms, siEeo8+c&ar-. - e-*count.; e-*valsGe-*count-6H 3 lval'copy+v.; e-*symsGe-*count-6H 3 malloc+strlen+C-*sym.=6.; strcpy+e-*symsGe-*count-6H, C-*sym.;

2

void lenv'de8+lenv- e, lval- C, lval- v. / w&ile +e-*par. / e 3 e-*par; 2 lenv'put+e, C, v.; 2 5- Uuiltins -5 (de8ine AAPPJLT+args, cond, 8mt, %%%. ! i8 +0+cond.. / lval- err 3 lval'err+8mt, ((''IA'AL#P''.; lval'del+args.; return err; 2 (de8ine AAPPJLT'TOPJ+8unc, args, index, expect. ! AAPPJLT+args, args-*cellGindexH-*type 33 expect, !

%=%

"Runction 7Ns7 passed incorrect type 8or argument Ni% #ot Ns, Jxpected Ns%", ! 8unc, index, ltype'name+args-*cellGindexH-*type., ltype'name+expect.. (de8ine AAPPJLT'MV"+8unc, args, num. ! AAPPJLT+args, args-*count 33 num, ! "Runction 7Ns7 passed incorrect number o8 arguments% #ot Ni, Jxpected Ni%", ! 8unc, args-*count, num. (de8ine AAPPJLT'MKT'J"PTO+8unc, args, index. ! AAPPJLT+args, args-*cellGindexH-*count 03 1, ! "Runction 7Ns7 passed /2 8or argument Ni%", 8unc, index.; lval- lval'eval+lenv- e, lval- v.; lval- builtin'lambda+lenv- e, lval- a. / AAPPJLT'MV"+"!!", a, 2.; AAPPJLT'TOPJ+"!!", a, 1, AIAA'[JZPL.; AAPPJLT'TOPJ+"!!", a, 6, AIAA'[JZPL.; 8or +int i 3 1; i ) a-*cellG1H-*count; i==. / AAPPJLT+a, +a-*cellG1H-*cellGiH-*type 33 AIAA'PO"., "Cannot de8ine non-symbol% #ot Ns, Jxpected Ns%", ltype'name+a-*cellG1H-*cellGiH-*type., ltype'name+AIAA'PO"..; 2 lval- 8ormals 3 lval'pop+a, 1.; lval- body 3 lval'pop+a, 1.; lval'del+a.; return lval'lambda+8ormals, body.; 2 lval- builtin'list+lenv- e, lval- a. / a-*type 3 AIAA'[JZPL; return a; 2 lval- builtin'&ead+lenv- e, lval- a. / AAPPJLT'MV"+"&ead", a, 6.; AAPPJLT'TOPJ+"&ead", a, 1, AIAA'[JZPL.; AAPPJLT'MKT'J"PTO+"&ead", a, 1.; lval- v 3 lval'taCe+a, 1.; w&ile +v-*count * 6. / lval'del+lval'pop+v, 6..; 2 return v;

2

lval- builtin'tail+lenv- e, lval- a. / AAPPJLT'MV"+"tail", a, 6.; AAPPJLT'TOPJ+"tail", a, 1, AIAA'[JZPL.; AAPPJLT'MKT'J"PTO+"tail", a, 1.; lval- v 3 lval'taCe+a, 1.; lval'del+lval'pop+v, 1..; return v;

2

lval- builtin'eval+lenv- e, lval- a. / AAPPJLT'MV"+"eval", a, 6.;

%=0

AAPPJLT'TOPJ+"tail", a, 1, AIAA'[JZPL.; lval- x 3 lval'taCe+a, 1.; x-*type 3 AIAA'PJZPL; return lval'eval+e, x.;

2

lval- builtin'Toin+lenv- e, lval- a. / 8or +int i 3 1; i ) a-*count; i==. / AAPPJLT'TOPJ+"Toin", a, i, AIAA'[JZPL.; 2 lval- x 3 lval'pop+a, 1.; w&ile +a-*count. / lval- y 3 lval'pop+a, 1.; x 3 lval'Toin+x, y.; 2 lval'del+a.; return x;

2

lval- builtin'op+lenv- e, lval- a, c&ar- op. / 8or +int i 3 1; i ) a-*count; i==. / AAPPJLT'TOPJ+op, a, i, AIAA'MV".; 2 lval- x 3 lval'pop+a, 1.; i8 ++strcmp+op, "-". 33 1. @@ a-*count 33 1. / x-*num 3 -x-*num; 2 w&ile +a-*count * 1. / lval- y 3 lval'pop+a, 1.; i8 i8 i8 i8 +strcmp+op, "=". 33 1. / x-*num =3 y-*num; 2 +strcmp+op, "-". 33 1. / x-*num -3 y-*num; 2 +strcmp+op, "-". 33 1. / x-*num -3 y-*num; 2 +strcmp+op, "5". 33 1. / i8 +y-*num 03 1. / lval'del+x.; lval'del+y.; lval'del+a.; return lval'err+"Division Uy Yero%".; 2 x-*num 53 y-*num;

2 2

lval'del+y.; lval'del+a.; return x; 2 lvallvallvallvalbuiltin'add+lenvbuiltin'sub+lenvbuiltin'mul+lenvbuiltin'div+lenve, e, e, e, lvallvallvallvala. a. a. a. / / / / return return return return builtin'op+e, builtin'op+e, builtin'op+e, builtin'op+e, a, a, a, a, "=".; "-".; "-".; "5".; 2 2 2 2

lval- builtin'var+lenv- e, lval- a, c&ar- 8unc. / AAPPJLT'TOPJ+8unc, a, 1, AIAA'[JZPL.; lval- syms 3 a-*cellG1H;

%=9

8or +int i 3 1; i ) syms-*count; i==. / AAPPJLT+a, +syms-*cellGiH-*type 33 AIAA'PO"., "Runction 7Ns7 cannot de8ine non-symbol% #ot Ns, Jxpected Ns%", 8unc, ltype'name+syms-*cellGiH-*type., ltype'name+AIAA'PO"..; 2 AAPPJLT+a, +syms-*count 33 a-*count-6., "Runction 7Ns7 passed too many arguments 8or symbols% #ot Ni, Jxpected Ni%", 8unc, syms-*count, a-*count-6.; 8or +int i 3 1; i ) syms-*count; i==. / i8 +strcmp+8unc, "de8". 33 1. / lenv'de8+e, syms-*cellGiH, a*cellGi=6H.; 2 i8 +strcmp+8unc, "3". 33 1. / lenv'put+e, syms-*cellGiH, a*cellGi=6H.; 2 2 lval'del+a.; return lval'sexpr+.; 2 lval- builtin'de8+lenv- e, lval- a. / return builtin'var+e, a, "de8".; 2 lval- builtin'put+lenv- e, lval- a. / return builtin'var+e, a, "3".; 2 lval- builtin'ord+lenv- e, lval- a, c&ar- op. / AAPPJLT'MV"+op, a, 2.; AAPPJLT'TOPJ+op, a, 1, AIAA'MV".; AAPPJLT'TOPJ+op, a, 6, AIAA'MV".; int r; i8 +strcmp+op, "*". i8 +strcmp+op, ")". i8 +strcmp+op, "*3". i8 +strcmp+op, ")3". lval'del+a.; return lval'num+r.; builtin'gt+lenvbuiltin'lt+lenvbuiltin'ge+lenvbuiltin'le+lenv33 33 33 33 1. 1. 1. 1. / / / / r r r r 3 3 3 3 +a-*cellG1H-*num +a-*cellG1H-*num +a-*cellG1H-*num +a-*cellG1H-*num * ) *3 )3 a-*cellG6H-*num.; a-*cellG6H-*num.; a-*cellG6H-*num.; a-*cellG6H-*num.; 2 2 2 2

2

lvallvallvallval-

e, e, e, e,

lvallvallvallval-

a. a. a. a.

/ / / /

return return return return

builtin'ord+e, builtin'ord+e, builtin'ord+e, builtin'ord+e,

a, a, a, a,

"*".; ")".; "*3".; ")3".;

2 2 2 2

lval- builtin'cmp+lenv- e, lval- a, c&ar- op. / AAPPJLT'MV"+op, a, 2.; int r; i8 +strcmp+op, "33". 33 1. / r 3 lval'e>+a-*cellG1H, a-*cellG6H.; 2 i8 +strcmp+op, "03". 33 1. / r 3 0lval'e>+a-*cellG1H, a-*cellG6H.; 2 lval'del+a.; return lval'num+r.; 2 lval- builtin'e>+lenv- e, lval- a. / return builtin'cmp+e, a, "33".; 2 lval- builtin'ne+lenv- e, lval- a. / return builtin'cmp+e, a, "03".; 2 lval- builtin'i8+lenv- e, lval- a. / AAPPJLT'MV"+"i8", a, 3.; AAPPJLT'TOPJ+"i8", a, 1, AIAA'MV".; AAPPJLT'TOPJ+"i8", a, 6, AIAA'[JZPL.; AAPPJLT'TOPJ+"i8", a, 2, AIAA'[JZPL.;

%=*

5- "arC Uot& Jxpressions as evaluable -5 lval- x; a-*cellG6H-*type 3 AIAA'PJZPL; a-*cellG2H-*type 3 AIAA'PJZPL; i8 +a-*cellG1H-*num. / 5- B8 condition is true evaluate 8irst expression -5 x 3 lval'eval+e, lval'pop+a, 6..; 2 else / 5- Kt&erwise evaluate second expression -5 x 3 lval'eval+e, lval'pop+a, 2..; 2 5- Delete argument list and return -5 lval'del+a.; return x;

2

void lenv'add'builtin+lenv- e, c&ar- name, lbuiltin 8unc. / lval- C 3 lval'sym+name.; lval- v 3 lval'builtin+8unc.; lenv'put+e, C, v.; lval'del+C.; lval'del+v.; 2 void lenv'add'builtins+lenv- e. / 5- Iariable Runctions -5 lenv'add'builtin+e, "!!", builtin'lambda.; lenv'add'builtin+e, "de8", builtin'de8.; lenv'add'builtin+e, "3", builtin'put.; 5- Aist Runctions -5 lenv'add'builtin+e, "list", builtin'list.; lenv'add'builtin+e, "&ead", builtin'&ead.; lenv'add'builtin+e, "tail", builtin'tail.; lenv'add'builtin+e, "eval", builtin'eval.; lenv'add'builtin+e, "Toin", builtin'Toin.; 5- "at&ematical Runctions -5 lenv'add'builtin+e, "=", builtin'add.; lenv'add'builtin+e, "-", builtin'sub.; lenv'add'builtin+e, "-", builtin'mul.; lenv'add'builtin+e, "5", builtin'div.; 5- Comparison Runctions -5 lenv'add'builtin+e, "i8", lenv'add'builtin+e, "33", builtin'ne.; lenv'add'builtin+e, "*", builtin'lt.; lenv'add'builtin+e, "*3", builtin'le.; 2 5- Jvaluation -5 lval- lval'call+lenv- e, lval- 8, lval- a. / i8 +8-*builtin. / return 8-*builtin+e, a.; 2 builtin'i8.; builtin'e>.; lenv'add'builtin+e, "03", builtin'gt.; lenv'add'builtin+e, ")", builtin'ge.; lenv'add'builtin+e, ")3",

%==

int given 3 a-*count; int total 3 8-*8ormals-*count; w&ile +a-*count. / i8 +8-*8ormals-*count 33 1. / lval'del+a.; return lval'err+"Runction passed too many arguments% #ot Ni, Jxpected Ni%", given, total.; 2 lval- sym 3 lval'pop+8-*8ormals, 1.; i8 +strcmp+sym-*sym, "@". 33 1. / i8 +8-*8ormals-*count 03 6. / lval'del+a.; return lval'err+"Runction 8ormat invalid% Pymbol 7@7 not 8ollowed by single symbol%".; 2 lval- nsym 3 lval'pop+8-*8ormals, 1.; lenv'put+8-*env, nsym, builtin'list+e, a..; lval'del+sym.; lval'del+nsym.; breaC;

2

lval- val 3 lval'pop+a, 1.; lenv'put+8-*env, sym, val.; lval'del+sym.; lval'del+val.; 2 lval'del+a.; i8 +8-*8ormals-*count * 1 @@ strcmp+8-*8ormals-*cellG1H-*sym, "@". 33 1. / i8 +8-*8ormals-*count 03 2. / return lval'err+"Runction 8ormat invalid% Pymbol 7@7 not 8ollowed by single symbol%".; 2 lval'del+lval'pop+8-*8ormals, 1..; lval- sym 3 lval'pop+8-*8ormals, 1.; lval- val 3 lval'>expr+.; lenv'put+8-*env, sym, val.; lval'del+sym.; lval'del+val.; 2 i8 +8-*8ormals-*count 33 1. / 8-*env-*par 3 e; return builtin'eval+8-*env, lval'add+lval'sexpr+., lval'copy+8*body...; 2 else / return lval'copy+8.; 2 2 lval- lval'eval'sexpr+lenv- e, lval- v. /

%=;

8or +int i 3 1; i ) v-*count; i==. / v-*cellGiH 3 lval'eval+e, v*cellGiH.; 2 8or +int i 3 1; i ) v-*count; i==. / i8 +v-*cellGiH-*type 33 AIAA'JLL. / return lval'taCe+v, i.; 2 2 i8 +v-*count 33 1. / return v; 2 i8 +v-*count 33 6. / return lval'eval+e, lval'taCe+v, 1..; 2 lval- 8 3 lval'pop+v, 1.; i8 +8-*type 03 AIAA'RVM. / lval- err 3 lval'err+ "P-Jxpression starts wit& incorrect type% #ot Ns, Jxpected Ns%", ltype'name+8-*type., ltype'name+AIAA'RVM..; lval'del+8.; lval'del+v.; return err; 2 lval- result 3 lval'call+e, 8, v.; lval'del+8.; return result;

2

lval- lval'eval+lenv- e, lval- v. / i8 +v-*type 33 AIAA'PO". / return lenv'get+e, v.; 2 i8 +v-*type 33 AIAA'PJZPL. / return lval'eval'sexpr+e, v.; 2 return v; 2 5- Leading -5 lval- lval'read'num+mpc'ast't- t. / long x 3 strtol+t-*contents, MVAA, 61.; return errno 03 JLAM#J W lval'num+x. lval'err+"Bnvalid Mumber%".; 2 lval- lval'read+mpc'ast't- t. / i8 +strstr+t-*tag, "number".. / return lval'read'num+t.; 2 i8 +strstr+t-*tag, "symbol".. / return lval'sym+t-*contents.; 2 lval- x 3 MVAA; i8 +strcmp+t-*tag, "*". 33 1. / x 3 lval'sexpr+.; 2 i8 +strstr+t-*tag, "sexpr".. / x 3 lval'sexpr+.; 2 i8 +strstr+t-*tag, ">expr".. / x 3 lval'>expr+.; 2 8or +int i 3 1; i ) t-*c&ildren'num; i==. / i8 +strcmp+t-*c&ildrenGiH-*contents, "+". 33 i8 +strcmp+t-*c&ildrenGiH-*contents, ".". 33 i8 +strcmp+t-*c&ildrenGiH-*contents, "2". 33 i8 +strcmp+t-*c&ildrenGiH-*contents, "/". 33 i8 +strcmp+t-*c&ildrenGiH-*tag, "regex". 33 x 3 lval'add+x, lval'read+t-*c&ildrenGiH..; 2 return x; 2 5- "ain -5 int main+int argc, c&ar-- argv. / 1. 1. 1. 1. 1. / / / / / continue; continue; continue; continue; continue; 2 2 2 2 2

%=@

mpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tmpc'parser't-

Mumber Pymbol Pexpr [expr Jxpr Aispy

3 3 3 3 3 3

mpc'new+"number".; mpc'new+"symbol".; mpc'new+"sexpr".; mpc'new+">expr".; mpc'new+"expr".; mpc'new+"lispy".; ! ! ! ! ! ! !

mpca'lang+"PC'AAM#'DJRAVAT, " number 5-WG1-4H=5 ; symbol 5Ga-EA-Y1-4'=!!--!!5!!!!3)*0@H=5 ; sexpr 7+7 )expr*- 7.7 ; >expr 7/7 )expr*- 727 ; expr )number* ? )symbol* ? )sexpr* ? )>expr* ; lispy 5Q5 )expr*- 5X5 ; ", Mumber, Pymbol, Pexpr, [expr, Jxpr, Aispy.; puts+"Aispy Iersion 1%1%1%1%4".; puts+"Press Ctrl=c to Jxit!n".; lenv- e 3 lenv'new+.; lenv'add'builtins+e.; w&ile +6. / c&ar- input 3 readline+"lispy* ".; add'&istory+input.; mpc'result't r; i8 +mpc'parse+")stdin*", input, Aispy, @r.. / lval- x 3 lval'eval+e, lval'read+r%output..; lval'println+x.; lval'del+x.; mpc'ast'delete+r%output.; 2 else / mpc'err'print+r%error.; mpc'err'delete+r%error.; 2 8ree+input.; 2 lenv'del+e.;

mpc'cleanup+<, Mumber, Pymbol, Pexpr, [expr, Jxpr, Aispy.; return 1; 2

Bonus %ar+s
%=+

• • • • • •

H Create builtin logical operators or ??, and @@ and not 0 and add them to the language. H ?efine a recursive Lisp function that returns the nt& item of that list. H ?efine a recursive Lisp function that returns 6 if an element is a member of a list, otherwise 1. H ?efine a Lisp function that returns the last element of a list. H ?efine in Lisp logical operator functions such as or, and and not. H Add a specific boolean type to the language with the builtin variables true and 8alse.

%=C

'trings • Chapter 14

Libraries

)tring / !ow long is it.

7ur Lisp is finally pretty functional. 'e should be able to write almost any functions we want. 'e can build some <uite comple- constructs using it, and even do some cool things that can't be done in lots of other heavyweight and popular languages$ 1very time we update our program and run it again it is getting annoying having to type in again all of our functions. In this chapter we'll add the functionality to load code from a file and run it. "his will allow us to start building a standard library up. Along the way we'll also add support for code comments, strings, and printing.

!tring "ype
4or the user to load a file we'll have to let them supply a string consisting of the file name. 7ur language supports symbols, but still doesn't support strings, which can include spaces and other characters. 'e need to add this possible lval type to specify the file names we need. 'e start, as in other chapters, by adding an entry to our enum and adding an entry to our lval to represent the type's data.
enum / AIAA'JLL, AIAA'MV", AIAA'PO", AIAA'PTL, AIAA'RVM, AIAA'PJZPL, AIAA'[JZPL 2; 5- Uasic -5 long num; c&ar- err; c&ar- sym; c&ar- str;

3e-t we can add a function for constructing string lval, very similar to how we construct constructing symbols. %;&

lval- lval'str+c&ar- s. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'PTL; v-*str 3 malloc+strlen+s. = 6.; strcpy+v-*str, s.; return v; 2

'e also need to add the relevant entries into our functions that deal with lval. 4or &eletion...
case AIAA'PTL 8ree+v-*str.; breaC;

4or Copying...
case AIAA'PTL breaC; x-*str 3 malloc+strlen+v-*str. = 6.; strcpy+x-*str, v-*str.;

4or $<uality...
case AIAA'PTL return +strcmp+x-*str, y-*str. 33 1.;

4or "ype 4ame...
case AIAA'PTL return "Ptring";

4or -rinting we need to do a little more. "he string we store internally is different to the string we want to print. 'e want to print a string as a user might input it, using escape characters such as !n to represent a newline. 'e therefore need to escape it before we print it. Luckily we can make use of a mpc function that will do this for us. In the printing function we add the following...
case AIAA'PTL lval'print'str+v.; breaC;

'here...
void lval'print'str+lval- v. / 5- "aCe a Copy o8 t&e string -5 c&ar- escaped 3 malloc+strlen+v-*str.=6.; strcpy+escaped, v-*str.; 5- Pass it t&roug& t&e escape 8unction -5 escaped 3 mpc8'escape+escaped.; 5- Print it between " c&aracters -5 print8+"!"Ns!"", escaped.; 5- 8ree t&e copied string -5 8ree+escaped.; 2

'eading !trings
%;%

3ow we need to add support for parsing strings. As usual this re<uires first adding a new grammar rule called string and add it to our parser. "he rule we are going to use that represents a string is going to be the same as for C style strings. "his means a string is essentially a series of escape characters, or normal characters, between two <uotation marks "". 'e can specify this as a regular e-pression inside our grammar string as follows.
string 5!"+!!!!%?GQ!"H.-!"5 ;

"his looks pretty complicated but makes a lot more sense when e-plained in parts. It reads like this. A string is a " character, followed by 2ero or more of either a backslash !! followed by any other character %, or anything that isn't a " character GQ!!"H. 4inally it ends with another " character. 'e also need to add a case to deal with this in the lval'read function.
i8 +strstr+t-*tag, "string".. / return lval'read'str+t.; 2

ecause the input string is input in an escaped form we need to create a function lval'read'str which deals with this. "his function is a little tricky because it has to do a few tasks. 4irst it must strip the input string of the " characters on either side. "hen it must unescape the string, converting series of characters such as !n to their actual encoded characters. 4inally it has to create a new lval and clean up anything that has happened in, between.
lval- lval'read'str+mpc'ast't- t. / 5- Cut o88 t&e 8inal >uote c&aracter -5 t-*contentsGstrlen+t-*contents.-6H 3 7!17; 5- Copy t&e string missing out t&e 8irst >uote c&aracter -5 c&ar- unescaped 3 malloc+strlen+t-*contents=6..; strcpy+unescaped, t-*contents=6.; 5- Pass t&roug& t&e unescape 8unction -5 unescaped 3 mpc8'unescape+unescaped.; 5- Construct a new lval using t&e string -5 lval- str 3 lval'str+unescaped.; 5- Rree t&e string and return -5 8ree+unescaped.; return str; 2

If this all works we should be able to play around with strings in the prompt. 3e-t we'll add functions which can actually make use of them.
lispy* "&ello" "&ello" lispy* "&ello!n" "&ello!n" lispy* "&ello!"" "&ello!"" lispy* &ead /"&ello" "world"2 /"&ello"2 lispy* eval +&ead /"&ello" "world"2.

%;0

"&ello" lispy*

Comments
'hile we're building in new synta- to the language we may as well look at comments. Lust like in C, we can use comments in inform other people Dor ourselvesE about what the code is meant to do or why it has been written. In C comments go between 5- and -5. Lisp comments, on the other hand, start with ; and run to the end of the line. I attempted to research why Lisps use ; for comments, but it appears that the origins of this have been lost in the mists of time. In absence of real truth I imagine it as a small rebellion against the imperative languages such as C and Lava which use semicolons so shamelessly and fre<uently to separateJterminate statements. Compared to Lisp all these languages are just comments$ )o in lisp a comment is defined by a semicolon ; followed by any number of characters that are not newline characters represented by either !r or !n. 'e can use another rege- to define it.
comment 5;GQ!!r!!nH-5 ;

As with strings we need to create a new parser and use this to update our language in mpca'lang. 'e also need to remember to add the parser to mpc'cleanup, and update the first integer argument to reflect the new number of parsers passed in. 7ur final grammar now look like this.
mpca'lang+"PC'AAM#'DJRAVAT, " number 5-WG1-4H=5 ; symbol 5Ga-EA-Y1-4'=!!--!!5!!!!3)*0@H=5 ; string 5!"+!!!!%?GQ!"H.-!"5 ; comment 5;GQ!!r!!nH-5 ; sexpr 7+7 )expr*- 7.7 ; >expr 7/7 )expr*- 727 ; expr )number* ? )symbol* ? )string* ? )comment* ? )sexpr* ? )>expr*; lispy 5Q5 )expr*- 5X5 ; ", Mumber, Pymbol, Ptring, Comment, Pexpr, [expr, ! ! ! ! ! ! ! ! ! ! Jxpr, Aispy.;

And the cleanup function looks like this.
mpc'cleanup+:, Mumber, Pymbol, Ptring, Comment, Pexpr, [expr, Jxpr, Aispy.;

ecause comments are only for programmings reading the code, our internal function for reading them in just consists of ignoring them. 'e can add a clause to deal with them in a similar way to brackets and parenthesis in lval'read.

%;9

i8 +strstr+t-*c&ildrenGiH-*tag, "comment".. / continue; 2

Comments won't be of much use on the interactive prompt, but they will be very helpful for adding into files of code to annotate them.

Load /unction
'e want to built a function that can load and evaluate a file when passed a string of its name. "o implement this function we'll need to make used of our grammar as we'll need it to to read in the file contents, parse, and evaluate them. 7ur load function is going to rely on our mpc'parser- called Aispy. "herefore, just like with functions, we need to forward declare our parser pointers, and place them to the top of the file.
mpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tMumber; Pymbol; Ptring; Comment; Pexpr; [expr; Jxpr; Aispy;

7ur load function will be just like any other builtin. 'e need to start by checking that the input argument is a single string. "hen we can use the mpc'8parse'contents function to read in the contents of a file using a grammar. Lust like mpc'parse this parses the contents of a file into some mpc'result object, which is our case is an a&stract synta" tree again or an error. )lightly differently to our command prompt, on successfully parsing a file we shouldn't treat it like one e-pression. 'hen typing into a file we let users list multiple e-pressions and evaluate all of them individually. "o achieve this behaviour we need to loop over each e-pression in the contents of the file and evaluate it one by one. If there are any errors we should print them and continue. If there is a parse error instead of chucking it away we're going to e-tract the message and put it into a error lval which we return. If there are no errors the return value for this builtin can just be the empty e-pression. "he full code for this looks like this.
lval- builtin'load+lenv- e, lval- a. / AAPPJLT'MV"+"load", a, 6.; AAPPJLT'TOPJ+"load", a, 1, AIAA'PTL.; 5- Parse Rile given by string name -5 mpc'result't r; i8 +mpc'8parse'contents+a-*cellG1H-*str, Aispy, @r.. / 5- Lead contents -5 lval- expr 3 lval'read+r%output.; mpc'ast'delete+r%output.; 5- Jvaluate eac& Jxpression -5

%;*

w&ile +expr-*count. / lval- x 3 lval'eval+e, lval'pop+expr, 1..; 5- B8 Jvaluation leads to error print it -5 i8 +x-*type 33 AIAA'JLL. / lval'println+x.; 2 lval'del+x.; 2 5- Delete expressions and arguments -5 lval'del+expr.; lval'del+a.; 5- Leturn empty list -5 return lval'sexpr+.; 2 else / 5- #et Parse Jrror as Ptring -5 c&ar- err'msg 3 mpc'err'string+r%error.; mpc'err'delete+r%error.; 5- Create new error message using it -5 lval- err 3 lval'err+"Could not load Aibrary Ns", err'msg.; 8ree+err'msg.; lval'del+a.; 5- Cleanup and return error -5 return err; 2 2

Command Line Arguments
'ith the ability to load files, we can take the chance to add in some functionality typical of other programming languages. 'hen file names are given as arguments to the command line we can try to run these files. 4or e-ample to run a python file one might write pyt&on 8ilename%py. "hese command line arguments are accessible using the argc and argv variables that are given to main. "he argc variable gives the number of arguments, and argv specifies each string. "he argc is always set to at least one, where the first argument is always the complete command invoked. "hat means if argc is set to 6 we can invoke the interpreter, otherwise we can run each of the arguments through the builtin'load function.
5- Pupplied wit& list o8 8iles -5 i8 +argc *3 2. / 5- loop over eac& supplied 8ilename +starting 8rom 6. -5 8or +int i 3 6; i ) argc; i==. / 5- Create an argument list wit& a single argument being t&e 8ilename -5 lval- args 3 lval'add+lval'sexpr+., lval'str+argvGiH..; 5- Pass to builtin load and get t&e result -5 lval- x 3 builtin'load+e, args.;

%;=

5- B8 t&e result is an error be sure to print it -5 i8 +x-*type 33 AIAA'JLL. / lval'println+x.; 2 lval'del+x.; 2 2

It's now possible to write some basic program and try to invoke it using this method.
lispy example%lspy

-rint /unction
If we are running programs from the command line we might want them to output some data, rather than just define functions and other values. 'e can add a print function to our Lisp which makes use of our e-isting lval'print function. "his function prints each argument separated by a space and then prints a newline character to finish. It returns the empty e-pression.
lval- builtin'print+lenv- e, lval- a. / 5- Print eac& argument 8ollowed by a space -5 8or +int i 3 1; i ) a-*count; i==. / lval'print+a-*cellGiH.; putc&ar+7 7.; 2 5- Print a newline and delete arguments -5 putc&ar+7!n7.; lval'del+a.; return lval'sexpr+.; 2

$rror /unction
'e can also make use of strings to add in an error reporting function. "his can take as input a user supplied string and provide it as an error message for lval'err.
lval- builtin'error+lenv- e, lval- a. / AAPPJLT'MV"+"error", a, 6.; AAPPJLT'TOPJ+"error", a, 1, AIAA'PTL.; 5- Construct Jrror 8rom 8irst argument -5 lval- err 3 lval'err+a-*cellG1H-*str.; 5- Delete arguments and return -5 lval'del+a.; return err; 2

%;;

"he final step is to register these as builtins. 3ow finally we can start building up libraries and writing them to files$
5- Ptring Runctions -5 lenv'add'builtin+e, "load", builtin'load.; lenv'add'builtin+e, "error", builtin'error.; lenv'add'builtin+e, "print", builtin'print.; lispy* print "Hello $orld0" "Hello $orld0" +. lispy* error "T&is is an error" Jrror T&is is an error lispy* load "&ello%lspy" "Hello $orld0" +. lispy*

/inishing 5p
"his is the last chapter in which we are going to e-plicitly work on our C implementation of Lisp. "he result of this chapter will be the final state of your language implementation while I am still involved. "he final line count should clock in somewhere close to %&&& lines of code. 'riting this amount of code is not trivial. If you've made it this far you've written a real program and started on a proper project. "he skills you've learnt here should be transferable, and give you the confidence to seek out your own goals and targets. (ou now have a comple- and beautiful program which you can interact and play with. "his is something you should be proud of. #o show it off to your parents and friends$ In the ne-t chapter we start using our Lisp to build up a standard library of common functions. After that I describe some possible improvements and directions in which the language should be taken. Although we've finished with my involvement this is really this is only the beginning. "hanks for following along, and good luck with whatever C you write in the future$

'eference
strings*c
(include "mpc%&"

%;@

(i8de8 '$BM32 static c&ar bu88erG21F:H; c&ar- readline+c&ar- prompt. / 8puts+"lispy* ", stdout.; 8gets+bu88er, 21F:, stdin.; c&ar- cpy 3 malloc+strlen+bu88er.=6.; strcpy+cpy, bu88er.; cpyGstrlen+cpy.-6H 3 7!17; return cpy; 2 void add'&istory+c&ar- unused. /2 (else (include )editline5readline%&* (include )editline5&istory%&* (endi8 5- Parser Declariations -5 mpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tmpc'parser'tMumber; Pymbol; Ptring; Comment; Pexpr; [expr; Jxpr; Aispy;

5- Rorward Declarations -5 struct lval; struct lenv; typede8 struct lval lval; typede8 struct lenv lenv; 5- Aisp Ialue -5 enum / AIAA'JLL, AIAA'MV", AIAA'PO", AIAA'PTL, AIAA'RVM, AIAA'PJZPL, AIAA'[JZPL 2; typede8 lval-+-lbuiltin.+lenv-, lval-.; struct lval / int type; 5- Uasic -5 long num; c&ar- err; c&ar- sym; c&ar- str; 5- Runction -5 lbuiltin builtin; lenv- env; lval- 8ormals;

%;+

lval- body; 5- Jxpression -5 int count; lval-- cell;

2;

lval- lval'num+long x. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'MV"; v-*num 3 x; return v; 2 lval- lval'err+c&ar- 8mt, %%%. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'JLL; va'list va; va'start+va, 8mt.; v-*err 3 malloc+;62.; vsnprint8+v-*err, ;66, 8mt, va.; v-*err 3 realloc+v-*err, strlen+v-*err.=6.; va'end+va.; return v; 2 lval- lval'sym+c&ar- s. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'PO"; v-*sym 3 malloc+strlen+s. = 6.; strcpy+v-*sym, s.; return v; 2 lval- lval'str+c&ar- s. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'PTL; v-*str 3 malloc+strlen+s. = 6.; strcpy+v-*str, s.; return v; 2 lval- lval'builtin+lbuiltin 8unc. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'RVM; v-*builtin 3 8unc; return v; 2 lenv- lenv'new+void.; lval- lval'lambda+lval- 8ormals, lval- body. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'RVM; v-*builtin 3 MVAA; v-*env 3 lenv'new+.; v-*8ormals 3 8ormals; v-*body 3 body; return v; 2

%;C

lval- lval'sexpr+void. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'PJZPL; v-*count 3 1; v-*cell 3 MVAA; return v; 2 lval- lval'>expr+void. / lval- v 3 malloc+siEeo8+lval..; v-*type 3 AIAA'[JZPL; v-*count 3 1; v-*cell 3 MVAA; return v; 2 void lenv'del+lenv- e.; void lval'del+lval- v. / switc& case case i8 +v-*type. / AIAA'MV" breaC; AIAA'RVM +0v-*builtin. / lenv'del+v-*env.; lval'del+v-*8ormals.; lval'del+v-*body.;

2 2

2 breaC; case AIAA'JLL 8ree+v-*err.; breaC; case AIAA'PO" 8ree+v-*sym.; breaC; case AIAA'PTL 8ree+v-*str.; breaC; case AIAA'[JZPL case AIAA'PJZPL 8or +int i 3 1; i ) v-*count; i==. / lval'del+v-*cellGiH.; 2 8ree+v-*cell.; breaC;

8ree+v.; lenv- lenv'copy+lenv- e.; lval- lval'copy+lval- v. / lval- x 3 malloc+siEeo8+lval..; x-*type 3 v-*type; switc& +v-*type. / case AIAA'RVM i8 +v-*builtin. / x-*builtin 3 v-*builtin; 2 else / x-*builtin 3 MVAA; x-*env 3 lenv'copy+v-*env.; x-*8ormals 3 lval'copy+v-*8ormals.; x-*body 3 lval'copy+v-*body.; 2 breaC; case AIAA'MV" x-*num 3 v-*num; breaC;

%@&

case AIAA'JLL x-*err 3 malloc+strlen+v-*err. = 6.; strcpy+x-*err, v*err.; breaC; case AIAA'PO" x-*sym 3 malloc+strlen+v-*sym. = 6.; strcpy+x-*sym, v*sym.; breaC; case AIAA'PTL x-*str 3 malloc+strlen+v-*str. = 6.; strcpy+x-*str, v*str.; breaC; case AIAA'PJZPL case AIAA'[JZPL x-*count 3 v-*count; x-*cell 3 malloc+siEeo8+lval-. - x-*count.; 8or +int i 3 1; i ) x-*count; i==. / x-*cellGiH 3 lval'copy+v-*cellGiH.; 2 breaC; 2 return x; 2 lval- lval'add+lval- v, lval- x. / v-*count==; v-*cell 3 realloc+v-*cell, siEeo8+lval-. - v-*count.; v-*cellGv-*count-6H 3 x; return v; 2 lval- lval'Toin+lval- x, lval- y. / 8or +int i 3 1; i ) y-*count; i==. / x 3 lval'add+x, y-*cellGiH.; 2 8ree+y-*cell.; 8ree+y.; return x; 2 lval- lval'pop+lval- v, int i. / lval- x 3 v-*cellGiH; memmove+@v-*cellGiH, @v-*cellGi=6H, siEeo8+lval-. - +v-*count-i-6..; v-*count--; v-*cell 3 realloc+v-*cell, siEeo8+lval-. - v-*count.; return x; 2 lval- lval'taCe+lval- v, int i. / lval- x 3 lval'pop+v, i.; lval'del+v.; return x; 2 void lval'print+lval- v.; void lval'print'expr+lval- v, c&ar open, c&ar close. / putc&ar+open.; 8or +int i 3 1; i ) v-*count; i==. / lval'print+v-*cellGiH.; i8 +i 03 +v-*count-6.. / putc&ar+7 7.; 2 2 putc&ar+close.; 2

%@%

void lval'print'str+lval- v. / 5- "aCe a Copy o8 t&e string -5 c&ar- escaped 3 malloc+strlen+v-*str.=6.; strcpy+escaped, v-*str.; 5- Pass it t&roug& t&e escape 8unction -5 escaped 3 mpc8'escape+escaped.; 5- Print it between " c&aracters -5 print8+"!"Ns!"", escaped.; 5- 8ree t&e copied string -5 8ree+escaped.; 2 void lval'print+lval- v. / switc& +v-*type. / case AIAA'RVM i8 +v-*builtin. / print8+")builtin*".; 2 else / print8+"+!! ".; lval'print+v-*8ormals.; putc&ar+7 7.; lval'print+v*body.; putc&ar+7.7.; 2 breaC; case AIAA'MV" print8+"Nli", v-*num.; breaC; case AIAA'JLL print8+"Jrror Ns", v-*err.; breaC; case AIAA'PO" print8+"Ns", v-*sym.; breaC; case AIAA'PTL lval'print'str+v.; breaC; case AIAA'PJZPL lval'print'expr+v, 7+7, 7.7.; breaC; case AIAA'[JZPL lval'print'expr+v, 7/7, 727.; breaC; 2 2 void lval'println+lval- v. / lval'print+v.; putc&ar+7!n7.; 2 int lval'e>+lval- x, lval- y. / i8 +x-*type 03 y-*type. / return 1; 2 +x-*type. / AIAA'MV" return +x-*num 33 y-*num.; AIAA'JLL return +strcmp+x-*err, y-*err. 33 1.; AIAA'PO" return +strcmp+x-*sym, y-*sym. 33 1.; AIAA'PTL return +strcmp+x-*str, y-*str. 33 1.; AIAA'RVM +x-*builtin. / return x-*builtin 33 y-*builtin; 2 else / return lval'e>+x-*8ormals, y-*8ormals. @@ lval'e>+x-*body, y*body.; 2 case AIAA'[JZPL case AIAA'PJZPL i8 +x-*count 03 y-*count. / return 1; 2 8or +int i 3 1; i ) x-*count; i==. / i8 +0lval'e>+x-*cellG1H, y-*cellG1H.. / return 1; 2 2 return 6; breaC; 2 return 1; 2 switc& case case case case case i8

%@0

c&ar- ltype'name+int t. / switc&+t. / case AIAA'RVM return "Runction"; case AIAA'MV" return "Mumber"; case AIAA'JLL return "Jrror"; case AIAA'PO" return "Pymbol"; case AIAA'PTL return "Ptring"; case AIAA'PJZPL return "P-Jxpression"; case AIAA'[JZPL return "[-Jxpression"; de8ault return "VnCnown"; 2 2 5- Aisp Jnvironment -5 struct lenv / lenv- par; int count; c&ar-- syms; lval-- vals; 2; lenv- lenv'new+void. / lenv- e 3 malloc+siEeo8+lenv..; e-*par 3 MVAA; e-*count 3 1; e-*syms 3 MVAA; e-*vals 3 MVAA; return e; 2 void lenv'del+lenv- e. / 8or +int i 3 1; i ) e-*count; i==. / 8ree+e-*symsGiH.; lval'del+e-*valsGiH.; 2 8ree+e-*syms.; 8ree+e-*vals.; 8ree+e.; 2 lenv- lenv'copy+lenv- e. / lenv- n 3 malloc+siEeo8+lenv..; n-*par 3 e-*par; n-*count 3 e-*count; n-*syms 3 malloc+siEeo8+c&ar-. - n-*count.; n-*vals 3 malloc+siEeo8+lval-. - n-*count.; 8or +int i 3 1; i ) e-*count; i==. / n-*symsGiH 3 malloc+strlen+e-*symsGiH. = 6.; strcpy+n-*symsGiH, e-*symsGiH.; n-*valsGiH 3 lval'copy+e-*valsGiH.; 2 return n; 2 lval- lenv'get+lenv- e, lval- C. / 8or +int i 3 1; i ) e-*count; i==. / i8 +strcmp+e-*symsGiH, C-*sym. 33 1. / return lval'copy+e-*valsGiH.; 2 2

%@9

2

i8 +e-*par. / return lenv'get+e-*par, C.; 2 else / return lval'err+"Vnbound Pymbol 7Ns7", C-*sym.; 2

void lenv'put+lenv- e, lval- C, lval- v. / 8or +int i 3 1; i ) e-*count; i==. / i8 +strcmp+e-*symsGiH, C-*sym. 33 1. / lval'del+e-*valsGiH.; e-*valsGiH 3 lval'copy+v.; e-*symsGiH 3 realloc+e-*symsGiH, strlen+C-*sym.=6.; strcpy+e-*symsGiH, C-*sym.; return; 2 2 e-*count==; e-*vals 3 realloc+e-*vals, siEeo8+lval-. - e-*count.; e-*syms 3 realloc+e-*syms, siEeo8+c&ar-. - e-*count.; e-*valsGe-*count-6H 3 lval'copy+v.; e-*symsGe-*count-6H 3 malloc+strlen+C-*sym.=6.; strcpy+e-*symsGe-*count-6H, C-*sym.;

2

void lenv'de8+lenv- e, lval- C, lval- v. / w&ile +e-*par. / e 3 e-*par; 2 lenv'put+e, C, v.; 2 5- Uuiltins -5 (de8ine AAPPJLT+args, cond, 8mt, %%%. ! i8 +0+cond.. / lval- err 3 lval'err+8mt, ((''IA'AL#P''.; lval'del+args.; return err; 2 (de8ine AAPPJLT'TOPJ+8unc, args, index, expect. ! AAPPJLT+args, args-*cellGindexH-*type 33 expect, ! "Runction 7Ns7 passed incorrect type 8or argument Ni% #ot Ns, Jxpected Ns%", ! 8unc, index, ltype'name+args-*cellGindexH-*type., ltype'name+expect.. (de8ine AAPPJLT'MV"+8unc, args, num. ! AAPPJLT+args, args-*count 33 num, ! "Runction 7Ns7 passed incorrect number o8 arguments% #ot Ni, Jxpected Ni%", ! 8unc, args-*count, num. (de8ine AAPPJLT'MKT'J"PTO+8unc, args, index. ! AAPPJLT+args, args-*cellGindexH-*count 03 1, ! "Runction 7Ns7 passed /2 8or argument Ni%", 8unc, index.; lval- lval'eval+lenv- e, lval- v.; lval- builtin'lambda+lenv- e, lval- a. / AAPPJLT'MV"+"!!", a, 2.; AAPPJLT'TOPJ+"!!", a, 1, AIAA'[JZPL.; AAPPJLT'TOPJ+"!!", a, 6, AIAA'[JZPL.;

%@*

8or +int i 3 1; i ) a-*cellG1H-*count; i==. / AAPPJLT+a, +a-*cellG1H-*cellGiH-*type 33 AIAA'PO"., "Cannot de8ine non-symbol% #ot Ns, Jxpected Ns%", ltype'name+a-*cellG1H-*cellGiH-*type., ltype'name+AIAA'PO"..; 2 lval- 8ormals 3 lval'pop+a, 1.; lval- body 3 lval'pop+a, 1.; lval'del+a.; 2 return lval'lambda+8ormals, body.;

lval- builtin'list+lenv- e, lval- a. / a-*type 3 AIAA'[JZPL; return a; 2 lval- builtin'&ead+lenv- e, lval- a. / AAPPJLT'MV"+"&ead", a, 6.; AAPPJLT'TOPJ+"&ead", a, 1, AIAA'[JZPL.; AAPPJLT'MKT'J"PTO+"&ead", a, 1.; lval- v 3 lval'taCe+a, 1.; w&ile +v-*count * 6. / lval'del+lval'pop+v, 6..; 2 return v; 2 lval- builtin'tail+lenv- e, lval- a. / AAPPJLT'MV"+"tail", a, 6.; AAPPJLT'TOPJ+"tail", a, 1, AIAA'[JZPL.; AAPPJLT'MKT'J"PTO+"tail", a, 1.; lval- v 3 lval'taCe+a, 1.; lval'del+lval'pop+v, 1..; return v; 2 lval- builtin'eval+lenv- e, lval- a. / AAPPJLT'MV"+"eval", a, 6.; AAPPJLT'TOPJ+"tail", a, 1, AIAA'[JZPL.; lval- x 3 lval'taCe+a, 1.; x-*type 3 AIAA'PJZPL; return lval'eval+e, x.;

2

lval- builtin'Toin+lenv- e, lval- a. / 8or +int i 3 1; i ) a-*count; i==. / AAPPJLT'TOPJ+"Toin", a, i, AIAA'[JZPL.; 2 lval- x 3 lval'pop+a, 1.; w&ile +a-*count. / lval- y 3 lval'pop+a, 1.; x 3 lval'Toin+x, y.; 2 lval'del+a.; return x;

%@=

2 lval- builtin'op+lenv- e, lval- a, c&ar- op. / 8or +int i 3 1; i ) a-*count; i==. / AAPPJLT'TOPJ+op, a, i, AIAA'MV".; 2 lval- x 3 lval'pop+a, 1.; i8 ++strcmp+op, "-". 33 1. @@ a-*count 33 1. / x-*num 3 -x-*num; 2 w&ile +a-*count * 1. / lval- y 3 lval'pop+a, 1.; i8 i8 i8 i8 +strcmp+op, "=". 33 1. / x-*num =3 y-*num; 2 +strcmp+op, "-". 33 1. / x-*num -3 y-*num; 2 +strcmp+op, "-". 33 1. / x-*num -3 y-*num; 2 +strcmp+op, "5". 33 1. / i8 +y-*num 03 1. / lval'del+x.; lval'del+y.; lval'del+a.; return lval'err+"Division Uy Yero%".; 2 x-*num 53 y-*num;

2 2 lval'del+y.;

2

lval'del+a.; return x; builtin'add+lenvbuiltin'sub+lenvbuiltin'mul+lenvbuiltin'div+lenve, e, e, e, lvallvallvallvala. a. a. a. / / / / return return return return builtin'op+e, builtin'op+e, builtin'op+e, builtin'op+e, a, a, a, a, "=".; "-".; "-".; "5".; 2 2 2 2

lvallvallvallval-

lval- builtin'var+lenv- e, lval- a, c&ar- 8unc. / AAPPJLT'TOPJ+8unc, a, 1, AIAA'[JZPL.; lval- syms 3 a-*cellG1H; 8or +int i 3 1; i ) syms-*count; i==. / AAPPJLT+a, +syms-*cellGiH-*type 33 AIAA'PO"., "Runction 7Ns7 cannot de8ine non-symbol% #ot Ns, Jxpected Ns%", 8unc, ltype'name+syms-*cellGiH-*type., ltype'name+AIAA'PO"..; 2 AAPPJLT+a, +syms-*count 33 a-*count-6., "Runction 7Ns7 passed too many arguments 8or symbols% #ot Ni, Jxpected Ni%", 8unc, syms-*count, a-*count-6.; 8or +int i 3 1; i ) syms-*count; i==. / i8 +strcmp+8unc, "de8". 33 1. / lenv'de8+e, syms-*cellGiH, a*cellGi=6H.; 2 i8 +strcmp+8unc, "3". 33 1. / lenv'put+e, syms-*cellGiH, a*cellGi=6H.; 2 2 lval'del+a.; return lval'sexpr+.; 2

%@;

lval- builtin'de8+lenv- e, lval- a. / return builtin'var+e, a, "de8".; 2 lval- builtin'put+lenv- e, lval- a. / return builtin'var+e, a, "3".; 2 lval- builtin'ord+lenv- e, lval- a, c&ar- op. / AAPPJLT'MV"+op, a, 2.; AAPPJLT'TOPJ+op, a, 1, AIAA'MV".; AAPPJLT'TOPJ+op, a, 6, AIAA'MV".; int r; i8 +strcmp+op, "*". i8 +strcmp+op, ")". i8 +strcmp+op, "*3". i8 +strcmp+op, ")3". lval'del+a.; return lval'num+r.; 2 lvallvallvallvalbuiltin'gt+lenvbuiltin'lt+lenvbuiltin'ge+lenvbuiltin'le+lenve, e, e, e, lvallvallvallvala. a. a. a. / / / / return return return return builtin'ord+e, builtin'ord+e, builtin'ord+e, builtin'ord+e, a, a, a, a, "*".; ")".; "*3".; ")3".; 2 2 2 2 33 33 33 33 1. 1. 1. 1. / / / / r r r r 3 3 3 3 +a-*cellG1H-*num +a-*cellG1H-*num +a-*cellG1H-*num +a-*cellG1H-*num * ) *3 )3 a-*cellG6H-*num.; a-*cellG6H-*num.; a-*cellG6H-*num.; a-*cellG6H-*num.; 2 2 2 2

lval- builtin'cmp+lenv- e, lval- a, c&ar- op. / AAPPJLT'MV"+op, a, 2.; int r; i8 +strcmp+op, "33". 33 1. / r 3 lval'e>+a-*cellG1H, a-*cellG6H.; 2 i8 +strcmp+op, "03". 33 1. / r 3 0lval'e>+a-*cellG1H, a-*cellG6H.; 2 lval'del+a.; return lval'num+r.; 2 lval- builtin'e>+lenv- e, lval- a. / return builtin'cmp+e, a, "33".; 2 lval- builtin'ne+lenv- e, lval- a. / return builtin'cmp+e, a, "03".; 2 lval- builtin'i8+lenv- e, lval- a. / AAPPJLT'MV"+"i8", a, 3.; AAPPJLT'TOPJ+"i8", a, 1, AIAA'MV".; AAPPJLT'TOPJ+"i8", a, 6, AIAA'[JZPL.; AAPPJLT'TOPJ+"i8", a, 2, AIAA'[JZPL.; lval- x; a-*cellG6H-*type 3 AIAA'PJZPL; a-*cellG2H-*type 3 AIAA'PJZPL; i8 +a-*cellG1H-*num. / x 3 lval'eval+e, lval'pop+a, 6..; 2 else / x 3 lval'eval+e, lval'pop+a, 2..; 2 lval'del+a.; return x;

2

lval- lval'read+mpc'ast't- t.; lval- builtin'load+lenv- e, lval- a. / AAPPJLT'MV"+"load", a, 6.; AAPPJLT'TOPJ+"load", a, 1, AIAA'PTL.;

%@@

5- Parse Rile given by string name -5 mpc'result't r; i8 +mpc'8parse'contents+a-*cellG1H-*str, Aispy, @r.. / 5- Lead contents -5 lval- expr 3 lval'read+r%output.; mpc'ast'delete+r%output.; 5- Jvaluate eac& Jxpression -5 w&ile +expr-*count. / lval- x 3 lval'eval+e, lval'pop+expr, 1..; 5- B8 Jvaluation leads to error print it -5 i8 +x-*type 33 AIAA'JLL. / lval'println+x.; 2 lval'del+x.; 2 5- Delete expressions and arguments -5 lval'del+expr.; lval'del+a.; 5- Leturn empty list -5 return lval'sexpr+.; 2 else / 5- #et Parse Jrror as Ptring -5 c&ar- err'msg 3 mpc'err'string+r%error.; mpc'err'delete+r%error.; 5- Create new error message using it -5 lval- err 3 lval'err+"Could not load Aibrary Ns", err'msg.; 8ree+err'msg.; lval'del+a.; 5- Cleanup and return error -5 return err;

2 2

lval- builtin'print+lenv- e, lval- a. / 5- Print eac& argument 8ollowed by a space -5 8or +int i 3 1; i ) a-*count; i==. / lval'print+a-*cellGiH.; putc&ar+7 7.; 2 5- Print a newline and delete arguments -5 putc&ar+7!n7.; lval'del+a.; return lval'sexpr+.; 2 lval- builtin'error+lenv- e, lval- a. / AAPPJLT'MV"+"error", a, 6.; AAPPJLT'TOPJ+"error", a, 1, AIAA'PTL.; 5- Construct Jrror 8rom 8irst argument -5 lval- err 3 lval'err+a-*cellG1H-*str.; 5- Delete arguments and return -5 lval'del+a.;

%@+

2

return err;

void lenv'add'builtin+lenv- e, c&ar- name, lbuiltin 8unc. / lval- C 3 lval'sym+name.; lval- v 3 lval'builtin+8unc.; lenv'put+e, C, v.; lval'del+C.; lval'del+v.; 2 void lenv'add'builtins+lenv- e. / 5- Iariable Runctions -5 lenv'add'builtin+e, "!!", builtin'lambda.; lenv'add'builtin+e, "de8", builtin'de8.; lenv'add'builtin+e, "3", builtin'put.; 5- Aist Runctions -5 lenv'add'builtin+e, "list", builtin'list.; lenv'add'builtin+e, "&ead", builtin'&ead.; lenv'add'builtin+e, "tail", builtin'tail.; lenv'add'builtin+e, "eval", builtin'eval.; lenv'add'builtin+e, "Toin", builtin'Toin.; 5- "at&ematical Runctions -5 lenv'add'builtin+e, "=", builtin'add.; lenv'add'builtin+e, "-", builtin'sub.; lenv'add'builtin+e, "-", builtin'mul.; lenv'add'builtin+e, "5", builtin'div.; 5- Comparison Runctions -5 lenv'add'builtin+e, "i8", lenv'add'builtin+e, "33", builtin'ne.; lenv'add'builtin+e, "*", builtin'lt.; lenv'add'builtin+e, "*3", builtin'le.; builtin'i8.; builtin'e>.; lenv'add'builtin+e, "03", builtin'gt.; lenv'add'builtin+e, ")", builtin'ge.; lenv'add'builtin+e, ")3",

5- Ptring Runctions -5 lenv'add'builtin+e, "load", builtin'load.; lenv'add'builtin+e, "error", builtin'error.; lenv'add'builtin+e, "print", builtin'print.; 2 5- Jvaluation -5 lval- lval'call+lenv- e, lval- 8, lval- a. / i8 +8-*builtin. / return 8-*builtin+e, a.; 2 int given 3 a-*count; int total 3 8-*8ormals-*count; w&ile +a-*count. / i8 +8-*8ormals-*count 33 1. / lval'del+a.; return lval'err+"Runction passed too many arguments% #ot Ni, Jxpected Ni%", given, total.; 2

%@C

lval- sym 3 lval'pop+8-*8ormals, 1.; i8 +strcmp+sym-*sym, "@". 33 1. / i8 +8-*8ormals-*count 03 6. / lval'del+a.; return lval'err+"Runction 8ormat invalid% Pymbol 7@7 not 8ollowed by single symbol%".; 2 lval- nsym 3 lval'pop+8-*8ormals, 1.; lenv'put+8-*env, nsym, builtin'list+e, a..; lval'del+sym.; lval'del+nsym.; breaC; 2 lval- val 3 lval'pop+a, 1.; lenv'put+8-*env, sym, val.; lval'del+sym.; lval'del+val.;

2

lval'del+a.; i8 +8-*8ormals-*count * 1 @@ strcmp+8-*8ormals-*cellG1H-*sym, "@". 33 1. / i8 +8-*8ormals-*count 03 2. / return lval'err+"Runction 8ormat invalid% Pymbol 7@7 not 8ollowed by single symbol%".; 2 lval'del+lval'pop+8-*8ormals, 1..; lval- sym 3 lval'pop+8-*8ormals, 1.; lval- val 3 lval'>expr+.; lenv'put+8-*env, sym, val.; lval'del+sym.; lval'del+val.;

2

i8 +8-*8ormals-*count 33 1. / 8-*env-*par 3 e; return builtin'eval+8-*env, lval'add+lval'sexpr+., lval'copy+8*body...; 2 else / return lval'copy+8.; 2 2 lval- lval'eval'sexpr+lenv- e, lval- v. / 8or +int i 3 1; i ) v-*count; i==. / v-*cellGiH 3 lval'eval+e, v*cellGiH.; 2 8or +int i 3 1; i ) v-*count; i==. / i8 +v-*cellGiH-*type 33 AIAA'JLL. / return lval'taCe+v, i.; 2 2 i8 +v-*count 33 1. / return v; 2 i8 +v-*count 33 6. / return lval'eval+e, lval'taCe+v, 1..; 2 lval- 8 3 lval'pop+v, 1.; i8 +8-*type 03 AIAA'RVM. /

%+&

2

lval- err 3 lval'err+ "P-Jxpression starts wit& incorrect type% #ot Ns, Jxpected Ns%", ltype'name+8-*type., ltype'name+AIAA'RVM..; lval'del+8.; lval'del+v.; return err;

lval- result 3 lval'call+e, 8, v.; lval'del+8.; return result; 2 lval- lval'eval+lenv- e, lval- v. / i8 +v-*type 33 AIAA'PO". / return lenv'get+e, v.; 2 i8 +v-*type 33 AIAA'PJZPL. / return lval'eval'sexpr+e, v.; 2 return v; 2 5- Leading -5 lval- lval'read'num+mpc'ast't- t. / long x 3 strtol+t-*contents, MVAA, 61.; return errno 03 JLAM#J W lval'num+x. lval'err+"Bnvalid Mumber%".; 2 lval- lval'read'str+mpc'ast't- t. / 5- Cut o88 t&e 8inal >uote c&aracter -5 t-*contentsGstrlen+t-*contents.-6H 3 7!17; 5- Copy t&e string missing out t&e 8irst >uote c&aracter -5 c&ar- unescaped 3 malloc+strlen+t-*contents=6..; strcpy+unescaped, t-*contents=6.; 5- Pass t&roug& t&e unescape 8unction -5 unescaped 3 mpc8'unescape+unescaped.; 5- Construct a new lval using t&e string -5 lval- str 3 lval'str+unescaped.; 5- Rree t&e string and return -5 8ree+unescaped.; return str; 2 lval- lval'read+mpc'ast't- t. / i8 +strstr+t-*tag, "number".. / return lval'read'num+t.; 2 i8 +strstr+t-*tag, "string".. / return lval'read'str+t.; 2 i8 +strstr+t-*tag, "symbol".. / return lval'sym+t-*contents.; 2 lval- x 3 MVAA; i8 +strcmp+t-*tag, "*". 33 1. / x 3 lval'sexpr+.; 2 i8 +strstr+t-*tag, "sexpr".. / x 3 lval'sexpr+.; 2 i8 +strstr+t-*tag, ">expr".. / x 3 lval'>expr+.; 2 8or +int i 3 1; i ) t-*c&ildren'num; i==. / i8 +strcmp+t-*c&ildrenGiH-*contents, "+". 33 1. / continue; i8 +strcmp+t-*c&ildrenGiH-*contents, ".". 33 1. / continue; i8 +strcmp+t-*c&ildrenGiH-*contents, "2". 33 1. / continue; i8 +strcmp+t-*c&ildrenGiH-*contents, "/". 33 1. / continue; i8 +strcmp+t-*c&ildrenGiH-*tag, "regex". 33 1. / continue; i8 +strstr+t-*c&ildrenGiH-*tag, "comment".. / continue; 2 x 3 lval'add+x, lval'read+t-*c&ildrenGiH..; 2 2 2 2 2 2

%+%

2

return x;

5- "ain -5 int main+int argc, c&ar-- argv. / Mumber Pymbol Ptring Comment Pexpr [expr Jxpr Aispy 3 3 3 3 3 3 3 3 mpc'new+"number".; mpc'new+"symbol".; mpc'new+"string".; mpc'new+"comment".; mpc'new+"sexpr".; mpc'new+">expr".; mpc'new+"expr".; mpc'new+"lispy".; ! ! ! ! ! ! ! ! ! ! Jxpr, Aispy.;

mpca'lang+"PC'AAM#'DJRAVAT, " number 5-WG1-4H=5 ; symbol 5Ga-EA-Y1-4'=!!--!!5!!!!3)*0@H=5 ; string 5!"+!!!!%?GQ!"H.-!"5 ; comment 5;GQ!!r!!nH-5 ; sexpr 7+7 )expr*- 7.7 ; >expr 7/7 )expr*- 727 ; expr )number* ? )symbol* ? )string* ? )comment* ? )sexpr* ? )>expr*; lispy 5Q5 )expr*- 5X5 ; ", Mumber, Pymbol, Ptring, Comment, Pexpr, [expr, lenv- e 3 lenv'new+.; lenv'add'builtins+e.; 5- Bnteractive Prompt -5 i8 +argc 33 6. / puts+"Aispy Iersion 1%1%1%6%1".; puts+"Press Ctrl=c to Jxit!n".; w&ile +6. / c&ar- input 3 readline+"lispy* ".; add'&istory+input.; mpc'result't r; i8 +mpc'parse+")stdin*", input, Aispy, @r.. /

lval- x 3 lval'eval+e, lval'read+r%output..; lval'println+x.; lval'del+x.; mpc'ast'delete+r%output.; 2 else / mpc'err'print+r%error.; mpc'err'delete+r%error.; 2 8ree+input.; 2 2

%+0

5- Pupplied wit& list o8 8iles -5 i8 +argc *3 2. / 5- loop over eac& supplied 8ilename +starting 8rom 6. -5 8or +int i 3 6; i ) argc; i==. / 5- Create an argument list wit& a single argument being t&e 8ilename -5 lval- args 3 lval'add+lval'sexpr+., lval'str+argvGiH..; 5- Pass to builtin load and get t&e result -5 lval- x 3 builtin'load+e, args.; 5- B8 t&e result is an error be sure to print it -5 i8 +x-*type 33 AIAA'JLL. / lval'println+x.; 2 lval'del+x.;

2 2

lenv'del+e.; mpc'cleanup+:, Mumber, Pymbol, Ptring, Comment, Pexpr, [expr, Jxpr, Aispy.; return 1; 2

Bonus %ar+s
• • • • • • •

H Adapt the builtin function Toin to work on strings. H Adapt the builtin function &ead to work on strings. H Adapt the builtin function tail to work on strings. H Create a builtin function read that reads in and converts a string to a O,e-pression. H Create a builtin function s&ow that can print the contents of strings as it is DunescapedE. H Create a special value oC to return instead of empty e-pressions +.. H Add functions to wrap all of C's file handling functions such as 8open and 8gets.

%+9

'tandard !i1rar5 • Chapter 1"

%inimalism

Library / uilt with just leather, paper, wood, and ink.

"he Lisp we've built has been purposefully minimal. 'e've only added the fewest number of core structures and builtins. If we chose these carefully, as we did, then it should allow us to add in everything else re<uired to the language. "he motivation behind minimalism is two,fold. "he first advantage is that it makes the core language simple to debug and easy to learn. "his is a great benefit to developers and users. Like 7ccam's Fa2or it is almost always better to trim away any waste if it results in a e<ually e-pressive language. "he second reason is that having a small language is also aesthetically nicer. It is clever, interesting, and fun to see how small we can make the core of a language, and still get something useful out of the other side. As hackers, which we should be by now, this is something we enjoy.

Atoms

%+*

'hen dealing with conditionals we added no new boolean type to our language. ecause of this we didn't add true or 8alse either. Instead we just used numbers. Feadability is still important though, so we can define some constants to represent these values. 7n a similar note, many lisps use the word nil to represent the empty list /2. 'e can add this in too. "hese constants are sometimes called atoms because of their fundamental and constant behaviour. "he user is not forced to use these named constants, and can use numbers and empty lists instead as they like. "his choice empowers users, something we believe in.
; Atoms +de8 /nil2 /2. +de8 /true2 6. +de8 /8alse2 1.

Building Bloc+s
'e've already come up with a number of cool functions I've been using in the e-amples. 7ne of these is our 8un function that allows us to declare functions in a neater way. 'e should definitely include this in our standard library.
; Runction De8initions +de8 /8un2 +! /8 b2 / de8 +&ead 8. +! +tail 8. b. 2..

'e also had our unpacC and pacC functions. "hese too are going to be essential for users. 'e should include these along with their curry and uncurry aliases.
; VnpacC Aist 8or Runction +8un /unpacC 8 l2 / eval +Toin +list 8. l. 2. ; PacC Aist 8or Runction +8un /pacC 8 @ xs2 /8 xs2. ; Curried and Vncurried calling +de8 /curry2 /unpacC2. +de8 /uncurry2 /pacC2.

)ay we want to do several things in order. 7ne way we can do this is to put each thing to do as an argument to some function. 'e known that arguments are evaluated in order from left to right, which is essentially se<uencing events. 4or functions such as print and load we don't care much about what it evaluates to, but do care about the order in which it happens. "herefore we can create a do function which evaluates a number of e-pressions in order and returns the last one. "his relies on the last function, which returns the final element of a list. 'e'll define this later.
; Per8orm Peveral t&ings in Pe>uence

%+=

+8un /do @ l2 / i8 +33 l /2. //22 /last l2 2.

)ometimes we want to save results to local variables using the 3 operator. 'hen we're inside a function this will implicitly only save results locally, but sometimes we want to open up an even more local scope. 4or this we can create a function let which creates an empty function for code to take place in, and evaluates it.
; Kpen new scope +8un /let b2 / ++! /'2 b. +.. 2.

'e can use this in conjunction with do to ensure that variables do not leak out of their scope.
lispy* let /do +3 /x2 611. +x.2 611 lispy* x Jrror Vnbound Pymbol 7x7 lispy*

Logical Operators
'e didn't define any local operators such as and and or in our language. "his might be a good thing to add in later. 4or now we can use arithmetic operators to emulate them. "hink for a second how these functions work when encountering 1 or 6 for their various inputs.
; Aogical Runctions +8un /not x2 /- 6 x2. +8un /or x y2 /= x y2. +8un /and x y2 /- x y2.

%iscellaneous /unctions
!ere are a couple of miscellaneous functions that don't really fit in anywhere. )ee if you can guess their intended functionality.
+8un /8lip 8 a b2 /8 b a2. +8un /g&ost @ xs2 /eval xs2. +8un /comp 8 g x2 /8 +g x.2.

"he 8lip function takes a function 8 and two arguments a and b. It then applies 8 to a and b in the reversed order. "his might be useful when we want a function to be partially evaluated. If we want to partially evaluate a function by only passing it in it's second argument we can use 8lip to give us a new function that takes the first two arguments in reversed order.

%+;

"his means if you want to apply the second argument of a function you can just apply the first argument to the 8lip of this function.
lispy* +. lispy* 6 lispy* +. lispy* +. lispy* 6 lispy* +8lip de8. 6 /x2 x de8 /de8ine-one2 ++8lip de8. 6. de8ine-one /y2 y

I couldn't think of a use for the g&ost function, but it seemed interesting. It simply takes in any number of arguments and evaluates them as if they were the e-pression itself. )o it just sits at the front of an e-pression like a ghost, not interacting of changing the behaviour of the program at all. If you can think of a use for it I'd love to hear.
lispy* g&ost = 2 2 F

"he comp function is used to compose two functions. It takes as input 8, g, and an argument to g. It then applies this argument to g and applies the result again to 8. "his can be used to compose two function together into a new function that applies both of them in series. Like before we can use this in combination with partial evaluation to build up comple- functions from simpler ones. 4or e-ample we can compose two functions. 7ne that negates a number and another that unpacks a list of numbers for multiplying together using -.
lispy* F lispy* -F lispy* +! /x2 lispy* +. lispy* -6< lispy* +unpacC -. /2 22 - ++unpacC -. /2 22. comp - +unpacC -. /8 +g x.2. de8 /mul-neg2 +comp - +unpacC -.. mul-neg /2 :2

List /unctions
"he &ead function is used to get the first element of a list, but what it returns is still wrapped in the list. If we want to actually get the element out of this list we need to e-tract it somehow.

%+@

)ingle element lists evaluate to just that element, so we can use the eval function to do this e-traction. 'e can also define a couple of helper functions for aid e-tracting the first, second and third elements of a list. 'e'll use these function a lot later.
; Rirst, Pecond, or +8un /8st l2 / eval +8un /snd l2 / eval +8un /trd l2 / eval T&ird +&ead +&ead +&ead Btem in Aist l. 2. +tail l.. 2. +tail +tail l... 2.

'e looked briefly at some recursive list functions a few chapters ago. 3aturally there are many more we can define using this techni<ue. "o find the length of a list we can recursive over it adding 6 to the length of the tail. "o find the nt& element of a list we can perform the tail operation and count down until we reach 1. "o get the last element of a list we can just access the element at the length minus one.
; Aist Aengt& +8un /len l2 / i8 +33 l /2. /12 /= 6 +len +tail l..2 2. ; Mt& item in Aist +8un /nt& n l2 / i8 +33 n 1. /8st l2 /nt& +- n 6. +tail l.2 2. ; Aast item in Aist +8un /last l2 /nt& +- +len l. 6. l2.

"here are lots of other useful functions that follow this same pattern. 'e can define functions for taking and dropping the first so many elements of a list, or functions for checking if a value is an element of a list.
; TaCe M items +8un /taCe n l2 / i8 +33 n 1. //22 /Toin +&ead l. +taCe +- n 6. +tail l..2 2. ; Drop M items +8un /drop n l2 / i8 +33 n 1. /l2 /drop +- n 6. +tail l.2 2. ; Pplit at M +8un /split n l2 /list +taCe n l. +drop n l.2. ; Jlement o8 Aist +8un /elem x l2 / i8 +33 l /2.

%++

/8alse2 /i8 +33 x +8st l.. /true2 /elem x +tail l.22 2.

"hese functions all follow similar patterns. It would be great if there was some way to e-tract this pattern so we don't have to type it out every time. 4or e-ample we may want a way we can perform some function on every element of a list. "his is a function we can define called map. It takes as input some function, and some list. 4or each item in the list it applies 8 to that item and appends it back onto the front of the list. It then applies map to the tail of the list.
; Apply Runction to Aist +8un /map 8 l2 / i8 +33 l /2. //22 /Toin +list +8 +8st l... +map 8 +tail l..2 2.

'ith this we can do some neat things that look a bit like looping. In some ways this concept is more powerful than looping. Instead of thinking about performing some function to each element of the list in turn, we can think about acting on all the elements at once. 'e map the list rather than changing each element.
lispy* map - /; < 9 : 2 22 FF2 /-; -< -9 -: -2 -22 -FF2 lispy* map +! /x2 /= x 612. /; 2 662 /6; 62 262 lispy* print /"&ello" "world"2 /"&ello" "world"2 +. lispy* map print /"&ello" "world"2 "&ello" "world" /+. +.2 lispy*

An adaptation of this idea is a 8ilter function which, takes in some functional condition, and only includes items of a list which match that condition.
; Apply Rilter to Aist +8un /8ilter 8 l2 / i8 +33 l /2. //22 /Toin +i8 +8 +8st l.. /&ead l2 //22. +8ilter 8 +tail l..2 2.

"his is what it looks like in practice.
lispy* 8ilter +! /x2 /* x 22. /; 2 66 -9 : 62 /; 66 :2

)ome loops don't e-actly act on a list, but accumulate some total or condense the list into a single value. "hese are loops such as sums and products. "hese can be e-pressed <uite similarly to the len function we've already defined.

%+C

"hese are called 'olds and they work like this. )upplied with a function 8, a &ase value E and a list l they merge each element in the list with the total, starting with the base value.
; Rold Ae8t +8un /8oldl 8 E l2 / i8 +33 l /2. /E2 /8oldl 8 +8 E +8st l.. +tail l.2 2.

:sing folds we can define the sum and product functions in a very elegant way.
+8un /sum l2 /8oldl = 1 l2. +8un /product l2 /8oldl - 6 l2.

Conditional /unctions
y defining our 8un function we've already shown how powerful our language is in its ability to define functions that look like new synta-. Another e-ample of this is found in emulating the C switc& and case statements. In C these are built into the language, but for our language we can define them as part of a library. 'e can define a function select that takes in 2ero or more two,element lists as input. 4or each two element list in the arguments it first evaluates the first element of the pair. If this is true then it evaluates and returns the second item, otherwise it performs the same thing again on the rest of the list.
+8un /select @ cs2 / i8 +33 cs /2. /error "Mo Pelection Round"2 /i8 +8st +8st cs.. /snd +8st cs.2 /unpacC select +tail cs.22 2.

'e can also define a function ot&erwise to always evaluate to true. "his works a little bit like the de8ault keyword in C.
; De8ault Case +de8 /ot&erwise2 true. ; Print Day o8 "ont& su88ix +8un /mont&-day-su88ix i2 / select /+33 i 1. "st"2 /+33 i 6. "nd"2 /+33 i 3. "rd"2 /ot&erwise "t&"2 2.

"his is actually somewhat more powerful than the C switc& statement. In C rather than passing in conditions the input value is compared only for e<uality with a number of constant candidates. 'e can also define this function in our Lisp, where we compare a value to a number of candidates. In this function we take some value x followed by 2ero or more two,

%C&

element lists again. If the first element in the two,element list is e<ual to x, the second element is evaluated, otherwise the process continues down the list.
+8un /case x @ cs2 / i8 +33 cs /2. /error "Mo Case Round"2 /i8 +33 x +8st +8st cs... /snd +8st cs.2 /unpacC case +Toin +list x. +tail cs..22 2.

"he synta- for this function becomes really nice and simple. )ee if you can think up any other control structures or useful functions that you'd like to implement using these sorts of methods.
+8un /day-name x2 / case x /1 ""onday"2 /6 "Tuesday"2 /2 "$ednesday"2 /3 "T&ursday"2 /F "Rriday"2 /; "Paturday"2 /< "Punday"2 2.

/ibonacci
3o standard library would be complete without an obligatory definition of the 4ibonacci function. :sing all of the above things we've defined we can write a cute little 8ib function that is really <uite readable, and clear semantically.
; Ribonacci +8un /8ib n2 / select / +33 n 1. /12 2 / +33 n 6. /62 2 / ot&erwise /= +8ib +- n 6.. +8ib +- n 2..2 2 2.

"his is the end of the standard library I've written. uilding up a standard library is a fun part of language design, because you get to be creative and opinionated on what goes in and stays out. "ry to come up with something you are happy with. 1-ploring what is possible to define and do can be very interesting.

'eference
%C%

prelude*lspy
;;; ;;; ;;; Aispy Ptandard Prelude

;;; Atoms +de8 /nil2 /2. +de8 /true2 6. +de8 /8alse2 1. ;;; Runctional Runctions ; Runction De8initions +de8 /8un2 +! /8 b2 / de8 +&ead 8. +! +tail 8. b. 2.. ; Kpen new scope +8un /let b2 / ++! /'2 b. +.. 2. ; VnpacC Aist to Runction +8un /unpacC 8 l2 / eval +Toin +list 8. l. 2. ; Vnapply Aist to Runction +8un /pacC 8 @ xs2 /8 xs2. ; Curried and Vncurried calling +de8 /curry2 /unpacC2. +de8 /uncurry2 /pacC2. ; Per8orm Peveral t&ings in Pe>uence +8un /do @ l2 / i8 +33 l /2. //22 /last l2 2. ;;; Aogical Runctions ; Aogical Runctions +8un /not x2 /- 6 x2. +8un /or x y2 /= x y2. +8un /and x y2 /- x y2. ;;; Mumeric Runctions ; "inimum o8 Arguments +8un /min @ xs2 / i8 +33 +tail xs. /2. /8st xs2 /do +3 /rest2 +unpacC min +tail xs... +3 /item2 +8st xs.. +i8 +) item rest. /item2 /rest2. 2

%C0

2. ; "inimum o8 Arguments +8un /max @ xs2 / i8 +33 +tail xs. /2. /8st xs2 /do +3 /rest2 +unpacC max +tail xs... +3 /item2 +8st xs.. +i8 +* item rest. /item2 /rest2. 2 2. ;;; Conditional Runctions +8un /select @ cs2 / i8 +33 cs /2. /error "Mo Pelection Round"2 /i8 +8st +8st cs.. /snd +8st cs.2 /unpacC select +tail cs.22 2. +8un /case x @ cs2 / i8 +33 cs /2. /error "Mo Case Round"2 /i8 +33 x +8st +8st cs... /snd +8st cs.2 /unpacC case +Toin +list x. +tail cs..22 2. +de8 /ot&erwise2 true. ;;; "isc Runctions +8un /8lip 8 a b2 /8 b a2. +8un /g&ost @ xs2 /eval xs2. +8un /comp 8 g x2 /8 +g x.2. ;;; Aist Runctions ; Rirst, Pecond, or +8un /8st l2 / eval +8un /snd l2 / eval +8un /trd l2 / eval T&ird +&ead +&ead +&ead Btem in Aist l. 2. +tail l.. 2. +tail +tail l... 2.

; Aist Aengt& +8un /len l2 / i8 +33 l /2. /12 /= 6 +len +tail l..2 2. ; Mt& item in Aist +8un /nt& n l2 / i8 +33 n 1. /8st l2 /nt& +- n 6. +tail l.2 2. ; Aast item in Aist +8un /last l2 /nt& +- +len l. 6. l2. ; Apply Runction to Aist

%C9

+8un /map 8 l2 / i8 +33 l /2. //22 /Toin +list +8 +8st l... +map 8 +tail l..2 2. ; Apply Rilter to Aist +8un /8ilter 8 l2 / i8 +33 l /2. //22 /Toin +i8 +8 +8st l.. /&ead l2 //22. +8ilter 8 +tail l..2 2. ; Leturn all o8 list but last element +8un /init l2 / i8 +33 +tail l. /2. //22 /Toin +&ead l. +init +tail l..2 2. ; Leverse Aist +8un /reverse l2 / i8 +33 l /2. //22 /Toin +reverse +tail l.. +&ead l.2 2. ; Rold Ae8t +8un /8oldl 8 E l2 / i8 +33 l /2. /E2 /8oldl 8 +8 E +8st l.. +tail l.2 2. ; Rold Lig&t +8un /8oldr 8 E l2 / i8 +33 l /2. /E2 /8 +8st l. +8oldr 8 E +tail l..2 2. +8un /sum l2 /8oldl = 1 l2. +8un /product l2 /8oldl - 6 l2. ; TaCe M items +8un /taCe n l2 / i8 +33 n 1. //22 /Toin +&ead l. +taCe +- n 6. +tail l..2 2. ; Drop M items +8un /drop n l2 / i8 +33 n 1. /l2 /drop +- n 6. +tail l.2 2. ; Pplit at M +8un /split n l2 /list +taCe n l. +drop n l.2.

%C*

; TaCe $&ile +8un /taCe-w&ile 8 l2 / i8 +not +unpacC 8 +&ead l... //22 /Toin +&ead l. +taCe-w&ile 8 +tail l..2 2. ; Drop $&ile +8un /drop-w&ile 8 l2 / i8 +not +unpacC 8 +&ead l... /l2 /drop-w&ile 8 +tail l.2 2. ; Jlement o8 Aist +8un /elem x l2 / i8 +33 l /2. /8alse2 /i8 +33 x +8st l.. /true2 /elem x +tail l.22 2. ; Rind element in list o8 pairs +8un /looCup x l2 / i8 +33 l /2. /error "Mo Jlement Round"2 /do +3 /Cey2 +8st +8st l... +3 /val2 +snd +8st l... +i8 +33 Cey x. /val2 /looCup x +tail l.2. 2 2. ; Yip two lists toget&er into a list o8 pairs +8un /Eip x y2 / i8 +or +33 x /2. +33 y /2.. //22 /Toin +list +Toin +&ead x. +&ead y... +Eip +tail x. +tail y..2 2. ; VnEip a list o8 pairs into two lists +8un /unEip l2 / i8 +33 l /2. ///2 /222 /do +3 /x2 +8st l.. +3 /xs2 +unEip +tail l... +list +Toin +&ead x. +8st xs.. +Toin +tail x. +snd xs... 2 2. ;;; Kt&er Run ; Ribonacci +8un /8ib n2 / select / +33 n 1. 1 2 / +33 n 6. 6 2 / ot&erwise += +8ib +- n 6.. +8ib +- n 2... 2 2.

%C=

Bonus %ar+s
• • • • • •

H Fewrite the len function using 8oldl. H Fewrite the elem function using 8oldl. H Incorporate your standard library directly into the language. .ake it load at start,up. H 'rite some documentation for your standard library, e-plaining what each of the functions do. H 'rite some e-ample programs using your standard library, for users to learn from. H 'rite some test cases for each of the functions in your standard library.

%C;

Bonus Pro6ects • Chapter 1#

Only the Beginning
Although we've done a lot with our Lisp, it is still some way off from a fully complete, production strength programming language. If you tried to use it for any sufficiently large project there are a number of issues you would eventually run into, and improvements you'd have to make. )olving these problems would be what would bring it more into the scope of a fully fledged programming language. !ere are some of these issues you would likely encounter, potential solutions to these problems, and some other fun ideas for other improvements too. )ome may take a few hundred lines of code, others a few thousand. "he choice of what to tackle is up to you. If you've become fond of your language you may enjoy doing some of these projects.

4ati,e "ypes
Currently our language only wraps the native C long and c&ar- types. "his is pretty limiting if you want to do any kind of useful computation. 7ur operations on these data types are also pretty limited. Ideally our language should wrap all of the native C types and allow for methods of manipulating them. 7ne of the most important additions would be the ability to manipulate decimal numbers. 4or this you could wrap the double type and relevant operations. 'ith more than one number type we need to make sure the arithmetic operators such as = and - work on them all, and them in combination. Adding support for native types should be interesting for people wishing to do computation with decimal and floating,point numbers in their language.

5ser &efined "ypes
As well as adding support for native types it would be good to give users the ability to add their own new types, just like how we use structs in C. "he synta- or method you use to do this would be up to you. "his is a really essential part making our language usable for any reasonably si2ed project. "his task may be interesting to anyone who has a specific idea of how they would like to develop the language, and what they want a final design to look like.

%C@

Important List / 5lay$ 1 !A55( and go home.

List Literal
)ome lisps use s<uare brackets GH to give a literal notation for lists of evaluated values lists. "his syntactic sugar for writing something like list 611 += 61 21. 311. Instead it lets you write G611 += 61 21. 311H. In some situations this is clearly nicer, but it does use up the GH characters which could possibly be used for more interesting purposes. "his should be a simple addition for people looking to try out adding e-tra synta-.

Operating !ystem 1nteraction
7ne essential part of bootstrapping a language is to give it proper abilities for opening, reading, and writing files. "his means wrapping all the C functions such as 8read, 8write, 8getc, etc in Lisp e<uivalents. "his is a fairly straight forward task, but does re<uire writing <uite a large number of wrapper functions. "his is why we've not done it for our language so far. 7n a similar note it would be great to give our language access to whatever operating systems calls are appropriate. 'e should give it the ability to change directory, list files in a directory and that sort of thing. "his is an easy task but again re<uires a lot of wrapping of C functions.

%C+

It is essential for any real practical use of this language as something like a scripting language. 5eople who wish to make use of their language for doing simple scripting tasks and string manipulation may be interested in implementing this project.

%acros
.any other Lisps allow you to write things like +de8 x 611. to define the value 611 to x. In our lisp this wouldn't work because it would attempt to evaluate the x to whatever value was stored as x in the environment. In other Lisps these functions are called macros, and when encountered they stop the evaluation of their arguments, and manipulate them un,evaluated. "hey let you write things that look like normal function calls, but actually do comple- and interesting things. "hese are kind of a fun thing to have in a language. "hey make it so you can add a little bit of magic to some of the workings. In many cases this can make synta- nicer or allow a user to not repeat themselves. 5ersonally I like how our language handles things like de8 and i8 without resorting to macros, but if you dislike how it works currently, and want it to be more similar to conventional Lisps, this might be something you are interested in implementing.

.ariable

ashtable

At the moment when we lookup variable names in our language we just do a linear search over all of the variables in the environment. "his gets more and more inefficient the more variables we have defined. A more efficient way to do this is to implement a Hash a&le. "his techni<ue converts the variable name to some integer and uses this to inde- into an array of a known si2e to find the value associated with this symbol. "his is a really important data structure in programming and will crop up everywhere because of its fantastic performance under heavy loads. Anyone who is interested in learning more about data structures and algorithms would be smart to take a shot at implementing this data structure or one of its variations.

-ool Allocation
7ur Lisp is very simple, it is not fast. Its performance is relative to some scripting languages such as 5ython and Fuby. .ost of the performance overhead in our program comes from the fact that doing almost anything re<uires us to construct and destruct lval. 'e therefore call malloc very often. "his is a slow function as it re<uires the operating system to do some management for us. 'hen doing calculations there is lots of copying, allocation and deallocation of lval types. %CC

If we wish to reduce this overhead we need to lower the number of calls to malloc. 7ne method of doing this is to call malloc once at the beginning of the program, allocating a large pool of memory. 'e should then replace all our malloc calls with calls to some function that slices and dices up this memory for use in the program. "his means that we are emulating some of the behaviour of the operating system, but in a faster local way. "his idea is called memory pool allocation and is a common techni<ue used in game development, and other performance sensitive applications. "his can be tricky to implement correctly, but conceptually does not need to be comple-. If you want a <uick method for getting large gains in performance looking into this might interest you.

3arbage Collection
Almost all other implementations of Lisps assign variables differently to ours. "hey do not store a copy of a value in the environment, but actually a pointer, or reference, to it directly. ecause pointers are used, rather than copies, just like in C, there is much less overhead re<uired when using large data structures.

#arbage Collection / 5ick up that can.

If we store pointers to values, rather than copies, we need to ensure that the data pointed to is not deleted before some other value tries to make use of it. Instead we want it to get deleted when there are no longer any references to it. 7ne method to do this, called 3ark and $weep, is to monitor those values that are in the environment, as well as every value that has been allocated. 'hen a variable is put into the environment it, and everything it references, is marked. "hen, when we wish to free memory, we can then iterate over every value that has been allocated, and delete any that are not marked. "his is called 6ar&age %ollection and is an integral part to many programming languages. As with pool allocation, implementing a 6ar&age %ollector does not need to be complicated, but it does need to be done carefully, in particularly if you wish to make it efficient. 0&&

Implementing this would be essential to making this language practical for working with large amounts of data. A particularly good tutorial on implementing a garbage collector in C can be found here. "his should interest anyone who is concerned with the language's performance and wishes to change the semantics of how variables are stored and modified in the language.

"ail Call Optimisation
7ur programming language uses recursion to do its looping. "his is conceptually a really neat way to do it, but practically it is <uite poor. Fecursive functions call themselves to collect all of the partial results of a computation, and only then combine all the results together. "his is a wasteful way of doing computation when partial results can be accumulated as some total over a loop. "his is particularly problematic for loops that are intended to run for many, or infinite, iterations. )ome recursive functions can be automatically converted to corresponding w&ile loops, which accumulate totals step by step, rather than altogether. "his automatic conversion is called tail call optimisation and is an essential optimisation for programs that do a lot of looping using recursion. 5eople who are interested in compiler optimisations and the correspondences between different forms of computation might find this project interesting.

Le#ical !coping
'hen our language tries to lookup a variable that has been undefined it throws an error. It would be better if it could tell us which variables are undefined before evaluating the program. "his would let us avoid typos and other annoying bugs. 4inding these issues before the program is run is called le"ical scoping, and uses the rules for variable definition to try and infer which variables are defined and which aren't at each point in the program, without doing any evaluation. "his could be a difficult task to get e-actly right, but should be interesting to anyone who wants to make their programming language more safe to use, and less bug,prone.

0&%

)tatic 1lectricity / A hair,raising alternative.

!tatic "yping
1very value in our program has an associated type with it. "his we know before any evaluation has taken place. 7ur builtin functions also only take certain types as input. 'e should be able to use this information to infer the types of new user defined functions and values. 'e can also use this information to check that functions are being called with the correct types before we run the program. "his will reduce any errors stemming from calling functions with incorrect types before evaluation. "his checking is called static typing. "ype systems are a really interesting and fundamental part of computer science. "hey are currently the best method we know of detecting errors before running a program. Anyone interesting in programming language safety and type systems should find this project really interesting.

Conclusion
.any thanks for following along with this book. I hope you've found something of interest in its pages. If you did enjoy it please tell your friends about it$ If you are going to continue developing your language then best of luck and I hope you learn many more cool things about C, programming languages, and computer science.

0&0

.ost of all I hope you've had fun building your own Lisp. :ntil ne-t time$

0&9

Credits • Build 7our 8 n !isp

!pecial "han+s
)pecial thanks to my friends and family for their support, in particular 4rancesca )haw for helping me along the way, and putting up with me spending all my time on this project$ "hanks to .iran Lipovaca, 4rederic "rottier,!ebert, and Lonathan "ang, authors of Learn you a !askell, Learn you some 1rlang, and 'rite (ourself a )cheme in *+ !ours for inspiration, and their ideas, and thoughts.

Beta 'eaders
"hanks to all my eta readers for their valuable feedback, corrections, suggestions, and encouragement. .any thanks to Feddit users neelaryan, bitsbytesbikes, aces!?, CodyChan, northClan, da*c9&ff, nowords, o2hank, cracke2, stubarfoo, vie2ebanaan, L.agnum+;, u31>;80Crpf9, fortynine2eronine, skeeto, miketaylr, wonnernaus, arthalion, codyriou-, sigjuice, yoshi6, u,n,sky,

1mage Credits
.any thanks to everyone who has made their images and photos available under Creative Commons. I hope by making this book available to read online for free, I have given a small something back to the creativity and good will of the community. All images are licensed under CC BY 8*0 unless otherwise stated.
• • • • • • • • • • • • • • • •

Ada Lo,elace =1>1?;1>?8@ by .athematical Association of America /ridge by sweethappychick%C+= %i+e "yson by bir2er Amelia on %acBoo+ -ro by 5aulo 7rdove2a smashed Computer by cosmic yard sale Co,er of program0 1>AB0 by %ucha by .ary .argret 3erman -ointer by Fory 3olan 'eptile -ar+ C1 by randon !olton Octopus .ulgaris =1 thin+(@ by 5at ?avid /eli# by andreavallejos "he Dmas tree has been drin+ing by 6evin ?ooley /or understanding recursion*** by Andreas. -lumbing A-1s by )alim >irji !elf !torage* 3host %ural by rad Coy Building site in Berlin by Ingo Fonner L1!- "heory 6 -ractice by 5aul ?owney

0&*

• • • • • • • • • • • •

!trawberry %acro by atramos %utant0 4o $#plaination by 7rin Mebest $mergence of mysterious Blac+ Bo# by thierry ehrmann !C/)%1")800A by :lrick Curry set by >era P Lean,Christophe Our -ug 1s Cute When e 1s Asleep by >ery.oto.oto !tring in the !un by )yops%st !t Eohn2s College Old Library ; West !ide by ben.gallagher +id to do list0 list0 Be happy and go home by Carissa Fogers /at luc+* by )ascha 1rni, .rb !tatic $lectricity by andrechinn Ada Lo,elace -ortrait by Alfred 1dward Chalon is licensed in the 5ublic ?omain

4or those who want to show their support, I accept tips in the form of... 7itcoin +ogecoin
69$["2$Ot2:T<pMOYvVcLsoE#:9wCwP&mp

D;ToEp9epcAYC8M3EV4x;6c"9uYD92PbwV

0&=

Sponsor Documents

Or use your account on DocShare.tips

Hide

Forgot your password?

Or register your new account on DocShare.tips

Hide

Lost your password? Please enter your email address. You will receive a link to create a new password.

Back to log-in

Close