In Forth you program by compiling new words. A word definition starts with a ":" and ends with a ";". E.g. ": ANewWord quit ;" would define a new word called ANewWord, which would execute the existing word "quit" when invoked.
So what do we need to implement compilation? We'll start collecting definitions when ":" is encountered, and compile the gathered data when ";" ends the definition. So we need a place to put stuff until the actual compilation:
var compilationStack = List[String]()
We also need to know whether to execute or compile a word, ":" will enter compilation mode, and ";" will switch back to interpret mode:
var mode = Mode.interpret
object Mode extends Enumeration {
val interpret, compile = Value
}
Finally, our implementation will need to be able to differentiate between name and implementation:
var nameDefinition = true
With that out of the way, we need to add compile time behaviour to the words:
abstract class AbstractWord {
val name:String
def eval
def compile {
compilationStack = compilationStack ::: name :: Nil
}
}The default compile implementation is to just add its own name to the compilation stack. ":" just needs to switch mode, while ";" also needs to compile the compilation stack. It does this by using the first stack element as name, and the rest as a list of word names to be executed:
object Colon extends AbstractWord {
val name = ":"
def eval {
mode = Mode.compile
}
override def compile {
}
}
object SemiColon extends AbstractWord {
val name = ";"
def eval {
}
override def compile {
mode = Mode.interpret
val newName = compilationStack.head
val words = compilationStack.tail
dict += (newName -> new Word(name = newName, words = words))
compilationStack = List[String]()
nameDefinition = true
}
}In the main loop we need to handle compilation:
def main(args: Array[String]) {
...
for (item <- items) {
if (mode == Mode.interpret) {
...
}
} else {
if(nameDefinition) {
compilationStack = item :: compilationStack
nameDefinition = false
} else {
dict.get(item) match {
case Some(s:AbstractWord) => s.compile
case None => Console.print("Skipping " + item)
}
}
}
}
...
}
def initDict {
...
dict += (":" -> Colon)
dict += (";" -> SemiColon)
}And that's it! A minimal Forth compiler ready to go:
Welcome to scalaFORTH
> : .. . . ;
ok
> 1 2 .. 1 2 . .
2 1 2 1 ok
>
Next time we'll tackle numbers at compile time. As of now scalaFORTH looks like this:
object FORTHv3 {
var stack = List[BigDecimal]()
var dict = Map[String, AbstractWord]()
var compilationStack = List[String]()
var mode = Mode.interpret
var nameDefinition = true
def main(args: Array[String]) {
initDict
Console.println("Welcome to scalaFORTH")
Console.print("> ")
while (true) {
val line = Console.in.readLine
val items = line.split(' ')
for (item <- items) {
if (mode == Mode.interpret) {
dict.get(item) match {
case Some(s:AbstractWord) => s.eval
case None => stack = BigDecimal.apply(item) :: stack
}
} else {
if(nameDefinition) {
compilationStack = item :: compilationStack
nameDefinition = false
} else {
dict.get(item) match {
case Some(s:AbstractWord) => s.compile
case None => Console.print("Skipping " + item)
}
}
}
}
Console.println(" ok")
Console.print("> ")
}
}
def initDict {
dict += ("." -> Dot)
dict += ("quit" -> Quit)
dict += (":" -> Colon)
dict += (";" -> SemiColon)
}
abstract class AbstractWord {
val name:String
def eval
def compile {
compilationStack = compilationStack ::: name :: Nil
}
}
class Word(var immediate: Boolean = false, val name: String, val words:List[String] = List()) extends AbstractWord {
def eval {
for (word <- words) {
dict.get(word) match {
case Some(s:AbstractWord) => s.eval
case None => Console.err.println("Word not found: " + word)
}
}
}
}
object Dot extends AbstractWord {
val name = "."
def eval {
Console.out.print(stack.head + " ")
stack=stack.tail
}
}
object Quit extends AbstractWord {
val name = "quit"
def eval {
Console.out.print("Live long and prosper! ")
System.exit(0)
}
}
object Mode extends Enumeration {
val interpret, compile = Value
}
object Colon extends AbstractWord {
val name = ":"
def eval {
mode = Mode.compile
}
override def compile {
}
}
object SemiColon extends AbstractWord {
val name = ";"
def eval {
}
override def compile {
mode = Mode.interpret
val newName = compilationStack.head
val words = compilationStack.tail
dict += (newName -> new Word(name = newName, words = words))
compilationStack = List[String]()
nameDefinition = true
}
}
}
No comments:
Post a Comment