• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

[SWIFT]Obsługa błędów w prostym kalkulatorze

VPS Starter Arubacloud
0 głosów
761 wizyt
pytanie zadane 7 marca 2017 w Android, Swift, Symbian przez Piotrinformatyk Początkujący (340 p.)

Witam :) Pisałem wcześniej, ale nikt nie odpowiedział: https://forum.pasja-informatyki.pl/232719/swift-obsluga-bledow-mnozenie-i-dzielenie-przed-dodawaniem-i-odejmowaniem?show=232719#q232719 

Zmieniłem kod na tyle, że myślę, że lepiej będzie jak nowe pytanie zadam. Gdyby nie jeden błąd to pewnie bym nie napisał, ale nie rozumiem skąd on wynika:

Błąd: Expected declaration. Jak rozwinę to mi pisze: In declaration of 'Parser'. Znajduje się on przy wywoływaniu funkcji na samym dole. 

Uczę się z książki i mam dodać odejmowanie (zrobiłem bardzo szybko), mnożenie i dzielenie (połowicznie bo nie uwzględnia pierwszeństwa co próbuję zrobić) i dodanie index-u do komunikatu błędu(tak średnio wiem co mam zrobić).

Ktoś pomoże mi zrozumieć co źle robię? Najlepiej też z tym indexem jakby mi ktoś pomógł

 


enum Token {
    //rodzaje tokenów
    case Number(Int)
    case Plus
    case Minus
    case Division
    case Multiplication
    case OpeningBracket
    case ClosingBracket
}

//analizator leksykalny - budowa
class Lexer {
    //String.CharacterView ma właściwości startIndex i endIndex
    let input: String.CharacterView
    var position: String.CharacterView.Index
    
    enum theError: Error {                        //zgłasza błąd
        case IncorrectCharacter(Character)
    }
    
    init(input: String) {
        self.input = input.characters             //inicjalizujemy właściwośc wraz z danymi wejściowymi,ma wartość wskazującą początek tych danych wejściowych
        self.position = self.input.startIndex
    }
    
    //może być wywołana zawsze
    func peek() -> Character? {                  //spradzenie kolejnego znaku w danych wejściowych. Wskazuje analizatorowi, że zostały przetworzone wszystkie dane wejściowe
        guard position < input.endIndex else {
            return nil
        }
        return input[position]
    }
    
    //IMPLEMENTACJA ALGORYTMU ANALIZATORA
    
    //można wywołać tylko wtedy, gdy aktualne położenie nie jest końcem danych wejściowych
    func advance() {                                                //przejście do kolejnego znaku
        assert(position < input.endIndex, "You can not go beyond the data input") //gdy false - wpadnięcie w pułapkę i wyświetlenie komunikatu
        position = input.index(after: position)
    }
    func getNumber () -> Int {              //wyodrębnia liczby całkowite z danych wejściowych
        var value = 0
        
        while let nextCharacter = peek() {
            switch nextCharacter {
            case "0"..."9":
                let digitValue = Int(String(nextCharacter))!
                value = 10*value + digitValue
                advance()
            default:
                return value
            }
        }
        return value
    }
    func lex() throws -> [Token] { //throws, czyli funkcja/metoda może wygenerować błąd
        var tokens = [Token]()      //tablica z tokenami
        
        while let nextCharacter = peek() {   //przetworzenie wszystkich danych wejściowych
            switch nextCharacter {
             case "(":
                tokens.append(.OpeningBracket)
                advance()
                
            case ")":
                tokens.append(.ClosingBracket)
            case "0"..."9":
                let value = getNumber()
                tokens.append(.Number(value))
                
            case "*":
                tokens.append(.Multiplication)
                advance()
                
            case "/":
                tokens.append(.Multiplication)
                advance()
                
            case "+":
                tokens.append(.Plus)
                advance()
                
            case "-":
                tokens.append(.Minus)
                advance()
                
            case " ":
                advance()
                
            default:
                throw theError.IncorrectCharacter(nextCharacter)   //throws  zatrzymuje funkcje i przekazuje błąd do komponentu wywołąjącego dany błąd zgodnego z protokołem Error
            }
        }
        return tokens
    }
}

