feat: add lexer

This commit is contained in:
Forrest Loomis 2023-09-09 20:54:52 -04:00
parent 8129c36220
commit 40cafa53f0
7 changed files with 168 additions and 0 deletions

12
Dockerfile Normal file
View File

@ -0,0 +1,12 @@
ARG GO_VERSION=1.21.1
FROM --platform=${BUILDPLATFORM} golang:${GO_VERSION}-alpine AS base
WORKDIR /src
# RUN --mount=type=bind,target=go.mod \
# --mount=type=bind,target=go.sum \
# go mod download
FROM base as tests
WORKDIR /src
RUN --mount=type=bind,target=. \
go test ./...

15
compose.yaml Normal file
View File

@ -0,0 +1,15 @@
---
version: "3"
services:
base:
build:
target: base
volumes:
- .:/src
working_dir: /src
format:
extends:
service: base
entrypoint: "gofmt -w *.go"

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module git.cybercyst.me/monkeylang
go 1.21.1

60
lexer/lexer.go Normal file
View File

@ -0,0 +1,60 @@
package lexer
import "git.cybercyst.me/monkeylang/token"
type Lexer struct {
input string
position int
readPosition int
ch byte
}
func New(input string) *Lexer {
l := &Lexer{input: input}
l.readChar()
return l
}
func (l *Lexer) readChar() {
if l.readPosition >= len(l.input) {
l.ch = 0
} else {
l.ch = l.input[l.readPosition]
}
l.position = l.readPosition
l.readPosition += 1
}
func (l *Lexer) NextToken() token.Token {
var tok token.Token
switch l.ch {
case '=':
tok = newToken(token.ASSIGN, l.ch)
case ';':
tok = newToken(token.SEMICOLON, l.ch)
case '(':
tok = newToken(token.LPAREN, l.ch)
case ')':
tok = newToken(token.RPAREN, l.ch)
case ',':
tok = newToken(token.COMMA, l.ch)
case '+':
tok = newToken(token.PLUS, l.ch)
case '{':
tok = newToken(token.LBRACE, l.ch)
case '}':
tok = newToken(token.RBRACE, l.ch)
case 0:
tok.Literal = ""
tok.Type = token.EOF
}
l.readChar()
return tok
}
func newToken(tokenType token.TokenType, ch byte) token.Token {
return token.Token{Type: tokenType, Literal: string(ch)}
}

40
lexer/lexer_test.go Normal file
View File

@ -0,0 +1,40 @@
package lexer
import (
"testing"
"git.cybercyst.me/monkeylang/token"
)
func TextNextToken(t *testing.T) {
input := `=+(){},;`
tests := []struct {
expectedType token.TokenType
expectedLiteral string
}{
{token.ASSIGN, "="},
{token.PLUS, "+"},
{token.LPAREN, "("},
{token.RPAREN, ")"},
{token.LBRACE, "{"},
{token.RBRACE, "}"},
{token.COMMA, ","},
{token.SEMICOLON, ";"},
}
l := New(input)
for i, tt := range tests {
tok := l.NextToken()
if tok.Type != tt.expectedType {
t.Fatalf("tests[%d] - tokentype wrong. expected=%q, got=%q",
i, tt.expectedType, tok.Type)
}
if tok.Literal != tt.expectedLiteral {
t.Fatalf("tests[%d] - literal wrong. expected=%q, got=%q",
i, tt.expectedLiteral, tok.Literal)
}
}
}

7
main.go Normal file
View File

@ -0,0 +1,7 @@
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}

31
token/token.go Normal file
View File

@ -0,0 +1,31 @@
package token
type TokenType string
type Token struct {
Type TokenType
Literal string
}
const (
ILLEGAL = "ILLEGAL"
EOF = "EOF" // Identifiers + literals
IDENT = "IDENT" // add, foobar, x, y, ...
INT = "INT" // 1343456
// Operators
ASSIGN = "="
PLUS = "+"
// Delimiters
COMMA = ","
SEMICOLON = ";"
LPAREN = "("
RPAREN = ")"
LBRACE = "{"
RBRACE = "}"
// Keywords
FUNCTION = "FUNCTION"
LET = "LET"
)