Skip to content

Commit e811707

Browse files
committed
Convert import to an expression
1 parent 73c7fe8 commit e811707

File tree

8 files changed

+59
-76
lines changed

8 files changed

+59
-76
lines changed

ShapeScript/EvaluationContext.swift

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ extension EvaluationContext {
328328
#endif
329329
}
330330

331-
func importFile(at path: String) throws {
331+
func importFile(at path: String) throws -> Value {
332332
let url = try resolveURL(for: path)
333333
if importStack.contains(url) {
334334
throw RuntimeErrorType.circularImport(for: url)
@@ -370,6 +370,7 @@ extension EvaluationContext {
370370
}
371371
do {
372372
try program.evaluate(in: self)
373+
return .void
373374
} catch {
374375
throw RuntimeErrorType.importError(
375376
ProgramError(error),
@@ -378,49 +379,40 @@ extension EvaluationContext {
378379
)
379380
}
380381
case "txt":
381-
let text: String
382382
do {
383-
text = try String(contentsOf: url)
383+
return try .string(String(contentsOf: url))
384384
} catch {
385385
throw RuntimeErrorType.fileParsingError(
386386
for: path,
387387
at: url,
388388
message: error.localizedDescription
389389
)
390390
}
391-
try addValue(.string(text))
392391
case "json":
393-
let data: Data
394392
do {
395-
data = try Data(contentsOf: url)
393+
return try Value(jsonData: Data(contentsOf: url))
394+
} catch let error as ParserError {
395+
let source = try String(contentsOf: url)
396+
throw RuntimeErrorType.importError(
397+
.parserError(error),
398+
for: url,
399+
in: source
400+
)
396401
} catch {
397402
throw RuntimeErrorType.fileParsingError(
398403
for: path,
399404
at: url,
400405
message: error.localizedDescription
401406
)
402407
}
403-
let value: Value
404-
do {
405-
value = try Value(jsonData: data)
406-
} catch {
407-
let source = try String(contentsOf: url)
408-
throw RuntimeErrorType.importError(
409-
ProgramError(error),
410-
for: url,
411-
in: source
412-
)
413-
}
414-
try addValue(value)
415408
default:
416409
do {
417410
if let geometry = try delegate?.importGeometry(for: url)?.with(
418411
transform: childTransform,
419412
material: material,
420413
sourceLocation: sourceLocation
421414
) {
422-
children.append(.mesh(geometry))
423-
return
415+
return .mesh(geometry)
424416
}
425417
} catch let error as ProgramError {
426418
guard let source = try? String(contentsOf: url) else {

ShapeScript/Interpreter.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,15 +1071,6 @@ extension Statement {
10711071
}
10721072
try elseBody?.evaluate(in: context)
10731073
}
1074-
case let .import(expression):
1075-
let pathValue = try expression.evaluate(
1076-
as: .string,
1077-
for: Keyword.import.rawValue,
1078-
in: context
1079-
)
1080-
let path = pathValue.stringValue
1081-
context.sourceIndex = expression.range.lowerBound
1082-
try RuntimeError.wrap(context.importFile(at: path), at: expression.range)
10831074
}
10841075
}
10851076
}
@@ -1366,6 +1357,15 @@ extension Expression {
13661357
of: value.errorDescription,
13671358
options: value.members
13681359
), at: member.range)
1360+
case let .import(expression):
1361+
let pathValue = try expression.evaluate(
1362+
as: .string,
1363+
for: Keyword.import.rawValue,
1364+
in: context
1365+
)
1366+
let path = pathValue.stringValue
1367+
context.sourceIndex = expression.range.lowerBound
1368+
return try RuntimeError.wrap(context.importFile(at: path), at: expression.range)
13691369
}
13701370
}
13711371

ShapeScript/Parser.swift

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ public enum StatementType: Equatable {
3232
case ifelse(Expression, Block, else: Block?)
3333
case switchcase(Expression, [CaseStatement], else: Block?)
3434
case expression(Expression)
35-
case `import`(Expression)
3635
}
3736

3837
public struct Statement: Equatable {
@@ -68,6 +67,7 @@ public enum ExpressionType: Equatable {
6867
indirect case prefix(PrefixOperator, Expression)
6968
indirect case infix(Expression, InfixOperator, Expression)
7069
indirect case member(Expression, Identifier)
70+
indirect case `import`(Expression)
7171
}
7272

7373
public struct Expression: Equatable {
@@ -403,13 +403,6 @@ private extension ArraySlice where Element == Token {
403403
return .switchcase(condition, cases, else: defaultCase)
404404
}
405405

406-
mutating func readImport() throws -> StatementType? {
407-
guard readToken(.keyword(.import)) else {
408-
return nil
409-
}
410-
return try .import(require(readExpressions(), as: "file path"))
411-
}
412-
413406
mutating func readOperand() throws -> Expression? {
414407
let start = self
415408
let token = readToken()
@@ -466,6 +459,8 @@ private extension ArraySlice where Element == Token {
466459
}
467460
type = .tuple(expressions)
468461
range = range.lowerBound ..< endToken.range.upperBound
462+
case .keyword(.import):
463+
type = try .import(require(readExpressions(), as: "file path"))
469464
case .dot, .linebreak, .keyword, .infix, .lbrace, .rbrace, .rparen, .eof:
470465
self = start
471466
return nil
@@ -635,7 +630,7 @@ private extension ArraySlice where Element == Token {
635630

636631
mutating func readStatementType() throws -> StatementType? {
637632
if let statement = try readDefine() ?? readOption() ??
638-
readForLoop() ?? readIfElse() ?? readSwitch() ?? readImport()
633+
readForLoop() ?? readIfElse() ?? readSwitch()
639634
{
640635
return statement
641636
}

ShapeScript/Types.swift

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,8 @@ extension Expression {
613613
return
614614
}
615615
}
616+
case let .import(expression):
617+
expression.inferTypes(for: &params, in: context, with: .string)
616618
case let .infix(lhs, .step, rhs):
617619
lhs.inferTypes(for: &params, in: context, with: .range)
618620
rhs.inferTypes(for: &params, in: context, with: .number)
@@ -746,6 +748,23 @@ extension Expression {
746748
case let .member(expression, member):
747749
let type = try expression.staticType(in: context)
748750
return type.memberType(member.name) ?? .any
751+
case let .import(expression):
752+
var file: String?
753+
switch expression.type {
754+
case let .string(string):
755+
file = string
756+
case let .tuple(expressions):
757+
if case let .string(string)? = expressions.last?.type {
758+
file = string
759+
}
760+
default:
761+
break
762+
}
763+
switch file?.components(separatedBy: ".").last?.lowercased() ?? "" {
764+
case "txt": return .string
765+
case "shape", "json", "": return .any
766+
default: return .mesh
767+
}
749768
}
750769
}
751770
}
@@ -856,8 +875,6 @@ extension Statement {
856875
caseStatement.inferTypes(for: &params, in: context, with: type)
857876
}
858877
elseBody?.inferTypes(for: &params, in: context)
859-
case let .import(expression):
860-
expression.inferTypes(for: &params, in: context, with: .string)
861878
case .option:
862879
return
863880
}
@@ -946,23 +963,6 @@ extension Statement {
946963
}
947964
}
948965
return type
949-
case let .import(expression):
950-
var file: String?
951-
switch expression.type {
952-
case let .string(string):
953-
file = string
954-
case let .tuple(expressions):
955-
if case let .string(string)? = expressions.last?.type {
956-
file = string
957-
}
958-
default:
959-
break
960-
}
961-
switch file?.components(separatedBy: ".").last?.lowercased() ?? "" {
962-
case "txt": return .string
963-
case "shape", "json", "": return .any
964-
default: return .mesh
965-
}
966966
}
967967
}
968968
}
@@ -976,7 +976,7 @@ extension Array where Element == Statement {
976976
let type = (try? definition.staticType(in: context)) ?? .any
977977
context.define(identifier.name, as: .placeholder(type))
978978
}
979-
case .command, .option, .forloop, .ifelse, .switchcase, .expression, .import:
979+
case .command, .option, .forloop, .ifelse, .switchcase, .expression:
980980
break
981981
}
982982
}

ShapeScriptTests/InterpreterTests.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1829,6 +1829,14 @@ class InterpreterTests: XCTestCase {
18291829
XCTAssertEqual(delegate.imports, ["File1.shape"])
18301830
}
18311831

1832+
func testImportExpression() throws {
1833+
let program = try parse("define foo import \"File1.shape\"")
1834+
let delegate = TestDelegate()
1835+
let context = EvaluationContext(source: program.source, delegate: delegate)
1836+
try? program.evaluate(in: context) // Throws file not found, but we can ignore
1837+
XCTAssertEqual(delegate.imports, ["File1.shape"])
1838+
}
1839+
18321840
// MARK: Command invocation
18331841

18341842
func testInvokeCustomVoidFunctionInsideExpression() {

docs/ios/import.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,7 @@ Depending on the format, imported models may include their own [materials](mater
4141
In addition to scripts and standard model formats, text files can be imported as a [string](literals.md#strings) value.
4242

4343
```swift
44-
define text {
45-
import "Text.txt"
46-
}
44+
define text import "Text.txt"
4745
```
4846

4947
Imported strings can then be displayed directly, or further processed using ShapeScript's [string functions](functions.md#strings):
@@ -57,9 +55,7 @@ for line in text.lines {
5755
As well as plain text, ShapeScript also supports importing JSON files as [structured data](literals.md#structured-data):
5856

5957
```swift
60-
define data {
61-
import "Data.json"
62-
}
58+
define data import "Data.json"
6359
```
6460

6561
## Dynamic Imports

docs/mac/import.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,7 @@ Depending on the format, imported models may include their own [materials](mater
4141
In addition to scripts and standard model formats, text files can be imported as a [string](literals.md#strings) value.
4242

4343
```swift
44-
define text {
45-
import "Text.txt"
46-
}
44+
define text import "Text.txt"
4745
```
4846

4947
Imported strings can then be displayed directly, or further processed using ShapeScript's [string functions](functions.md#strings):
@@ -57,9 +55,7 @@ for line in text.lines {
5755
As well as plain text, ShapeScript also supports importing JSON files as [structured data](literals.md#structured-data):
5856

5957
```swift
60-
define data {
61-
import "Data.json"
62-
}
58+
define data import "Data.json"
6359
```
6460

6561
## Dynamic Imports

docs/src/import.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,7 @@ Depending on the format, imported models may include their own [materials](mater
4141
In addition to scripts and standard model formats, text files can be imported as a [string](literals.md#strings) value.
4242

4343
```swift
44-
define text {
45-
import "Text.txt"
46-
}
44+
define text import "Text.txt"
4745
```
4846

4947
Imported strings can then be displayed directly, or further processed using ShapeScript's [string functions](functions.md#strings):
@@ -57,9 +55,7 @@ for line in text.lines {
5755
As well as plain text, ShapeScript also supports importing JSON files as [structured data](literals.md#structured-data):
5856

5957
```swift
60-
define data {
61-
import "Data.json"
62-
}
58+
define data import "Data.json"
6359
```
6460

6561
## Dynamic Imports

0 commit comments

Comments
 (0)