class Parser {
    let tokens: [Token]
    var position = 0
    
    enum theError: Error {
        case unexpectedEndOfOutput
        case incorrectToken(Token)
    }
    
    init(tokens: [Token]) {
        self.tokens = tokens
    }
    
    func getNextToken() -> Token? {                     //peek() i advance() w jedną
        guard position < tokens.count else {
            return nil
        }
        let token = tokens[position]
        position += 1
        return token
    }
    
    func getNumber() throws -> Int {
        guard let token = getNextToken() else {
            throw theError.unexpectedEndOfOutput
        }
        switch token {
        case .Number(let value):
            return value
        case .Division:
            throw theError.incorrectToken(token)
        case .Multiplication:
            throw theError.incorrectToken(token)
        case .Plus:
            throw theError.incorrectToken(token)
        case .Minus:
            throw theError.incorrectToken(token)
        case .OpeningBracket:
            throw theError.incorrectToken(token)
        case .ClosingBracket:
            throw theError.incorrectToken(token)
        }
    }
    
    func parse() throws -> Int {
        var value = try getNumber()
        
        while let token = getNextToken() {
            switch token {
            case .OpeningBracket:
                try getBrackets()
            
            case .ClosingBracket:
                try getNumber()
                
            case .Multiplication:
                let nextNumber = try getNumber()
                value *= nextNumber
                
            case .Division:
                let nextNumber = try getNumber()
                value / nextNumber
                
            case .Plus:
                let nextNumber = try getNumber()
                value += nextNumber
                
            case .Minus:
                let nextNumber = try getNumber()
                value -= nextNumber
                
            case .Number:
                throw theError.incorrectToken(token)
            }
        }
        return value
    }
    
    func getBrackets() throws {
        var value = try getNumber()
        
        while let token = getNextToken() {
            switch token {
            case .Plus:
                let nextNumber = try getNumber()
                value += nextNumber
            case .Minus:
                let nextNumber = try getNumber()
                value -= nextNumber
            }
        }
    
}

func evaluate(input: String) {
    print("Calculation: \(input)")
    
    let lexer = Lexer(input: input)
    do {
        //wprowadza nowy zasięg, jeżeli jakieś wywołanie try spowoduje zgłoszenie błędu nastąpi wykonanie odpowiedniego bloku catch
        let tokens = try lexer.lex()
        print("Data output lexical analyzer: \(tokens)")
        
        let parser = Parser(tokens: tokens)
        let result = try parser.parse()
        print("Output parser: \(result)")
    }
    catch Lexer.theError.IncorrectCharacter(let character) {                        //zgłoszona wartość błędu jest przypisywana stłej error
        print("Data output contains invalid character: \(character)")
    }
    catch Parser.theError.unexpectedEndOfOutput {
        print("Unexpected end of data output during processing.")
    }
    catch Parser.theError.incorrectToken(let token) {
        print("Incorrect token during processing: \(token)")
    }
    catch {
        print("Following an unexpected error: \(error).")
    }
}

evaluate(input: "10 + 5 + 3")
evaluate(input: "10 + + 3")
evaluate(input: "10 - 5 + 3")
evaluate(input: "(10 * 3) - (3 * 5)")

 

komentarz 8 marca 2017 przez Raymond.Z Obywatel (1,800 p.)
Brakuje klamerki zamykające klasę po metodzie evaluate.
komentarz 8 marca 2017 przez Piotrinformatyk Początkujący (340 p.)
Sprawdziłem i nie brakuje nigdzie klamerki. Jak dodaję każe mi ją usunąć.
komentarz 8 marca 2017 przez Raymond.Z Obywatel (1,800 p.)
No ja z tego co widzę to brakuje. Przynajmniej patrząc na ten kod.

http://ideone.com/91dxow

 

Zobacz na funkcję getBrackets(), ma klamerke zamykającą dla switcha... while'a i jedna klamerka taka wskazująca jakbyś chciał zamknąć klasę... a gdzie dla samej funkcji jest?
komentarz 8 marca 2017 przez Piotrinformatyk Początkujący (340 p.)
A no jednak ślepy jestem ;) O dziwo Xcode pozwolił zawinąć  zarówno funkcję jak i konstrukcję switch. Musiałem dodać blok default, a teraz ciągle stoi na uruchamia ...
komentarz 8 marca 2017 przez Piotrinformatyk Początkujący (340 p.)
uruchamianiu*
komentarz 8 marca 2017 przez Raymond.Z Obywatel (1,800 p.)
Wiesza Ci się przez te nawiasy, dlaczego? Do tego już sam musisz dojść, więcej Cię to nauczy niz gotowa odpowiedź ode mnie. Analiza kodu :-)
komentarz 8 marca 2017 przez Piotrinformatyk Początkujący (340 p.)
Do bloku default pododawałem try getNextNumber(), try getNumber() i try parse(). Oczywiście nie wszystkie naraz, tylko osobno, aby sprawdzić skutek. Przy każdym muli niestety, ale jak usunę nawias, liczbę lub znak to szybko reaguję. Stoi w debug area na: "Calculation: (10 * 3) - (3 * 5)". To oznacza, że wpadł w pułapkę liczenia, tak?
komentarz 8 marca 2017 przez Piotrinformatyk Początkujący (340 p.)
Próbuję do tego dojść, ale nie widzę, gdzie miałby się zaciąć. Chodzi o to, że po nawiasie zamykającym szuka liczby? Nie powinno dać wtedy komunikatu o nieprawidłowym tokenie zamiast ciągle się uruchamiać?
komentarz 8 marca 2017 przez Raymond.Z Obywatel (1,800 p.)
Najprostszy sposób krok po kroku sprawdzaj w którym momencie się zapętla i pomyśl dlaczego tak jest. Mogą to być nawet printy.

Polecam zacząć po kolei od wyświetlenia "Calculation", bo to ostatni komunikat, gdzie wiadomo, że wszystko jest ok.
komentarz 8 marca 2017 przez Piotrinformatyk Początkujący (340 p.)
Zauważyłem już pierwszą nieprawidłowość. W funkcji lex() w bloku ")" times ciągle rośnie. To oznacza, że się zapętliło sprawdzanie tego znaku, nie?
komentarz 8 marca 2017 przez Raymond.Z Obywatel (1,800 p.)
Tak. Warto zauważyć, że przy każdym innym znaku masz wywołanie funkcji advance(), może jej brakuje w tym bloku|?
komentarz 8 marca 2017 przez Piotrinformatyk Początkujący (340 p.)
Podczas edycji tego bloku musiałem przypadkiem usunąć advance(). Przeszło dalej :) Przetworzyło wszystkie znaki  i pokazało komunikat:

Incorrect token during processing: OpeningBracket
komentarz 8 marca 2017 przez Raymond.Z Obywatel (1,800 p.)
No z tym już sam sobie musisz poradzić to Twój kod i Twoje błędy, na łatwiznę po gotową odpowiedź nie ma co chodzić :-)
komentarz 8 marca 2017 przez Piotrinformatyk Początkujący (340 p.)

Dostając gotową odpowiedź niewiele się nowicjusz nie nauczy pewnie. Choć są jakieś wyjątki od tej reguły?

 

Pozbyłem się nawiasu, ale ciągle mam problem z minusem.

Mój aktualny kod:

enum Token {
    //rodzaje tokenów
    case Number(Int)
    case Plus
    case Minus
    case Division
    case Multiplication
//    case OpeningBracket
//    case ClosingBracket
}

//analizator leksykalny - budowa
class Lexer {
    //String.CharacterView ma właściwości startIndex i endIndex
    let input: String.CharacterView
    var position: String.CharacterView.Index
    
    enum theError: Error {                        //zgłasza błąd
        case IncorrectCharacter(Character)
    }
    
