Boot code to SPAD
I would like to convert boot code to SPAD because:
- It would help to remove reliance on Lisp code and therefore allow more parallel code.
- It would help me to modify the compiler code as a first stage help to convert the library code to other languages.
I managed to compile this boot code to an Abstract Syntax Tree (AST) I then wrote a code generator to write AST back to boot code. This code was different from the original (because the AST does not store format information) but when I compiled to Lisp the code generated was identical - all lines (except those starting with ';') were identical.
So I am sure my AST contains all the information from the boot files.
Its then not difficult to generate SPAD or Aldor or any other language from this AST. But the problems are:
- How to handle global variables, especially dynamic variables. I think each function would have to have an additional parameter to pass around the runtime values of these dynamic variables.
- The boot 'where' keyword seems to hold inner functions and variables. How should inner functions be converted to SPAD? SPAD has lambdas but can SPAD lambda capture variables (closures)?
- The native Lisp code would have to be translated by hand, to handle input/output to console, files and database (which would be different for each language).
- This file compiles the boot code to an AST.
- In order to get the compiler to compile I had to make a few tweaks to the boot code, these files. compile properly.
- This file generates boot code from the AST.
- Other files allow the compiler to distinguish blocks (pile mode).
Since I now have confidence in the AST I can now go on to generate SPAD from the AST. I have started this in this file but this is work in progress and I still have a lot more to do on this.
Perhaps another option would be to just translate boot to SPAD manually but that's hard because Boot/Lisp passes everything around in global variables whereas well written SPAD should encapsulate information in domains. This means you cant just translate one function at a time and check if it works before going on to the next.
I think it would be hard to change one function at a time because all these functions are linked by lots of global variables.
I suspect it would only be possible to untangle all that mess if you have a very deep understanding of the interpreter and of boot. Since I would like to remove boot code I would rather not spend a lot of time learning about it.
One thing that I can easily do, since I have an AST, is generate tables showing which functions use which variables and another showing which variables are used in which functions. But I think it would take more knowledge than I have to untangle them. I might even be able to infer which variables only get assigned to one type (int,float,string..) so that they could be assigned those types in SPAD. However many boot variables could be polymorphic or complicated list structures.
> But how do you generate static > type language from a dynamic type language?
I mapped functions in boot to functions in SPAD. For convenience I just put them in packages one for each boot file
Variables in boot can map to variables which are instances of SExpression. This instance of SExpression can have have common lisp functions like '+' defined. At runtime this function would add if its a number and fail if its not. So its checking the type dynamically at runtime - just like boot/lisp.
In order to get the boot code to compile it is first necessary to make a few minor manual changes to the code. It is easy to know where these changes are required because the Eclipse IDE will indicate with red error markings. However it is tedious making these changes so I have put prefromatted interpreter boot code here.
- Whitespace: comments must be on separate line and at same indent as adjacent statements.
- Whitespace: Lines cannot be extended by indenting.
- Line folding:
- Lists can only be split after comma
- Lisp literal: new line must start with '('
|if-then-else must be on 1 line or||if a then
- if changed to if1
- and changed to and1
- or changed to or1
- not changed to not1
- is changed to is1
- package changed to package1
- local changed to local1
- until changed to until1
FunctionDef [x] == changed to FunctionDef([x])==
-1 changed to (-1)
open ended segment
parenthesis must be added around start if not int or ID
[i for i in (index+1)..]
|a := b||a : MExpression := b||where: MExpression is an implemetation of SExpression with some operations defined on it.|
|a := b + c||a : MExpression := b + c||for example '+' would be defined if this instance of MExpression is a number.|
|[x,y] := b||x : MExpression:= b.1
y : MExpression:= b.2
|assumes 'b' is a list with 2 elements|
List handling:For an introduction to list handling in boot see page here.
If we knew the types then we could translate like this:
x := [a, b]
x := [a, b]
This assumes a and b are the same type '%' so x will have type List %
x := [a, :b]
x := concat(a,b)
concat: (S, %) -> % if a is Integer and b is List Integer then x will be List Integer.
x := [a, :b, c]
but in general all types are SExpression so we need a way to handle colon ':':
x := [a, b]
x := [a, :b]
x := [a, :b, c]
For an introduction to 'is' keyword in boot see page here.
Some structures like 'is' seem especially difficult to convert to SPAD because the syntax and the semantics seem intertwined, or perhaps I should say the compile-time and run-time code are interdependent. So, in the example below, we are binding and assigning the variables 'a' and 'b' but this depends on run-time (match function).
x is [a, :b] => b
( checkMatch:Boolean := match(x,[a@Symbol, colon@Symbol,b@Symbol]) if checkMatch then a := x.1 b := CDR x checkMatch ) => b
/* rules for indentation
* newline(indent) inserted after every declaration
* newline(indent) inserted after every statement in block
* should these be before?