mirror of
https://github.com/nicklockwood/SwiftFormat.git
synced 2026-06-16 10:34:34 +00:00
83 lines
2.3 KiB
Swift
83 lines
2.3 KiB
Swift
//
|
|
// Transpiler.swift
|
|
// Parsing
|
|
//
|
|
// Created by Nick Lockwood on 19/03/2019.
|
|
// Copyright © 2019 Nick Lockwood. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
// MARK: interface
|
|
|
|
public enum TranspilerError: Error, Equatable {
|
|
case undefinedVariable(String)
|
|
}
|
|
|
|
public func transpile(_ program: [Statement]) throws -> String {
|
|
let context = Context()
|
|
for statement in program {
|
|
try statement.transpile(in: context)
|
|
}
|
|
return context.output
|
|
}
|
|
|
|
// MARK: implementation
|
|
|
|
enum Type {
|
|
case number
|
|
case string
|
|
}
|
|
|
|
final class Context {
|
|
var variables: [String: Type] = [:]
|
|
var output = ""
|
|
}
|
|
|
|
extension Statement {
|
|
|
|
func transpile(in context: Context) throws {
|
|
switch self {
|
|
case .declaration(name: let name, value: let expression):
|
|
let (type, value) = try expression.transpile(in: context)
|
|
context.variables[name] = type
|
|
// TODO: escape reserved names
|
|
context.output.append("let \(name) = \(value)\n")
|
|
case .print(let expression):
|
|
let (_, value) = try expression.transpile(in: context)
|
|
context.output.append("print(\(value))\n")
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Expression {
|
|
|
|
func transpile(in context: Context) throws -> (Type, String) {
|
|
switch self {
|
|
case .number(let double):
|
|
return (.number, String(double))
|
|
case .string(let string):
|
|
let escapedString = string
|
|
.replacingOccurrences(of: "\\", with: "\\\\")
|
|
.replacingOccurrences(of: "\"", with: "\\\"")
|
|
return (.string, "\"\(escapedString)\"")
|
|
case .variable(let name):
|
|
guard let type = context.variables[name] else {
|
|
throw TranspilerError.undefinedVariable(name)
|
|
}
|
|
// TODO: escape reserved names
|
|
return (type, name)
|
|
case .addition(lhs: let expression1, rhs: let expression2):
|
|
let (type1, lhs) = try expression1.transpile(in: context)
|
|
let (type2, rhs) = try expression2.transpile(in: context)
|
|
switch (type1, type2) {
|
|
case (.number, .number):
|
|
return (.number, "\(lhs) + \(rhs)")
|
|
case (.string, _),
|
|
(_, .string):
|
|
return (.string, "\"\\(\(lhs))\\(\(rhs))\"")
|
|
}
|
|
}
|
|
}
|
|
}
|