Skip to content

Instantly share code, notes, and snippets.

@zuk
Last active December 16, 2015 14:20
Show Gist options
  • Select an option

  • Save zuk/5448193 to your computer and use it in GitHub Desktop.

Select an option

Save zuk/5448193 to your computer and use it in GitHub Desktop.
import scala.io.Source
import scala.util.matching.Regex
import scala.collection._
object Spreadsheet extends App {
val RefFormat = new Regex("([a-z]+)([1-9]+)", "row", "col")
val LineFormat = new Regex("^([a-z]+)([1-9]+):(.*)", "row", "col", "expr")
val ExprInt = new Regex("(-?\\d+)", "v")
val ExprFloat = new Regex("(-?\\d+\\.\\d+)", "v")
if (args.length != 1) { println("You must specify an input filename!") }
val filename = args(0)
// the raw spreadsheet, unevaluated
var ss = mutable.Map[String, mutable.Map[Int, immutable.List[String]]]()
// the evaluated spreadsheet
var ssEval = mutable.Map[String, mutable.Map[Int, EvalResult]]()
// load and parse the input file
parseFile
//println(ss.mkString("\n"))
evalSpreadsheet
printEvalSpreadsheet
def parseFile {
// NOTE: we're assuming UTF-8 encoding for the text file
val lines = Source.fromFile(filename, "utf-8").getLines.
filterNot { line => line.matches("^\\s+$") }. // get rid of blank lines
map { line => line.replaceAll("\\r\\n", "") } // strip newline/cr chars
//println(lines.mkString("\n"))
lines.foreach { line =>
// NOTE: we ignore the dimension line since it's not needed
line match {
case LineFormat(x, y, expr) => {
val row = if (ss contains x)
ss(x)
else
mutable.Map[Int, immutable.List[String]]()
row(y.toInt) = expr.split("\\s+").toList
ss(x) = row
}
case _ => // ignore
}
}
}
def evalSpreadsheet {
ss.foreach { row =>
val x = row._1
if (!(ssEval contains x))
ssEval(x) = mutable.Map[Int, EvalResult]()
row._2.foreach { col =>
val y = col._1
try {
ssEval(x)(y) = evalRef(x, y)
} catch {
case e: Exception => ssEval(x)(y) = new EvalResult(e)
}
}
}
}
def evalRef(x: String, y: Int): EvalResult = {
if (!(ssEval contains x))
ssEval(x) = mutable.Map[Int, EvalResult]()
if (ssEval(x) contains y) {
ssEval(x)(y)
} else {
val res = new EvalResult(evalExpression(ss(x)(y)))
ssEval(x)(y) = res
res
}
}
def evalExpression(expr: immutable.List[String], stack: mutable.Stack[Float]): Float = {
if (expr.length == 0) {
stack.pop()
} else {
stack.push(expr.head match {
case ExprInt(v) => v.toInt
case ExprFloat(v) => v.toFloat
case "+" => applyOp(stack, (a, b) => a + b)
case "-" => applyOp(stack, (a, b) => a - b)
case "*" => applyOp(stack, (a, b) => a * b)
case "/" => applyOp(stack, (a, b) => a / b)
case RefFormat(x, y) => evalRef(x, y.toInt).result
})
evalExpression(expr.tail, stack)
}
}
def evalExpression(expr: immutable.List[String]): Float = {
evalExpression(expr, new mutable.Stack[Float]())
}
def applyOp(stack: mutable.Stack[Float], op: (Float, Float) => Float): Float = {
val op2 = stack.pop()
val op1 = stack.pop()
op(op1, op2)
}
def printEvalSpreadsheet {
ssEval.foreach { row =>
val x = row._1
row._2.foreach { col =>
val y = col._1
println(x.toString + y.toString + ":" + col._2.toString)
}
}
}
class EvalResult(res: Float, err: Exception) {
def result: Float = res
def this(res: Float) = this(res, null)
def this(err: Exception) = this(Float.NaN, err)
override def toString: String = {
if (err == null)
res.toString
else
err.toString
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment