initial commit
This commit is contained in:
commit
1ce2505427
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
compiler
|
129
compiler.go
Normal file
129
compiler.go
Normal file
@ -0,0 +1,129 @@
|
||||
package main
|
||||
|
||||
type PrimitiveType uint32
|
||||
|
||||
const (
|
||||
Primitive_I8 PrimitiveType = iota
|
||||
Primitive_I16
|
||||
Primitive_I32
|
||||
Primitive_I64
|
||||
Primitive_U8
|
||||
Primitive_U16
|
||||
Primitive_U32
|
||||
Primitive_U64
|
||||
)
|
||||
|
||||
type TypeType uint32
|
||||
|
||||
const (
|
||||
Type_Primitive TypeType = iota
|
||||
Type_Named
|
||||
Type_Array
|
||||
Type_Tuple
|
||||
)
|
||||
|
||||
type Type struct {
|
||||
Type TypeType
|
||||
Value any
|
||||
}
|
||||
|
||||
type NamedType struct {
|
||||
TypeName string
|
||||
}
|
||||
|
||||
type ArrayType struct {
|
||||
ElementType Type
|
||||
}
|
||||
|
||||
type TupleType struct {
|
||||
Types []Type
|
||||
}
|
||||
|
||||
type StatementType uint32
|
||||
|
||||
const (
|
||||
Statement_Expression StatementType = iota
|
||||
Statement_Block
|
||||
Statement_Return
|
||||
Statement_DeclareLocalVariable
|
||||
)
|
||||
|
||||
type Statement struct {
|
||||
Type StatementType
|
||||
Value any
|
||||
}
|
||||
|
||||
type BlockStatement struct {
|
||||
Block Block
|
||||
}
|
||||
|
||||
type ReturnStatement struct {
|
||||
Value *Expression
|
||||
}
|
||||
|
||||
type DeclareLocalVariableStatement struct {
|
||||
Variable string
|
||||
Initializer Expression
|
||||
}
|
||||
|
||||
type ExpressionType uint32
|
||||
|
||||
const (
|
||||
Expression_Assignment ExpressionType = iota
|
||||
Expression_Literal
|
||||
Expression_VariableReference
|
||||
Expression_Arithmetic
|
||||
)
|
||||
|
||||
type Expression struct {
|
||||
Type ExpressionType
|
||||
Value any
|
||||
}
|
||||
|
||||
type AssignmentExpression struct {
|
||||
Variable string
|
||||
Value Expression
|
||||
}
|
||||
|
||||
type LiteralExpression struct {
|
||||
Type PrimitiveType
|
||||
Value any
|
||||
}
|
||||
|
||||
type VariableReferenceExpression struct {
|
||||
Variable string
|
||||
}
|
||||
|
||||
type ArithmeticOperation uint32
|
||||
|
||||
const (
|
||||
Arithmetic_Add ArithmeticOperation = iota
|
||||
Arithmetic_Sub
|
||||
Arithmetic_Mul
|
||||
Arithmetic_Div
|
||||
Arithmetic_Mod
|
||||
)
|
||||
|
||||
type ArithmeticExpression struct {
|
||||
Operation ArithmeticOperation
|
||||
Left Expression
|
||||
Right Expression
|
||||
}
|
||||
|
||||
type Block struct {
|
||||
Statements []Statement
|
||||
}
|
||||
|
||||
type CompiledFunction struct {
|
||||
Parameters []Type
|
||||
ReturnValue Type
|
||||
Body Block
|
||||
}
|
||||
|
||||
type CompiledFile struct {
|
||||
Functions []CompiledFunction
|
||||
}
|
||||
|
||||
func compiler() (*CompiledFile, error) {
|
||||
return nil, nil
|
||||
}
|
6
example/helloworld.lang
Normal file
6
example/helloworld.lang
Normal file
@ -0,0 +1,6 @@
|
||||
import lang.stdlib;
|
||||
|
||||
void main() {
|
||||
u8 a = 1u8;
|
||||
println("Hello World");
|
||||
}
|
166
lexer.go
Normal file
166
lexer.go
Normal file
@ -0,0 +1,166 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var Whitespace []rune = []rune{' ', '\t', '\r', '\n'}
|
||||
var Separators []rune = []rune{'(', ')', '{', '}', ';'}
|
||||
|
||||
type LexType uint32
|
||||
|
||||
const (
|
||||
Type_Identifier LexType = iota
|
||||
Type_Keyword
|
||||
Type_Separator
|
||||
Type_LiteralString
|
||||
Type_LiteralNumber
|
||||
)
|
||||
|
||||
type Keyword uint32
|
||||
|
||||
const (
|
||||
Keyword_Import Keyword = iota
|
||||
Keyword_Void
|
||||
)
|
||||
|
||||
type Separator uint32
|
||||
|
||||
const (
|
||||
Separator_OpenParen Separator = iota
|
||||
Separator_CloseParen
|
||||
Separator_OpenCurly
|
||||
Separator_CloseCurly
|
||||
Separator_Semicolon
|
||||
)
|
||||
|
||||
type LiteralType uint32
|
||||
|
||||
const (
|
||||
Literal_String LiteralType = iota
|
||||
Literal_Number
|
||||
)
|
||||
|
||||
type LexToken struct {
|
||||
Type LexType
|
||||
Position uint64
|
||||
Value any
|
||||
}
|
||||
|
||||
type Literal struct {
|
||||
Type LiteralType
|
||||
Value any
|
||||
}
|
||||
|
||||
func stringLiteral(runes []rune) (string, []rune, error) {
|
||||
idx := 1 // Always starts with "
|
||||
literal := ""
|
||||
for idx < len(runes) && runes[idx] != '"' {
|
||||
if runes[idx] == '\\' {
|
||||
if idx == len(runes)-1 {
|
||||
return "", nil, errors.New("unmatched escape sequence")
|
||||
}
|
||||
|
||||
// TODO \n, \r, \uXXXX, ... escape sequences
|
||||
|
||||
idx++
|
||||
}
|
||||
|
||||
literal += string(runes[idx])
|
||||
idx++
|
||||
}
|
||||
|
||||
if idx == len(runes) {
|
||||
return "", nil, errors.New("unclosed string literal")
|
||||
}
|
||||
|
||||
idx++
|
||||
return literal, runes[idx:], nil
|
||||
}
|
||||
|
||||
// source -> token, remaining source, error
|
||||
func nextToken(program string) (string, string, error) {
|
||||
// Skip whitespace
|
||||
start := 0
|
||||
runes := []rune(program)
|
||||
for start < len(runes) && slices.Contains(Whitespace, runes[start]) {
|
||||
start++
|
||||
}
|
||||
|
||||
if start == len(runes) {
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
if runes[start] == '"' {
|
||||
// String literal
|
||||
literal, remaining, err := stringLiteral(runes[start:])
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return "\"" + literal + "\"", string(remaining), nil
|
||||
}
|
||||
|
||||
end := start
|
||||
for end < len(runes) && !slices.Contains(Whitespace, runes[end]) && !slices.Contains(Separators, runes[end]) {
|
||||
end++
|
||||
}
|
||||
|
||||
if start == end {
|
||||
end++
|
||||
}
|
||||
|
||||
return string(runes[start:end]), string(runes[end:]), nil
|
||||
}
|
||||
|
||||
func parseToken(token string) (LexToken, error) {
|
||||
if strings.HasPrefix(token, "\"") {
|
||||
return LexToken{Type: Type_LiteralString, Value: token[1 : len(token)-1]}, nil
|
||||
}
|
||||
|
||||
switch token {
|
||||
case "void":
|
||||
return LexToken{Type: Type_Keyword, Value: Keyword_Void}, nil
|
||||
case "import":
|
||||
return LexToken{Type: Type_Keyword, Value: Keyword_Import}, nil
|
||||
case "(":
|
||||
return LexToken{Type: Type_Separator, Value: Separator_OpenParen}, nil
|
||||
case ")":
|
||||
return LexToken{Type: Type_Separator, Value: Separator_CloseParen}, nil
|
||||
case "{":
|
||||
return LexToken{Type: Type_Separator, Value: Separator_OpenCurly}, nil
|
||||
case "}":
|
||||
return LexToken{Type: Type_Separator, Value: Separator_CloseCurly}, nil
|
||||
case ";":
|
||||
return LexToken{Type: Type_Separator, Value: Separator_Semicolon}, nil
|
||||
default:
|
||||
return LexToken{Type: Type_Identifier, Value: token}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func lexer(program string) ([]LexToken, error) {
|
||||
var tokens []LexToken
|
||||
|
||||
for len(program) > 0 {
|
||||
token, rest, err := nextToken(program)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(token) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
lexToken, err := parseToken(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
program = rest
|
||||
tokens = append(tokens, lexToken)
|
||||
}
|
||||
|
||||
return tokens, nil
|
||||
}
|
25
main.go
Normal file
25
main.go
Normal file
@ -0,0 +1,25 @@
|
||||
// The compiler
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) != 2 {
|
||||
log.Fatalln("Usage: " + os.Args[0] + " <file>")
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(os.Args[1])
|
||||
if err != nil {
|
||||
log.Fatalln("Cannot open input file.", err)
|
||||
}
|
||||
|
||||
ast, err := lexer(string(content))
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
log.Printf("%+#v\n", ast)
|
||||
}
|
Loading…
Reference in New Issue
Block a user