Permalink
Browse files

interpreting working

  • Loading branch information...
1 parent b719bf1 commit 3e16ddb3badabedee19486ac9d24afe97420cd19 @brucespang committed Nov 23, 2011
View
@@ -4,3 +4,4 @@ project/build/target/**
project/target/**
lib_managed/**
lisp/**
+tags
@@ -11,14 +11,14 @@ trait AbstractLexer {
// If the result of the handler is a lexer, we start lexing from the rest of the string,
// and append the result of the sublexer to the list of tokens
// Otherwise, we append the token to the list of tokens.
- type Handler = Function1[String, Either[AbstractLexer, Token]]
+ type Handler = (String => Either[AbstractLexer, Token])
// ListMap to preserve the order of the patterns for predictable overriding behavior
protected var patterns = ListMap[Regex,Handler]()
def pattern(pattern:Regex) = {
{ handler:Handler =>
- val newPattern = ("(" + pattern + ")").r
+ val newPattern = ("^(" + pattern + ")").r
patterns.update(newPattern, handler)
}
}
@@ -55,11 +55,9 @@ trait AbstractLexer {
}
protected def longestSubstring(string:String, pattern:Regex):String = {
- // starting from the end of the string, iterate until we find a match
- string match {
- case "" => return ""
- case pattern(_) => return string
- case _ => longestSubstring(string.init, pattern)
+ pattern.findAllIn(string).toList.sortBy(_.length) match {
+ case List() => ""
+ case longest :: rest => longest
}
}
@@ -2,57 +2,169 @@ package lisp
import lisp.tokens._
import lisp.Parser.AST
+import collection.mutable.Map
-class Interpreter {
+case class Lambda(val args:List[WordToken], val body:AST, val env:Environment) {
+ def apply(values:List[Any], Interpreter:Interpreter):Any = {
+ if(args.length != values.length)
+ throw new Exception
+
+ // Combine each arg with its respective value and convert them to a map
+ // Ideally this would all be immutable, but we don't have that yet
+ val bindings = collection.mutable.Map() ++ args.zip(values).toMap
+
+ // Create new environment and eval the expression in it
+ val newEnv = env.extend(bindings)
+
+ Interpreter.evalSeq(body, newEnv)
+ }
+}
+
+case class Pair(val car:Any, val cdr:Any) {
+ override def toString() = "(" + car + " . " + cdr + ")"
+}
- class Environment(parent:Environment=Root, val bindings:Map[WordToken, Any] = Map()) {
- def extend(bindings:Map[WordToken, Any]):Environment = new Environment(this, bindings)
+case class Environment(parent:Environment=RootEnvironment, val bindings:Map[WordToken, Any] = Map()) {
+ def extend(bindings:Map[WordToken, Any]):Environment = new Environment(this, bindings)
- def lookup(token:WordToken):Any = bindings(token)
- def lookup(token:TrueToken):TrueToken = token
- def lookup(token:FalseToken):FalseToken = token
+ def define(wordToken:WordToken, expression:Any):SymbolToken = {
+ bindings(wordToken) = expression
+ SymbolToken("ok")
}
- object Root extends Environment {
- protected var bindings = Map[String,Any](
- "add" -> '+
- )
+ def lookup(token:Any):Any = {
+ token match {
+ case token: WordToken =>
+ val word = token.asInstanceOf[WordToken]
+ if(bindings.contains(word))
+ bindings(word)
+ else
+ handleLookupMiss(token)
+ case token: Token =>
+ token
+ }
}
- case class Lambda(val args:List[WordToken], val body:AST, val env:Environment) {
- def apply(values:List[Token]) = {
- if(args.length != values.length)
- throw new Exception
+ def handleLookupMiss(token:Any):Any = {
+ parent.lookup(token)
+ }
+}
+
+class UndefinedException extends Exception
+
+object RootEnvironment extends Environment(null, Map[WordToken,Any]()) {
+ object Lib {
+ def add(nums:List[DecimalToken]) = {
+ DecimalToken(nums.foldLeft(0.0)((total,i) => total + i.value))
+ }
+
+ def subtract(nums:List[DecimalToken]) = {
+ DecimalToken(nums.tail.foldLeft(nums.head.value)((total,i) => total - i.value))
+ }
+
+ def multiply(nums:List[DecimalToken]) = {
+ DecimalToken(nums.foldLeft(1.0)((total,i) => total * i.value))
+ }
+
+ def divide(nums:List[DecimalToken]) = {
+ DecimalToken(nums.tail.foldLeft(nums.head.value)((total,i) => total / i.value))
+ }
+
+ def cons(vals:List[Any]) = vals match {
+ case first :: last :: Nil => Pair(first, last)
+ case _ => throw new Exception("Too many arguments -- CONS")
+ }
+
+ def car(pairs:List[Pair]) = pairs match {
+ case pair :: Nil => pair.car
+ case _ => throw new Exception("Too many arguments -- CAR")
+ }
- // Combine each arg with its respective value and convert them to a map
- val bindings = args.zip(values).toMap
+ def cdr(pairs:List[Pair]) = pairs match {
+ case pair :: Nil => pair.cdr
+ case _ => throw new Exception("Too many arguments -- CDR")
+ }
- // Create new environment and eval the expression in it
- val newEnv = env.extend(bindings)
+ def equal(vals:List[Any]):BooleanToken = {
+ if(vals.length < 2)
+ throw new Exception("Too few arguments -- EQ?")
+ vals.tail.foldLeft[Tuple2[BooleanToken,Any]]((TrueToken(), vals.head)) { (current,next) =>
+ current._1 match {
+ case TrueToken() => (if(current._2 == next) TrueToken() else FalseToken(), next)
+ case FalseToken() => current
+ }
+ }._1
+ }
+ }
- evalSeq(body, newEnv)
+ val libFuns = Map[WordToken, (List[Any] => Any)](
+ WordToken("+") -> { decimals => Lib.add(decimals.asInstanceOf[List[DecimalToken]]) },
+ WordToken("-") -> { decimals => Lib.subtract(decimals.asInstanceOf[List[DecimalToken]]) },
+ WordToken("*") -> { decimals => Lib.multiply(decimals.asInstanceOf[List[DecimalToken]]) },
+ WordToken("/") -> { decimals => Lib.divide(decimals.asInstanceOf[List[DecimalToken]]) },
+ WordToken("cons") -> Lib.cons,
+ WordToken("car") -> { pair => Lib.car(pair.asInstanceOf[List[Pair]]) },
+ WordToken("cdr") -> { pair => Lib.cdr(pair.asInstanceOf[List[Pair]]) },
+ WordToken("eq?") -> { vals => Lib.equal(vals) },
+ WordToken("print") -> { vals => println(vals.mkString(" ")) }
+ )
+
+ override def handleLookupMiss(token:Any):((List[Any],Interpreter) => Any) = {
+ token match {
+ case token:WordToken if libFuns.contains(token) =>
+ { (vals:List[Any], Interpreter:Interpreter) => libFuns(token)(vals)}
+ case _ => throw new UndefinedException
}
}
+}
- def eval(expression:AST, env:Environment):AST = {
+class Interpreter {
+
+ def eval(expression:Any, env:Environment=RootEnvironment):Any = {
expression match {
case List(WordToken("lambda"), args:List[WordToken], body:AST) =>
- // Function definition
Lambda(args, body, env)
case List(WordToken("define"), name:WordToken, expression:Any) =>
- // Constant definition
- env.extend(Map(name -> expression))
- case (procedure :: exprs) =>
+ env.define(name, eval(expression, env))
+ case WordToken("define") :: (args:List[WordToken]) :: (expression:AST) =>
+ env.define(args.head, Lambda(args.tail, expression, env))
+ case WordToken("begin") :: exps =>
+ evalSeq(exps, env)
+ case WordToken("if") :: cond :: exprs =>
+ if(eval(cond, env) == TrueToken())
+ eval(exprs.head, env)
+ else
+ eval(exprs.last, env)
+ case WordToken("cond") :: clauses =>
+ eval(expandCond(clauses), env)
+ case procedure :: exprs =>
// Procedure evaluation
- val fun:Lambda = eval(procedure, env)
+ val fun = eval(procedure, env).asInstanceOf[{def apply(args:List[Any], Interpreter:Interpreter):Any}]
val args = exprs.map (eval(_, env))
- fun.apply(args)
- case value:Token =>
+ fun.apply(args, this)
+ case value =>
env.lookup(value)
}
}
- def evalSeq(expressions:AST, env:Environment):Token = {
+ def evalSeq(expressions:List[Any], env:Environment=RootEnvironment):Any = {
expressions.map(eval(_, env)).last
}
+
+ def makeIf(clause:Any, then:Any, consequent:Any) = List(WordToken("if"), clause, then, consequent)
+
+ def seqToExpr(clauses:List[Any]) = WordToken("begin") :: clauses
+
+ def expandCond(clauses:List[Any]):Any = {
+ def condPredicate(clause:List[Any]) = clause.head
+ def condActions(clause:List[Any]) = clause.tail
+
+ clauses match {
+ case List(WordToken("else"), clauses) :: rest if rest.length > 0 => throw new Exception("else clause is not last -- cond")
+ case (WordToken("else") :: clauses) :: rest => seqToExpr(clauses)
+ case (head:List[Any]) :: tail => makeIf(condPredicate(head), seqToExpr(condActions(head)), expandCond(tail))
+ case List() => FalseToken()
+ }
+ }
+
}
View
@@ -4,7 +4,7 @@ import lisp.tokens._
import scala.util.matching.Regex
class Lexer extends AbstractLexer {
- class StringLexer(val caller:Lexer, var string:String="") extends AbstractLexer{
+ class StringLexer(val caller:Lexer) extends AbstractLexer{
override def handleLexFinish(text:String, tokens:List[Token]):(List[Token],String) = {
val charTokens = tokens.reverse
val stringToken = StringToken(charTokens.map { token =>
@@ -30,6 +30,26 @@ class Lexer extends AbstractLexer {
pattern(".".r) { c => CharToken(c.head) }
}
+ class SymbolLexer(val caller:Lexer) extends AbstractLexer{
+ override def handleLexFinish(text:String, tokens:List[Token]):(List[Token],String) = {
+ val charTokens = tokens.reverse
+ val token = SymbolToken(charTokens.map { token =>
+ token match {
+ case t: CharToken => t.char
+ case _ => null
+ }
+ }.filter(_.isInstanceOf[Char]).mkString)
+
+ (List(token), text)
+ }
+
+ // whitespace ends symbol
+ pattern("\\s".r) { _ => HaltingToken() }
+
+ // match any character
+ pattern(".".r) { c => CharToken(c.head) }
+ }
+
class CommentLexer(val caller:Lexer) extends AbstractLexer {
pattern("[^$]".r) { _ => new IgnoreToken }
pattern("$".r) { _ => caller }
@@ -38,12 +58,14 @@ class Lexer extends AbstractLexer {
pattern("\\(".r) ( _ => ParenToken('open) )
pattern("\\)".r) ( _ => ParenToken('close) )
pattern("\"".r) ( _ => new StringLexer(this) )
+ pattern("\\'".r) ( _ => new SymbolLexer(this) )
pattern(";".r) ( _ => new CommentLexer(this) )
pattern("\\#t".r) ( _ => TrueToken() )
pattern("\\#f".r) ( _ => FalseToken() )
+ pattern("null".r) ( _ => NullToken() )
pattern("[0-9]+\\.[0-9]+".r) ( num => DecimalToken(num.toDouble) )
- pattern("[0-9]+".r) ( num => IntToken(num.toInt) )
- pattern("[(\\+|\\-|\\*|\\/|\\*\\*|\\.|\\<|\\=|\\>|\\<\\=|\\>\\=)]".r) ( punct => PunctuationToken(punct))
+ pattern("[0-9]+".r) ( num => DecimalToken(num.toDouble) )
+ pattern("[(\\+|\\-|\\*|\\/|\\*\\*|\\.|\\<|\\=|\\>|\\<\\=|\\>\\=)]".r) ( punct => WordToken(punct))
pattern("[0-9A-Za-z!$%&*+-./:<=>?@^_~]+".r) ( word => WordToken(word))
pattern("\\s+".r) { _ => WhitespaceToken()}
}
@@ -8,24 +8,29 @@ object Parser {
type AST = List[Any]
}
+class MismatchedException extends Exception
+
class Parser {
import Parser.AST
def parse(tokens:List[Token]):AST = {
_parse(tokens, List())._1
}
- protected def _parse(tokens:List[Token], list:AST):(AST, List[Token]) = {
+ protected def _parse(tokens:List[Token], list:AST, level:Int=0):(AST, List[Token]) = {
tokens match {
case ParenToken('open) :: tail =>
- val (parsed, rest) = _parse(tail, List())
- _parse(rest, parsed :: list)
+ val (parsed, rest) = _parse(tail, List(), level + 1)
+ _parse(rest, parsed :: list, level)
case ParenToken('close) :: tail =>
(list.reverse, tail)
case token :: tail =>
_parse(tail, token :: list)
case List() =>
- (list, List())
+ level match {
+ case 0 => (list, List())
+ case _ => throw new MismatchedException
+ }
}
}
}
View
@@ -0,0 +1,34 @@
+import lisp.{Lexer, Parser, Interpreter}
+import lisp.MismatchedException
+
+object REPL {
+ val lexer = new Lexer
+ val parser = new Parser
+ val interpreter = new Interpreter
+
+ def main(args: Array[String]):Unit = {
+ if(args.length > 0)
+ epl(args.mkString(" "))
+ else
+ run()
+ }
+
+ def run(currentCommand:String=""):Unit = {
+ val prompt = if(currentCommand.length > 0) " | " else ">> "
+ val command = readLine(prompt)
+
+ try {
+ println(epl(command + currentCommand))
+ run()
+ } catch {
+ case e:MismatchedException => run(command + currentCommand)
+ }
+ }
+
+ protected def epl(command:String) = {
+ val tokens = lexer.lex(command)
+ val ast = parser.parse(tokens)
+
+ interpreter.evalSeq(ast)
+ }
+}
Oops, something went wrong.

0 comments on commit 3e16ddb

Please sign in to comment.