Hey guys, I've been working on a Swift Interpreter for a while, and the results are starting to look good. It works by parsing the Swift code using SwiftSyntax then then traversing the AST tree. Here's a demo of it working!
Note: right now the repo is private. If you'd like to help or use it, message me and I can get you access.
Yes, I realize this is taking forever. (Who knew writing an interpreter would be hard? ) Anyways, here's two updates on the rewrite.
1. My generics hack
Here's the repo. The most important feature is the ability to construct any generic you want at runtime. i.e. the following types can be constructed at runtime without being explicitly there at compile-time.
// compile time struct
struct MyStruct<T, U> {}
// ... later one at runtime the following can be constructed
MyStruct<Int, Double>
MyStruct<MyClass, SomeOtherType>
This generics hack is what is going to allow the interpreter to construct PATs, generic types, and call generic functions even when specific types of the values which the interpreter will create and pass around are not known at compile time. The demo video of the old interpreter in my OP has no real notion of types, and so it relied on type-eraser to handle generics.
2. A Swift bytecode compiler in the works
The rewrite is a bytecode interpreter, not just a simple AST-walker. Here's a screenshot of it going from source to bytecode.
Just a small update. I'm taking a break from this project for a little bit. I've learned a lot about compilers and interpreters in the last year though. Really fun. I'll update this thread if/when I return to the project.
This is a pretty cool project. Do you have any plans to release the current sources in case other contributors are able to / want to take a crack at it?
I would like to but the truth is that I’m unhappy with the current state. I’d rather the first public commit be something I’m sure others could meaningfully contribute to.
Maybe in the meantime the implementation could be discussed. There is a big design decision I’m struggling to make right now with the type system. That problem was actually part of the reason I needed to take a break.
Here's a very crude version of an alternative "low-tech" approach (running "/usr/bin/swift" from within the app)
import SwiftUI
// MARK: sandbox is disabled
// tried com.apple.security.temporary-exception.files.absolute-path.read-only /usr/bin/swift
// but that didn't work for some reason
// TODO: proper error checking and remove force unwrapping
struct ContentView: View {
@State var source = #"print("Hello, World!")"#
@State var output = ""
@State var outcome = ""
@State var executing = false
var body: some View {
VStack(alignment: .leading) {
HStack {
Spacer()
Button("run", action: run)
.disabled(executing)
.keyboardShortcut(.defaultAction)
}
TextEditor(text: $source)
.monospaced()
.font(.title2)
TextEditor(text: $output)
.disabled(true)
.font(.title2)
.foregroundColor(.secondary)
Text(outcome)
.font(.title2)
.foregroundColor(.red)
}
.padding()
}
func run() {
let fileUrl = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("foo.swift")
let outputUrl = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("output.txt")
try! source.data(using: .utf8)!.write(to: fileUrl)
try! "".data(using: .utf8)!.write(to: outputUrl)
let task = try! NSUserUnixTask(url: URL(fileURLWithPath: "/usr/bin/swift"))
let file = try! FileHandle(forWritingTo: outputUrl)
task.standardOutput = file
task.standardError = file
output = ""
outcome = ""
executing = true
task.execute(withArguments: [fileUrl.path]) { error in
let data = try! Data(contentsOf: outputUrl)
output = String(data: data, encoding: .utf8)!
outcome = error?.localizedDescription ?? ""
executing = false
try? file.close()
}
}
}
// smart quotes workaround
extension NSTextView {
open override var frame: CGRect {
didSet {
isAutomaticQuoteSubstitutionEnabled = false
}
}
}
@main struct macUApp: App {
var body: some Scene {
WindowGroup { ContentView() }
}
}
Obviously this depends on "/usr/bin/swift" being available on the computer. Note that this was very quickly cooked and the code is littered with unsafe optional unwrapping and lacks proper error handling.
I had to disable sandbox as I wasn't able to talk macOS into using a provided temporary exception absolute file path readonly access. With sandbox enabled the app will refuse with: "The file "swift" couldn't be opened because you don't have permission to view it."
Just wanted to add that this is really awesome! I've been looking for a nice embeddable language that has similar advantages to Swift. It would be awesome to see this grow!