<commit_before>/* sbt -- Simple Build Tool
 * Copyright 2009, 2010  <NAME>
 */
package xsbt.boot


import Pre._
import java.lang.Character.isWhitespace
import java.io.{BufferedReader, File, FileInputStream, InputStreamReader, Reader, StringReader}
import java.net.{MalformedURLException, URL}
import java.util.regex.Pattern

class ConfigurationParser extends NotNull
{
	def apply(file: File): LaunchConfiguration = Using(new InputStreamReader(new FileInputStream(file), "UTF-8"))(apply)
	def apply(s: String): LaunchConfiguration = Using(new StringReader(s))(apply)
	def apply(reader: Reader): LaunchConfiguration = Using(new BufferedReader(reader))(apply)
	private def apply(in: BufferedReader): LaunchConfiguration =
	{
		def readLine(accum: List[Line], index: Int): List[Line] =
		{
			val line = in.readLine()
			if(line eq null) accum.reverse else readLine(ParseLine(line, index) ::: accum, index+1)
		}
		processSections(processLines(readLine(Nil, 0)))
	}
	// section -> configuration instance  processing
	def processSections(sections: SectionMap): LaunchConfiguration =
	{
		val ((scalaVersion, scalaClassifiers), m1) = processSection(sections, "scala", getScala)
		val ((app, appClassifiers), m2) = processSection(m1, "app", getApplication)
		val (repositories, m3) = processSection(m2, "repositories", getRepositories)
		val (boot, m4) = processSection(m3, "boot", getBoot)
		val (logging, m5) = processSection(m4, "log", getLogging)
		val (properties, m6) = processSection(m5, "app-properties", getAppProperties)
		val (cacheDir, m7) = processSection(m6, "ivy", getIvy)
		check(m7, "section")
		val classifiers = Classifiers("" :: scalaClassifiers, "" :: appClassifiers)
		new LaunchConfiguration(scalaVersion, IvyOptions(cacheDir, classifiers, repositories), app, boot, logging, properties)
	}
	def getScala(m: LabelMap) =
	{
		val (scalaVersion, m1) = getVersion(m, "Scala version", "scala.version")
		val (scalaClassifiers, m2) = ids(m1, "classifiers", Nil)
		check(m2, "label")
		(scalaVersion, scalaClassifiers) // the added "" ensures that the main jars are retrieved
	}
	def getVersion(m: LabelMap, label: String, defaultName: String): (Version, LabelMap) = process(m, "version", processVersion(label, defaultName))
	def processVersion(label: String, defaultName: String)(value: Option[String]): Version =
		value.map(version(label)).getOrElse(new Version.Implicit(defaultName, None))
	def version(label: String)(value: String): Version =
	{
		if(isEmpty(value)) error(label + " cannot be empty (omit version declaration to use the default version)")
		try { parsePropertyValue(label, value)(Version.Implicit.apply) }
		catch { case e: BootException =>  new Version.Explicit(value) }
	}
	def processSection[T](sections: SectionMap, name: String, f: LabelMap => T) =
		process[String,LabelMap,T](sections, name, m => f(m default(x => None)))
	def process[K,V,T](sections: ListMap[K,V], name: K, f: V => T): (T, ListMap[K,V]) = ( f(sections(name)), sections - name)
	def check(map: ListMap[String, _], label: String): Unit = if(map.isEmpty) () else error(map.keys.mkString("Invalid " + label + "(s): ", ",",""))
	def check[T](label: String, pair: (T, ListMap[String, _])): T = { check(pair._2, label); pair._1 }
	def id(map: LabelMap, name: String, default: String): (String, LabelMap) =
		(orElse(getOrNone(map, name), default), map - name)
	def getOrNone[K,V](map: ListMap[K,Option[V]], k: K) = orElse(map.get(k), None)
	def ids(map: LabelMap, name: String, default: List[String]) =
	{
		val result = map(name).map(value => trim(value.split(",")).filter(isNonEmpty))
		(orElse(result, default), map - name)
	}
	def bool(map: LabelMap, name: String, default: Boolean): (Boolean, LabelMap) =
	{
		val (b, m) = id(map, name, default.toString)
		(toBoolean(b), m)
	}
	def toFiles(paths: List[String]): List[File] = paths.map(toFile)
	def toFile(path: String): File = new File(path.replace('/', File.separatorChar))// if the path is relative, it will be resolved by Launch later
	def file(map: LabelMap, name: String, default: File): (File, LabelMap) =
		(orElse(getOrNone(map, name).map(toFile), default), map - name)

