Skip to content

Commit

Permalink
Improve error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
DePasqualeOrg committed Sep 29, 2024
1 parent 4a6eb1a commit 5371bf6
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 79 deletions.
16 changes: 8 additions & 8 deletions Sources/Environment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Environment {
}

if args.count != 1 || !(args[0] is ObjectValue) {
throw JinjaError.runtimeError("`namespace` expects either zero arguments or a single object argument")
throw JinjaError.runtime("`namespace` expects either zero arguments or a single object argument")
}

return args[0]
Expand All @@ -41,15 +41,15 @@ class Environment {
return arg.value as! Int % 2 != 0
}
else {
throw JinjaError.runtimeError("Cannot apply test 'odd' to type: \(type(of:args.first))")
throw JinjaError.runtime("Cannot apply test 'odd' to type: \(type(of:args.first))")
}
},
"even": { args in
if let arg = args.first as? NumericValue {
return arg.value as! Int % 2 == 0
}
else {
throw JinjaError.runtimeError("Cannot apply test 'even' to type: \(type(of:args.first))")
throw JinjaError.runtime("Cannot apply test 'even' to type: \(type(of:args.first))")
}
},
"false": { args in
Expand Down Expand Up @@ -100,7 +100,7 @@ class Environment {
args[0] is UndefinedValue
},
"equalto": { _ in
throw JinjaError.notSupportError
throw JinjaError.syntaxNotSupported
},
]

Expand Down Expand Up @@ -135,7 +135,7 @@ class Environment {
case let value as Bool:
arg = String(value)
default:
throw JinjaError.runtimeError("Unknown arg type:\(type(of: args[0].value))")
throw JinjaError.runtime("Unknown arg type:\(type(of: args[0].value))")
}

try fn(arg)
Expand Down Expand Up @@ -166,7 +166,7 @@ class Environment {

return ObjectValue(value: object)
default:
throw JinjaError.runtimeError("Cannot convert to runtime value: \(input) type:\(type(of: input))")
throw JinjaError.runtime("Cannot convert to runtime value: \(input) type:\(type(of: input))")
}
}

