OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, the Fletch project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE.md file. |
| 4 |
| 5 /// Tools for loading and parsing platform-configuration files. |
| 6 library plaform_configuration; |
| 7 |
| 8 import "dart:async"; |
| 9 import "package:charcode/ascii.dart"; |
| 10 import "../compiler_new.dart" as api; |
| 11 |
| 12 /// Parses an Ini-like format. |
| 13 /// |
| 14 /// Sections are initialized with a name enclosed in brackets. |
| 15 /// Each section contain zero or more properties of the form "name:value". |
| 16 /// Empty lines are ignored. |
| 17 /// Lines starting with # are ignored. |
| 18 /// Duplicate names are not allowed. |
| 19 /// |
| 20 /// If an error is found, a [FormatException] is thrown, using [sourceUri] in |
| 21 /// the error message. |
| 22 /// |
| 23 /// Example |
| 24 /// ``` |
| 25 /// [a] |
| 26 /// b:c |
| 27 /// |
| 28 /// [d] |
| 29 /// e:file:///tmp/bla |
| 30 /// ``` |
| 31 /// Will parse to {"a": {"b":"c"}, "d": {"e": "file:///tmp/bla"}}. |
| 32 |
| 33 Map<String, Map<String, String>> parseIni(List<int> source, |
| 34 {Set<String> allowedSections, Uri sourceUri}) { |
| 35 int startOfLine = 0; |
| 36 int currentLine = 0; |
| 37 |
| 38 error(String message, int index) { |
| 39 int column = index - startOfLine + 1; |
| 40 throw new FormatException( |
| 41 "$sourceUri:$currentLine:$column: $message", sourceUri, index); |
| 42 } |
| 43 |
| 44 Map<String, Map<String, String>> result = |
| 45 new Map<String, Map<String, String>>(); |
| 46 Map<String, String> currentSection = null; |
| 47 |
| 48 if (source.length == 0) return result; |
| 49 bool endOfFile = false; |
| 50 |
| 51 // Iterate once per $lf in file. |
| 52 while (!endOfFile) { |
| 53 currentLine += 1; |
| 54 int endOfLine = source.indexOf($lf, startOfLine); |
| 55 if (endOfLine == -1) { |
| 56 // The dart2js provider adds a final 0 to the file. |
| 57 endOfLine = source.last == 0 ? source.length - 1 : source.length; |
| 58 endOfFile = true; |
| 59 } |
| 60 if (startOfLine != endOfLine) { |
| 61 int firstChar = source[startOfLine]; |
| 62 if (firstChar == $hash) { |
| 63 // Comment, do nothing. |
| 64 } else if (firstChar == $open_bracket) { |
| 65 // Section header |
| 66 int endOfHeader = source.indexOf($close_bracket, startOfLine); |
| 67 if (endOfHeader == -1) { |
| 68 error("'[' must be matched by ']' on the same line.", startOfLine); |
| 69 } |
| 70 if (endOfHeader == startOfLine + 1) { |
| 71 error("Empty header name", startOfLine + 1); |
| 72 } |
| 73 if (endOfHeader != endOfLine - 1) { |
| 74 error("Section heading lines must end with ']'", endOfHeader + 1); |
| 75 } |
| 76 int startOfSectionName = startOfLine + 1; |
| 77 String sectionName = |
| 78 new String.fromCharCodes(source, startOfSectionName, endOfHeader); |
| 79 currentSection = new Map<String, String>(); |
| 80 if (result.containsKey(sectionName)) { |
| 81 error("Duplicate section name '$sectionName'", startOfSectionName); |
| 82 } |
| 83 if (allowedSections != null && !allowedSections.contains(sectionName)) { |
| 84 error("Unrecognized section name '$sectionName'", startOfSectionName); |
| 85 } |
| 86 result[sectionName] = currentSection; |
| 87 } else { |
| 88 // Property line |
| 89 if (currentSection == null) { |
| 90 error("Property outside section", startOfLine); |
| 91 } |
| 92 int separator = source.indexOf($colon, startOfLine); |
| 93 if (separator == startOfLine) { |
| 94 error("Empty property name", startOfLine); |
| 95 } |
| 96 if (separator == -1 || separator > endOfLine) { |
| 97 error("Property line without ':'", startOfLine); |
| 98 } |
| 99 String propertyName = |
| 100 new String.fromCharCodes(source, startOfLine, separator); |
| 101 if (currentSection.containsKey(propertyName)) { |
| 102 error("Duplicate property name '$propertyName'", startOfLine); |
| 103 } |
| 104 String propertyValue = |
| 105 new String.fromCharCodes(source, separator + 1, endOfLine); |
| 106 currentSection[propertyName] = propertyValue; |
| 107 } |
| 108 } |
| 109 startOfLine = endOfLine + 1; |
| 110 } |
| 111 return result; |
| 112 } |
| 113 |
| 114 const String librariesSection = "libraries"; |
| 115 const String dartSpecSection = "dart-spec"; |
| 116 const String featuresSection = "features"; |
| 117 |
| 118 Map<String, Uri> libraryMappings( |
| 119 Map<String, Map<String, String>> sections, Uri baseLocation) { |
| 120 assert(sections.containsKey(librariesSection)); |
| 121 Map<String, Uri> result = new Map<String, Uri>(); |
| 122 sections[librariesSection].forEach((String name, String value) { |
| 123 result[name] = baseLocation.resolve(value); |
| 124 }); |
| 125 return result; |
| 126 } |
| 127 |
| 128 final Set<String> allowedSections = |
| 129 new Set.from([librariesSection, dartSpecSection, featuresSection]); |
| 130 |
| 131 Future<Map<String, Uri>> load(Uri location, api.CompilerInput provider) { |
| 132 return provider.readFromUri(location).then((contents) { |
| 133 if (contents is String) { |
| 134 contents = contents.codeUnits; |
| 135 } |
| 136 return libraryMappings( |
| 137 parseIni(contents, |
| 138 allowedSections: allowedSections, sourceUri: location), |
| 139 location); |
| 140 }); |
| 141 } |
OLD | NEW |