	def getIvy(m: LabelMap): Option[File] =
	{
		val (cacheDir, m1) = file(m, "cache-directory", null) // fix this later
		check(m1, "label")
		if(cacheDir eq null) None else Some(cacheDir)
	}
	def getBoot(m: LabelMap): BootSetup =
	{
		val (dir, m1) = file(m, "directory", toFile("project/boot"))
		val (props, m2) = file(m1, "properties", toFile("project/build.properties"))
		val (search, m3) = getSearch(m2, props)
		val (enableQuick, m4) = bool(m3, "quick-option", false)
		val (promptFill, m5) = bool(m4, "prompt-fill", false)
		val (promptCreate, m6) = id(m5, "prompt-create", "")
		check(m6, "label")
		BootSetup(dir, props, search, promptCreate, enableQuick, promptFill)
	}
	def getLogging(m: LabelMap): Logging = check("label", process(m, "level", getLevel))
	def getLevel(m: Option[String]) = m.map(LogLevel.apply).getOrElse(new Logging(LogLevel.Info))
	def getSearch(m: LabelMap, defaultPath: File): (Search, LabelMap) =
		ids(m, "search", Nil) match
		{
			case (Nil, newM) => (Search.none, newM)
			case (tpe :: Nil, newM) => (Search(tpe, List(defaultPath)), newM)
			case (tpe :: paths, newM) => (Search(tpe, toFiles(paths)), newM)
		}

	def getApplication(m: LabelMap): (Application, List[String]) =
	{
		val (org, m1) = id(m, "org", "org.scala-tools.sbt")
		val (name, m2) = id(m1, "name", "sbt")
		val (rev, m3) = getVersion(m2, name + " version", name + ".version")
		val (main, m4) = id(m3, "class", "xsbt.Main")
		val (components, m5) = ids(m4, "components", List("default"))
		val (crossVersioned, m6) = id(m5, "cross-versioned", "true")
		val (resources, m7) = ids(m6, "resources", Nil)
		val (classifiers, m8) = ids(m7, "classifiers", Nil)
		check(m8, "label")
		val classpathExtra = toArray(toFiles(resources))
		val app = new Application(org, name, rev, main, components, toBoolean(crossVersioned), classpathExtra)
		(app, classifiers)
	}
	def getRepositories(m: LabelMap): List[Repository] =
	{
		import Repository.{Ivy, Maven, Predefined}
		m.toList.map {
			case (key, None) => Predefined(key)
			case (key, Some(value)) =>
				val r = trim(value.split(",",2))
				val url = try { new URL(r(0)) } catch { case e: MalformedURLException => error("Invalid URL specified for '" + key + "': " + e.getMessage) }
				if(r.length == 2) Ivy(key, url, r(1)) else Maven(key, url)
		}
	}
	def getAppProperties(m: LabelMap): List[AppProperty] =
		for((name, Some(value)) <- m.toList) yield
		{
			val map = ListMap( trim(value.split(",")).map(parsePropertyDefinition(name)) : _*)
			AppProperty(name)(map.get("quick"), map.get("new"), map.get("fill"))
		}
	def parsePropertyDefinition(name: String)(value: String) = value.split("=",2) match {
		case Array(mode,value) => (mode, parsePropertyValue(name, value)(defineProperty(name)))
		case x => error("Invalid property definition '" + x + "' for property '" + name + "'")
	}
	def defineProperty(name: String)(action: String, requiredArg: String, optionalArg: Option[String]) =
		action match
		{
			case "prompt" => new PromptProperty(requiredArg, optionalArg)
			case "set" => new SetProperty(requiredArg)
			case _ => error("Unknown action '" + action + "' for property '"  + name + "'")
		}
	private lazy val propertyPattern = Pattern.compile("""(.+)\((.*)\)(?:\[(.*)\])?""") // examples: prompt(Version)[1.0] or set(1.0)
	def parsePropertyValue[T](name: String, definition: String)(f: (String, String, Option[String]) => T): T =
	{
		val m = propertyPattern.matcher(definition)
		if(!m.matches()) error("Invalid property definition '" + definition + "' for property '" + name + "'")
		val optionalArg = m.group(3)
		f(m.group(1), m.group(2), if(optionalArg eq null) None else Some(optionalArg))
	}
	def trim(s: Array[String]) = s.map(_.trim).toList

