diff --git a/golox/internal/runner/scanner.go b/golox/internal/runner/scanner.go index e5e9351..fff253d 100644 --- a/golox/internal/runner/scanner.go +++ b/golox/internal/runner/scanner.go @@ -1,6 +1,8 @@ package runner import ( + "strconv" + lerrors "github.com/AYM1607/crafting-interpreters/golox/internal/errors" ) @@ -101,6 +103,11 @@ func (s *Scanner) scanToken() { case '\n': s.line += 1 default: + // NOTE: adding this here to avoid listing all digits in a case. + if isDigit(c) { + s.scanNumber() + return + } lerrors.EmitError(s.line, "Unexpected character.") } } @@ -134,6 +141,14 @@ func (s *Scanner) peek() byte { return s.source[s.current] } +func (s *Scanner) peekNex() byte { + idx := s.current + 1 + if idx >= len(s.source) { + return 0 + } + return s.source[idx] +} + func (s *Scanner) scanString() { for s.peek() != '"' && !s.isAtEnd() { // Lox allows multi-line strings. @@ -156,6 +171,36 @@ func (s *Scanner) scanString() { s.addTokenWithLiteral(STRING, val) } +func (s *Scanner) scanNumber() { + // Consume all digits preceding a dot (if any) + for isDigit(s.peek()) { + s.advance() + } + + // Look for a decimal part. + // Only literals in the form 123 and 123.123 are allowed. + if s.peek() == '.' && isDigit(s.peekNex()) { + // Only consume the dot if we're sure the format is valid. + s.advance() + + // Consume the rest of the digis. + for isDigit(s.peek()) { + s.advance() + } + } + // NOTE: Ignoring error because we're sure the string follows the float + // format. This should probably still report it but will leave as-is + // for now. + val, _ := strconv.ParseFloat( + s.source[s.start:s.current], + 64, + ) + s.addTokenWithLiteral( + NUMBER, + val, + ) +} + // addToken produces a single token without a literal value. func (s *Scanner) addToken(typ TokenType) { s.addTokenWithLiteral(typ, nil) diff --git a/golox/internal/runner/scanner_util.go b/golox/internal/runner/scanner_util.go new file mode 100644 index 0000000..a9e6eac --- /dev/null +++ b/golox/internal/runner/scanner_util.go @@ -0,0 +1,5 @@ +package runner + +func isDigit(c byte) bool { + return c >= '0' && c <= '9' +}