<commit_before>/*
 * This file is part of Apparat.
 *
 * Apparat is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Apparat is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Apparat. If not, see <http://www.gnu.org/licenses/>.
 *
 * Copyright (C) 2009 <NAME>
 * http://www.joa-ebert.com/
 *
 */
package apparat.bytecode

import apparat.abc.{Abc, AbcMethod, AbcMethodBody}
import apparat.utils.{Dumpable, IndentingPrintWriter}
import combinator.{Failure, Success, BytecodeChain}
import operations.AbstractOp
import annotation.tailrec
import operations.Nop
import apparat.graph.immutable.BytecodeControlFlowGraphBuilder

object Bytecode {
	def fromMethod(method: AbcMethod)(implicit abc: Abc) = method.body match {
		case Some(body) => fromBody(body)
		case None => None
	}

	def fromBody(body: AbcMethodBody)(implicit abc: Abc) = BytecodeDecoder(body.code, body.exceptions, body)

	def bytecode(body: => List[AbstractOp]) = new Bytecode(body, new MarkerManager(), new Array(0), None)
}

class Bytecode(var ops: List[AbstractOp], val markers: MarkerManager, var exceptions: Array[BytecodeExceptionHandler], var body: Option[AbcMethodBody]) extends Dumpable {
	override def dump(writer: IndentingPrintWriter) = BytecodeDump.`type` match {
		case BytecodeDumpTypeDefault => new BytecodeDump(ops, markers, exceptions, body) dump writer
		case BytecodeDumpTypeCFG => BytecodeControlFlowGraphBuilder(this).dotExport to writer
	}

	def storeIn(body: AbcMethodBody)(implicit abc: Abc) = {
		val (code, exceptions) = BytecodeEncoder(this)

		body.code = code
		body.exceptions = exceptions
	}

	def filterNot(f: AbstractOp => Boolean) = {
		/*@tailrec*/ def loop(list: List[AbstractOp]): List[AbstractOp] = list match {//FIXME make tailrec
			case x :: Nil => f(x) match {
				case true => {
					val nop = Nop()
					markers.forwardMarker(x, nop)
					nop :: Nil
				}
				case false => x :: Nil
			}
			case x :: xs => f(x) match {
				case true => {
					markers.forwardMarker(x, xs.head)
					loop(xs)
				}
				case false => x :: loop(xs)
			}
			case Nil => Nil
		}

		ops = loop(ops)
	}

	def removeAny(op: AbstractOp) = filterNot(_ ~== op)

	def removeAll(f: PartialFunction[AbstractOp, Boolean]) = filterNot(f)

	def remove(op: AbstractOp) = filterNot(_ == op)
	
	def contains[A](chain: BytecodeChain[A]) = indexOf(chain) != -1

	def indexOf[A](chain: BytecodeChain[A]) = {
		@tailrec def loop(list: List[AbstractOp], index: Int): Int = {
			if(list.isEmpty) -1
			else {
				chain(list) match {
					case Success(_, remaining) => index
					case Failure(_) => loop(list drop 1, index + 1)
				}
			}
		}

		loop(ops, 0)
	}

	def rewrite[A <: AbstractOp](rule: BytecodeChain[List[A]]) = replace(rule) { a => a }

	def replace[A](chain: BytecodeChain[A])(body: A => List[AbstractOp]) = replaceFrom(0, chain)(body)

	def replaceFrom[A](fromIndex: Int, chain: BytecodeChain[A])(body: A => List[AbstractOp]) = {<commit_msg>- fix tailrec for Bytecode.filterNot<commit_after>/*
 * This file is part of Apparat.
 *
 * Apparat is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Apparat is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Apparat. If not, see <http://www.gnu.org/licenses/>.
 *
 * Copyright (C) 2009 <NAME>
 * http://www.joa-ebert.com/
 *
 */
package apparat.bytecode

import apparat.abc.{Abc, AbcMethod, AbcMethodBody}
import apparat.utils.{Dumpable, IndentingPrintWriter}
import combinator.{Failure, Success, BytecodeChain}
import operations.AbstractOp
import annotation.tailrec
import operations.Nop
import apparat.graph.immutable.BytecodeControlFlowGraphBuilder

object Bytecode {
	def fromMethod(method: AbcMethod)(implicit abc: Abc) = method.body match {
		case Some(body) => fromBody(body)
		case None => None
	}

	def fromBody(body: AbcMethodBody)(implicit abc: Abc) = BytecodeDecoder(body.code, body.exceptions, body)

	def bytecode(body: => List[AbstractOp]) = new Bytecode(body, new MarkerManager(), new Array(0), None)
}

class Bytecode(var ops: List[AbstractOp], val markers: MarkerManager, var exceptions: Array[BytecodeExceptionHandler], var body: Option[AbcMethodBody]) extends Dumpable {
	override def dump(writer: IndentingPrintWriter) = BytecodeDump.`type` match {
		case BytecodeDumpTypeDefault => new BytecodeDump(ops, markers, exceptions, body) dump writer
		case BytecodeDumpTypeCFG => BytecodeControlFlowGraphBuilder(this).dotExport to writer
	}

	def storeIn(body: AbcMethodBody)(implicit abc: Abc) = {
		val (code, exceptions) = BytecodeEncoder(this)

		body.code = code
		body.exceptions = exceptions
	}

	def filterNot(f: AbstractOp => Boolean) = {
		@tailrec def loop(list: List[AbstractOp], acc:List[AbstractOp]=Nil): List[AbstractOp] = list match {
			case x :: Nil => f(x) match {
				case true => {
					val nop = Nop()
					markers.forwardMarker(x, nop)
					nop :: acc
				}
				case false => x :: acc
			}
			case x :: xs => f(x) match {
				case true => {
					markers.forwardMarker(x, xs.head)
					loop(xs, acc)
				}
				case false => loop(xs, x :: acc)
			}
			case _ => acc
		}
		ops = loop(ops) reverse
	}

	def removeAny(op: AbstractOp) = filterNot(_ ~== op)

	def removeAll(f: PartialFunction[AbstractOp, Boolean]) = filterNot(f)

	def remove(op: AbstractOp) = filterNot(_ == op)
	
	def contains[A](chain: BytecodeChain[A]) = indexOf(chain) != -1

	def indexOf[A](chain: BytecodeChain[A]) = {
		@tailrec def loop(list: List[AbstractOp], index: Int): Int = {
			if(list.isEmpty) -1
			else {
				chain(list) match {
					case Success(_, remaining) => index
					case Failure(_) => loop(list drop 1, index + 1)
				}
			}
		}

		loop(ops, 0)
	}

	def rewrite[A <: AbstractOp](rule: BytecodeChain[List[A]]) = replace(rule) { a => a }

	def replace[A](chain: BytecodeChain[A])(body: A => List[AbstractOp]) = replaceFrom(0, chain)(body)

	def replaceFrom[A](fromIndex: Int, chain: BytecodeChain[A])(body: A => List[AbstractOp]) = {
{'fraction_non_alphanumeric': 0.1213407494145199, 'fraction_numerical': 0.00351288056206089, 'mean_word_length': 4.192249240121581, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6645038', 'n_tokens_mistral': 2400, 'n_tokens_neox': 2160, 'n_words': 670}