Skip to content

Commit 22c2aec

Browse files
committed
Support source code position annotations in expressions.
1 parent ca479e0 commit 22c2aec

File tree

10 files changed

+60
-15
lines changed

10 files changed

+60
-15
lines changed

Sources/LispKit/Compiler/Compiler.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,8 @@ public final class Compiler {
358358
}
359359

360360
/// Generates instructions to push the given expression onto the stack.
361-
public func pushValue(_ expr: Expr) throws {
361+
public func pushValue(_ value: Expr) throws {
362+
let expr = value.datum
362363
switch expr {
363364
case .undef, .uninit(_):
364365
self.emit(.pushUndef)
@@ -396,6 +397,8 @@ public final class Compiler {
396397
throw RuntimeError.eval(.illegalKeywordUsage, expr)
397398
case .values(_):
398399
preconditionFailure("cannot push multiple values onto stack")
400+
case .syntax(_, _):
401+
preconditionFailure("cannot push syntax onto stack")
399402
}
400403
}
401404

Sources/LispKit/Compiler/Parser.swift

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,16 @@ public final class Parser {
5959
self.sourceId = sourceId
6060
}
6161

62+
/// Returns true if the scanner has reached the end of the input.
6263
public var finished: Bool {
6364
return !self.scanner.hasNext()
6465
}
6566

67+
/// Parses the next expression.
6668
public func parse(_ prescan: Bool = true) throws -> Expr {
6769
var res: Expr
6870
let token = self.scanner.token
71+
let pos = self.sourcePosition
6972
switch token.kind {
7073
case .error:
7174
let lexicalError = token.errorVal!
@@ -78,8 +81,8 @@ public final class Parser {
7881
_ = try self.parse()
7982
return try self.parse(prescan)
8083
case .ident:
81-
res = .symbol(self.symbols.intern(
82-
self.scanner.foldCase ? token.strVal.lowercased() : token.strVal))
84+
res = .symbol(
85+
self.symbols.intern(self.scanner.foldCase ? token.strVal.lowercased() : token.strVal))
8386
case .truelit:
8487
res = .true
8588
case .falselit:
@@ -124,7 +127,7 @@ public final class Parser {
124127
self.scanner.next()
125128
var exprs = Exprs()
126129
while !self.scanner.hasToken(.eof, .rparen) {
127-
exprs.append(try self.parse())
130+
exprs.append(try self.parse().datum)
128131
}
129132
guard self.scanner.hasToken(.rparen) else {
130133
throw RuntimeError.syntax(.closingParenthesisMissing)
@@ -147,29 +150,28 @@ public final class Parser {
147150
res = .bytes(MutableBox(bytes))
148151
case .quote:
149152
self.scanner.next()
150-
return Expr.makeList(.symbol(symbols.quote), try self.parse())
153+
return Expr.makeList(.syntax(pos, .symbol(symbols.quote)), try self.parse())
151154
case .backquote:
152155
self.scanner.next()
153-
return Expr.makeList(.symbol(symbols.quasiquote), try self.parse())
156+
return Expr.makeList(.syntax(pos, .symbol(symbols.quasiquote)), try self.parse())
154157
case .comma:
155158
self.scanner.next()
156-
return Expr.makeList(.symbol(symbols.unquote), try self.parse())
159+
return Expr.makeList(.syntax(pos, .symbol(symbols.unquote)), try self.parse())
157160
case .commaat:
158161
self.scanner.next()
159-
return Expr.makeList(.symbol(symbols.unquoteSplicing), try self.parse())
162+
return Expr.makeList(.syntax(pos, .symbol(symbols.unquoteSplicing)), try self.parse())
160163
case .dot:
161164
self.scanner.next()
162165
throw RuntimeError.syntax(.unexpectedDot)
163166
}
164167
if prescan {
165168
self.scanner.next()
166169
}
167-
return res
170+
return .syntax(pos, res)
168171
}
169172

170173
/// Returns the source position of the current token.
171174
private var sourcePosition: SourcePosition {
172175
return SourcePosition(self.sourceId, self.scanner.lpos.line, self.scanner.lpos.col)
173176
}
174-
175177
}

Sources/LispKit/Data/Equality.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ public func equalExpr(_ this: Expr, _ that: Expr) -> Bool {
196196
return eqvExpr(t1, t2) && equals(e1, e2)
197197
case (.error(let e1), .error(let e2)):
198198
return e1 == e2
199+
case (.syntax(let p1, let e1), .syntax(let p2, let e2)):
200+
return p1 == p2 && equals(e1, e2)
199201
default:
200202
return false
201203
}
@@ -266,6 +268,8 @@ public func eqvExpr(_ lhs: Expr, _ rhs: Expr) -> Bool {
266268
return eqvExpr(t1, t2) && eqvExpr(e1, e2)
267269
case (.error(let e1), .error(let e2)):
268270
return e1 === e2
271+
case (.syntax(let p1, let e1), .syntax(let p2, let e2)):
272+
return p1 == p2 && eqvExpr(e1, e2)
269273
default:
270274
return false
271275
}
@@ -333,6 +337,8 @@ public func eqExpr(_ lhs: Expr, _ rhs: Expr) -> Bool {
333337
return eqvExpr(t1, t2) && eqExpr(e1, e2)
334338
case (.error(let e1), .error(let e2)):
335339
return e1 === e2
340+
case (.syntax(let p1, let e1), .syntax(let p2, let e2)):
341+
return p1 == p2 && eqExpr(e1, e2)
336342
default:
337343
return false
338344
}

Sources/LispKit/Data/Expr.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public enum Expr: Trackable, Hashable {
5555
case port(Port)
5656
indirect case tagged(Expr, Expr)
5757
case error(RuntimeError)
58+
indirect case syntax(SourcePosition, Expr)
5859

5960
/// Returns the type of this expression.
6061
public var type: Type {
@@ -117,6 +118,8 @@ public enum Expr: Trackable, Hashable {
117118
return .taggedType
118119
case .error(_):
119120
return .errorType
121+
case .syntax(_, _):
122+
return .syntaxType
120123
}
121124
}
122125

@@ -196,13 +199,28 @@ public enum Expr: Trackable, Hashable {
196199
}
197200
}
198201

202+
/// Returns the source position associated with this expression, or `outer` in case there is
203+
/// no syntax annotation.
204+
public func pos(_ outer: SourcePosition = SourcePosition.unknown) -> SourcePosition {
205+
switch self {
206+
case .syntax(let p, _):
207+
return p
208+
case .pair(.syntax(let p, _), _):
209+
return p
210+
default:
211+
return outer
212+
}
213+
}
214+
199215
/// Returns the given expression with all symbols getting interned.
200216
public var datum: Expr {
201217
switch self {
202218
case .symbol(let sym):
203219
return .symbol(sym.interned)
204220
case .pair(let car, let cdr):
205221
return .pair(car.datum, cdr.datum)
222+
case .syntax(_, let expr):
223+
return expr.datum
206224
default:
207225
return self
208226
}
@@ -247,6 +265,8 @@ public enum Expr: Trackable, Hashable {
247265
switch self {
248266
case .pair(let car, let cdr):
249267
return car.requiresTracking || cdr.requiresTracking
268+
case .syntax(_, let expr):
269+
return expr.requiresTracking
250270
case .box(_), .mpair(_), .vector(_), .record(_), .table(_), .promise(_),
251271
.procedure(_), .special(_), .error(_):
252272
return true
@@ -294,6 +314,8 @@ public enum Expr: Trackable, Hashable {
294314
case .error(let err):
295315
err.mark(tag)
296316
return
317+
case .syntax(_, let datum):
318+
expr = datum
297319
default:
298320
return
299321
}
@@ -847,8 +869,11 @@ extension Expr: CustomStringConvertible {
847869
return "#<tag \(stringReprOf(tag)): \(stringReprOf(expr))>"
848870
case .error(let error):
849871
return "#<\(error.inlineDescription)>"
872+
case .syntax(_, let expr):
873+
return stringReprOf(expr)
850874
}
851875
}
876+
852877
return stringReprOf(self)
853878
}
854879

Sources/LispKit/Data/Hash.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ public func equalHash(_ expr: Expr) -> Int {
139139
return (eqvHash(tag) &* 31 &+ hash(expr)) &* 31 &+ 28
140140
case .error(let err):
141141
return err.hashValue &* 31 &+ 29
142+
case .syntax(let pos, let expr):
143+
return ((pos.hashValue &* 31) &+ hash(expr)) &* 31 &+ 30
142144
}
143145
}
144146

@@ -206,6 +208,8 @@ public func eqvHash(_ expr: Expr) -> Int {
206208
return (eqvHash(tag) &* 31 &+ eqvHash(expr)) &* 31 &+ 28
207209
case .error(let err):
208210
return err.hashValue &* 31 &+ 29
211+
case .syntax(let pos, let expr):
212+
return ((pos.hashValue &* 31) &+ eqvHash(expr)) &* 31 &+ 30
209213
}
210214
}
211215

@@ -270,5 +274,7 @@ public func eqHash(_ expr: Expr) -> Int {
270274
return (eqvHash(tag) &* 31 &+ eqHash(expr)) &* 31 &+ 28
271275
case .error(let err):
272276
return err.hashValue &* 31 &+ 29
277+
case .syntax(let pos, let expr):
278+
return ((pos.hashValue &* 31) &+ eqHash(expr)) &* 31 &+ 30
273279
}
274280
}

Sources/LispKit/Data/Type.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public enum Type: Int, CustomStringConvertible {
6565
case binaryInputPortType
6666
case binaryOutputPortType
6767
case taggedType
68+
case syntaxType
6869

6970
public var description: String {
7071
switch self {
@@ -154,6 +155,8 @@ public enum Type: Int, CustomStringConvertible {
154155
return "mpair"
155156
case .taggedType:
156157
return "tagged"
158+
case .syntaxType:
159+
return "syntax"
157160
}
158161
}
159162

Sources/LispKit/Primitives/PortLibrary.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ public final class PortLibrary: NativeLibrary {
407407
let input = try self.textInputFrom(expr)
408408
let parser = Parser(symbols: self.context.symbols, input: input)
409409
do {
410-
return try parser.parse(false)
410+
return try parser.parse(false).datum
411411
} catch let error as RuntimeError {
412412
guard case .syntax(.empty) = error.descriptor else {
413413
throw error

Sources/LispKit/Runtime/NativeLibrary.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ open class NativeLibrary: Library {
226226
do {
227227
let parser = Parser(symbols: self.context.symbols, src: code)
228228
while !parser.finished {
229-
self.execute(expr: try parser.parse())
229+
self.execute(expr: try parser.parse().datum)
230230
}
231231
} catch let error as RuntimeError { // handle Lisp-related issues
232232
preconditionFailure("compilation failure \(error) when compiling: \(code)")

Sources/LispKit/Runtime/VirtualMachine.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ public final class VirtualMachine: TrackedObject {
367367
let parser = Parser(symbols: self.context.symbols, src: str, foldCase: foldCase)
368368
var exprs = Exprs()
369369
while !parser.finished {
370-
exprs.append(try parser.parse())
370+
exprs.append(try parser.parse().datum) // TODO: remove .datum
371371
}
372372
return exprs
373373
}

Tests/LispKitTests/LispKitTestCase.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ open class LispKitTestCase: XCTestCase {
8484

8585
public func value(_ str: String) -> Expr {
8686
do {
87-
return try Parser(symbols: self.context!.symbols, src: str).parse()
87+
return try Parser(symbols: self.context!.symbols, src: str).parse().datum
8888
} catch {
8989
preconditionFailure("malformed expression: \(str)")
9090
}
@@ -118,7 +118,7 @@ open class LispKitTestCase: XCTestCase {
118118
let parser = Parser(symbols: self.context!.symbols, src: code)
119119
var res = [Test]()
120120
while !parser.finished {
121-
let spec = try parser.parse()
121+
let spec = try parser.parse().datum // TODO: remove .datum
122122
guard case .pair(.string(let descr), .pair(let target, let source)) = spec else {
123123
preconditionFailure("malformed test spec in file \(filename): \(spec)")
124124
}

0 commit comments

Comments
 (0)