This commit is contained in:
Pavel Flegr 2026-03-05 14:55:09 +01:00
commit 8f4f858d86
30 changed files with 9765 additions and 0 deletions

235
internal/lang/ast.go Normal file
View file

@ -0,0 +1,235 @@
package lang
type Program struct {
PackagePath string
Imports []ImportDecl
Interfaces []InterfaceDecl
Classes []ClassDecl
Workers []WorkerDecl
Functions []FunctionDecl
}
type ImportDecl struct {
Alias string
Path string
}
type InterfaceDecl struct {
Name string
Methods []FunctionSignature
}
type ClassDecl struct {
Name string
Fields []FieldDecl
Parents []string
Methods []FunctionDecl
}
type WorkerDecl struct {
Name string
Fields []WorkerFieldDecl
Methods []FunctionDecl
}
type WorkerFieldDecl struct {
Mutable bool
Name string
Type string
Value Expr
}
type FieldDecl struct {
Mutable bool
Name string
Type string
}
type FunctionSignature struct {
Name string
Params []Param
ReturnType string
}
type FunctionDecl struct {
Name string
Params []Param
ReturnType string
Body []Stmt
}
type Param struct {
Name string
Type string
}
type Stmt interface {
stmtNode()
}
type Expr interface {
exprNode()
}
type VarDecl struct {
Mutable bool
Name string
Type string
Value Expr
}
func (VarDecl) stmtNode() {}
type MultiVarDecl struct {
Mutable bool
Names []string
Value Expr
}
func (MultiVarDecl) stmtNode() {}
type AssignStmt struct {
Name string
Value Expr
}
func (AssignStmt) stmtNode() {}
type AddAssignStmt struct {
Name string
Value Expr
}
func (AddAssignStmt) stmtNode() {}
type MultiAssignStmt struct {
Names []string
Value Expr
}
func (MultiAssignStmt) stmtNode() {}
type ReturnStmt struct {
Value Expr
}
func (ReturnStmt) stmtNode() {}
type ThrowStmt struct {
Value Expr
}
func (ThrowStmt) stmtNode() {}
type GoStmt struct {
Value Expr
}
func (GoStmt) stmtNode() {}
type ExprStmt struct {
Value Expr
}
func (ExprStmt) stmtNode() {}
type IfStmt struct {
Cond Expr
Then []Stmt
Else []Stmt
}
func (IfStmt) stmtNode() {}
type WhileStmt struct {
Cond Expr
Body []Stmt
}
func (WhileStmt) stmtNode() {}
type TryCatchStmt struct {
TryBody []Stmt
CatchName string
CatchType string
CatchBody []Stmt
}
func (TryCatchStmt) stmtNode() {}
type SelectStmt struct {
Cases []SelectCase
}
func (SelectStmt) stmtNode() {}
type SelectCase struct {
Source Expr
Body []Stmt
}
type IdentExpr struct {
Name string
}
func (IdentExpr) exprNode() {}
type IntExpr struct {
Value string
}
func (IntExpr) exprNode() {}
type StringExpr struct {
Value string
}
func (StringExpr) exprNode() {}
type BoolExpr struct {
Value bool
}
func (BoolExpr) exprNode() {}
type NullExpr struct{}
func (NullExpr) exprNode() {}
type UnaryExpr struct {
Op string
Value Expr
}
func (UnaryExpr) exprNode() {}
type BinaryExpr struct {
Left Expr
Op string
Right Expr
}
func (BinaryExpr) exprNode() {}
type CallExpr struct {
Callee Expr
Args []Expr
TypeArgs []string
}
func (CallExpr) exprNode() {}
type SelectorExpr struct {
Receiver Expr
Name string
}
func (SelectorExpr) exprNode() {}
type LambdaExpr struct {
Params []Param
ImplicitIt bool
Body []Stmt
}
func (LambdaExpr) exprNode() {}

File diff suppressed because it is too large Load diff

2507
internal/lang/generate_go.go Normal file

File diff suppressed because it is too large Load diff

167
internal/lang/lexer.go Normal file
View file

