add type aliases, rework compiler, remove optimization

This commit is contained in:
Neemek 2026-07-12 11:34:17 +02:00
parent 94b12f28ab
commit d54249cffe
Signed by: neemek
GPG key ID: 84FFE4D7D40AB25E
12 changed files with 869 additions and 1597 deletions

View file

@ -178,7 +178,7 @@ func (p *Parser) Parse(path string) (*Program, error) {
}, nil
}
func (p *Parser) accept(tokenType TokenType) bool {
func (p *Parser) accept(tokenType TokenKind) bool {
if p.curr == nil {
log.Fatal("unexpected current token nil")
return false
@ -198,7 +198,7 @@ func (p *Parser) accept(tokenType TokenType) bool {
return false
}
func (p *Parser) acceptAll(tokenTypes ...TokenType) bool {
func (p *Parser) acceptAll(tokenTypes ...TokenKind) bool {
if int(p.pos)+len(tokenTypes) > len(p.tokens) {
return false
}
@ -213,7 +213,7 @@ func (p *Parser) acceptAll(tokenTypes ...TokenType) bool {
return true
}
func (p *Parser) expect(tokenType TokenType, reason string) error {
func (p *Parser) expect(tokenType TokenKind, reason string) error {
if !p.accept(tokenType) {
return p.error(fmt.Sprintf("Expected token %s, got %s; %s", tokenType, p.curr.Type, reason), p.curr)
}
@ -291,6 +291,33 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
t := p.curr
switch t.Type {
case TokenType:
p.advance()
start := p.prev.Start
if err := p.expect(TokenName, "types must have a name"); err != nil {
return nil, err
}
name := p.prev
if err := p.expect(TokenAssign, "type aliases must be defined with an assign"); err != nil {
return nil, err
}
sig, err := p.parseSignature()
if err != nil {
return nil, err
}
return &AliasNode{
name,
sig,
start,
p.prev.End,
}, nil
case TokenIf:
p.advance()
@ -320,59 +347,6 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
t.End,
}, nil
case TokenFunc:
p.advance()
start := p.prev.Start
var name *Token
if p.accept(TokenName) { // can be unnamed, but accept name if it is named
name = p.prev
}
params, err := p.parseParams()
if err != nil {
return nil, err
}
var yield TypeSignature
if p.accept(TokenArrow) {
yield, err = p.parseSignature()
if err != nil {
return nil, err
}
}
logic, err := p.expression(true)
if err != nil {
return nil, err
}
names := "*"
if name != nil {
names = name.Lexeme
}
fn := &FunctionNode{
names,
params,
yield,
logic,
start,
p.prev.End,
}
if name != nil {
return &AssignNode{
&ReferenceNode{name.Lexeme, name.Start, name.End},
fn,
true,
start,
p.prev.End,
}, nil
}
return fn, nil
case TokenReturn:
p.advance()
start := p.prev.Start
@ -440,7 +414,7 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
}
}
func isBinaryOperator(tokenType TokenType) bool {
func isBinaryOperator(tokenType TokenKind) bool {
switch tokenType {
case TokenPlus, TokenMinus, TokenStar, TokenSlash, TokenPipe, TokenDoubleAmpersand, TokenDoublePipe, TokenEquals, TokenBangEquals, TokenLessThan, TokenLessThanOrEqual, TokenGreaterThan, TokenGreaterThanOrEqual:
return true
@ -449,7 +423,7 @@ func isBinaryOperator(tokenType TokenType) bool {
}
}
func binaryPrecedence(op TokenType) int {
func binaryPrecedence(op TokenKind) int {
switch op {
case TokenDoubleAmpersand, TokenDoublePipe:
return 1
@ -464,7 +438,7 @@ func binaryPrecedence(op TokenType) int {
}
}
func tokenToBinaryOperation(tokenType TokenType) BinaryOperation {
func tokenToBinaryOperation(tokenType TokenKind) BinaryOperation {
switch tokenType {
case TokenPlus:
return BinaryAddition
@ -477,9 +451,9 @@ func tokenToBinaryOperation(tokenType TokenType) BinaryOperation {
case TokenPipe:
panic("unimplemented bitwise ops")
case TokenDoubleAmpersand:
return BinaryAnd
return BinaryBooleanAnd
case TokenDoublePipe:
return BinaryOr
return BinaryBooleanOr
case TokenEquals:
return BinaryEquality
@ -509,22 +483,28 @@ func (p *Parser) binary() (Node, error) {
values := NewStack[Node](256)
values.pushItem(t)
reduce := func() {
r := values.Pop()
l := values.Pop()
opToken := ops.Pop()
op := tokenToBinaryOperation(opToken.Type)
start, _ := l.Bounds()
_, end := r.Bounds()
values.Push(&BinaryNode{
op,
l,
r,
opToken,
start,
end,
})
}
for isBinaryOperator(p.curr.Type) {
for ops.Current > 0 && binaryPrecedence(p.curr.Type) <= binaryPrecedence(ops.Peek().Type) {
r := values.Pop()
l := values.Pop()
op := tokenToBinaryOperation(ops.Pop().Type)
start, _ := l.Bounds()
_, end := r.Bounds()
values.Push(&BinaryNode{
op,
l,
r,
start,
end,
})
reduce()
}
ops.Push(p.curr)
@ -539,20 +519,7 @@ func (p *Parser) binary() (Node, error) {
}
for ops.Current > 0 {
r := values.Pop()
l := values.Pop()
op := tokenToBinaryOperation(ops.Pop().Type)
start, _ := l.Bounds()
_, end := l.Bounds()
values.Push(&BinaryNode{
op,
l,
r,
start,
end,
})
reduce()
}
return values.Pop(), nil
@ -573,7 +540,7 @@ func (p *Parser) chain() (Node, error) {
f = &AccessNode{
f,
p.prev.Lexeme,
p.prev,
name.Start,
name.End,
}
@ -602,6 +569,24 @@ func (p *Parser) chain() (Node, error) {
f,
args,
start,
p.prev.End,
}
} else if p.accept(TokenOpenBracket) {
start := p.prev.Start
index, err := p.expression(false)
if err != nil {
return nil, err
}
if err := p.expect(TokenCloseBracket, "opening bracket must be closed"); err != nil {
return nil, err
}
f = &IndexNode{
f,
index,
start,
p.prev.End,
}
@ -716,7 +701,7 @@ func (p *Parser) factor() (Node, error) {
}
}
value, err := p.condition()
value, err := p.expression(false)
if err != nil {
return nil, err
}
@ -736,7 +721,7 @@ func (p *Parser) factor() (Node, error) {
// unary minus
case TokenMinus:
p.advance()
first := p.prev
op := p.prev
f, err := p.factor()
if err != nil {
@ -745,13 +730,14 @@ func (p *Parser) factor() (Node, error) {
return &UnaryNode{
UnaryNegate,
f,
first.Start,
op,
op.Start,
p.prev.End,
}, nil
case TokenBang:
p.advance()
start := p.prev.Start
op := p.prev
v, err := p.factor()
if err != nil {
@ -761,7 +747,8 @@ func (p *Parser) factor() (Node, error) {
return &UnaryNode{
UnaryNot,
v,
start,
op,
op.Start,
p.prev.End,
}, nil
@ -799,32 +786,54 @@ func (p *Parser) factor() (Node, error) {
p.advance()
start := p.prev.Start
var name *Token
if p.accept(TokenName) { // can be unnamed, but accept name if it is named
name = p.prev
}
params, err := p.parseParams()
if err != nil {
return nil, err
}
var sig TypeSignature = &NilSignature{}
var yield TypeSignature
if p.accept(TokenArrow) {
sig, err = p.parseSignature()
yield, err = p.parseSignature()
if err != nil {
return nil, err
}
}
b, err := p.block(false)
logic, err := p.expression(true)
if err != nil {
return nil, err
}
return &FunctionNode{
"*",
names := "*"
if name != nil {
names = name.Lexeme
}
fn := &FunctionNode{
names,
params,
sig,
b,
yield,
logic,
start,
p.prev.End,
}, nil
}
if name != nil {
return &AssignNode{
&ReferenceNode{name.Lexeme, name.Start, name.End},
fn,
true,
start,
p.prev.End,
}, nil
}
return fn, nil
case TokenOpenParenthesis:
p.advance()
@ -889,440 +898,6 @@ func (p *Parser) factor() (Node, error) {
}
}
func (p *Parser) prop() (Node, error) {
start := p.curr.Start
v, err := p.factor()
if err != nil {
return nil, err
}
// parse chains of prop-getting ( "".split().join().length.round() )
for p.accept(TokenDot) {
if err := p.expect(TokenName, "property must be a name"); err != nil {
return nil, err
}
property := (*p.prev).Lexeme
v = &AccessNode{
v,
property,
start,
p.prev.End,
}
// if called, also add
if (*p.curr).Type == TokenOpenParenthesis {
args, err := p.parseArgs()
if err != nil {
return nil, err
}
v = &InvokeNode{
v,
args,
start,
p.prev.End,
}
}
}
return v, nil
}
func (p *Parser) product() (Node, error) {
start := p.curr.Start
left, err := p.prop()
if err != nil {
return nil, err
}
for p.accept(TokenStar) || p.accept(TokenSlash) {
op := BinaryMultiplication
if (*p.prev).Type == TokenSlash {
op = BinaryDivision
}
f, err := p.prop()
if err != nil {
return nil, err
}
left = &BinaryNode{
op,
left,
f,
start,
p.prev.End,
}
}
return left, nil
}
func (p *Parser) term() (Node, error) {
start := p.curr.Start
left, err := p.product()
if err != nil {
return nil, err
}
for p.accept(TokenPlus) || p.accept(TokenMinus) {
op := BinaryAddition
if (*p.prev).Type == TokenMinus {
op = BinarySubtraction
}
pr, err := p.product()
if err != nil {
return nil, err
}
left = &BinaryNode{
op,
left,
pr,
start,
p.prev.End,
}
}
return left, nil
}
func (p *Parser) comparison() (Node, error) {
start := p.curr.Start
left, err := p.term()
if err != nil {
return nil, err
}
op := BinaryEquality
switch (*p.curr).Type {
case TokenEquals:
op = BinaryEquality
case TokenBangEquals:
op = BinaryInequality
case TokenGreaterThan:
op = BinaryGreater
case TokenLessThan:
op = BinaryLess
case TokenLessThanOrEqual:
op = BinaryLessEqual
case TokenGreaterThanOrEqual:
op = BinaryGreaterEqual
default:
return left, nil
}
p.advance()
t, err := p.term()
if err != nil {
return nil, err
}
return &BinaryNode{
op,
left,
t,
start,
p.prev.End,
}, nil
}
func (p *Parser) condition() (Node, error) {
start := p.curr.Start
left, err := p.comparison()
if err != nil {
return nil, err
}
op := BinaryEquality
switch (*p.curr).Type {
case TokenDoubleAmpersand:
op = BinaryAnd
case TokenDoublePipe:
op = BinaryOr
default:
return left, nil
}
p.advance()
c, err := p.condition()
if err != nil {
return left, err
}
return &BinaryNode{
op,
left,
c,
start,
p.prev.End,
}, nil
}
func (p *Parser) statement() (Node, error) {
switch (*p.curr).Type {
case TokenIf:
start := p.curr.Start
p.advance()
condition, err := p.condition()
if err != nil {
return nil, err
}
then, err := p.block(false)
if err != nil {
return nil, err
}
var otherwise Node
if p.accept(TokenElse) {
// allow else if
if p.curr.Type == TokenIf {
otherwise, err = p.statement()
} else {
otherwise, err = p.block(false)
}
if err != nil {
return nil, err
}
}
return &ConditionalNode{
condition,
then,
otherwise,
start,
p.prev.End,
}, nil
case TokenName:
p.advance()
name := p.prev
if (*p.curr).Type == TokenDot {
var v Node = &ReferenceNode{
name.Lexeme,
name.Start,
name.End,
}
// parse chains of prop-getting ( "".split().join().length.round() )
for p.accept(TokenDot) {
if err := p.expect(TokenName, "property must be name"); err != nil {
return nil, err
}
property := (*p.prev).Lexeme
v = &AccessNode{
v,
property,
name.Start,
p.prev.End,
}
// if called, also add
if (*p.curr).Type == TokenOpenParenthesis {
args, err := p.parseArgs()
if err != nil {
return nil, err
}
v = &InvokeNode{
v,
args,
name.Start,
p.prev.End,
}
}
}
return v, nil
} else if p.curr.Type == TokenOpenParenthesis {
args, err := p.parseArgs()
if err != nil {
return nil, err
}
return &InvokeNode{
&ReferenceNode{
name.Lexeme,
name.Start,
name.End,
},
args,
name.Start,
p.prev.End,
}, nil
} else if p.accept(TokenAssign) || p.accept(TokenDeclare) {
isDeclaration := p.prev.Type == TokenDeclare
c, err := p.condition()
if err != nil {
return nil, err
}
return &AssignNode{ // THIS COULD BE MORE PERMISSIVE; its a new system
&ReferenceNode{
name.Lexeme,
name.Start,
name.End,
},
c,
isDeclaration,
name.Start,
p.prev.End,
}, nil
}
return nil, p.error("invalid statement", p.curr)
case TokenFunc:
p.advance()
funcStart := p.prev.Start
if err := p.expect(TokenName, "function must have a name"); err != nil {
return nil, err
}
name := p.prev
params, err := p.parseParams()
if err != nil {
return nil, err
}
var yield TypeSignature = &NilSignature{}
if p.accept(TokenArrow) {
yield, err = p.parseSignature()
if err != nil {
return nil, err
}
}
b, err := p.block(false)
if err != nil {
return nil, err
}
return &AssignNode{
&ReferenceNode{
name.Lexeme,
name.Start,
name.End,
},
&FunctionNode{
name.Lexeme,
params,
yield,
b,
funcStart,
p.prev.End,
},
true,
funcStart,
p.prev.End,
}, nil
case TokenWhile:
p.advance()
start := p.prev.Start
c, err := p.condition()
if err != nil {
return nil, err
}
b, err := p.block(false)
if err != nil {
return nil, err
}
return &LoopNode{
c,
b,
start,
p.prev.End,
}, nil
case TokenReturn:
p.advance()
start := p.prev.Start
c, err := p.condition()
if err != nil {
return nil, err
}
return &ReturnNode{
c,
start,
p.prev.End,
}, nil
case TokenBreakpoint:
p.advance()
return &BreakpointNode{}, nil
case TokenImport:
defer p.advance()
return nil, p.error("import statements must be top-level", p.curr)
default:
defer p.advance()
return nil, p.error("invalid statement", p.curr)
}
}
func (p *Parser) block(canBeStatement bool) (Node, error) {
if canBeStatement {
if !p.accept(TokenOpenBrace) {
if p.curr.Type == TokenEOF {
return nil, nil
}
return p.statement()
}
} else {
if err := p.expect(TokenOpenBrace, "a block is required"); err != nil {
return nil, err
}
}
start := p.prev.Start
statements := make([]Node, 0)
for !p.accept(TokenCloseBrace) {
s, err := p.statement()
if err != nil {
return nil, err
}
statements = append(statements, s)
}
return &BlockNode{
statements,
start,
p.prev.End,
}, nil
}
func (p *Parser) parseArgs() ([]Node, error) {
args := make([]Node, 0)
@ -1514,7 +1089,9 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
s = &AnySignature{}
default:
return nil, p.error("unsupported type: "+name, p.prev)
s = &NamedSignature{
name,
}
}
}