Expand All @@ -177,7 +177,7 @@ class Environment {

func declareVariable(name: String, value: any RuntimeValue) throws -> any RuntimeValue {
if self.variables.contains(where: { $0.0 == name }) {
throw JinjaError.syntaxError("Variable already declared: \(name)")
throw JinjaError.syntax("Variable already declared: \(name)")
}

self.variables[name] = value
Expand All @@ -200,7 +200,7 @@ class Environment {
return try parent.resolve(name: name) as! Self
}

throw JinjaError.runtimeError("Unknown variable: \(name)")
throw JinjaError.runtime("Unknown variable: \(name)")
}

func lookupVariable(name: String) -> any RuntimeValue {
Expand Down
20 changes: 15 additions & 5 deletions Sources/Error.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,20 @@

import Foundation

enum JinjaError: Error {
case syntaxError(String)
case parserError(String)
case runtimeError(String)
enum JinjaError: Error, LocalizedError {
case syntax(String)
case parser(String)
case runtime(String)
case todo(String)
case notSupportError
case syntaxNotSupported

var errorDescription: String? {
switch self {
case .syntax(let message): return "Syntax error: \(message)"
case .parser(let message): return "Parser error: \(message)"
case .runtime(let message): return "Runtime error: \(message)"
case .todo(let message): return "Todo error: \(message)"
case .syntaxNotSupported: return "Syntax not supported"
}
}
}
10 changes: 5 additions & 5 deletions Sources/Lexer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -170,14 +170,14 @@ func tokenize(_ source: String, options: PreprocessOptions = PreprocessOptions()
if src[cursorPosition] == "\\" {
cursorPosition += 1
if cursorPosition >= src.count {
throw JinjaError.syntaxError("Unexpected end of input")
throw JinjaError.syntax("Unexpected end of input")
}

let escaped = String(src[cursorPosition])
cursorPosition += 1

guard let unescaped = escapeCharacters[escaped] else {
throw JinjaError.syntaxError("Unexpected escaped character: \(escaped)")
throw JinjaError.syntax("Unexpected escaped character: \(escaped)")
}
str.append(unescaped)
continue
Expand All @@ -186,7 +186,7 @@ func tokenize(_ source: String, options: PreprocessOptions = PreprocessOptions()
str.append(String(src[cursorPosition]))
cursorPosition += 1
if cursorPosition >= src.count {
throw JinjaError.syntaxError("Unexpected end of input")
throw JinjaError.syntax("Unexpected end of input")
}
}
return str
Expand Down Expand Up @@ -219,7 +219,7 @@ func tokenize(_ source: String, options: PreprocessOptions = PreprocessOptions()
let lastTokenType = tokens.last?.type

if lastTokenType == .text || lastTokenType == nil {
throw JinjaError.syntaxError("Unexpected character: \(char)")
throw JinjaError.syntax("Unexpected character: \(char)")
}

switch lastTokenType {
Expand Down Expand Up @@ -283,7 +283,7 @@ func tokenize(_ source: String, options: PreprocessOptions = PreprocessOptions()
continue
}

throw JinjaError.syntaxError("Unexpected character: \(char)")
throw JinjaError.syntax("Unexpected character: \(char)")
}

return tokens
Expand Down
26 changes: 13 additions & 13 deletions Sources/Parser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func parse(tokens: [Token]) throws -> Program {
let prev = tokens[current]
current += 1
if prev.type != type {
throw JinjaError.parserError("Parser Error: \(error). \(prev.type) != \(type).")
throw JinjaError.parser("Parser Error: \(error). \(prev.type) != \(type).")
}

return prev
Expand All @@ -36,7 +36,7 @@ func parse(tokens: [Token]) throws -> Program {
argument = KeywordArgumentExpression(key: identifier, value: value as! Expression)
}
else {
throw JinjaError.syntaxError("Expected identifier for keyword argument")
throw JinjaError.syntax("Expected identifier for keyword argument")
}
}

Expand Down Expand Up @@ -100,12 +100,12 @@ func parse(tokens: [Token]) throws -> Program {
}

if slices.isEmpty {
throw JinjaError.syntaxError("Expected at least one argument for member/slice expression")
throw JinjaError.syntax("Expected at least one argument for member/slice expression")
}

if isSlice {
if slices.count > 3 {
throw JinjaError.syntaxError("Expected 0-3 arguments for slice expression")
throw JinjaError.syntax("Expected 0-3 arguments for slice expression")
}

return SliceExpression(
Expand Down Expand Up @@ -135,7 +135,7 @@ func parse(tokens: [Token]) throws -> Program {
else {
property = try parsePrimaryExpression()
if !(property is Identifier) {
throw JinjaError.syntaxError("Expected identifier following dot operator")
throw JinjaError.syntax("Expected identifier following dot operator")
}
}

Expand Down Expand Up @@ -166,7 +166,7 @@ func parse(tokens: [Token]) throws -> Program {
current += 1
var filter = try parsePrimaryExpression()
if !(filter is Identifier) {
throw JinjaError.syntaxError("Expected identifier for the test")
throw JinjaError.syntax("Expected identifier for the test")
}

if typeof(.openParen) {
Expand Down Expand Up @@ -202,7 +202,7 @@ func parse(tokens: [Token]) throws -> Program {
operand = TestExpression(operand: operand as! Expression, negate: negate, test: test)
}
else {
throw JinjaError.syntaxError("Expected identifier for the test")
throw JinjaError.syntax("Expected identifier for the test")
}
}
return operand
Expand Down Expand Up @@ -380,7 +380,7 @@ func parse(tokens: [Token]) throws -> Program {
current += 1
let expression = try parseExpressionSequence()
if tokens[current].type != .closeParen {
throw JinjaError.syntaxError("Expected closing parenthesis, got \(tokens[current].type) instead")
throw JinjaError.syntax("Expected closing parenthesis, got \(tokens[current].type) instead")
}
current += 1
return expression
Expand Down Expand Up @@ -416,7 +416,7 @@ func parse(tokens: [Token]) throws -> Program {

return ObjectLiteral(value: values)
default:
throw JinjaError.syntaxError("Unexpected token: \(token.type)")
throw JinjaError.syntax("Unexpected token: \(token.type)")
}
}

Expand Down Expand Up @@ -449,7 +449,7 @@ func parse(tokens: [Token]) throws -> Program {
let loopVariable = try parseExpressionSequence(primary: true)

if !(loopVariable is Identifier || loopVariable is TupleLiteral) {
throw JinjaError.syntaxError(
throw JinjaError.syntax(
"Expected identifier/tuple for the loop variable, got \(type(of:loopVariable)) instead"
)
}
Expand All @@ -469,7 +469,7 @@ func parse(tokens: [Token]) throws -> Program {
return For(loopvar: loopVariable, iterable: iterable as! Expression, body: body)
}

throw JinjaError.syntaxError(
throw JinjaError.syntax(
"Expected identifier/tuple for the loop variable, got \(type(of:loopVariable)) instead"
)
}
Expand All @@ -496,7 +496,7 @@ func parse(tokens: [Token]) throws -> Program {
try expect(type: .endFor, error: "Expected endfor token")
try expect(type: .closeStatement, error: "Expected %} token")
default:
throw JinjaError.syntaxError("Unknown statement type: \(tokens[current].type)")
throw JinjaError.syntax("Unknown statement type: \(tokens[current].type)")
}

return result
Expand All @@ -521,7 +521,7 @@ func parse(tokens: [Token]) throws -> Program {
case .openExpression:
return try parseJinjaExpression()
default:
throw JinjaError.syntaxError("Unexpected token type: \(tokens[current].type)")
throw JinjaError.syntax("Unexpected token type: \(tokens[current].type)")
}
}

Expand Down
Loading

0 comments on commit 5371bf6

Please sign in to comment.