@ -0,0 +1,167 @@
package lang
import (
"fmt"
"unicode"
)
type lexer struct {
src []rune
pos int
}
func lex(input string) ([]token, error) {
l := &lexer{src: []rune(input)}
var tokens []token
for {
tok, err := l.next()
if err != nil {
return nil, err
}
tokens = append(tokens, tok)
if tok.kind == tokenEOF {
return tokens, nil
}
}
}
func (l *lexer) next() (token, error) {
l.skipWhitespace()
start := l.pos
if l.pos >= len(l.src) {
return token{kind: tokenEOF, pos: start}, nil
}
ch := l.src[l.pos]
switch {
case isIdentStart(ch):
l.pos++
for l.pos < len(l.src) && isIdentPart(l.src[l.pos]) {
l.pos++
}
lexeme := string(l.src[start:l.pos])
if kind, ok := keywords[lexeme]; ok {
return token{kind: kind, lexeme: lexeme, pos: start}, nil
}
return token{kind: tokenIdent, lexeme: lexeme, pos: start}, nil
case unicode.IsDigit(ch):
l.pos++
for l.pos < len(l.src) && unicode.IsDigit(l.src[l.pos]) {
l.pos++
}
return token{kind: tokenInt, lexeme: string(l.src[start:l.pos]), pos: start}, nil
case ch == '"':
l.pos++
for l.pos < len(l.src) && l.src[l.pos] != '"' {
if l.src[l.pos] == '\\' {
l.pos++
if l.pos >= len(l.src) {
return token{}, fmt.Errorf("unterminated string at %d", start)
}
}
l.pos++
}
if l.pos >= len(l.src) {
return token{}, fmt.Errorf("unterminated string at %d", start)
}
l.pos++
return token{kind: tokenString, lexeme: string(l.src[start:l.pos]), pos: start}, nil
default:
l.pos++
switch ch {
case '(':
return token{kind: tokenLParen, lexeme: "(", pos: start}, nil
case ')':
return token{kind: tokenRParen, lexeme: ")", pos: start}, nil
case '{':
return token{kind: tokenLBrace, lexeme: "{", pos: start}, nil
case '}':
return token{kind: tokenRBrace, lexeme: "}", pos: start}, nil
case ',':
return token{kind: tokenComma, lexeme: ",", pos: start}, nil
case '.':
return token{kind: tokenDot, lexeme: ".", pos: start}, nil
case ':':
return token{kind: tokenColon, lexeme: ":", pos: start}, nil
case ';':
return token{kind: tokenSemicolon, lexeme: ";", pos: start}, nil
case '+':
if l.match('=') {
return token{kind: tokenPlusAssign, lexeme: "+=", pos: start}, nil
}
return token{kind: tokenPlus, lexeme: "+", pos: start}, nil
case '-':
if l.match('>') {
return token{kind: tokenArrow, lexeme: "->", pos: start}, nil
}
return token{kind: tokenMinus, lexeme: "-", pos: start}, nil
case '*':
return token{kind: tokenStar, lexeme: "*", pos: start}, nil
case '/':
if l.match('/') {
for l.pos < len(l.src) && l.src[l.pos] != '\n' {
l.pos++
}
return l.next()
}
return token{kind: tokenSlash, lexeme: "/", pos: start}, nil
case '%':
return token{kind: tokenPercent, lexeme: "%", pos: start}, nil
case '!':
if l.match('=') {
return token{kind: tokenNeq, lexeme: "!=", pos: start}, nil
}
return token{kind: tokenBang, lexeme: "!", pos: start}, nil
case '=':
if l.match('=') {
return token{kind: tokenEq, lexeme: "==", pos: start}, nil
}
return token{kind: tokenAssign, lexeme: "=", pos: start}, nil
case '<':
if l.match('=') {
return token{kind: tokenLte, lexeme: "<=", pos: start}, nil
}
return token{kind: tokenLt, lexeme: "<", pos: start}, nil
case '>':
if l.match('=') {
return token{kind: tokenGte, lexeme: ">=", pos: start}, nil
}
return token{kind: tokenGt, lexeme: ">", pos: start}, nil
case '&':
if l.match('&') {
return token{kind: tokenAnd, lexeme: "&&", pos: start}, nil
}
case '|':
if l.match('|') {
return token{kind: tokenOr, lexeme: "||", pos: start}, nil
}
}
return token{}, fmt.Errorf("unexpected character %q at %d", ch, start)
}
}
func (l *lexer) match(expected rune) bool {
if l.pos >= len(l.src) || l.src[l.pos] != expected {
return false
}
l.pos++
return true
}
func (l *lexer) skipWhitespace() {
for l.pos < len(l.src) {
if unicode.IsSpace(l.src[l.pos]) {
l.pos++
continue
}
break
}
}
func isIdentStart(ch rune) bool {
return unicode.IsLetter(ch) || ch == '_'
}
func isIdentPart(ch rune) bool {
return isIdentStart(ch) || unicode.IsDigit(ch)
}

