This GitLab instance reached the end of its service life. It won't be possible to create new users or projects.

Please read the deprecation notice for more information concerning the deprecation timeline

Visit migration.git.tu-berlin.de (internal network only) to import your old projects to the new GitLab platform 📥

Source.scala 4.83 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
package de.bbisping.coupledsim.tool.control

import scala.scalajs.js.Date
import scala.util.Try
import de.bbisping.coupledsim.tool.arch.Action
import de.bbisping.coupledsim.tool.arch.Control
import de.bbisping.coupledsim.ts.Syntax
import de.bbisping.coupledsim.ts
import de.bbisping.coupledsim.tool.control.Structure.NodeLabel
import de.bbisping.coupledsim.util.Parsing

class Source(val main: Control) extends ModelComponent {
  
  private var source: String = ""
  private var ast: Syntax.Definition = null
  private var problems: List[Source.Problem] = List()
  
  private val samples = Samples.namedSamples
  
  def init() {
    broadcast(Source.ExamplesChange(samples))
  }
  
  def changeCode(code: String) = {
    source = code
    problems = List()
    
    val beginParse = Date.now
    val parser = new ts.Parser(code)
    
    parser.parse match {
      case parser.ParseSuccess(esDef, _) =>
        println("Parsing took: " + (Date.now - beginParse) + "ms.")
        broadcast(Source.ProblemChange(source, List()))
        setAst(esDef, updateSource = false)
        
      case fail @ parser.ParseFail(msg, rest) =>
        val idx = fail.position
        markProblems(List(Source.Problem(msg, idx.line + 1, idx.col + 1)))
    }
  }
    
  def markProblems(errs: List[Source.Problem]) = {
    problems ++= errs
    broadcast(Source.ProblemChange(source, problems))
  }
  
  private def checkNodeName(name: String) = {
    name != "" &&
    name.forall(c => c.isLetterOrDigit || c == '_')
  }
  
  def updateEventDeclarationAttributes(updates: List[(String, NodeLabel)]): Boolean = {
    val names = updates.map(_._1)
    if (names.forall(checkNodeName(_))) {
      val affectedAst = ast
      val oldDecls = affectedAst.defs collect { case n: Syntax.NodeDeclaration => n }
      
      val newDecls = updates.map { case (nodeName, annotations) =>
        val oldDecl = oldDecls.find(d => d.name == nodeName)
        val pos = oldDecl.map(_.pos).getOrElse(Parsing.Pos0)
        val attribs = oldDecl.map(_.attribs).getOrElse(List()).toMap ++ annotations.toStringPairList
        (oldDecl, Syntax.NodeDeclaration(nodeName, attribs.toList, pos))
      }
      
      // group by oldDecls and project them away
      val newVsOldDecls = newDecls.groupBy(_._1).mapValues(_.map(_._2))
      
      val defsUpdatedOld = {
        affectedAst.defs.map {
          case d: Syntax.NodeDeclaration =>
            newVsOldDecls.get(Some(d)) match {
              case Some(dn :: _) =>
                dn
              case None =>
                d
              case _ =>
                ??? // from the context we know that empty groups are not possible
            }
          case o => o
        }
      }
      
      // enqueue updates that dont belong to an old declaration
      val (astBefore, astAfter) = defsUpdatedOld.splitAt(1 + defsUpdatedOld.lastIndexWhere(_.isInstanceOf[Syntax.NodeDeclaration]))
      val newDefs = astBefore ::: newVsOldDecls.getOrElse(None, List()) ::: astAfter 
      
      val newAst = Syntax.Definition(
          Syntax.fillInPos(newDefs))
      setAst(newAst)
      
      true
    } else {
      false
    }
  }
  
  def setAst(newAst: Syntax.Definition, updateSource: Boolean = true) {
    ast = newAst
    if (updateSource) {
      source = new ts.PrettyPrinter().showDefinition(newAst).toString
    }
    broadcast(Source.SourceChange(source, ast))
  }
  
  override def notify(c: ModelComponent.Change) = c match {
    case Structure.StructureChangeFailed(p) =>
      println("structure problems " + p)
      val problems = p.expr.map { e => Source.Problem(p.msg, e.position.line + 1, e.position.col) } 
      markProblems(problems)
    case _ => 
  }
  
}

object Source {
  case class Problem(msg: String, line: Int, col: Int)
  
  abstract sealed class SourceAction extends Action {
    override def implement(target: ModelComponent): Boolean = target match {
      case s: Source => 
        implementSource(s)
      case _ =>
        false
    }
    
    def implementSource(source: Source): Boolean
  }
  
  case class LoadDefinition(code: String) extends SourceAction {
    override def implementSource(source: Source) = {
      source.changeCode(code)
      true
    }
  }
  
  case class UpdateEventDeclarationAttributes(updates: List[(String, NodeLabel)]) extends SourceAction {
    override def implementSource(source: Source) = {
      source.updateEventDeclarationAttributes(updates)
    }
  }
  
  case class SourceChange(source: String, ast: Syntax.Definition) extends ModelComponent.Change {  
    override def toString() = "SourceChange( ... )" 
  }
  
  case class ProblemChange(source: String, errs: List[Problem]) extends ModelComponent.Change  {  
    override def toString() = "ProblemChange( ..., " + errs.toString() + " )" 
  }
  
  case class ExamplesChange(samples: List[Samples.Example]) extends ModelComponent.Change {
    override def toString = "ExamplesChange"
  }
}