Skip to content

Commit

Permalink
Add the ability to parse float numbers, Added tests for float numbers
Browse files Browse the repository at this point in the history
Fix lookAhead issue : when number of steps is greater than the available characters the method was not seeking back correctly.
  • Loading branch information
AnasMostefaoui committed Mar 8, 2018
1 parent cbd28b9 commit d7bb2e5
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 17 deletions.
47 changes: 38 additions & 9 deletions LoxInterpreter/LoxScanner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,24 +147,53 @@ public class LoxScanner : ScannerInterface {
}
}

func scanDigit(from character:Character) {
func scanInteger(from character:String) -> String? {
var numberString:String = "\(character)"
var isFloat:Bool = false
var aheadCharacter:String? = nil


// parse the integer part
repeat {
guard let validCharacter = cursor.nextCharacter() else {
break
guard let nextCharacter = cursor.lookAhead(by: 1),
nextCharacter.isDigit() else {
break
}
aheadCharacter = String(validCharacter)
guard validCharacter.isDigit() || (isFloat == false && validCharacter == ".") else {

guard let validCharacter = cursor.nextCharacter() else {
break
}


numberString.append(validCharacter)
} while cursor.endOfFile == false

return numberString
}

func scanDigit(from character:Character) {
var numberString:String = "\(character)"
var floatPart:String = ""

// parse the integer part
guard let scannedInteger = self.scanInteger(from: numberString) else {
// emit error
return
}

guard let number = Float(numberString) else {
numberString = scannedInteger

if cursor.lookAhead(by: 1) == ".", let nextCharacter = cursor.lookAhead(by: 2), nextCharacter.isDigit() {
// float part
guard let next = cursor.nextCharacter(), let floatScanned = self.scanInteger(from: String(next)) else {
// emit error
return
}

floatPart = floatScanned
}



let finalNumberString = numberString + floatPart
guard let number = Float(finalNumberString) else {
let error = LoxError(fileName: self.fileName,
filePath: self.filePath,
lineNumber: cursor.line,
Expand Down
8 changes: 8 additions & 0 deletions LoxInterpreter/ScannerCursor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ public class ScannerCursor {
return character
}

func nextCharacter(_ condition:(_ character:String) -> Bool) -> Character? {
guard let next = self.lookAhead(by: 1), condition(next) else {
return nil
}
return self.nextCharacter()
}

public func seek(by offset:Int) {
let boundIndex = offset < 0 ? source.startIndex : source.endIndex

Expand All @@ -62,6 +69,7 @@ public class ScannerCursor {

for _ in 0..<numberOfCharacter {
guard let character = self.nextCharacter(updateLine: false) else {
self.seek(by: -1 * characters.count)
return nil
}
characters.append(character)
Expand Down
33 changes: 30 additions & 3 deletions LoxInterpreter/StringExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,36 @@ extension Character {
public func isDigit() -> Bool {

let digits = CharacterSet.decimalDigits
for scalar in self.unicodeScalars where digits.contains(scalar) {
return true
var isDigit = false
self.unicodeScalars.forEach {
isDigit = digits.contains($0)
if isDigit == false {
return
}
}
return false
return isDigit
// for scalar in self.unicodeScalars where digits.contains(scalar) {
// return true
// }
// return false
}
}

extension String {
public func isDigit() -> Bool {

let digits = CharacterSet.decimalDigits
var isDigit = false
self.unicodeScalars.forEach {
isDigit = digits.contains($0)
if isDigit == false {
return
}
}
return isDigit
// for scalar in self.unicodeScalars where digits.contains(scalar) {
// return true
// }
// return false
}
}
29 changes: 24 additions & 5 deletions LoxInterpreterTests/UnitTests/ScannerTests/ScannerUnitTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ yep
func test_if_it_can_detect_string_lexem_without_extra_character_at_end() {
let source =
"""
"hi"1
"hi"k
"""
let expectedLines = 1

Expand All @@ -512,13 +512,13 @@ yep
XCTAssertTrue(errors.isEmpty == false)
XCTAssertNotNil(tokens.first)
XCTAssertTrue(tokens.first!.lexem == "\"hi\"")
XCTAssertTrue(tokens.first!.lexem != "\"hi\"1")
XCTAssertTrue(tokens.first!.lexem != "\"hi\"k")

}

// scan digits
func test_if_it_can_scan_digit() {
let source = "1963"
func test_if_it_can_scan_integer_digit() {
let source = "1963."
let expectedLines = 1

scanner.source = source
Expand All @@ -527,7 +527,7 @@ yep
let lastToken = tokens[tokens.index(before: tokens.endIndex)]

XCTAssertEqual(lastToken.line, expectedLines)
XCTAssertEqual(tokens.count, 2)
XCTAssertEqual(tokens.count, 3)
XCTAssertTrue(errors.isEmpty)
XCTAssertNotNil(tokens.first)
XCTAssertTrue(tokens.first!.type == TokenType.number)
Expand All @@ -536,4 +536,23 @@ yep
XCTAssertTrue(value == 1963)

}

func test_if_it_can_scan_float_digit() {
let source = "1963.500.13.3."
let expectedLines = 1

scanner.source = source
let tokens = scanner.scan()
let errors = scanner.errors
let lastToken = tokens[tokens.index(before: tokens.endIndex)]

XCTAssertEqual(lastToken.line, expectedLines)
XCTAssertEqual(tokens.count, 5)
XCTAssertTrue(errors.isEmpty)
XCTAssertNotNil(tokens.first)
XCTAssertTrue(tokens.first!.type == TokenType.number)
XCTAssertTrue(tokens.first!.lexem == "1963.500")
let value = tokens.first!.literal! as? Float
XCTAssertTrue(value == 1963.500)
}
}

0 comments on commit d7bb2e5

Please sign in to comment.