Scanner working for single character tokens.
This commit is contained in:
parent
3d0f3b95d5
commit
d8dd8fd49a
6 changed files with 241 additions and 0 deletions
20
golox/cmd/golox/main.go
Normal file
20
golox/cmd/golox/main.go
Normal 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()
|
||||
}
|
||||
}
|
||||
18
golox/internal/runner/errors.go
Normal file
18
golox/internal/runner/errors.go
Normal 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.
|
||||
}
|
||||
37
golox/internal/runner/runner.go
Normal file
37
golox/internal/runner/runner.go
Normal 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)
|
||||
}
|
||||
}
|
||||
80
golox/internal/runner/scanner.go
Normal file
80
golox/internal/runner/scanner.go
Normal 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)
|
||||
}
|
||||
33
golox/internal/runner/token.go
Normal file
33
golox/internal/runner/token.go
Normal 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,
|
||||
)
|
||||
}
|
||||
53
golox/internal/runner/token_type.go
Normal file
53
golox/internal/runner/token_type.go
Normal 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"
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue