Browse Source

§1.3 first passing test

Frederic G. MARAND 5 years ago
parent
commit
515a961a6e
9 changed files with 185 additions and 0 deletions
  1. 1 0
      .gitignore
  2. 6 0
      .idea/misc.xml
  3. 8 0
      .idea/modules.xml
  4. 14 0
      .idea/runConfigurations/Test_Lexer.xml
  5. 6 0
      .idea/vcs.xml
  6. 8 0
      .idea/waiig15.iml
  7. 67 0
      lexer/lexer.go
  8. 41 0
      lexer/lexer_test.go
  9. 34 0
      token/token.go

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+.idea/workspace.xml

+ 6 - 0
.idea/misc.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="JavaScriptSettings">
+    <option name="languageLevel" value="ES6" />
+  </component>
+</project>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/waiig15.iml" filepath="$PROJECT_DIR$/.idea/waiig15.iml" />
+    </modules>
+  </component>
+</project>

+ 14 - 0
.idea/runConfigurations/Test_Lexer.xml

@@ -0,0 +1,14 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="Test Lexer" type="GoTestRunConfiguration" factoryName="Go Test" singleton="true">
+    <module name="waiig15" />
+    <working_directory value="$PROJECT_DIR$/lexer" />
+    <go_parameters value="-i" />
+    <parameters value="./lexer" />
+    <framework value="gotest" />
+    <kind value="FILE" />
+    <package value="fgm/waiig15" />
+    <directory value="$PROJECT_DIR$/" />
+    <filePath value="$PROJECT_DIR$/lexer/lexer_test.go" />
+    <method v="2" />
+  </configuration>
+</component>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>

+ 8 - 0
.idea/waiig15.iml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 67 - 0
lexer/lexer.go

@@ -0,0 +1,67 @@
+/*
+Lexer only supports single-byte character sets like ASCII.
+
+@TODO convert to Unicode / UTF-8.
+*/
+package lexer
+
+import (
+	"fgm/waiig15/token"
+	)
+
+type Lexer struct {
+	input        string
+	position     int  // current position in input (points to current char)
+	readPosition int  // current reading position in input (after current char)
+	ch           byte // current char under examination
+}
+
+func New(input string) *Lexer {
+	l := &Lexer{input: input}
+	l.readChar()
+	return l
+}
+
+// Give us the next character and advance our position in the input string.
+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) }
+}

+ 41 - 0
lexer/lexer_test.go

@@ -0,0 +1,41 @@
+package lexer
+
+import (
+	"fgm/waiig15/token"
+	"testing"
+)
+
+func TestNextToken(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, ";"},
+		{token.EOF, ""},
+	}
+
+	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)
+		}
+	}
+}

+ 34 - 0
token/token.go

@@ -0,0 +1,34 @@
+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"   // 123456, ...
+
+	// Operators.
+	ASSIGN = "="
+	PLUS   = "+"
+
+	// Delimiters.
+	COMMA     = ","
+	SEMICOLON = ";"
+
+	LPAREN = "("
+	RPAREN = ")"
+	LBRACE = "{"
+	RBRACE = "}"
+
+	// Keywords
+	FUNCTION = "FUNCTION"
+	LET      = "LET"
+)