blob: 0f61ca4ed518075f8231c75002d9747530a87edb [file] [log] [blame]
// Copyright (c) 2015, the Fletch project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE.md file.
/// Tools for loading and parsing platform-configuration files.
library platform_configuration;
import "dart:async";
import "package:charcode/ascii.dart";
import "../compiler_new.dart" as api;
/// Parses an Ini-like format.
///
/// Sections are initialized with a name enclosed in brackets.
/// Each section contain zero or more properties of the form "name:value".
/// Empty lines are ignored.
/// Lines starting with # are ignored.
/// Duplicate names are not allowed.
/// All keys and values will be passed through [String.trim].
///
/// If an error is found, a [FormatException] is thrown, using [sourceUri] in
/// the error message.
///
/// Example
/// ```
/// [a]
/// b:c
///
/// [d]
/// e:file:///tmp/bla
/// ```
/// Will parse to {"a": {"b":"c"}, "d": {"e": "file:///tmp/bla"}}.
Map<String, Map<String, String>> parseIni(List<int> source,
{Set<String> allowedSections, Uri sourceUri}) {
int startOfLine = 0;
int currentLine = 0;
error(String message, int index) {
int column = index - startOfLine + 1;
throw new FormatException(
"$sourceUri:$currentLine:$column: $message", sourceUri, index);
}
Map<String, Map<String, String>> result =
new Map<String, Map<String, String>>();
Map<String, String> currentSection = null;
if (source.length == 0) return result;
bool endOfFile = false;
// Iterate once per $lf in file.
while (!endOfFile) {
currentLine += 1;
int endOfLine = source.indexOf($lf, startOfLine);
if (endOfLine == -1) {
// The dart2js provider adds a final 0 to the file.
endOfLine = source.last == 0 ? source.length - 1 : source.length;
endOfFile = true;
}
if (startOfLine != endOfLine) {
int firstChar = source[startOfLine];
if (firstChar == $hash) {
// Comment, do nothing.
} else if (firstChar == $open_bracket) {
// Section header
int endOfHeader = source.indexOf($close_bracket, startOfLine);
if (endOfHeader == -1) {
error("'[' must be matched by ']' on the same line.", startOfLine);
}
if (endOfHeader == startOfLine + 1) {
error("Empty header name", startOfLine + 1);
}
if (endOfHeader != endOfLine - 1) {
error("Section heading lines must end with ']'", endOfHeader + 1);
}
int startOfSectionName = startOfLine + 1;
String sectionName =
new String.fromCharCodes(source, startOfSectionName, endOfHeader)
.trim();
currentSection = new Map<String, String>();
if (result.containsKey(sectionName)) {
error("Duplicate section name '$sectionName'", startOfSectionName);
}
if (allowedSections != null && !allowedSections.contains(sectionName)) {
error("Unrecognized section name '$sectionName'", startOfSectionName);
}
result[sectionName] = currentSection;
} else {
// Property line
if (currentSection == null) {
error("Property outside section", startOfLine);
}
int separator = source.indexOf($colon, startOfLine);
if (separator == startOfLine) {
error("Empty property name", startOfLine);
}
if (separator == -1 || separator > endOfLine) {
error("Property line without ':'", startOfLine);
}
String propertyName =
new String.fromCharCodes(source, startOfLine, separator).trim();
if (currentSection.containsKey(propertyName)) {
error("Duplicate property name '$propertyName'", startOfLine);
}
String propertyValue =
new String.fromCharCodes(source, separator + 1, endOfLine).trim();
currentSection[propertyName] = propertyValue;
}
}
startOfLine = endOfLine + 1;
}
return result;
}
const String librariesSection = "libraries";
const String dartSpecSection = "dart-spec";
const String featuresSection = "features";
Map<String, Uri> libraryMappings(
Map<String, Map<String, String>> sections, Uri baseLocation) {
assert(sections.containsKey(librariesSection));
Map<String, Uri> result = new Map<String, Uri>();
sections[librariesSection].forEach((String name, String value) {
result[name] = baseLocation.resolve(value);
});
return result;
}
final Set<String> allowedSections =
new Set.from([librariesSection, dartSpecSection, featuresSection]);
Future<Map<String, Uri>> load(Uri location, api.CompilerInput provider) {
return provider
.readFromUri(location, inputKind: api.InputKind.binary)
.then((api.Input input) {
return libraryMappings(
parseIni(input.data,
allowedSections: allowedSections, sourceUri: location),
location);
});
}