    init(input: String) {
        self.input = input.characters             //inicjalizujemy właściwośc wraz z danymi wejściowymi,ma wartość wskazującą początek tych danych wejściowych
        self.position = self.input.startIndex
    }
    
    //może być wywołana zawsze
    func peek() -> Character? {                  //spradzenie kolejnego znaku w danych wejściowych. Wskazuje analizatorowi, że zostały przetworzone wszystkie dane wejściowe
        guard position < input.endIndex else {
            return nil
        }
        return input[position]
    }
    
    //IMPLEMENTACJA ALGORYTMU ANALIZATORA
    
    //można wywołać tylko wtedy, gdy aktualne położenie nie jest końcem danych wejściowych
    func advance() {                                                //przejście do kolejnego znaku
        assert(position < input.endIndex, "You can not go beyond the data input") //gdy false - wpadnięcie w pułapkę i wyświetlenie komunikatu
        position = input.index(after: position)
    }
    func getNumber () -> Int {              //wyodrębnia liczby całkowite z danych wejściowych
        var value = 0
        
        while let nextCharacter = peek() {
            switch nextCharacter {
            case "0"..."9":
                let digitValue = Int(String(nextCharacter))!
                value = 10*value + digitValue
                advance()
            default:
                return value
            }
        }
        return value
    }
    func lex() throws -> [Token] { //throws, czyli funkcja/metoda może wygenerować błąd
        var tokens = [Token]()      //tablica z tokenami
        
        while let nextCharacter = peek() {   //przetworzenie wszystkich danych wejściowych
            switch nextCharacter {
//             case "(":
//                tokens.append(.OpeningBracket)
//                advance()
//                
//            case ")":
//                tokens.append(.ClosingBracket)
//                advance()
//                
            case "0"..."9":
                let value = getNumber()
                tokens.append(.Number(value))
                
            case "*":
                tokens.append(.Multiplication)
                advance()
                
            case "/":
                tokens.append(.Multiplication)
                advance()
                
            case "+":
                tokens.append(.Plus)
                advance()
                
            case "-":
                tokens.append(.Minus)
                advance()
                
            case " ":
                advance()
                
            default:
                throw theError.IncorrectCharacter(nextCharacter)   //throws  zatrzymuje funkcje i przekazuje błąd do komponentu wywołąjącego dany błąd zgodnego z protokołem Error
            }
        }
        return tokens
    }
}

class Parser {
    let tokens: [Token]
    var position = 0
    
    enum theError: Error {
        case unexpectedEndOfOutput
        case incorrectToken(Token)
    }
    
    init(tokens: [Token]) {
        self.tokens = tokens
    }
    
    func getNextToken() -> Token? {                     //peek() i advance() w jedną
        guard position < tokens.count else {
            return nil
        }
        let token = tokens[position]
        position += 1
        return token
    }
    
    func getNumber() throws -> Int {
        guard let token = getNextToken() else {
            throw theError.unexpectedEndOfOutput
        }
        switch token {
        case .Number(let value):
            return value
        case .Division:
            throw theError.incorrectToken(token)
        case .Multiplication:
            throw theError.incorrectToken(token)
        case .Plus:
            throw theError.incorrectToken(token)
        case .Minus:
            throw theError.incorrectToken(token)
//        case .OpeningBracket:
//            throw theError.incorrectToken(token)
//        case .ClosingBracket:
//            throw theError.incorrectToken(token)
        }
    }
    
    func parse() throws -> Int {
        var value = try getNumber()
        
        while let token = getNextToken() {
            switch token {
//            case .OpeningBracket:
//                try getBrackets()
//            
//            case .ClosingBracket:
//                 try getBrackets()
//                
            case .Multiplication:
                let nextNumber = try getNumber()
                value *= nextNumber
                try anotherPlusMinus()
                
            case .Division:
                let nextNumber = try getNumber()
                value / nextNumber
                try anotherPlusMinus()
                
            case .Plus:
                let nextNumber = try getNumber()
                value += nextNumber
                
            case .Minus:
                let nextNumber = try getNumber()
                value -= nextNumber
                
            case .Number:
                throw theError.incorrectToken(token)
            }
        }
        return value
    }
    
