PageRenderTime 41ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/src/main/scala/com/yuroyoro/esoteric/brainfuck/BrainFuck.scala

http://github.com/yuroyoro/esoteric-languages-on-scala
Scala | 146 lines | 121 code | 22 blank | 3 comment | 4 complexity | 8d093e49cb33530ddf3c778a56d80c19 MD5 | raw file
  1. package com.yuroyoro.esoteric.brainfuck
  2. import com.yuroyoro.esoteric.parser._
  3. /**
  4. * Brainf*ck派生言語のジェネレータ
  5. */
  6. object BrainFuck {
  7. import scala.util.parsing.input.{Position, NoPosition}
  8. sealed abstract class Op {
  9. def exec( runtime:Runtime )
  10. val token:String
  11. val pos:Position
  12. override def toString = token
  13. }
  14. case class Inc( token:String, pos:Position ) extends Op {
  15. def exec( runtime:Runtime ) = runtime.inc
  16. }
  17. case class Dec( token:String, pos:Position ) extends Op{
  18. def exec( runtime:Runtime ) = runtime.dec
  19. }
  20. case class Next( token:String, pos:Position ) extends Op{
  21. def exec( runtime:Runtime ) = runtime.next
  22. }
  23. case class Prev( token:String, pos:Position ) extends Op{
  24. def exec( runtime:Runtime ) = runtime.prev
  25. }
  26. case class Get( token:String, pos:Position ) extends Op{
  27. def exec( runtime:Runtime ) = runtime.get
  28. }
  29. case class Put( token:String, pos:Position ) extends Op{
  30. def exec( runtime:Runtime ) = runtime.put
  31. }
  32. case class NoOp() extends Op{
  33. val token = ""
  34. val pos:Position = NoPosition
  35. def exec( runtime:Runtime ) = {}
  36. }
  37. case class Loop( op:List[Op], val pos:Position ) extends Op{
  38. val token = ""
  39. override def toString = "[" + op.mkString + "]"
  40. def exec( runtime:Runtime ) = {
  41. var cnt = 0
  42. while( runtime.m != 0 ){
  43. if( cnt > runtime.maxLoop )
  44. throw BFException("ループの実行回数が最大値を超えました。最大値:{%s}".format( runtime.maxLoop ))
  45. op.foreach( _.exec( runtime ) )
  46. cnt = cnt + 1
  47. }
  48. }
  49. }
  50. case class BFException(msg:String) extends java.lang.RuntimeException(msg)
  51. class Runtime( val op:List[Op], val size:Int, val maxLoop:Int ){
  52. val offset = size / 2
  53. var mem = new Array[Int]( size )
  54. var pt = 0
  55. def p = pt + offset
  56. def inRange_? = if( pt < 0 || pt > size) throw BFException(
  57. "ポインタの位置がメモリの範囲を超えました。ポインタは-%s - %sの範囲にある必要があります。ポインタ位置:{%s}".format(offset, offset, p ))
  58. def m = {
  59. inRange_?
  60. mem(p)
  61. }
  62. def m( b:Int ) = {
  63. inRange_?
  64. mem(p) = b
  65. }
  66. def inc = { m( m + 1 ) }
  67. def dec = { m( m - 1 ) }
  68. def next = { pt = pt + 1 }
  69. def prev = { pt = pt - 1 }
  70. def get = m( readChar.toInt )
  71. def put = print( m.toChar )
  72. def reset = {
  73. pt = 0
  74. mem = new Array[Int]( size )
  75. }
  76. def run = op.foreach{ e =>
  77. try{ e.exec( this ) }
  78. catch{ case BFException( msg ) => throw BFException("""line:%s column %s : %s""".format( e.pos.line, e.pos.column, msg))
  79. }
  80. }
  81. }
  82. class Parser(
  83. incTokens:List[String],
  84. decTokens:List[String],
  85. nextTokens:List[String],
  86. prevTokens:List[String],
  87. putTokens:List[String],
  88. getTokens:List[String],
  89. startTokens:List[String],
  90. endTokens:List[String],
  91. size:Int, maxLoop:Int
  92. )extends EsotericParser[Op, Runtime] {
  93. def inc :Parser[Op] = (tks(incTokens)) ^^ { case ~(p,x) => Inc( x, p ) }
  94. def dec :Parser[Op] = (tks(decTokens)) ^^ { case ~(p,x) => Dec( x, p ) }
  95. def next:Parser[Op] = (tks(nextTokens)) ^^ { case ~(p,x) => Next( x, p ) }
  96. def prev:Parser[Op] = (tks(prevTokens)) ^^ { case ~(p,x) => Prev( x, p ) }
  97. def put :Parser[Op] = (tks(putTokens)) ^^ { case ~(p,x) => Put( x, p ) }
  98. def get :Parser[Op] = (tks(getTokens)) ^^ { case ~(p,x) => Get( x, p ) }
  99. def start :Parser[Op] = (tks(startTokens)) ^^ ( x => NoOp() )
  100. def end :Parser[Op] = (tks(endTokens)) ^^ ( x => NoOp() )
  101. def token :Parser[Op] = inc ||| dec ||| next |||
  102. prev ||| get ||| put
  103. def loop :Parser[Op] = wrap( start ~> rep(instruction) <~ end ) ^^ {
  104. case ~(p,op) => new Loop( op ,p )
  105. }
  106. def commentOp:Parser[Op] = not( token | start | end ) <~ any ^^ ( x => NoOp() )
  107. def instruction :Parser[Op] = loop | token | commentOp
  108. def brainfuck :Parser[Runtime] = rep(instruction) ^^ {op => new Runtime(op, size, maxLoop) }
  109. def prog = brainfuck
  110. def run(s:String) = parse( s ) foreach{ _.run }
  111. def genHelloWorld(sep:String) = {
  112. "+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.>+.".toList.map {
  113. case '+' => incTokens.head
  114. case '-' => decTokens.head
  115. case '>' => nextTokens.head
  116. case '<' => prevTokens.head
  117. case '.' => putTokens.head
  118. case ',' => getTokens.head
  119. case '[' => startTokens.head
  120. case ']' => endTokens.head
  121. }.mkString( sep )
  122. }
  123. }
  124. }