2024-03-11 22:05:36 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"slices"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
type TypeType uint32
|
|
|
|
|
|
|
|
const (
|
|
|
|
Type_Primitive TypeType = iota
|
|
|
|
Type_Void
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2024-03-12 22:00:03 +01:00
|
|
|
type ParsedParameter struct {
|
|
|
|
Name string
|
|
|
|
Type Type
|
|
|
|
}
|
|
|
|
|
2024-03-11 22:05:36 +01:00
|
|
|
type ParsedFunction struct {
|
2024-03-12 22:00:03 +01:00
|
|
|
Name string
|
|
|
|
Parameters []ParsedParameter
|
2024-03-11 22:05:36 +01:00
|
|
|
ReturnType Type
|
|
|
|
Body Block
|
|
|
|
}
|
|
|
|
|
|
|
|
type Import struct {
|
|
|
|
Import string
|
|
|
|
}
|
|
|
|
|
|
|
|
type ParsedFile struct {
|
|
|
|
Imports []Import
|
|
|
|
Functions []ParsedFunction
|
|
|
|
}
|
|
|
|
|
|
|
|
func expectSeparator(tokens []LexToken, separators ...Separator) (Separator, []LexToken, error) {
|
|
|
|
var separatorNames []string
|
|
|
|
for _, sep := range separators {
|
|
|
|
separatorNames = append(separatorNames, string(Separators[sep]))
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(tokens) == 0 {
|
|
|
|
return InvalidValue, nil, errors.New("expected one of " + strings.Join(separatorNames, " "))
|
|
|
|
}
|
|
|
|
|
|
|
|
separator := tokens[0]
|
|
|
|
if separator.Type != Type_Separator || !slices.Contains(separators, separator.Value.(Separator)) {
|
2024-03-11 22:08:03 +01:00
|
|
|
return InvalidValue, nil, errors.New("expected one of " + strings.Join(separatorNames, " "))
|
2024-03-11 22:05:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return separator.Value.(Separator), tokens[1:], nil
|
|
|
|
}
|
|
|
|
|
2024-03-12 22:00:03 +01:00
|
|
|
func expectIdentifier(tokens []LexToken) (string, []LexToken, error) {
|
|
|
|
if len(tokens) == 0 {
|
|
|
|
return "", nil, errors.New("expected identifier")
|
|
|
|
}
|
|
|
|
|
|
|
|
token := tokens[0]
|
|
|
|
if token.Type != Type_Separator && token.Type != Type_Identifier {
|
|
|
|
return "", nil, errors.New("expected identifier")
|
|
|
|
}
|
|
|
|
|
|
|
|
return token.Value.(string), tokens[1:], nil
|
|
|
|
}
|
|
|
|
|
2024-03-11 22:05:36 +01:00
|
|
|
func parseImport(tokens []LexToken) (*Import, []LexToken, error) {
|
|
|
|
var err error
|
|
|
|
|
|
|
|
if len(tokens) < 3 {
|
|
|
|
return nil, nil, errors.New("incomplete import")
|
|
|
|
}
|
|
|
|
|
|
|
|
// tokens[0] == import keyword
|
|
|
|
|
|
|
|
identifier := tokens[1]
|
|
|
|
if identifier.Type != Type_Identifier {
|
|
|
|
return nil, nil, errors.New("expected identifier")
|
|
|
|
}
|
|
|
|
|
|
|
|
_, tokens, err = expectSeparator(tokens[2:], Separator_Semicolon)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &Import{Import: identifier.Value.(string)}, tokens, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseType(tokens []LexToken) (*Type, []LexToken, error) {
|
|
|
|
var err error
|
|
|
|
|
|
|
|
if len(tokens) == 0 {
|
|
|
|
return nil, nil, errors.New("expected type")
|
|
|
|
}
|
|
|
|
|
|
|
|
tok := tokens[0]
|
|
|
|
if tok.Type == Type_Keyword && tok.Value.(Keyword) == Keyword_Void {
|
|
|
|
return &Type{Type: Type_Void, Value: nil}, tokens[1:], nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if tok.Type == Type_Separator && tok.Value.(Separator) == Separator_OpenParen {
|
|
|
|
// Tuple type
|
|
|
|
var types []Type
|
|
|
|
tokens = tokens[1:]
|
|
|
|
for {
|
|
|
|
var parsedType *Type
|
|
|
|
parsedType, tokens, err = parseType(tokens)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
types = append(types, *parsedType)
|
|
|
|
|
|
|
|
var sep Separator
|
|
|
|
sep, tokens, err = expectSeparator(tokens, Separator_Comma, Separator_CloseParen)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if sep == Separator_CloseParen {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(types) == 0 {
|
|
|
|
return nil, nil, errors.New("empty tuple")
|
|
|
|
}
|
|
|
|
|
|
|
|
return &Type{Type: Type_Tuple, Value: TupleType{Types: types}}, tokens, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if tok.Type == Type_Identifier {
|
|
|
|
return &Type{Type: Type_Named, Value: tok.Value}, tokens[1:], nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, nil, errors.New("expected type")
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseFunction(tokens []LexToken) (*ParsedFunction, []LexToken, error) {
|
|
|
|
var err error
|
|
|
|
|
2024-03-12 22:00:03 +01:00
|
|
|
var name string
|
|
|
|
var parameters []ParsedParameter
|
2024-03-11 22:05:36 +01:00
|
|
|
var returnType *Type
|
|
|
|
var body Block
|
|
|
|
|
|
|
|
returnType, tokens, err = parseType(tokens)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2024-03-12 22:00:03 +01:00
|
|
|
if len(tokens) < 2 {
|
|
|
|
return nil, nil, errors.New("incomplete function declaration")
|
|
|
|
}
|
|
|
|
|
|
|
|
name, tokens, err = expectIdentifier(tokens)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, tokens, err = expectSeparator(tokens, Separator_OpenParen)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
if len(tokens) == 0 {
|
|
|
|
return nil, nil, errors.New("incomplete function declaration")
|
|
|
|
}
|
|
|
|
|
|
|
|
token := tokens[0]
|
|
|
|
if token.Type == Type_Separator && token.Value.(Separator) == Separator_CloseParen {
|
|
|
|
tokens = tokens[1:]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(parameters) > 0 {
|
|
|
|
if token.Type != Type_Separator && token.Value.(Separator) != Separator_Comma {
|
|
|
|
return nil, nil, errors.New("expected ,")
|
|
|
|
}
|
|
|
|
|
|
|
|
tokens = tokens[1:]
|
|
|
|
}
|
|
|
|
|
|
|
|
var paramType *Type
|
|
|
|
paramType, tokens, err = parseType(tokens)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var paramName string
|
|
|
|
paramName, tokens, err = expectIdentifier(tokens)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
parameters = append(parameters, ParsedParameter{Name: paramName, Type: *paramType})
|
|
|
|
}
|
|
|
|
|
|
|
|
_, tokens, err = expectSeparator(tokens, Separator_OpenCurly)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: body, closing curly
|
2024-03-11 22:05:36 +01:00
|
|
|
|
2024-03-12 22:00:03 +01:00
|
|
|
return &ParsedFunction{Name: name, Parameters: parameters, ReturnType: *returnType, Body: body}, tokens, nil
|
2024-03-11 22:05:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func parser(tokens []LexToken) (*ParsedFile, error) {
|
|
|
|
var err error
|
|
|
|
|
|
|
|
var functions []ParsedFunction
|
|
|
|
var imports []Import
|
|
|
|
|
|
|
|
for len(tokens) > 0 {
|
|
|
|
if tokens[0].Type == Type_Keyword && tokens[0].Value.(Keyword) == Keyword_Import {
|
|
|
|
var parsedImport *Import
|
|
|
|
parsedImport, tokens, err = parseImport(tokens)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
imports = append(imports, *parsedImport)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
var parsedFunction *ParsedFunction
|
|
|
|
parsedFunction, tokens, err = parseFunction(tokens)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
functions = append(functions, *parsedFunction)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &ParsedFile{Imports: imports, Functions: functions}, nil
|
|
|
|
}
|