	type LabelMap = ListMap[String, Option[String]]
	// section-name -> label -> value
	type SectionMap = ListMap[String, LabelMap]
	def processLines(lines: List[Line]): SectionMap =
	{
		type State = (SectionMap, Option[String])
		val s: State =
			( ( (ListMap.empty.default(x => ListMap.empty[String,Option[String]]), None): State) /: lines ) {
				case (x, Comment) => x
				case ( (map, _), s: Section ) => (map, Some(s.name))
				case ( (_, None), l: Labeled ) => error("Label " + l.label + " is not in a section")
				case ( (map, s @ Some(section)), l: Labeled ) =>
					val sMap = map(section)
					if( sMap.contains(l.label) ) error("Duplicate label '" + l.label + "' in section '" + section + "'")
					else ( map(section) = (sMap(l.label) = l.value), s )
			}
		s._1
	}
}

sealed trait Line extends NotNull
final class Labeled(val label: String, val value: Option[String]) extends Line
final class Section(val name: String) extends Line
object Comment extends Line

class ParseException(val content: String, val line: Int, val col: Int, val msg: String)
	extends BootException( "[" + (line+1) + ", " + (col+1) + "]" + msg + "\n" + content + "\n" + List.make(col," ").mkString + "^" )

object ParseLine
{
	def apply(content: String, line: Int) =
	{
		def error(col: Int, msg: String) = throw new ParseException(content, line, col, msg)
		def check(condition: Boolean)(col: Int, msg: String) = if(condition) () else error(col, msg)

		val trimmed = trimLeading(content)
		val offset = content.length - trimmed.length

		def section =
		{
			val closing = trimmed.indexOf(']', 1)
			check(closing > 0)(content.length, "Expected ']', found end of line")
			val extra = trimmed.substring(closing+1)
			val trimmedExtra = trimLeading(extra)
			check(isEmpty(trimmedExtra))(content.length - trimmedExtra.length, "Expected end of line, found '" + extra + "'")
			new Section(trimmed.substring(1,closing).trim)
		}
		def labeled =
		{
			trimmed.split(":",2) match {
				case Array(label, value) =>
					val trimmedValue = value.trim
					check(isNonEmpty(trimmedValue))(content.indexOf(':'), "Value for '" + label + "' was empty")
					 new Labeled(label, Some(trimmedValue))
				case x => new Labeled(x.mkString, None)
			}
		}
		
		if(isEmpty(trimmed)) Nil
		else
		{
			val processed =
				trimmed.charAt(0) match
				{
					case '#' => Comment
					case '[' => section
					case _ => labeled
				}
			processed :: Nil
		}
	}
}<commit_msg>update comment<commit_after>/* sbt -- Simple Build Tool
 * Copyright 2009, 2010  <NAME>
 */
package xsbt.boot


