Commit 2fd836ac authored by David Owens II's avatar David Owens II

Basic parser implemented

The basic parser implementation is up and running now, including the
proper atpkg-model separation.
parent 4c35567a
Pipeline #676 passed with stage
......@@ -65,18 +65,18 @@ tasks:
outputType: "static-library"
atpkg-model:
dependency: ["atpkg-parser"]
tool: "atllbuild"
source: ["model/src/**.swift"]
name: "atpkgmodel"
outputType: "static-library"
linkWithProduct: ["atpkgparser.a"]
atpkg-parser:
dependency: ["atpkg-model"]
tool: "atllbuild"
source: ["parsers/atpkg/src/**.swift"]
name: "atpkgparser"
outputType: "static-library"
linkWithProduct: ["atpkgmodel.a"]
atpkg-tests:
dependency: ["atpkg-parser", "atpkg-model"]
......
......@@ -12,26 +12,136 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import atpkgmodel
import Foundation
// public enum PackageParserError: ErrorType {
// case PackageFileDoesNotExist(filename: String)
// case MissingPackageDeclaration
// case InvalidPackageFile
// case UnexpectedToken(expected: Token, actual: Token)
// case MissingToken(expected: Token)
// case UnknownProperty(String)
// }
public enum ParseError: ErrorType {
case InvalidPackageFile
case ExpectedTokenType(TokenType, Token?)
case InvalidTokenForValueType(Token?)
}
public enum ParseValue {
case StringLiteral(String)
case IntegerLiteral(Int)
case FloatLiteral(Double)
case BoolLiteral(Bool)
case Map([String:ParseValue])
case Vector([ParseValue])
}
public class ParseType {
public var name: String = ""
public var properties: [String:ParseValue] = [:]
}
public class Parser {
let lexer: Lexer
private func next() -> Token? {
while true {
guard let token = lexer.next() else { return nil }
if token.type != .Comment && token.type != .Terminal {
return lexer.peek()
}
}
}
public init?(filepath: String) {
guard let content = try? NSString(contentsOfFile: filepath, encoding: NSUTF8StringEncoding) else {
return nil
}
let scanner = Scanner(content: content as String)
self.lexer = Lexer(scanner: scanner)
}
public func parse() throws -> ParseType {
guard let token = next() else { throw ParseError.InvalidPackageFile }
if token.type == .OpenParen {
return try parseType()
}
else {
throw ParseError.ExpectedTokenType(.OpenParen, token)
}
}
private func parseType() throws -> ParseType {
let type = ParseType()
type.name = try parseIdentifier()
type.properties = try parseKeyValuePairs()
return type
}
private func parseKeyValuePairs() throws -> [String:ParseValue] {
var pairs: [String:ParseValue] = [:]
while let token = next() where token.type != .CloseParen && token.type != .CloseBrace {
lexer.stall()
let key = try parseKey()
let value = try parseValue()
pairs[key] = value
}
lexer.stall()
return pairs
}
private func parseKey() throws -> String {
let colon = next()
if colon?.type != .Colon { throw ParseError.ExpectedTokenType(.Colon, lexer.peek()) }
return try parseIdentifier()
}
private func parseIdentifier() throws -> String {
guard let identifier = next() else { throw ParseError.ExpectedTokenType(.Identifier, lexer.peek()) }
if identifier.type != .Identifier { throw ParseError.ExpectedTokenType(.Identifier, lexer.peek()) }
return identifier.value
}
private func parseValue() throws -> ParseValue {
guard let token = next() else { throw ParseError.InvalidTokenForValueType(nil) }
switch token.type {
case .OpenBrace: lexer.stall(); return try parseMap()
case .OpenBracket: lexer.stall(); return try parseVector()
case .StringLiteral: return .StringLiteral(token.value)
default: throw ParseError.InvalidTokenForValueType(token)
}
}
private func parseVector() throws -> ParseValue {
if let token = next() where token.type != .OpenBracket { throw ParseError.ExpectedTokenType(.OpenBracket, token) }
var items: [ParseValue] = []
while let token = next() where token.type != .CloseBracket {
lexer.stall()
items.append(try parseValue())
}
lexer.stall()
if let token = next() where token.type != .CloseBracket { throw ParseError.ExpectedTokenType(.CloseBracket, token) }
return .Vector(items)
}
private func parseMap() throws -> ParseValue {
if let token = next() where token.type != .OpenBrace { throw ParseError.ExpectedTokenType(.OpenBrace, token) }
let items = try parseKeyValuePairs()
if let token = next() where token.type != .CloseBrace { throw ParseError.ExpectedTokenType(.CloseBrace, token) }
return .Map(items)
}
}
// extension Lexer {
// func parseableNext() -> Token? {
// while true {
// guard let token = self.next() else { return nil }
// if case .Comment = token {}
// else if case .Terminal = token {}
// else { return self.peek() }
// }
// }
// func take(expected: Token) throws -> Token {
......
......@@ -80,11 +80,18 @@ public class Lexer {
var scanner: Scanner
var current: Token? = nil
var shouldStall = false
public init(scanner: Scanner) {
self.scanner = scanner
}
public func next() -> Token? {
if shouldStall {
shouldStall = false
return current
}
func work() -> Token {
if scanner.next() == nil { return Token(type: .EOF) }
......@@ -179,6 +186,6 @@ public class Lexer {
}
public func stall() {
scanner.stall()
shouldStall = true
}
}
\ No newline at end of file
......@@ -15,7 +15,23 @@
import Foundation
import atpkgparser
import atpkgmodel
extension ParseValue {
var stringLiteral: String? {
if case let .StringLiteral(value) = self { return value }
return nil
}
var map: [String:ParseValue]? {
if case let .Map(value) = self { return value }
return nil
}
var vector: [ParseValue]? {
if case let .Vector(value) = self { return value }
return nil
}
}
class ParserTests: Test {
required init() {}
......@@ -27,8 +43,41 @@ class ParserTests: Test {
static func testBasic() throws {
let filepath = "./parsers/atpkg/tests/collateral/basic.atpkg"
let package = try parsePackageDefinition(filepath)
guard let parser = Parser(filepath: filepath) else {
try test.assert(false); return
}
let result = try parser.parse()
try test.assert(package.name == "basic")
let name = result.properties["name"]
try test.assert(name != nil)
try test.assert(name?.stringLiteral == "\"basic\"")
let version = result.properties["version"]
try test.assert(version != nil)
try test.assert(version?.stringLiteral == "\"0.1.0-dev\"")
let tasks = result.properties["tasks"]
try test.assert(tasks != nil)
let build = tasks?.map?["build"]
try test.assert(build != nil)
let tool = build?.map?["tool"]
try test.assert(tool != nil)
try test.assert(tool?.stringLiteral == "\"lldb-build\"")
let buildName = build?.map?["name"]
try test.assert(buildName != nil)
try test.assert(buildName?.stringLiteral == "\"json-swift\"")
let outputType = build?.map?["output-type"]
try test.assert(outputType != nil)
try test.assert(outputType?.stringLiteral == "\"lib\"")
let source = build?.map?["source"]
try test.assert(source != nil)
try test.assert(source?.vector != nil)
try test.assert(source?.vector?[0].stringLiteral == "\"src/**.swift\"")
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment