1 //******************************************************************************
3 //******************************************************************************
4 // This file is automatically generated from templates/step.swift. Rather than
5 // editing it directly, it's probably better to edit templates/step.swift and
6 // regenerate this file. Otherwise, your change might be lost if/when someone
7 // else performs that process.
8 //******************************************************************************
12 // Parse the string into an AST.
14 private func READ(str: String) throws -> MalVal {
15 return try read_str(str)
18 // Perform a simple evaluation of the `ast` object. If it's a symbol,
19 // dereference it and return its value. If it's a collection, call EVAL on all
20 // elements (or just the values, in the case of the hashmap). Otherwise, return
21 // the object unchanged.
23 private func eval_ast(ast: MalVal, _ env: Environment) throws -> MalVal {
24 if let symbol = as_symbolQ(ast) {
25 guard let val = env.get(symbol) else {
26 try throw_error("'\(symbol)' not found") // Specific text needed to match MAL unit tests
30 if let list = as_listQ(ast) {
31 var result = [MalVal]()
32 result.reserveCapacity(Int(list.count))
34 let eval = try EVAL(item, env)
37 return make_list(result)
39 if let vec = as_vectorQ(ast) {
40 var result = [MalVal]()
41 result.reserveCapacity(Int(vec.count))
43 let eval = try EVAL(item, env)
46 return make_vector(result)
48 if let hash = as_hashmapQ(ast) {
49 var result = [MalVal]()
50 result.reserveCapacity(Int(hash.count) * 2)
52 let new_v = try EVAL(v, env)
56 return make_hashmap(result)
61 // Walk the AST and completely evaluate it, handling macro expansions, special
62 // forms and function calls.
64 private func EVAL(ast: MalVal, _ env: Environment) throws -> MalVal {
68 // Not a list -- just evaluate and return.
70 let answer = try eval_ast(ast, env)
74 // Special handling if it's a list.
76 let list = as_list(ast)
82 // Standard list to be applied. Evaluate all the elements first.
84 let eval = try eval_ast(ast, env)
86 // The result had better be a list and better be non-empty.
88 let eval_list = as_list(eval)
89 if eval_list.isEmpty {
93 // Get the first element of the list and execute it.
95 let first = eval_list.first()
96 let rest = as_sequence(eval_list.rest())
98 if let fn = as_builtinQ(first) {
99 let answer = try fn.apply(rest)
103 // The first element wasn't a function to be executed. Return an
106 try throw_error("first list item does not evaluate to a function: \(first)")
109 // Convert the value into a human-readable string for printing.
111 private func PRINT(exp: MalVal) -> String {
112 return pr_str(exp, true)
115 // Perform the READ and EVAL steps. Useful for when you don't care about the
118 private func RE(text: String, _ env: Environment) -> MalVal? {
121 let ast = try READ(text)
123 return try EVAL(ast, env)
124 } catch let error as MalException {
125 print("Error evaluating input: \(error)")
127 print("Error evaluating input: \(error)")
129 } catch let error as MalException {
130 print("Error parsing input: \(error)")
132 print("Error parsing input: \(error)")
138 // Perform the full READ/EVAL/PRINT, returning a printable string.
140 private func REP(text: String, _ env: Environment) -> String? {
141 let exp = RE(text, env)
142 if exp == nil { return nil }
146 // Perform the full REPL.
148 private func REPL(env: Environment) {
150 if let text = _readline("user> ") {
151 if let output = REP(text, env) {
162 let env = Environment(outer: nil)