Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ on:

jobs:
test:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: swift-actions/setup-swift@v2
with:
swift-version: "5.10.1"
- uses: actions/checkout@v4

- run: sudo $GITHUB_WORKSPACE/ci/install-swift.bash
working-directory: /home/runner
- run: swift --version

- run: swift package resolve
- run: swift build
- run: swift test
5 changes: 4 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ let package = Package(
),
.testTarget(
name: "TypeScriptASTTests",
dependencies: ["TypeScriptAST"]
dependencies: ["TypeScriptAST"],
swiftSettings: [
.enableUpcomingFeature("BareSlashRegexLiterals")
]
),
]
)
44 changes: 44 additions & 0 deletions Sources/TypeScriptAST/Parser/CharacterEx.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
extension Character {
static let tab = Character("\t")
static let lf = Character("\n")
static let cr = Character("\r")
static let crLf = Character("\r\n")
static let space = Character(" ")
static let exclamation = Character("!")
static let doubleQuote = Character("\"")
static let dollar = Character("$")
static let percent = Character("%")
static let ampersand = Character("&")
static let singleQuote = Character("'")
static let leftParen = Character("(")
static let rightParen = Character(")")
static let asterisk = Character("*")
static let plus = Character("+")
static let comma = Character(",")
static let minus = Character("-")
static let dot = Character(".")
static let slash = Character("/")
static let _0 = Character("0")
static let _9 = Character("9")
static let colon = Character(":")
static let semicolon = Character(";")
static let leftAngleBracket = Character("<")
static let equal = Character("=")
static let rightAngleBracket = Character(">")
static let question = Character("?")
static let A = Character("A")
static let Z = Character("Z")
static let leftSquareBracket = Character("[")
static let backslash = Character("\\")
static let rightSquareBracket = Character("]")
static let underscore = Character("_")
static let backQuote = Character("`")
static let a = Character("a")
static let n = Character("n")
static let r = Character("r")
static let t = Character("t")
static let z = Character("z")
static let leftBrace = Character("{")
static let pipe = Character("|")
static let rightBrace = Character("}")
}
8 changes: 8 additions & 0 deletions Sources/TypeScriptAST/Parser/Keyword.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
public enum Keyword: String, Equatable & CustomStringConvertible {
case `import`
case from

public var description: String {
rawValue
}
}
73 changes: 73 additions & 0 deletions Sources/TypeScriptAST/Parser/Parser.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
public struct Parser {
public init(string: String) {
self.tokenizer = Tokenizer(string: string)
}

private var tokenizer: Tokenizer

public mutating func parse() -> TSSourceFile {
var es: [any ASTNode] = []
while let e = parseElement() {
es.append(e)
}
return TSSourceFile(es)
}

public mutating func parseElement() -> (any ASTNode)? {
while true {
switch nextToken {
case nil: return nil
case .keyword(let k):
switch k {
case .import:
if let x = parseImport() {
return x
}
default:
print("skip keyword: \(k)")
readToken()
}
default:
if let x = parseExpression() {
return x
}
}
}
}

private mutating func parseImport() -> TSImportDecl? {
let imp = readToken()
guard imp == .keyword(.import) else { return nil }
let lb = readToken()
guard lb == .symbol(.leftBrace) else { return nil }
var names: [String] = []
loop: while true {
guard let tk = readToken() else { return nil }
switch tk {
case .symbol(.rightBrace):
break loop
default:
names.append(tk.description)
}
}
let from = readToken()
guard from == .keyword(.from) else { return nil }
let pathToken = readToken()
guard case .stringLiteral(let path) = pathToken else { return nil }
return TSImportDecl(names: names, from: path)
}

private mutating func parseExpression() -> (any ASTNode)? {
readToken()
return nil
}

@discardableResult
private mutating func readToken() -> Token? {
tokenizer.read()
}

private var nextToken: Token? {
tokenizer.nextToken
}
}
54 changes: 54 additions & 0 deletions Sources/TypeScriptAST/Parser/Symbol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
public enum Symbol: String, Hashable & CustomStringConvertible {
case exclamation = "!" // 21
case exclamationEqual = "!="
case exclamationEqualEqual = "!=="
case percent = "%" // 25
case percentEqual = "%="
case ampersand = "&" // 26
case ampersandAmpersand = "&&"
case ampersandAmpersandEqual = "&&="
case ampersandEqual = "&="
case leftParen = "(" // 28
case rightParen = ")" // 29
case asterisk = "*" // 30
case asteriskEqual = "*="
case plus = "+" // 31
case plusPlus = "++"
case plusEqual = "+="
case comma = "," // 32
case minus = "-" // 33
case minusMinus = "--"
case minusEqual = "-="
case dot = "." // 34
case slash = "/" // 35
case slashEqual = "/="
case colon = ":" // 3a
case semicolon = ";" // 3b
case leftAngleBracket = "<" // 3c
case leftAngleBracketLeftAngleBracket = "<<"
case leftAngleBracketLeftAngleBracketEqual = "<<="
case equal = "=" // 3d
case equalEqual = "=="
case equalEqualEqual = "==="
case equalRightAngleBracket = "=>"
case rightAngleBracket = ">" // 3e
case rightAngleBracketRightAngleBracket = ">>"
case rightAngleBracketRightAngleBracketEqual = ">>="
case question = "?" // 3f
case questionDot = "?."
case questionQuestion = "??"
case questionQuestionEqual = "??="
case leftSquareBracket = "[" // 5b
case backslash = "\\" // 5c
case rightSquareBracket = "]" // 5d
case leftBrace = "{" // 7b
case pipe = "|" // 7c
case pipeEqual = "|="
case pipePipe = "||"
case pipePipeEqual = "||="
case rightBrace = "}" // 7d

public var description: String {
rawValue
}
}
33 changes: 33 additions & 0 deletions Sources/TypeScriptAST/Parser/Token.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
public enum Token: Equatable & CustomStringConvertible & CustomDebugStringConvertible {
case keyword(Keyword)
case identifier(String)
case symbol(Symbol)
case stringLiteral(String)

public var description: String {
switch self {
case .keyword(let x): return x.description
case .identifier(let x): return x
case .symbol(let x): return x.description
case .stringLiteral(let x): return "\"" + Self.escapeStringLiteralContent(x) + "\""
}
}

public var debugDescription: String {
switch self {
case .keyword(let x): return "keyword(\(x))"
case .identifier(let x): return "identifier(\(x))"
case .symbol(let x): return "symbol(\(x))"
case .stringLiteral(let x): return "stringLiteral(\(x))"
}
}

public static func escapeStringLiteralContent(_ string: String) -> String {
var s = string
s = s.replacingOccurrences(of: "\\", with: "\\\\")
s = s.replacingOccurrences(of: "\"", with: "\\\"")
s = s.replacingOccurrences(of: "\n", with: "\\n")
return s
}
}

Loading