From 40cafa53f031615230a6a128b61545a048a3862a Mon Sep 17 00:00:00 2001 From: Forrest Loomis Date: Sat, 9 Sep 2023 20:54:52 -0400 Subject: [PATCH] feat: add lexer --- Dockerfile | 12 +++++++++ compose.yaml | 15 ++++++++++++ go.mod | 3 +++ lexer/lexer.go | 60 +++++++++++++++++++++++++++++++++++++++++++++ lexer/lexer_test.go | 40 ++++++++++++++++++++++++++++++ main.go | 7 ++++++ token/token.go | 31 +++++++++++++++++++++++ 7 files changed, 168 insertions(+) create mode 100644 Dockerfile create mode 100644 compose.yaml create mode 100644 go.mod create mode 100644 lexer/lexer.go create mode 100644 lexer/lexer_test.go create mode 100644 main.go create mode 100644 token/token.go diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..90dab28 --- /dev/null +++ b/Dockerfile @@ -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 ./... diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..eb6b932 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,15 @@ +--- +version: "3" + +services: + base: + build: + target: base + volumes: + - .:/src + working_dir: /src + + format: + extends: + service: base + entrypoint: "gofmt -w *.go" diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..8278314 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.cybercyst.me/monkeylang + +go 1.21.1 diff --git a/lexer/lexer.go b/lexer/lexer.go new file mode 100644 index 0000000..03565b6 --- /dev/null +++ b/lexer/lexer.go @@ -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)} +} diff --git a/lexer/lexer_test.go b/lexer/lexer_test.go new file mode 100644 index 0000000..5df7544 --- /dev/null +++ b/lexer/lexer_test.go @@ -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) + } + } +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..a3dd973 --- /dev/null +++ b/main.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("Hello, World!") +} diff --git a/token/token.go b/token/token.go new file mode 100644 index 0000000..23c9a9a --- /dev/null +++ b/token/token.go @@ -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" +)