import Pre._
import java.lang.Character.isWhitespace
import java.io.{BufferedReader, File, FileInputStream, InputStreamReader, Reader, StringReader}
import java.net.{MalformedURLException, URL}
import java.util.regex.Pattern

class ConfigurationParser extends NotNull
{
	def apply(file: File): LaunchConfiguration = Using(new InputStreamReader(new FileInputStream(file), "UTF-8"))(apply)
	def apply(s: String): LaunchConfiguration = Using(new StringReader(s))(apply)
	def apply(reader: Reader): LaunchConfiguration = Using(new BufferedReader(reader))(apply)
	private def apply(in: BufferedReader): LaunchConfiguration =
	{
		def readLine(accum: List[Line], index: Int): List[Line] =
		{
			val line = in.readLine()
			if(line eq null) accum.reverse else readLine(ParseLine(line, index) ::: accum, index+1)
		}
		processSections(processLines(readLine(Nil, 0)))
	}
	// section -> configuration instance  processing
	def processSections(sections: SectionMap): LaunchConfiguration =
	{
		val ((scalaVersion, scalaClassifiers), m1) = processSection(sections, "scala", getScala)
		val ((app, appClassifiers), m2) = processSection(m1, "app", getApplication)
		val (repositories, m3) = processSection(m2, "repositories", getRepositories)
		val (boot, m4) = processSection(m3, "boot", getBoot)
		val (logging, m5) = processSection(m4, "log", getLogging)
		val (properties, m6) = processSection(m5, "app-properties", getAppProperties)
		val (cacheDir, m7) = processSection(m6, "ivy", getIvy)
		check(m7, "section")
		val classifiers = Classifiers("" :: scalaClassifiers, "" :: appClassifiers)  // the added "" ensures that the main jars are retrieved
		new LaunchConfiguration(scalaVersion, IvyOptions(cacheDir, classifiers, repositories), app, boot, logging, properties)
	}
	def getScala(m: LabelMap) =
	{
		val (scalaVersion, m1) = getVersion(m, "Scala version", "scala.version")
		val (scalaClassifiers, m2) = ids(m1, "classifiers", Nil)
		check(m2, "label")
		(scalaVersion, scalaClassifiers)
	}
	def getVersion(m: LabelMap, label: String, defaultName: String): (Version, LabelMap) = process(m, "version", processVersion(label, defaultName))
	def processVersion(label: String, defaultName: String)(value: Option[String]): Version =
		value.map(version(label)).getOrElse(new Version.Implicit(defaultName, None))
	def version(label: String)(value: String): Version =
	{
		if(isEmpty(value)) error(label + " cannot be empty (omit version declaration to use the default version)")
		try { parsePropertyValue(label, value)(Version.Implicit.apply) }
		catch { case e: BootException =>  new Version.Explicit(value) }
	}
	def processSection[T](sections: SectionMap, name: String, f: LabelMap => T) =
		process[String,LabelMap,T](sections, name, m => f(m default(x => None)))
	def process[K,V,T](sections: ListMap[K,V], name: K, f: V => T): (T, ListMap[K,V]) = ( f(sections(name)), sections - name)
	def check(map: ListMap[String, _], label: String): Unit = if(map.isEmpty) () else error(map.keys.mkString("Invalid " + label + "(s): ", ",",""))
	def check[T](label: String, pair: (T, ListMap[String, _])): T = { check(pair._2, label); pair._1 }
	def id(map: LabelMap, name: String, default: String): (String, LabelMap) =
		(orElse(getOrNone(map, name), default), map - name)
	def getOrNone[K,V](map: ListMap[K,Option[V]], k: K) = orElse(map.get(k), None)
	def ids(map: LabelMap, name: String, default: List[String]) =
	{
		val result = map(name).map(value => trim(value.split(",")).filter(isNonEmpty))
		(orElse(result, default), map - name)
	}
	def bool(map: LabelMap, name: String, default: Boolean): (Boolean, LabelMap) =
	{
		val (b, m) = id(map, name, default.toString)
		(toBoolean(b), m)
	}
	def toFiles(paths: List[String]): List[File] = paths.map(toFile)
	def toFile(path: String): File = new File(path.replace('/', File.separatorChar))// if the path is relative, it will be resolved by Launch later
	def file(map: LabelMap, name: String, default: File): (File, LabelMap) =
		(orElse(getOrNone(map, name).map(toFile), default), map - name)