    func anotherPlusMinus() throws {
        var value = try getNumber()
        
        while let token = getNextToken() {
            switch token {
            case .Number:
                let nextNumber = try getNumber()
                value += nextNumber
                switch token {
                case .Plus:
                    let nextNumber = try getNumber()
                    value += nextNumber
                case .Minus:
                    let nextNumber = try getNumber()
                    value -= nextNumber
                    
                default:
                   try parse()

                }
                
            default:
                try parse()
                
            }
        }
    }
    
}

func evaluate(input: String) {
    print("Calculation: \(input)")
    
    let lexer = Lexer(input: input)
    do {
        //wprowadza nowy zasięg, jeżeli jakieś wywołanie try spowoduje zgłoszenie błędu nastąpi wykonanie odpowiedniego bloku catch
        let tokens = try lexer.lex()
        print("Data output lexical analyzer: \(tokens)")
        
        let parser = Parser(tokens: tokens)
        let result = try parser.parse()
        print("Output parser: \(result)")
    }
    catch Lexer.theError.IncorrectCharacter(let character) {                        //zgłoszona wartość błędu jest przypisywana stłej error
        print("Data output contains invalid character: \(character)")
    }
    catch Parser.theError.unexpectedEndOfOutput {
        print("Unexpected end of data output during processing.")
    }
    catch Parser.theError.incorrectToken(let token) {
        print("Incorrect token during processing: \(token)")
    }
    catch {
        print("Following an unexpected error: \(error).")
    }
}

evaluate(input: "10 + 5 + 3")
evaluate(input: "10 + + 3")
evaluate(input: "10 - 5 + 3")
evaluate(input: "10 * 3 - 3 * 5")

 

komentarz 8 marca 2017 przez Piotrinformatyk Początkujący (340 p.)
Można poprosić o podpowiedź? :)
komentarz 8 marca 2017 przez Raymond.Z Obywatel (1,800 p.)
No widać ewidentnie, że throwuje Ci incorrectToken, kiedy jest minus... szukasz, gdzie to jest no i znajduje się to w funkcji getNumber... i tak dalej... dlaczego tam jest -? No kij wie... jest tam wywoływan metod getNextToken()... co się w niej dzieję takiego, że zwraca tego minusa... i myślisz i myślisz... aż przyniesie skutek, a jak nie ma to odpocznij godzine, dwie, dzień, tydzień.

Jutro zobaczę jak nie dasz rady, ale polecam samemu pogłówkować.
komentarz 8 marca 2017 przez Piotrinformatyk Początkujący (340 p.)
Próbuję :) Na dzisiaj dam sobie spokój już i jak wrócę z dni otwartych Politechniki Wrocławskiej (chcę zobaczyć oba wydziały gdzie jest informatyka w tym tygodniu) spróbuje znowu. Oki :)

Zaloguj lub zarejestruj się, aby odpowiedzieć na to pytanie.

Podobne pytania

0 głosów
1 odpowiedź 2,415 wizyt
pytanie zadane 29 sierpnia 2017 w C i C++ przez Mithriandil Początkujący (250 p.)
0 głosów
2 odpowiedzi 271 wizyt
pytanie zadane 25 stycznia 2017 w C i C++ przez Akiro Bywalec (2,910 p.)

93,078 zapytań

142,041 odpowiedzi

321,445 komentarzy

62,422 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj

Wprowadzenie do ITsec, tom 2

Można już zamawiać tom 2 książki "Wprowadzenie do bezpieczeństwa IT" - będzie to około 650 stron wiedzy o ITsec (17 rozdziałów, 14 autorów, kolorowy druk).

Planowana premiera: 30.09.2024, zaś planowana wysyłka nastąpi w drugim tygodniu października 2024.

Warto preorderować, tym bardziej, iż mamy dla Was kod: pasja (użyjcie go w koszyku), dzięki któremu uzyskamy dodatkowe 15% zniżki! Dziękujemy zaprzyjaźnionej ekipie Sekuraka za kod dla naszej Społeczności!

...