Scanner working for single character tokens.

This commit is contained in:
Mariano Uvalle 2023-05-06 22:34:12 +00:00
parent 3d0f3b95d5
commit d8dd8fd49a
6 changed files with 241 additions and 0 deletions

20
golox/cmd/golox/main.go Normal file
View file

@ -0,0 +1,20 @@
package main
import (
"fmt"
"os"
"github.com/AYM1607/crafting-interpreters/golox/internal/runner"
)
func main() {
argc := len(os.Args)
if argc > 2 {
fmt.Println("Usage: golox [script]")
os.Exit(64)
} else if argc == 2 {
} else {
runner.RunPrompt()
}
}

View file

@ -0,0 +1,18 @@
package runner
import "fmt"
func emitError(line int, message string) {
report(line, "", message)
}
func report(line int, where, message string) {
fmt.Printf(
"[%d] Error%s: %s\n",
line,
where,
message,
)
// TODO: The book sets `hadError` as true here, need to figure out where
// that's used.
}

View file

@ -0,0 +1,37 @@
package runner
import (
"bufio"
"fmt"
"os"
)
func RunPrompt() {
s := bufio.NewScanner(os.Stdin)
fmt.Print("> ")
for s.Scan() {
line := s.Text()
Run(line)
// TODO: resed hadError wherever it is set.
fmt.Print("> ")
}
}
func RunFile(path string) error {
fBytes, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("could not read script file: %w", err)
}
Run(string(fBytes))
// TODO: check hadError and exit with a 65 code if so.
return nil
}
func Run(source string) {
s := NewScanner(source)
tokens := s.ScanTokens()
for _, t := range tokens {
fmt.Println(t)
}
}

View file

@ -0,0 +1,80 @@
package runner
type Scanner struct {
source string
// State.
tokens []Token
start int
current int
line int
}
func NewScanner(source string) *Scanner {
return &Scanner{
source: source,
tokens: []Token{},
start: 0,
current: 0,
line: 1,
}
}
func (s *Scanner) ScanTokens() []Token {
for !s.isAtEnd() {
s.start = s.current
s.scanToken()
}
s.tokens = append(s.tokens, NewToken(EOF, "", nil, s.line))
return s.tokens
}
func (s *Scanner) scanToken() {
c := s.advance()
switch c {
case '(':
s.addToken(LPAREN)
case ')':
s.addToken(RPAREN)
case '{':
s.addToken(LBRACE)
case '}':
s.addToken(RBRACE)
case ',':
s.addToken(COMMA)
case '.':
s.addToken(DOT)
case '-':
s.addToken(MINUS)
case '+':
s.addToken(PLUS)
case ';':
s.addToken(SEMI)
case '*':
s.addToken(STAR)
}
}
func (s *Scanner) advance() byte {
idx := s.current
s.current += 1
return s.source[idx]
}
func (s *Scanner) addToken(typ TokenType) {
s.addTokenWithLiteral(typ, nil)
}
func (s *Scanner) addTokenWithLiteral(typ TokenType, literal interface{}) {
lexme := s.source[s.start:s.current]
s.tokens = append(
s.tokens,
NewToken(typ, lexme, literal, s.line),
)
}
func (s *Scanner) isAtEnd() bool {
return s.current >= len(s.source)
}

View file

@ -0,0 +1,33 @@
package runner
import "fmt"
type Token struct {
Type TokenType
Lexme string
Literal interface{}
Line int
}
func NewToken(
typ TokenType,
lexme string,
lit interface{},
line int,
) Token {
return Token{
Type: typ,
Lexme: lexme,
Literal: lit,
Line: line,
}
}
func (t Token) String() string {
return fmt.Sprintf(
"%s %s %v",
t.Type,
t.Lexme,
t.Literal,
)
}

View file

@ -0,0 +1,53 @@
package runner
type TokenType string
const (
// Single character tokens.
LPAREN TokenType = "LPAREN"
RPAREN TokenType = "RPAREN"
LBRACE TokenType = "LBRACE"
RBRACE TokenType = "RBRACE"
COMMA TokenType = "COMMA"
DOT TokenType = "DOT"
MINUS TokenType = "MINUS"
PLUS TokenType = "PLUS"
SEMI TokenType = "SEMI"
SLASH TokenType = "SLASH"
STAR TokenType = "STAR"
// One or two character tokens.
BANG TokenType = "BANG"
BANG_EQUAL TokenType = "BANG_EQUAL"
EQUAL TokenType = "EQUAL"
EQUAL_EQUAL TokenType = "EQUAL_EQUAL"
GT TokenType = "GT"
GTE TokenType = "GTE"
LT TokenType = "LT"
LTE TokenType = "LTE"
// Literals.
IDENT TokenType = "IDENT"
STRING TokenType = "STRING"
NUMBER TokenType = "NUMBER"
// Keywords
AND TokenType = "AND"
CLASS TokenType = "CLASS"
ELSE TokenType = "ELSE"
FALSE TokenType = "FALSE"
FUN TokenType = "FUN"
FOR TokenType = "FOR"
IF TokenType = "IF"
NIL TokenType = "NIL"
OR TokenType = "OR"
PRINT TokenType = "PRINT"
RETURN TokenType = "RETURN"
SUPER TokenType = "SUPER"
THIS TokenType = "THIS"
TRUE TokenType = "TRUE"
VAR TokenType = "VAR"
WHILE TokenType = "WHILE"
EOF TokenType = "EOF"
)