	def getIvy(m: LabelMap): Option[File] =
	{
		val (cacheDir, m1) = file(m, "cache-directory", null) // fix this later
		check(m1, "label")
		if(cacheDir eq null) None else Some(cacheDir)
	}
	def getBoot(m: LabelMap): BootSetup =
	{
		val (dir, m1) = file(m, "directory", toFile("project/boot"))
		val (props, m2) = file(m1, "properties", toFile("project/build.properties"))
		val (search, m3) = getSearch(m2, props)
		val (enableQuick, m4) = bool(m3, "quick-option", false)
		val (promptFill, m5) = bool(m4, "prompt-fill", false)
		val (promptCreate, m6) = id(m5, "prompt-create", "")
		check(m6, "label")
		BootSetup(dir, props, search, promptCreate, enableQuick, promptFill)
	}
	def getLogging(m: LabelMap): Logging = check("label", process(m, "level", getLevel))
	def getLevel(m: Option[String]) = m.map(LogLevel.apply).getOrElse(new Logging(LogLevel.Info))
	def getSearch(m: LabelMap, defaultPath: File): (Search, LabelMap) =
		ids(m, "search", Nil) match
		{
			case (Nil, newM) => (Search.none, newM)
			case (tpe :: Nil, newM) => (Search(tpe, List(defaultPath)), newM)
			case (tpe :: paths, newM) => (Search(tpe, toFiles(paths)), newM)
		}

	def getApplication(m: LabelMap): (Application, List[String]) =
	{
		val (org, m1) = id(m, "org", "org.scala-tools.sbt")
		val (name, m2) = id(m1, "name", "sbt")
		val (rev, m3) = getVersion(m2, name + " version", name + ".version")
		val (main, m4) = id(m3, "class", "xsbt.Main")
		val (components, m5) = ids(m4, "components", List("default"))
		val (crossVersioned, m6) = id(m5, "cross-versioned", "true")
		val (resources, m7) = ids(m6, "resources", Nil)
		val (classifiers, m8) = ids(m7, "classifiers", Nil)
		check(m8, "label")
		val classpathExtra = toArray(toFiles(resources))
		val app = new Application(org, name, rev, main, components, toBoolean(crossVersioned), classpathExtra)
		(app, classifiers)
	}
	def getRepositories(m: LabelMap): List[Repository] =
	{
		import Repository.{Ivy, Maven, Predefined}
		m.toList.map {
			case (key, None) => Predefined(key)
			case (key, Some(value)) =>
				val r = trim(value.split(",",2))
				val url = try { new URL(r(0)) } catch { case e: MalformedURLException => error("Invalid URL specified for '" + key + "': " + e.getMessage) }
				if(r.length == 2) Ivy(key, url, r(1)) else Maven(key, url)
		}
	}
	def getAppProperties(m: LabelMap): List[AppProperty] =
		for((name, Some(value)) <- m.toList) yield
		{
			val map = ListMap( trim(value.split(",")).map(parsePropertyDefinition(name)) : _*)
			AppProperty(name)(map.get("quick"), map.get("new"), map.get("fill"))
		}
	def parsePropertyDefinition(name: String)(value: String) = value.split("=",2) match {
		case Array(mode,value) => (mode, parsePropertyValue(name, value)(defineProperty(name)))
		case x => error("Invalid property definition '" + x + "' for property '" + name + "'")
	}
	def defineProperty(name: String)(action: String, requiredArg: String, optionalArg: Option[String]) =
		action match
		{
			case "prompt" => new PromptProperty(requiredArg, optionalArg)
			case "set" => new SetProperty(requiredArg)
			case _ => error("Unknown action '" + action + "' for property '"  + name + "'")
		}
	private lazy val propertyPattern = Pattern.compile("""(.+)\((.*)\)(?:\[(.*)\])?""") // examples: prompt(Version)[1.0] or set(1.0)
	def parsePropertyValue[T](name: String, definition: String)(f: (String, String, Option[String]) => T): T =
	{
		val m = propertyPattern.matcher(definition)
		if(!m.matches()) error("Invalid property definition '" + definition + "' for property '" + name + "'")
		val optionalArg = m.group(3)
		f(m.group(1), m.group(2), if(optionalArg eq null) None else Some(optionalArg))
	}
	def trim(s: Array[String]) = s.map(_.trim).toList