1056
internal/lang/parser.go Normal file

File diff suppressed because it is too large Load diff

86
internal/lang/token.go Normal file
View file

@ -0,0 +1,86 @@
package lang
type tokenKind string
const (
tokenEOF tokenKind = "EOF"
tokenIdent tokenKind = "IDENT"
tokenInt tokenKind = "INT"
tokenString tokenKind = "STRING"
tokenTrue tokenKind = "TRUE"
tokenFalse tokenKind = "FALSE"
tokenNull tokenKind = "NULL"
tokenImport tokenKind = "IMPORT"
tokenPackage tokenKind = "PACKAGE"
tokenClass tokenKind = "CLASS"
tokenWorker tokenKind = "WORKER"
tokenInterface tokenKind = "INTERFACE"
tokenFun tokenKind = "FUN"
tokenOverride tokenKind = "OVERRIDE"
tokenVal tokenKind = "VAL"
tokenVar tokenKind = "VAR"
tokenIf tokenKind = "IF"
tokenElse tokenKind = "ELSE"
tokenWhile tokenKind = "WHILE"
tokenSelect tokenKind = "SELECT"
tokenReturn tokenKind = "RETURN"
tokenGo tokenKind = "GO"
tokenTry tokenKind = "TRY"
tokenCatch tokenKind = "CATCH"
tokenThrow tokenKind = "THROW"
tokenLParen tokenKind = "("
tokenRParen tokenKind = ")"
tokenLBrace tokenKind = "{"
tokenRBrace tokenKind = "}"
tokenComma tokenKind = ","
tokenDot tokenKind = "."
tokenColon tokenKind = ":"
tokenSemicolon tokenKind = ";"
tokenPlus tokenKind = "+"
tokenMinus tokenKind = "-"
tokenStar tokenKind = "*"
tokenSlash tokenKind = "/"
tokenPercent tokenKind = "%"
tokenBang tokenKind = "!"
tokenAssign tokenKind = "="
tokenPlusAssign tokenKind = "+="
tokenEq tokenKind = "=="
tokenNeq tokenKind = "!="
tokenLt tokenKind = "<"
tokenLte tokenKind = "<="
tokenGt tokenKind = ">"
tokenGte tokenKind = ">="
tokenAnd tokenKind = "&&"
tokenOr tokenKind = "||"
tokenArrow tokenKind = "->"
)
var keywords = map[string]tokenKind{
"fun": tokenFun,
"import": tokenImport,
"package": tokenPackage,
"class": tokenClass,
"worker": tokenWorker,
"interface": tokenInterface,
"val": tokenVal,
"var": tokenVar,
"override": tokenOverride,
"if": tokenIf,
"else": tokenElse,
"while": tokenWhile,
"select": tokenSelect,
"return": tokenReturn,
"go": tokenGo,
"try": tokenTry,
"catch": tokenCatch,
"throw": tokenThrow,
"true": tokenTrue,
"false": tokenFalse,
"null": tokenNull,
}
type token struct {
kind tokenKind
lexeme string
pos int
}