feat: gasm-devkit 0.1.0 — GAsm lexer, parser, linter, formatter, LSP and amd64 assembler
Assisted-by: Qwen 3.8 Max Preview
This commit is contained in:
+108
@@ -0,0 +1,108 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package token defines the lexical tokens of Go's Plan 9 assembler (GAsm)
|
||||
// and the source positions attached to them. It has no dependencies and is
|
||||
// shared by the lexer, parser, formatter, linter and language server.
|
||||
package token
|
||||
|
||||
import "strconv"
|
||||
|
||||
// Kind classifies a lexical token.
|
||||
type Kind int
|
||||
|
||||
// The token kinds. The zero value is Illegal so that an uninitialised Token
|
||||
// is obviously invalid.
|
||||
const (
|
||||
Illegal Kind = iota
|
||||
EOF
|
||||
Newline
|
||||
Comment
|
||||
|
||||
// Literals and names.
|
||||
Ident // instruction mnemonic, label, register or symbol name
|
||||
Number // integer or floating-point literal (sign carried separately)
|
||||
String // "..."
|
||||
Rune // '.'
|
||||
|
||||
// Punctuation and operators.
|
||||
LParen // (
|
||||
RParen // )
|
||||
Comma // ,
|
||||
Plus // +
|
||||
Minus // -
|
||||
Star // *
|
||||
Slash // /
|
||||
Colon // :
|
||||
Dollar // $
|
||||
LAngle // <
|
||||
RAngle // >
|
||||
LShift // <<
|
||||
RShift // >>
|
||||
Arrow // ->
|
||||
At // @
|
||||
Hash // #
|
||||
)
|
||||
|
||||
var kindNames = map[Kind]string{
|
||||
Illegal: "ILLEGAL",
|
||||
EOF: "EOF",
|
||||
Newline: "NEWLINE",
|
||||
Comment: "COMMENT",
|
||||
Ident: "IDENT",
|
||||
Number: "NUMBER",
|
||||
String: "STRING",
|
||||
Rune: "RUNE",
|
||||
LParen: "(",
|
||||
RParen: ")",
|
||||
Comma: ",",
|
||||
Plus: "+",
|
||||
Minus: "-",
|
||||
Star: "*",
|
||||
Slash: "/",
|
||||
Colon: ":",
|
||||
Dollar: "$",
|
||||
LAngle: "<",
|
||||
RAngle: ">",
|
||||
LShift: "<<",
|
||||
RShift: ">>",
|
||||
Arrow: "->",
|
||||
At: "@",
|
||||
Hash: "#",
|
||||
}
|
||||
|
||||
// String returns a human-readable name for the kind.
|
||||
func (k Kind) String() string {
|
||||
if s, ok := kindNames[k]; ok {
|
||||
return s
|
||||
}
|
||||
return "Kind(" + strconv.Itoa(int(k)) + ")"
|
||||
}
|
||||
|
||||
// Position is a byte offset plus one-based line and column within a file.
|
||||
type Position struct {
|
||||
Offset int // byte offset, zero-based
|
||||
Line int // line number, one-based
|
||||
Column int // column number, one-based (in runes)
|
||||
}
|
||||
|
||||
// String renders the position as "line:column".
|
||||
func (p Position) String() string {
|
||||
return strconv.Itoa(p.Line) + ":" + strconv.Itoa(p.Column)
|
||||
}
|
||||
|
||||
// IsValid reports whether the position carries a real line number.
|
||||
func (p Position) IsValid() bool { return p.Line > 0 }
|
||||
|
||||
// Token is a single lexical token together with its literal text and span.
|
||||
type Token struct {
|
||||
Kind Kind
|
||||
Text string
|
||||
Pos Position // inclusive start
|
||||
End Position // exclusive end
|
||||
}
|
||||
|
||||
// String renders the token for diagnostics and debugging.
|
||||
func (t Token) String() string {
|
||||
return t.Pos.String() + " " + t.Kind.String() + " " + strconv.Quote(t.Text)
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package token
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestKindString(t *testing.T) {
|
||||
cases := map[Kind]string{
|
||||
EOF: "EOF",
|
||||
Ident: "IDENT",
|
||||
Number: "NUMBER",
|
||||
LParen: "(",
|
||||
LShift: "<<",
|
||||
Arrow: "->",
|
||||
Illegal: "ILLEGAL",
|
||||
}
|
||||
for k, want := range cases {
|
||||
if got := k.String(); got != want {
|
||||
t.Errorf("Kind(%d).String() = %q, want %q", int(k), got, want)
|
||||
}
|
||||
}
|
||||
// An out-of-range kind falls back to the numeric form.
|
||||
if got := Kind(9999).String(); got != "Kind(9999)" {
|
||||
t.Errorf("Kind(9999).String() = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPosition(t *testing.T) {
|
||||
p := Position{Offset: 10, Line: 3, Column: 7}
|
||||
if got := p.String(); got != "3:7" {
|
||||
t.Errorf("Position.String() = %q, want 3:7", got)
|
||||
}
|
||||
if !p.IsValid() {
|
||||
t.Error("position with a line should be valid")
|
||||
}
|
||||
var zero Position
|
||||
if zero.IsValid() {
|
||||
t.Error("zero position should be invalid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenString(t *testing.T) {
|
||||
tok := Token{Kind: Ident, Text: "MOVQ", Pos: Position{Line: 1, Column: 2}}
|
||||
got := tok.String()
|
||||
if got == "" || got[0] == ' ' {
|
||||
t.Errorf("Token.String() = %q", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user