	type LabelMap = ListMap[String, Option[String]]
	// section-name -> label -> value
	type SectionMap = ListMap[String, LabelMap]
	def processLines(lines: List[Line]): SectionMap =
	{
		type State = (SectionMap, Option[String])
		val s: State =
			( ( (ListMap.empty.default(x => ListMap.empty[String,Option[String]]), None): State) /: lines ) {
				case (x, Comment) => x
				case ( (map, _), s: Section ) => (map, Some(s.name))
				case ( (_, None), l: Labeled ) => error("Label " + l.label + " is not in a section")
				case ( (map, s @ Some(section)), l: Labeled ) =>
					val sMap = map(section)
					if( sMap.contains(l.label) ) error("Duplicate label '" + l.label + "' in section '" + section + "'")
					else ( map(section) = (sMap(l.label) = l.value), s )
			}
		s._1
	}
}

sealed trait Line extends NotNull
final class Labeled(val label: String, val value: Option[String]) extends Line
final class Section(val name: String) extends Line
object Comment extends Line

class ParseException(val content: String, val line: Int, val col: Int, val msg: String)
	extends BootException( "[" + (line+1) + ", " + (col+1) + "]" + msg + "\n" + content + "\n" + List.make(col," ").mkString + "^" )

object ParseLine
{
	def apply(content: String, line: Int) =
	{
		def error(col: Int, msg: String) = throw new ParseException(content, line, col, msg)
		def check(condition: Boolean)(col: Int, msg: String) = if(condition) () else error(col, msg)

		val trimmed = trimLeading(content)
		val offset = content.length - trimmed.length

		def section =
		{
			val closing = trimmed.indexOf(']', 1)
			check(closing > 0)(content.length, "Expected ']', found end of line")
			val extra = trimmed.substring(closing+1)
			val trimmedExtra = trimLeading(extra)
			check(isEmpty(trimmedExtra))(content.length - trimmedExtra.length, "Expected end of line, found '" + extra + "'")
			new Section(trimmed.substring(1,closing).trim)
		}
		def labeled =
		{
			trimmed.split(":",2) match {
				case Array(label, value) =>
					val trimmedValue = value.trim
					check(isNonEmpty(trimmedValue))(content.indexOf(':'), "Value for '" + label + "' was empty")
					 new Labeled(label, Some(trimmedValue))
				case x => new Labeled(x.mkString, None)
			}
		}
		
		if(isEmpty(trimmed)) Nil
		else
		{
			val processed =
				trimmed.charAt(0) match
				{
					case '#' => Comment
					case '[' => section
					case _ => labeled
				}
			processed :: Nil
		}
	}
}
{'fraction_non_alphanumeric': 0.16893335923839128, 'fraction_numerical': 0.007965805323489412, 'mean_word_length': 4.717578450430436, 'pattern_counts': {'":': 2, '<': 7, '<?xml version=': 0, '>': 69, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '6517914', 'n_tokens_mistral': 7555, 'n_tokens_neox': 6957, 'n_words': 1833}