feat: sonya now writes out more comprehensible error messages
This commit is contained in:
@@ -17,21 +17,20 @@ struct sonya {
|
||||
case .isEmpty:
|
||||
print("sonya: sonyafile is empty")
|
||||
exit(2)
|
||||
case .syntaxError:
|
||||
print("sonya: syntax error in sonyafile")
|
||||
case .syntaxError(let errs):
|
||||
for err in errs {
|
||||
print("line '\(err)' is unrecognized")
|
||||
}
|
||||
exit(3)
|
||||
case .noSuchRecipe:
|
||||
print("sonya: no recipe named '\(recipe)'")
|
||||
exit(4)
|
||||
case .noSuchDependency:
|
||||
print("sonya: unknown dependency")
|
||||
case .shellReturnedError(let code):
|
||||
print("sonya: command failed with code \(code)")
|
||||
exit(5)
|
||||
case .shellReturnedError:
|
||||
print("sonya: command failed")
|
||||
exit(6)
|
||||
case .noSuchVariable:
|
||||
print("sonya: undefined variable referenced in recipe '\(recipe)'")
|
||||
exit(7)
|
||||
exit(6)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,13 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum runResult {
|
||||
public enum runResult: Equatable {
|
||||
case OK
|
||||
case noSonyafile
|
||||
case isEmpty
|
||||
case syntaxError
|
||||
case syntaxError(errs: [String])
|
||||
case noSuchRecipe
|
||||
case noSuchDependency
|
||||
case shellReturnedError
|
||||
case shellReturnedError(Int32)
|
||||
case noSuchVariable
|
||||
}
|
||||
|
||||
@@ -24,8 +23,9 @@ public func runRecipe(_ recipeName: String, args: [String] = []) -> runResult {
|
||||
let tokenized = Lexer().tokenize(sonyafile)
|
||||
guard !tokenized.isEmpty else { return .isEmpty }
|
||||
|
||||
guard let ast = Parser().parse(tokenized) else { return .syntaxError }
|
||||
|
||||
// guard let ast = Parser().parse(tokenized) else { return .syntaxError }
|
||||
switch Parser().parse(tokenized) {
|
||||
case .success(let ast):
|
||||
guard let requestedRecipe = ast.rules[recipeName] else { return .noSuchRecipe }
|
||||
// check for dependencies
|
||||
if !requestedRecipe.dependencies.isEmpty {
|
||||
@@ -38,9 +38,13 @@ public func runRecipe(_ recipeName: String, args: [String] = []) -> runResult {
|
||||
for command in requestedRecipe.commands {
|
||||
guard let resolved = resolve(command, vars: ast.vars) else { return .noSuchVariable }
|
||||
|
||||
guard shell(resolved) == 0 else { return .shellReturnedError }
|
||||
let exitCode = shell(resolved)
|
||||
guard exitCode == 0 else { return .shellReturnedError(exitCode) }
|
||||
}
|
||||
|
||||
// all good
|
||||
return .OK
|
||||
case .failure(let error):
|
||||
return .syntaxError(errs: error.errorLines)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ enum Token : Equatable {
|
||||
case variable(name: String, value: String)
|
||||
case comment(String)
|
||||
case newline
|
||||
case error(line: String)
|
||||
}
|
||||
|
||||
struct Lexer {
|
||||
@@ -75,7 +76,7 @@ struct Lexer {
|
||||
return [.command(String(match.1))]
|
||||
}
|
||||
|
||||
return [] // unrecognized
|
||||
return [.error(line: line)] // unrecognized
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
struct errorLines: Error {
|
||||
var errorLines: [String]
|
||||
|
||||
init(_ errs: [String]) {
|
||||
errorLines = errs
|
||||
}
|
||||
}
|
||||
|
||||
struct sonyaAST {
|
||||
var vars: [String : String] = [:]
|
||||
var rules: [String : Rule] = [:]
|
||||
@@ -9,8 +17,9 @@ struct Rule {
|
||||
}
|
||||
|
||||
struct Parser {
|
||||
func parse(_ tokens: [Token]) -> sonyaAST? {
|
||||
func parse(_ tokens: [Token]) -> Result<sonyaAST, errorLines> {
|
||||
var ast = sonyaAST()
|
||||
var errors: [String] = []
|
||||
var currentRule: (name: String, rule: Rule)? = nil
|
||||
|
||||
for token in tokens {
|
||||
@@ -26,6 +35,8 @@ struct Parser {
|
||||
currentRule?.rule.dependencies.append(name)
|
||||
case .command(let cmd):
|
||||
currentRule?.rule.commands.append(cmd)
|
||||
case .error(let line):
|
||||
errors.append(line)
|
||||
case .newline, .comment:
|
||||
continue
|
||||
}
|
||||
@@ -33,7 +44,10 @@ struct Parser {
|
||||
|
||||
if let rule = currentRule { ast.rules[rule.name] = rule.rule }
|
||||
currentRule = nil
|
||||
return ast
|
||||
|
||||
if errors.isEmpty {
|
||||
return .success(ast)
|
||||
} else {
|
||||
return .failure(errorLines(errors))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user