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 /// All keys and values will be passed through [String.trim]. |
| 20 /// |
| 21 /// If an error is found, a [FormatException] is thrown, using [sourceUri] in |
| 22 /// the error message. |
| 23 /// |
| 24 /// Example |
| 25 /// ``` |
| 26 /// [a] |
| 27 /// b:c |
| 28 /// |
| 29 /// [d] |
| 30 /// e:file:///tmp/bla |
| 31 /// ``` |
| 32 /// Will parse to {"a": {"b":"c"}, "d": {"e": "file:///tmp/bla"}}. |
| 33 |
| 34 Map<String, Map<String, String>> parseIni(List<int> source, |
| 35 {Set<String> allowedSections, Uri sourceUri}) { |
| 36 int startOfLine = 0; |
| 37 int currentLine = 0; |
| 38 |
| 39 error(String message, int index) { |
| 40 int column = index - startOfLine + 1; |
| 41 throw new FormatException( |
| 42 "$sourceUri:$currentLine:$column: $message", sourceUri, index); |
| 43 } |
| 44 |
| 45 Map<String, Map<String, String>> result = |
| 46 new Map<String, Map<String, String>>(); |
| 47 Map<String, String> currentSection = null; |
| 48 |
| 49 if (source.length == 0) return result; |
| 50 bool endOfFile = false; |
| 51 |
| 52 // Iterate once per $lf in file. |
| 53 while (!endOfFile) { |
| 54 currentLine += 1; |
| 55 int endOfLine = source.indexOf($lf, startOfLine); |
| 56 if (endOfLine == -1) { |
| 57 // The dart2js provider adds a final 0 to the file. |
| 58 endOfLine = source.last == 0 ? source.length - 1 : source.length; |
| 59 endOfFile = true; |
| 60 } |
| 61 if (startOfLine != endOfLine) { |
| 62 int firstChar = source[startOfLine]; |
| 63 if (firstChar == $hash) { |
| 64 // Comment, do nothing. |
| 65 } else if (firstChar == $open_bracket) { |
| 66 // Section header |
| 67 int endOfHeader = source.indexOf($close_bracket, startOfLine); |
| 68 if (endOfHeader == -1) { |
| 69 error("'[' must be matched by ']' on the same line.", startOfLine); |
| 70 } |
| 71 if (endOfHeader == startOfLine + 1) { |
| 72 error("Empty header name", startOfLine + 1); |
| 73 } |
| 74 if (endOfHeader != endOfLine - 1) { |
| 75 error("Section heading lines must end with ']'", endOfHeader + 1); |
| 76 } |
| 77 int startOfSectionName = startOfLine + 1; |
| 78 String sectionName = new String.fromCharCodes( |
| 79 source, startOfSectionName, endOfHeader).trim(); |
| 80 currentSection = new Map<String, String>(); |
| 81 if (result.containsKey(sectionName)) { |
| 82 error("Duplicate section name '$sectionName'", startOfSectionName); |
| 83 } |
| 84 if (allowedSections != null && !allowedSections.contains(sectionName)) { |
| 85 error("Unrecognized section name '$sectionName'", startOfSectionName); |
| 86 } |
| 87 result[sectionName] = currentSection; |
| 88 } else { |
| 89 // Property line |
| 90 if (currentSection == null) { |
| 91 error("Property outside section", startOfLine); |
| 92 } |
| 93 int separator = source.indexOf($colon, startOfLine); |
| 94 if (separator == startOfLine) { |
| 95 error("Empty property name", startOfLine); |
| 96 } |
| 97 if (separator == -1 || separator > endOfLine) { |
| 98 error("Property line without ':'", startOfLine); |
| 99 } |
| 100 String propertyName = |
| 101 new String.fromCharCodes(source, startOfLine, separator).trim(); |
| 102 if (currentSection.containsKey(propertyName)) { |
| 103 error("Duplicate property name '$propertyName'", startOfLine); |
| 104 } |
| 105 String propertyValue = |
| 106 new String.fromCharCodes(source, separator + 1, endOfLine).trim(); |
| 107 currentSection[propertyName] = propertyValue; |
| 108 } |
| 109 } |
| 110 startOfLine = endOfLine + 1; |
| 111 } |
| 112 return result; |
| 113 } |
| 114 |
| 115 const String librariesSection = "libraries"; |
| 116 const String dartSpecSection = "dart-spec"; |
| 117 const String featuresSection = "features"; |
| 118 |
| 119 Map<String, Uri> libraryMappings( |
| 120 Map<String, Map<String, String>> sections, Uri baseLocation) { |
| 121 assert(sections.containsKey(librariesSection)); |
| 122 Map<String, Uri> result = new Map<String, Uri>(); |
| 123 sections[librariesSection].forEach((String name, String value) { |
| 124 result[name] = baseLocation.resolve(value); |
| 125 }); |
| 126 return result; |
| 127 } |
| 128 |
| 129 final Set<String> allowedSections = |
| 130 new Set.from([librariesSection, dartSpecSection, featuresSection]); |
| 131 |
| 132 Future<Map<String, Uri>> load(Uri location, api.CompilerInput provider) { |
| 133 return provider.readFromUri(location).then((contents) { |
| 134 if (contents is String) { |
| 135 contents = contents.codeUnits; |
| 136 } |
| 137 return libraryMappings( |
| 138 parseIni(contents, |
| 139 allowedSections: allowedSections, sourceUri: location), |
| 140 location); |
| 141 }); |
| 142 } |
OLD | NEW |