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

File diff suppressed because it is too large Load diff

View file

@ -7,21 +7,25 @@ import (
) )
type Token struct { type Token struct {
Type TokenType Type TokenKind
Start Pos Start Pos
End Pos End Pos
Line Pos Line Pos
Lexeme string Lexeme string
} }
func (t Token) Bounds() (Pos, Pos) {
return t.Start, t.End
}
func (t Token) String() string { func (t Token) String() string {
return fmt.Sprintf("token %s, '%s' %d -> %d, line %d", t.Type.String(), t.Lexeme, t.Start, t.End, t.Line) return fmt.Sprintf("token %s, '%s' %d -> %d, line %d", t.Type.String(), t.Lexeme, t.Start, t.End, t.Line)
} }
type TokenType uint64 type TokenKind uint64
const ( const (
TokenPlus TokenType = iota TokenPlus TokenKind = iota
TokenMinus TokenMinus
TokenStar TokenStar
TokenSlash TokenSlash
@ -52,6 +56,7 @@ const (
TokenIf TokenIf
TokenElse TokenElse
TokenImport TokenImport
TokenType
TokenComma TokenComma
TokenDot TokenDot
@ -77,7 +82,7 @@ const (
TokenError TokenError
) )
func (t TokenType) String() string { func (t TokenKind) String() string {
switch t { switch t {
case TokenPlus: case TokenPlus:
return "plus" return "plus"
@ -171,11 +176,28 @@ func (t TokenType) String() string {
return "arrow" return "arrow"
case TokenNewLine: case TokenNewLine:
return "newline" return "newline"
case TokenType:
return "type"
} }
panic("UNDEFINED TOKENTYPE STRING CONVERSION") panic("UNDEFINED TOKENTYPE STRING CONVERSION")
} }
var Keywords = map[string]TokenKind{
"true": TokenTrue,
"false": TokenFalse,
"nil": TokenNil,
"if": TokenIf,
"else": TokenElse,
"import": TokenImport,
"var": TokenVar,
"fn": TokenFunc,
"return": TokenReturn,
"while": TokenWhile,
"breakpoint": TokenBreakpoint,
"type": TokenType,
}
type Lexer struct { type Lexer struct {
src []rune src []rune
start Pos start Pos
@ -340,32 +362,12 @@ func (l *Lexer) NextToken() (Token, error) {
l.advance() l.advance()
} }
switch string(l.src[l.start:l.current]) { lexeme := string(l.src[l.start:l.current])
case "true": if k, ok := Keywords[lexeme]; ok {
return l.makeToken(TokenTrue), nil return l.makeToken(k), nil
case "false":
return l.makeToken(TokenFalse), nil
case "nil":
return l.makeToken(TokenNil), nil
case "if":
return l.makeToken(TokenIf), nil
case "else":
return l.makeToken(TokenElse), nil
case "var":
return l.makeToken(TokenVar), nil
case "fn":
return l.makeToken(TokenFunc), nil
case "while":
return l.makeToken(TokenWhile), nil
case "breakpoint":
return l.makeToken(TokenBreakpoint), nil
case "return":
return l.makeToken(TokenReturn), nil
case "import":
return l.makeToken(TokenImport), nil
default:
return l.makeToken(TokenName), nil
} }
return l.makeToken(TokenName), nil
} else if c == '0' && l.peek() != '.' { } else if c == '0' && l.peek() != '.' {
if l.peek() == 'x' { if l.peek() == 'x' {
l.advance() l.advance()
@ -400,7 +402,7 @@ func (l *Lexer) NextToken() (Token, error) {
} }
} }
func NewToken(t TokenType, start Pos, end Pos, line Pos, lexeme string) Token { func NewToken(t TokenKind, start Pos, end Pos, line Pos, lexeme string) Token {
return Token{ return Token{
Type: t, Type: t,
Start: start, Start: start,
@ -425,7 +427,7 @@ func (l *Lexer) Tokenize() ([]Token, error) {
return tokens, err return tokens, err
} }
func (l *Lexer) makeToken(t TokenType) Token { func (l *Lexer) makeToken(t TokenKind) Token {
return NewToken(t, l.start, l.current, l.line, string(l.src[l.start:l.current])) return NewToken(t, l.start, l.current, l.line, string(l.src[l.start:l.current]))
} }

View file

@ -6,37 +6,37 @@ import (
type LexerTestData struct { type LexerTestData struct {
source string source string
expectedTokens []TokenType expectedTokens []TokenKind
} }
func GetLexerTestData() map[string]LexerTestData { func GetLexerTestData() map[string]LexerTestData {
return map[string]LexerTestData{ return map[string]LexerTestData{
"hello_world_string(1)": { "hello_world_string(1)": {
"\"Hello world\"", "\"Hello world\"",
[]TokenType{TokenString, TokenEOF}, []TokenKind{TokenString, TokenEOF},
}, },
"empty_string(1)": { "empty_string(1)": {
"\"\"", "\"\"",
[]TokenType{TokenString, TokenEOF}, []TokenKind{TokenString, TokenEOF},
}, },
"simple number(1)": { "simple number(1)": {
"1024", "1024",
[]TokenType{TokenInteger, TokenEOF}, []TokenKind{TokenInteger, TokenEOF},
}, },
"simple_arithmetics(7)": { "simple_arithmetics(7)": {
"1 + 23 / 4 * 3", "1 + 23 / 4 * 3",
[]TokenType{ []TokenKind{
TokenInteger, TokenPlus, TokenInteger, TokenSlash, TokenInteger, TokenPlus, TokenInteger, TokenSlash,
TokenInteger, TokenStar, TokenInteger, TokenEOF, TokenInteger, TokenStar, TokenInteger, TokenEOF,
}, },
}, },
"condition(3)": { "condition(3)": {
"a <= 200", "a <= 200",
[]TokenType{TokenName, TokenLessThanOrEqual, TokenInteger, TokenEOF}, []TokenKind{TokenName, TokenLessThanOrEqual, TokenInteger, TokenEOF},
}, },
"if_statement(10)": { "if_statement(10)": {
"if a >= 200 {\n write(\"Hello world!\")\n}", "if a >= 200 {\n write(\"Hello world!\")\n}",
[]TokenType{ []TokenKind{
TokenIf, TokenName, TokenGreaterThanOrEqual, TokenInteger, TokenOpenBrace, TokenNewLine, TokenIf, TokenName, TokenGreaterThanOrEqual, TokenInteger, TokenOpenBrace, TokenNewLine,
TokenName, TokenOpenParenthesis, TokenString, TokenCloseParenthesis, TokenNewLine, TokenName, TokenOpenParenthesis, TokenString, TokenCloseParenthesis, TokenNewLine,
TokenCloseBrace, TokenEOF, TokenCloseBrace, TokenEOF,
@ -44,7 +44,7 @@ func GetLexerTestData() map[string]LexerTestData {
}, },
"if_else_statement(20)": { "if_else_statement(20)": {
"if 23 * 2/3 > 32 {\n write(\"It is larger!\")\n} else {\n write(\"It is lower!\")\n}", "if 23 * 2/3 > 32 {\n write(\"It is larger!\")\n} else {\n write(\"It is lower!\")\n}",
[]TokenType{ []TokenKind{
TokenIf, TokenInteger, TokenStar, TokenInteger, TokenSlash, TokenInteger, TokenGreaterThan, TokenInteger, TokenOpenBrace, TokenNewLine, TokenIf, TokenInteger, TokenStar, TokenInteger, TokenSlash, TokenInteger, TokenGreaterThan, TokenInteger, TokenOpenBrace, TokenNewLine,
TokenName, TokenOpenParenthesis, TokenString, TokenCloseParenthesis, TokenNewLine, TokenCloseBrace, TokenName, TokenOpenParenthesis, TokenString, TokenCloseParenthesis, TokenNewLine, TokenCloseBrace,
TokenElse, TokenOpenBrace, TokenNewLine, TokenName, TokenOpenParenthesis, TokenString, TokenCloseParenthesis, TokenNewLine, TokenCloseBrace, TokenElse, TokenOpenBrace, TokenNewLine, TokenName, TokenOpenParenthesis, TokenString, TokenCloseParenthesis, TokenNewLine, TokenCloseBrace,
@ -53,11 +53,11 @@ func GetLexerTestData() map[string]LexerTestData {
}, },
"empty_string": { "empty_string": {
"", "",
[]TokenType{TokenEOF}, []TokenKind{TokenEOF},
}, },
"full_arithmetic_equality": { "full_arithmetic_equality": {
"a + 2 == 10 * 2 / 3", "a + 2 == 10 * 2 / 3",
[]TokenType{ []TokenKind{
TokenName, TokenPlus, TokenInteger, TokenEquals, TokenName, TokenPlus, TokenInteger, TokenEquals,
TokenInteger, TokenStar, TokenInteger, TokenSlash, TokenInteger, TokenInteger, TokenStar, TokenInteger, TokenSlash, TokenInteger,
TokenEOF, TokenEOF,
@ -65,11 +65,11 @@ func GetLexerTestData() map[string]LexerTestData {
}, },
"name": { "name": {
"print", "print",
[]TokenType{TokenName, TokenEOF}, []TokenKind{TokenName, TokenEOF},
}, },
"bunch_of_parentheses": { "bunch_of_parentheses": {
"(((())))", "(((())))",
[]TokenType{ []TokenKind{
TokenOpenParenthesis, TokenOpenParenthesis, TokenOpenParenthesis, TokenOpenParenthesis, TokenOpenParenthesis, TokenOpenParenthesis, TokenOpenParenthesis, TokenOpenParenthesis,
TokenCloseParenthesis, TokenCloseParenthesis, TokenCloseParenthesis, TokenCloseParenthesis, TokenCloseParenthesis, TokenCloseParenthesis, TokenCloseParenthesis, TokenCloseParenthesis,
TokenEOF, TokenEOF,
@ -77,22 +77,22 @@ func GetLexerTestData() map[string]LexerTestData {
}, },
"space_before_string": { "space_before_string": {
"\n \"\"", "\n \"\"",
[]TokenType{TokenNewLine, TokenString, TokenEOF}, []TokenKind{TokenNewLine, TokenString, TokenEOF},
}, },
"write_call": { "write_call": {
"write(\"Hello world\")", "write(\"Hello world\")",
[]TokenType{TokenName, TokenOpenParenthesis, TokenString, TokenCloseParenthesis, TokenEOF}, []TokenKind{TokenName, TokenOpenParenthesis, TokenString, TokenCloseParenthesis, TokenEOF},
}, },
"complex_comparison": { "complex_comparison": {
"!(h__elo123 >= 1)", "!(h__elo123 >= 1)",
[]TokenType{ []TokenKind{
TokenBang, TokenOpenParenthesis, TokenName, TokenGreaterThanOrEqual, TokenInteger, TokenCloseParenthesis, TokenBang, TokenOpenParenthesis, TokenName, TokenGreaterThanOrEqual, TokenInteger, TokenCloseParenthesis,
TokenEOF, TokenEOF,
}, },
}, },
"3assignments_1condition": { "3assignments_1condition": {
"a = 8 * 32\nb = a > 256\nc = a <= 256\n!b == c", "a = 8 * 32\nb = a > 256\nc = a <= 256\n!b == c",
[]TokenType{ []TokenKind{
TokenName, TokenAssign, TokenInteger, TokenStar, TokenInteger, TokenNewLine, TokenName, TokenAssign, TokenInteger, TokenStar, TokenInteger, TokenNewLine,
TokenName, TokenAssign, TokenName, TokenGreaterThan, TokenInteger, TokenNewLine, TokenName, TokenAssign, TokenName, TokenGreaterThan, TokenInteger, TokenNewLine,
TokenName, TokenAssign, TokenName, TokenLessThanOrEqual, TokenInteger, TokenNewLine, TokenName, TokenAssign, TokenName, TokenLessThanOrEqual, TokenInteger, TokenNewLine,
@ -101,14 +101,14 @@ func GetLexerTestData() map[string]LexerTestData {
}, },
"function": { "function": {
"fn sum(a, b) {\n return a + b\n}", "fn sum(a, b) {\n return a + b\n}",
[]TokenType{ []TokenKind{
TokenFunc, TokenName, TokenOpenParenthesis, TokenName, TokenComma, TokenName, TokenCloseParenthesis, TokenFunc, TokenName, TokenOpenParenthesis, TokenName, TokenComma, TokenName, TokenCloseParenthesis,
TokenOpenBrace, TokenNewLine, TokenReturn, TokenName, TokenPlus, TokenName, TokenNewLine, TokenCloseBrace, TokenOpenBrace, TokenNewLine, TokenReturn, TokenName, TokenPlus, TokenName, TokenNewLine, TokenCloseBrace,
}, },
}, },
"while_loop": { "while_loop": {
"while a < 5 {\n a = a + 1\n}", "while a < 5 {\n a = a + 1\n}",
[]TokenType{ []TokenKind{
TokenWhile, TokenName, TokenLessThan, TokenInteger, TokenOpenBrace, TokenNewLine, TokenWhile, TokenName, TokenLessThan, TokenInteger, TokenOpenBrace, TokenNewLine,
TokenName, TokenAssign, TokenName, TokenPlus, TokenInteger, TokenNewLine, TokenCloseBrace, TokenEOF, TokenName, TokenAssign, TokenName, TokenPlus, TokenInteger, TokenNewLine, TokenCloseBrace, TokenEOF,
}, },
@ -117,14 +117,14 @@ func GetLexerTestData() map[string]LexerTestData {
"sum := fn(a, b) {\n" + "sum := fn(a, b) {\n" +
" return a + b\n" + " return a + b\n" +
"}", "}",
[]TokenType{ []TokenKind{
TokenName, TokenDeclare, TokenFunc, TokenOpenParenthesis, TokenName, TokenComma, TokenName, TokenCloseParenthesis, TokenName, TokenDeclare, TokenFunc, TokenOpenParenthesis, TokenName, TokenComma, TokenName, TokenCloseParenthesis,
TokenOpenBrace, TokenNewLine, TokenReturn, TokenName, TokenPlus, TokenName, TokenNewLine, TokenCloseBrace, TokenOpenBrace, TokenNewLine, TokenReturn, TokenName, TokenPlus, TokenName, TokenNewLine, TokenCloseBrace,
}, },
}, },
"list": { "list": {
"data := [3, 1, 4, 1]", "data := [3, 1, 4, 1]",
[]TokenType{ []TokenKind{
TokenName, TokenDeclare, TokenOpenBracket, TokenInteger, TokenComma, TokenInteger, TokenComma, TokenInteger, TokenComma, TokenInteger, TokenCloseBracket, TokenName, TokenDeclare, TokenOpenBracket, TokenInteger, TokenComma, TokenInteger, TokenComma, TokenInteger, TokenComma, TokenInteger, TokenCloseBracket,
}, },
}, },

View file

@ -16,7 +16,7 @@ type Node interface {
Bounds() (Pos, Pos) Bounds() (Pos, Pos)
} }
type Boundary interface { type Bounded interface {
Bounds() (Pos, Pos) Bounds() (Pos, Pos)
} }
@ -40,6 +40,8 @@ const (
FunctionNodeType FunctionNodeType
ReturnNodeType ReturnNodeType
AccessNodeType AccessNodeType
AliasNodeType
IndexNodeType
BreakpointNodeType BreakpointNodeType
) )
@ -68,7 +70,7 @@ func (n NodeType) String() string {
case AssignNodeType: case AssignNodeType:
return "Assign" return "Assign"
case InvokeNodeType: case InvokeNodeType:
return "Call" return "Invoke"
case FunctionNodeType: case FunctionNodeType:
return "Function" return "Function"
case ReturnNodeType: case ReturnNodeType:
@ -85,6 +87,10 @@ func (n NodeType) String() string {
return "Unary" return "Unary"
case CallNodeType: case CallNodeType:
return "Call" return "Call"
case AliasNodeType:
return "Alias"
case IndexNodeType:
return "Index"
} }
return "Invalid Node Type" return "Invalid Node Type"
} }
@ -231,7 +237,7 @@ func (n TupleNode) Bounds() (Pos, Pos) {
type AccessNode struct { type AccessNode struct {
source Node source Node
property string property *Token
start Pos start Pos
end Pos end Pos
@ -242,7 +248,7 @@ func (n AccessNode) Type() NodeType {
} }
func (n AccessNode) String() string { func (n AccessNode) String() string {
return fmt.Sprintf("(%s from %s)", n.property, n.source) return fmt.Sprintf("(%s from %s)", n.property.Lexeme, n.source)
} }
func (n AccessNode) Bounds() (Pos, Pos) { func (n AccessNode) Bounds() (Pos, Pos) {
@ -273,9 +279,9 @@ func (n BinaryOperation) String() string {
return "less or equal" return "less or equal"
case BinaryGreaterEqual: case BinaryGreaterEqual:
return "greater or equal" return "greater or equal"
case BinaryAnd: case BinaryBooleanAnd:
return "and" return "and"
case BinaryOr: case BinaryBooleanOr:
return "or" return "or"
} }
@ -288,8 +294,8 @@ const (
BinaryMultiplication BinaryMultiplication
BinaryDivision BinaryDivision
BinaryAnd BinaryBooleanAnd
BinaryOr BinaryBooleanOr
// Comparison // Comparison
BinaryEquality BinaryEquality
@ -322,9 +328,9 @@ func (n BinaryOperation) Symbol() string {
return "<=" return "<="
case BinaryGreaterEqual: case BinaryGreaterEqual:
return ">=" return ">="
case BinaryAnd: case BinaryBooleanAnd:
return "&&" return "&&"
case BinaryOr: case BinaryBooleanOr:
return "||" return "||"
} }
@ -337,8 +343,9 @@ type BinaryNode struct {
Left Node Left Node
Right Node Right Node
start Pos operator *Token
end Pos start Pos
end Pos
} }
func (n BinaryNode) Type() NodeType { func (n BinaryNode) Type() NodeType {
@ -386,8 +393,9 @@ type UnaryNode struct {
UnaryOperation UnaryOperation
value Node value Node
start Pos operator *Token
end Pos start Pos
end Pos
} }
func (n UnaryNode) Type() NodeType { func (n UnaryNode) Type() NodeType {
@ -404,7 +412,7 @@ func (n UnaryNode) Bounds() (Pos, Pos) {
// BooleanNode boolean value // BooleanNode boolean value
type BooleanNode struct { type BooleanNode struct {
value bool Boolean bool
start Pos start Pos
end Pos end Pos
@ -415,7 +423,7 @@ func (n BooleanNode) Type() NodeType {
} }
func (n BooleanNode) String() string { func (n BooleanNode) String() string {
return strconv.FormatBool(n.value) return strconv.FormatBool(n.Boolean)
} }
func (n BooleanNode) Bounds() (Pos, Pos) { func (n BooleanNode) Bounds() (Pos, Pos) {
@ -560,7 +568,7 @@ func (n InvokeNode) Bounds() (Pos, Pos) {
// CallNode call a function of a value // CallNode call a function of a value
type CallNode struct { type CallNode struct {
source Node source Node
name Token name *Token
args []Node args []Node
start Pos start Pos
@ -607,6 +615,18 @@ func (n FunctionNode) Bounds() (Pos, Pos) {
return n.start, n.end return n.start, n.end
} }
func (n FunctionNode) Signature() *FunctionSignature {
args := make([]TypeSignature, len(n.parameters))
for i, p := range n.parameters {
args[i] = p.Signature
}
return &FunctionSignature{
args,
n.yield,
}
}
// ReturnNode return a value out of this context // ReturnNode return a value out of this context
type ReturnNode struct { type ReturnNode struct {
value Node value Node
@ -627,6 +647,46 @@ func (n ReturnNode) Bounds() (Pos, Pos) {
return n.start, n.end return n.start, n.end
} }
type AliasNode struct {
name *Token
signature TypeSignature
start Pos
end Pos
}
func (n AliasNode) Type() NodeType {
return AliasNodeType
}
func (n AliasNode) String() string {
return fmt.Sprintf("alias %s to be %s", n.name, n.signature)
}
func (n AliasNode) Bounds() (Pos, Pos) {
return n.start, n.end
}
type IndexNode struct {
source Node
index Node
start Pos
end Pos
}
func (n IndexNode) Type() NodeType {
return IndexNodeType
}
func (n IndexNode) String() string {
return fmt.Sprintf("index %s of %s", n.source.String(), n.index)
}
func (n IndexNode) Bounds() (Pos, Pos) {
return n.start, n.end
}
type BreakpointNode struct { type BreakpointNode struct {
start Pos start Pos
end Pos end Pos

View file

@ -178,7 +178,7 @@ func (p *Parser) Parse(path string) (*Program, error) {
}, nil }, nil
} }
func (p *Parser) accept(tokenType TokenType) bool { func (p *Parser) accept(tokenType TokenKind) bool {
if p.curr == nil { if p.curr == nil {
log.Fatal("unexpected current token nil") log.Fatal("unexpected current token nil")
return false return false
@ -198,7 +198,7 @@ func (p *Parser) accept(tokenType TokenType) bool {
return false 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) { if int(p.pos)+len(tokenTypes) > len(p.tokens) {
return false return false
} }
@ -213,7 +213,7 @@ func (p *Parser) acceptAll(tokenTypes ...TokenType) bool {
return true return true
} }
func (p *Parser) expect(tokenType TokenType, reason string) error { func (p *Parser) expect(tokenType TokenKind, reason string) error {
if !p.accept(tokenType) { if !p.accept(tokenType) {
return p.error(fmt.Sprintf("Expected token %s, got %s; %s", tokenType, p.curr.Type, reason), p.curr) 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 t := p.curr
switch t.Type { 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: case TokenIf:
p.advance() p.advance()
@ -320,59 +347,6 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
t.End, t.End,
}, nil }, 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: case TokenReturn:
p.advance() p.advance()
start := p.prev.Start 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 { switch tokenType {
case TokenPlus, TokenMinus, TokenStar, TokenSlash, TokenPipe, TokenDoubleAmpersand, TokenDoublePipe, TokenEquals, TokenBangEquals, TokenLessThan, TokenLessThanOrEqual, TokenGreaterThan, TokenGreaterThanOrEqual: case TokenPlus, TokenMinus, TokenStar, TokenSlash, TokenPipe, TokenDoubleAmpersand, TokenDoublePipe, TokenEquals, TokenBangEquals, TokenLessThan, TokenLessThanOrEqual, TokenGreaterThan, TokenGreaterThanOrEqual:
return true return true
@ -449,7 +423,7 @@ func isBinaryOperator(tokenType TokenType) bool {
} }
} }
func binaryPrecedence(op TokenType) int { func binaryPrecedence(op TokenKind) int {
switch op { switch op {
case TokenDoubleAmpersand, TokenDoublePipe: case TokenDoubleAmpersand, TokenDoublePipe:
return 1 return 1
@ -464,7 +438,7 @@ func binaryPrecedence(op TokenType) int {
} }
} }
func tokenToBinaryOperation(tokenType TokenType) BinaryOperation { func tokenToBinaryOperation(tokenType TokenKind) BinaryOperation {
switch tokenType { switch tokenType {
case TokenPlus: case TokenPlus:
return BinaryAddition return BinaryAddition
@ -477,9 +451,9 @@ func tokenToBinaryOperation(tokenType TokenType) BinaryOperation {
case TokenPipe: case TokenPipe:
panic("unimplemented bitwise ops") panic("unimplemented bitwise ops")
case TokenDoubleAmpersand: case TokenDoubleAmpersand:
return BinaryAnd return BinaryBooleanAnd
case TokenDoublePipe: case TokenDoublePipe:
return BinaryOr return BinaryBooleanOr
case TokenEquals: case TokenEquals:
return BinaryEquality return BinaryEquality
@ -509,22 +483,28 @@ func (p *Parser) binary() (Node, error) {
values := NewStack[Node](256) values := NewStack[Node](256)
values.pushItem(t) 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 isBinaryOperator(p.curr.Type) {
for ops.Current > 0 && binaryPrecedence(p.curr.Type) <= binaryPrecedence(ops.Peek().Type) { for ops.Current > 0 && binaryPrecedence(p.curr.Type) <= binaryPrecedence(ops.Peek().Type) {
r := values.Pop() reduce()
l := values.Pop()
op := tokenToBinaryOperation(ops.Pop().Type)
start, _ := l.Bounds()
_, end := r.Bounds()
values.Push(&BinaryNode{
op,
l,
r,
start,
end,
})
} }
ops.Push(p.curr) ops.Push(p.curr)
@ -539,20 +519,7 @@ func (p *Parser) binary() (Node, error) {
} }
for ops.Current > 0 { for ops.Current > 0 {
r := values.Pop() reduce()
l := values.Pop()
op := tokenToBinaryOperation(ops.Pop().Type)
start, _ := l.Bounds()
_, end := l.Bounds()
values.Push(&BinaryNode{
op,
l,
r,
start,
end,
})
} }
return values.Pop(), nil return values.Pop(), nil
@ -573,7 +540,7 @@ func (p *Parser) chain() (Node, error) {
f = &AccessNode{ f = &AccessNode{
f, f,
p.prev.Lexeme, p.prev,
name.Start, name.Start,
name.End, name.End,
} }
@ -602,6 +569,24 @@ func (p *Parser) chain() (Node, error) {
f, f,
args, 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, start,
p.prev.End, 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 { if err != nil {
return nil, err return nil, err
} }
@ -736,7 +721,7 @@ func (p *Parser) factor() (Node, error) {
// unary minus // unary minus
case TokenMinus: case TokenMinus:
p.advance() p.advance()
first := p.prev op := p.prev
f, err := p.factor() f, err := p.factor()
if err != nil { if err != nil {
@ -745,13 +730,14 @@ func (p *Parser) factor() (Node, error) {
return &UnaryNode{ return &UnaryNode{
UnaryNegate, UnaryNegate,
f, f,
first.Start, op,
op.Start,
p.prev.End, p.prev.End,
}, nil }, nil
case TokenBang: case TokenBang:
p.advance() p.advance()
start := p.prev.Start op := p.prev
v, err := p.factor() v, err := p.factor()
if err != nil { if err != nil {
@ -761,7 +747,8 @@ func (p *Parser) factor() (Node, error) {
return &UnaryNode{ return &UnaryNode{
UnaryNot, UnaryNot,
v, v,
start, op,
op.Start,
p.prev.End, p.prev.End,
}, nil }, nil
@ -799,32 +786,54 @@ func (p *Parser) factor() (Node, error) {
p.advance() p.advance()
start := p.prev.Start 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() params, err := p.parseParams()
if err != nil { if err != nil {
return nil, err return nil, err
} }
var sig TypeSignature = &NilSignature{} var yield TypeSignature
if p.accept(TokenArrow) { if p.accept(TokenArrow) {
sig, err = p.parseSignature() yield, err = p.parseSignature()
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
b, err := p.block(false) logic, err := p.expression(true)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &FunctionNode{ names := "*"
"*", if name != nil {
names = name.Lexeme
}
fn := &FunctionNode{
names,
params, params,
sig, yield,
b, logic,
start, start,
p.prev.End, 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: case TokenOpenParenthesis:
p.advance() 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) { func (p *Parser) parseArgs() ([]Node, error) {
args := make([]Node, 0) args := make([]Node, 0)
@ -1514,7 +1089,9 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
s = &AnySignature{} s = &AnySignature{}
default: default:
return nil, p.error("unsupported type: "+name, p.prev) s = &NamedSignature{
name,
}
} }
} }

View file

@ -73,6 +73,7 @@ func GetTokenTestData() map[string]TokenTestData {
2, 2,
0, 0, 0, 0,
}, },
nil,
0, 0, 0, 0,
}, },
false, false,
@ -128,6 +129,7 @@ func GetTokenTestData() map[string]TokenTestData {
"b", "b",
0, 0, 0, 0,
}, },
nil,
0, 0, 0, 0,
}, },
true, true,
@ -189,12 +191,14 @@ func GetTokenTestData() map[string]TokenTestData {
1, 1,
0, 0, 0, 0,
}, },
nil,
0, 0, 0, 0,
}, },
&FloatNode{ &FloatNode{
5, 5,
0, 0, 0, 0,
}, },
nil,
0, 0, 0, 0,
}, },
&BinaryNode{ &BinaryNode{
@ -213,10 +217,13 @@ func GetTokenTestData() map[string]TokenTestData {
2, 2,
0, 0, 0, 0,
}, },
nil,
0, 0, 0, 0,
}, },
nil,
0, 0, 0, 0,
}, },
nil,
0, 0, 0, 0,
}, },
&BinaryNode{ &BinaryNode{
@ -229,8 +236,10 @@ func GetTokenTestData() map[string]TokenTestData {
2, 2,
0, 0, 0, 0,
}, },
nil,
0, 0, 0, 0,
}, },
nil,
0, 0, 0, 0,
}, },
false, false,
@ -263,6 +272,7 @@ func GetTokenTestData() map[string]TokenTestData {
15, 15,
0, 0, 0, 0,
}, },
nil,
0, 0, 0, 0,
}, },
false, false,
@ -298,6 +308,7 @@ func GetTokenTestData() map[string]TokenTestData {
0, 0,
0, 0, 0, 0,
}, },
nil,
0, 0, 0, 0,
}, },
do: &BlockNode{ do: &BlockNode{
@ -351,6 +362,7 @@ func GetTokenTestData() map[string]TokenTestData {
0, 0,
0, 0, 0, 0,
}, },
nil,
0, 0, 0, 0,
}, },
do: &BlockNode{ do: &BlockNode{
@ -458,6 +470,7 @@ func GetTokenTestData() map[string]TokenTestData {
"b", "b",
0, 0, 0, 0,
}, },
nil,
0, 0, 0, 0,
}, },
0, 0, 0, 0,
@ -529,6 +542,7 @@ func GetTokenTestData() map[string]TokenTestData {
"b", "b",
0, 0, 0, 0,
}, },
nil,
0, 0, 0, 0,
}, },
0, 0, 0, 0,
@ -564,7 +578,7 @@ func GetTokenTestData() map[string]TokenTestData {
"a", "a",
0, 0, 0, 0,
}, },
"b", &Token{TokenName, 0, 1, 0, "b"},
0, 0, 0, 0,
}, },
true, true,
@ -767,10 +781,10 @@ func NodeEquality(t *testing.T, n1 Node, n2 Node) {
NodeEquality(t, n1.(*BinaryNode).Right, n2.(*BinaryNode).Right) NodeEquality(t, n1.(*BinaryNode).Right, n2.(*BinaryNode).Right)
case BooleanNodeType: case BooleanNodeType:
if n1.(*BooleanNode).value != n2.(*BooleanNode).value { if n1.(*BooleanNode).Boolean != n2.(*BooleanNode).Boolean {
t.Errorf("Boolean node values don't match (%s and %s)", strconv.FormatBool(n1.(*BooleanNode).value), strconv.FormatBool(n2.(*BooleanNode).value)) t.Errorf("Boolean node values don't match (%s and %s)", strconv.FormatBool(n1.(*BooleanNode).Boolean), strconv.FormatBool(n2.(*BooleanNode).Boolean))
} else { } else {
t.Logf("Boolean node values match (%s)", strconv.FormatBool(n1.(*BooleanNode).value)) t.Logf("Boolean node values match (%s)", strconv.FormatBool(n1.(*BooleanNode).Boolean))
} }
case BlockNodeType: case BlockNodeType:
if len(n1.(*BlockNode).statements) != len(n2.(*BlockNode).statements) { if len(n1.(*BlockNode).statements) != len(n2.(*BlockNode).statements) {
@ -840,7 +854,7 @@ func NodeEquality(t *testing.T, n1 Node, n2 Node) {
} }
for i, p := range m.parameters { for i, p := range m.parameters {
if !n.parameters[i].Signature.Matches(p.Signature) { if !n.parameters[i].Signature.Contains(p.Signature) {
t.Errorf("Function node parameter signature %d does not match: %s and %s", i, p.Signature, n.parameters[i].Signature) t.Errorf("Function node parameter signature %d does not match: %s and %s", i, p.Signature, n.parameters[i].Signature)
} else if n.parameters[i].Name != p.Name { } else if n.parameters[i].Name != p.Name {
t.Errorf("Function node parameter name %d does not match: %s and %s", i, p.Name, n.parameters[i].Name) t.Errorf("Function node parameter name %d does not match: %s and %s", i, p.Name, n.parameters[i].Name)
@ -858,10 +872,11 @@ func NodeEquality(t *testing.T, n1 Node, n2 Node) {
a1 := n1.(*AccessNode) a1 := n1.(*AccessNode)
a2 := n2.(*AccessNode) a2 := n2.(*AccessNode)
if a1.property != a2.property { // only care about lexeme; the rest is debug info
t.Errorf("Access node property does not match: .%s != .%s", a1.property, a2.property) if a1.property.Lexeme != a2.property.Lexeme {
t.Errorf("Access node property does not match: .%s != .%s", a1.property.Lexeme, a2.property.Lexeme)
} else { } else {
t.Logf("Access node property matches: .%s", a1.property) t.Logf("Access node property matches: .%s", a1.property.Lexeme)
} }
NodeEquality(t, a1.source, a2.source) NodeEquality(t, a1.source, a2.source)
@ -875,7 +890,7 @@ func NodeEquality(t *testing.T, n1 Node, n2 Node) {
t.Logf("Both content types are yet to be determined") t.Logf("Both content types are yet to be determined")
} else if l1.content != nil || l2.content != nil { } else if l1.content != nil || l2.content != nil {
t.Errorf("one is nil, one is not") t.Errorf("one is nil, one is not")
} else if !l1.content.Matches(l2.content) { } else if !l1.content.Contains(l2.content) {
t.Errorf("signature doesn't match") t.Errorf("signature doesn't match")
} }

View file

@ -20,6 +20,7 @@ const (
TypeAny TypeAny
TypeComposite TypeComposite
TypeInner TypeInner
TypeNamed
) )
func (t Type) String() string { func (t Type) String() string {
@ -71,7 +72,7 @@ func SignatureOf(v Value) TypeSignature {
sig := SignatureOf(p) sig := SignatureOf(p)
if contains == nil { if contains == nil {
contains = sig contains = sig
} else if !contains.Matches(sig) { } else if !contains.Contains(sig) {
contains = &AnySignature{} contains = &AnySignature{}
break break
} }
@ -113,8 +114,12 @@ func SignatureOf(v Value) TypeSignature {
type TypeSignature interface { type TypeSignature interface {
Type() Type Type() Type
// Matches check if this type signature matches another. // Contains check if this type signature matches another.
Matches(TypeSignature) bool // For it to return true, the other type should be a part of this.
Contains(TypeSignature) bool
// Equal check if the other signature is the EXACT SAME as this one.
Equal(TypeSignature) bool
// String create a human-readable string version of the value type. // String create a human-readable string version of the value type.
String() string String() string
@ -126,12 +131,12 @@ func (*NilSignature) Type() Type {
return TypeNil return TypeNil
} }
func (s *NilSignature) Matches(other TypeSignature) bool { func (s *NilSignature) Contains(other TypeSignature) bool {
if other.Type() == TypeComposite { return other.Type() == TypeNil
return other.Matches(s) }
}
return other.Type() == TypeAny || other.Type() == TypeNil func (s *NilSignature) Equal(other TypeSignature) bool {
return other.Type() == TypeNil
} }
func (*NilSignature) String() string { func (*NilSignature) String() string {
@ -144,12 +149,12 @@ func (*StringSignature) Type() Type {
return TypeString return TypeString
} }
func (s *StringSignature) Matches(other TypeSignature) bool { func (s *StringSignature) Contains(other TypeSignature) bool {
if other.Type() == TypeComposite { return other.Type() == TypeString
return other.Matches(s) }
}
return other.Type() == TypeAny || other.Type() == TypeString func (s *StringSignature) Equal(other TypeSignature) bool {
return other.Type() == TypeString
} }
func (*StringSignature) String() string { func (*StringSignature) String() string {
@ -162,12 +167,12 @@ func (*FloatSignature) Type() Type {
return TypeFloat return TypeFloat
} }
func (s *FloatSignature) Matches(other TypeSignature) bool { func (s *FloatSignature) Contains(other TypeSignature) bool {
if other.Type() == TypeComposite { return other.Type() == TypeFloat
return other.Matches(s) }
}
return other.Type() == TypeAny || other.Type() == TypeFloat func (s *FloatSignature) Equal(other TypeSignature) bool {
return other.Type() == TypeFloat
} }
func (*FloatSignature) String() string { func (*FloatSignature) String() string {
@ -180,12 +185,12 @@ func (*IntegerSignature) Type() Type {
return TypeInteger return TypeInteger
} }
func (s *IntegerSignature) Matches(other TypeSignature) bool { func (s *IntegerSignature) Contains(other TypeSignature) bool {
if other.Type() == TypeComposite { return other.Type() == TypeInteger
return other.Matches(s) }
}
return other.Type() == TypeAny || other.Type() == TypeInteger func (s *IntegerSignature) Equal(other TypeSignature) bool {
return other.Type() == TypeInteger
} }
func (*IntegerSignature) String() string { func (*IntegerSignature) String() string {
@ -198,12 +203,12 @@ func (*BooleanSignature) Type() Type {
return TypeBoolean return TypeBoolean
} }
func (s *BooleanSignature) Matches(other TypeSignature) bool { func (s *BooleanSignature) Contains(other TypeSignature) bool {
if other.Type() == TypeComposite { return other.Type() == TypeBoolean
return other.Matches(s) }
}
return other.Type() == TypeAny || other.Type() == TypeBoolean func (s *BooleanSignature) Equal(other TypeSignature) bool {
return other.Type() == TypeBoolean
} }
func (*BooleanSignature) String() string { func (*BooleanSignature) String() string {
@ -218,16 +223,16 @@ func (*ListSignature) Type() Type {
return TypeList return TypeList
} }
func (s *ListSignature) Matches(other TypeSignature) bool { func (s *ListSignature) Contains(other TypeSignature) bool {
if other.Type() == TypeComposite { return other.Type() == TypeList && other.(*ListSignature).Contents.Contains(s.Contents)
return other.Matches(s) }
}
return other.Type() == TypeAny || (other.Type() == TypeList && other.(*ListSignature).Contents.Matches(s.Contents)) func (s *ListSignature) Equal(other TypeSignature) bool {
return other.Type() == TypeList && other.(*ListSignature).Contents.Equal(s.Contents)
} }
func (s *ListSignature) String() string { func (s *ListSignature) String() string {
return fmt.Sprintf("list[%s]", s.Contents) return fmt.Sprintf("[%s]", s.Contents)
} }
type TupleSignature struct { type TupleSignature struct {
@ -238,22 +243,29 @@ func (*TupleSignature) Type() Type {
return TypeTuple return TypeTuple
} }
func (s *TupleSignature) Matches(other TypeSignature) bool { func (s *TupleSignature) Contains(other TypeSignature) bool {
if other.Type() == TypeComposite {
return other.Matches(s)
}
if other.Type() == TypeAny {
return true
}
if other.Type() != TypeTuple || len(other.(*TupleSignature).Contents) != len(s.Contents) { if other.Type() != TypeTuple || len(other.(*TupleSignature).Contents) != len(s.Contents) {
return false return false
} }
n := other.(*TupleSignature).Contents n := other.(*TupleSignature).Contents
for i, c := range s.Contents { for i, c := range s.Contents {
if !c.Matches(n[i]) { if !c.Contains(n[i]) {
return false
}
}
return true
}
func (s *TupleSignature) Equal(other TypeSignature) bool {
if other.Type() != TypeTuple {
return false
}
n := other.(*TupleSignature).Contents
for i, c := range s.Contents {
if !c.Equal(n[i]) {
return false return false
} }
} }
@ -290,25 +302,13 @@ func (*ObjectSignature) Type() Type {
return TypeObject return TypeObject
} }
func (s *ObjectSignature) Matches(other TypeSignature) bool { func (s *ObjectSignature) Contains(other TypeSignature) bool {
if other.Type() == TypeComposite {
return other.Matches(s)
}
if other.Type() == TypeAny {
return true
}
if other.Type() != TypeObject { if other.Type() != TypeObject {
return false return false
} }
o := other.(*ObjectSignature) o := other.(*ObjectSignature)
if len(o.Members) != len(s.Members) {
return false
}
for name, member := range s.Members { for name, member := range s.Members {
v, ok := o.Members[name] v, ok := o.Members[name]
@ -316,7 +316,29 @@ func (s *ObjectSignature) Matches(other TypeSignature) bool {
return false return false
} }
if !v.Matches(member) { if !v.Contains(member) {
return false
}
}
return true
}
func (s *ObjectSignature) Equal(other TypeSignature) bool {
if other.Type() != TypeObject {
return false
}
if len(s.Members) != len(other.(*ObjectSignature).Members) {
return false
}
for name, member := range s.Members {
v, ok := other.(*ObjectSignature).Members[name]
if !ok {
return false
}
if !v.Equal(member) {
return false return false
} }
} }
@ -337,22 +359,14 @@ func (*FunctionSignature) Type() Type {
return TypeFunction return TypeFunction
} }
func (s *FunctionSignature) Matches(other TypeSignature) bool { func (s *FunctionSignature) Contains(other TypeSignature) bool {
if other.Type() == TypeComposite {
return other.Matches(s)
}
if other.Type() == TypeAny {
return true
}
if other.Type() != TypeFunction { if other.Type() != TypeFunction {
return false return false
} }
f := other.(*FunctionSignature) f := other.(*FunctionSignature)
if !s.Out.Matches(f.Out) { if !s.Out.Contains(f.Out) {
return false return false
} }
@ -362,7 +376,7 @@ func (s *FunctionSignature) Matches(other TypeSignature) bool {
for i, p := range s.In { for i, p := range s.In {
v := f.In[i] v := f.In[i]
if !p.Matches(v) { if !p.Contains(v) {
return false return false
} }
} }
@ -370,6 +384,26 @@ func (s *FunctionSignature) Matches(other TypeSignature) bool {
return true return true
} }
func (s *FunctionSignature) Equal(other TypeSignature) bool {
if other.Type() != TypeFunction {
return false
}
f := other.(*FunctionSignature)
if len(f.In) != len(s.In) {
return false
}
for i, p := range s.In {
v := f.In[i]
if !p.Equal(v) {
return false
}
}
return f.Out.Equal(s.Out)
}
func (s *FunctionSignature) String() string { func (s *FunctionSignature) String() string {
b := strings.Builder{} b := strings.Builder{}
@ -397,10 +431,14 @@ func (*AnySignature) Type() Type {
return TypeAny return TypeAny
} }
func (*AnySignature) Matches(_ TypeSignature) bool { func (*AnySignature) Contains(_ TypeSignature) bool {
return true return true
} }
func (s *AnySignature) Equal(t TypeSignature) bool {
return t.Type() == TypeAny
}
func (*AnySignature) String() string { func (*AnySignature) String() string {
return "any" return "any"
} }
@ -414,8 +452,12 @@ func (*CompositeSignature) Type() Type {
return TypeComposite return TypeComposite
} }
func (s *CompositeSignature) Matches(other TypeSignature) bool { func (s *CompositeSignature) Contains(other TypeSignature) bool {
return s.A.Matches(other) || s.B.Matches(other) return s.A.Contains(other) || s.B.Contains(other)
}
func (s *CompositeSignature) Equal(t TypeSignature) bool {
return t.Type() == TypeComposite && s.A.Equal(t.(*CompositeSignature).A) && s.B.Equal(t.(*CompositeSignature).B)
} }
func (s *CompositeSignature) String() string { func (s *CompositeSignature) String() string {
@ -448,10 +490,34 @@ func (*InnerSignature) Type() Type {
return TypeInner return TypeInner
} }
func (*InnerSignature) Matches(_ TypeSignature) bool { func (*InnerSignature) Contains(_ TypeSignature) bool {
return false return false
} }
func (s *InnerSignature) Equal(t TypeSignature) bool {
return t.Type() == TypeInner
}
func (*InnerSignature) String() string { func (*InnerSignature) String() string {
return "inner" return "inner"
} }
type NamedSignature struct {
Name string
}
func (*NamedSignature) Type() Type {
return TypeNamed
}
func (s *NamedSignature) Contains(t TypeSignature) bool {
return t.Type() == TypeNamed && s.Name == t.(*NamedSignature).Name
}
func (s *NamedSignature) Equal(t TypeSignature) bool {
return t.Type() == TypeNamed && s.Name == t.(*NamedSignature).Name
}
func (s *NamedSignature) String() string {
return s.Name
}

View file

@ -223,8 +223,8 @@ func (v *ObjectValue) Equals(other Value) bool {
return true return true
} }
var ObjectPrototype = map[string]Value{ var ObjectPrototype = map[string]*BuiltinFunctionValue{
"set": &BuiltinFunctionValue{ "set": {
"set", "set",
&FunctionSignature{ &FunctionSignature{
[]TypeSignature{&StringSignature{}, &ListSignature{}}, []TypeSignature{&StringSignature{}, &ListSignature{}},

View file

@ -66,7 +66,7 @@ func CompareValues(t *testing.T, got Value, want Value) {
t.Errorf("builtin function name mismatch: got %v, want %v", n.Name, m.Name) t.Errorf("builtin function name mismatch: got %v, want %v", n.Name, m.Name)
} }
if !n.Signature.Matches(m.Signature) { if !n.Signature.Contains(m.Signature) {
t.Errorf("builtin function parameter count mismatch: got %v, want %v", n, m) t.Errorf("builtin function parameter count mismatch: got %v, want %v", n, m)
} }

View file

@ -99,8 +99,8 @@ const (
// InstructionStringConversion Take the top value on the stack and convert it to a string // InstructionStringConversion Take the top value on the stack and convert it to a string
InstructionStringConversion InstructionStringConversion
// InstructionStringConcatenation Add two strings together, with the second value on the stack as left and the top as right // InstructionConcatStrings Add two strings together, with the second value on the stack as left and the top as right
InstructionStringConcatenation InstructionConcatStrings
// InstructionSwap swap the two top items on the stack (1, 2 -> 2, 1) // InstructionSwap swap the two top items on the stack (1, 2 -> 2, 1)
InstructionSwap InstructionSwap
@ -121,13 +121,11 @@ const (
// InstructionNil Push a nil literal to the stack // InstructionNil Push a nil literal to the stack
InstructionNil InstructionNil
// InstructionNewList Push a new (empty) list to the stack
InstructionNewList
// InstructionAppend Append to a list. stack: (... > list > item) => (... > list) // InstructionAppend Append to a list. stack: (... > list > item) => (... > list)
InstructionAppend InstructionAppend
// InstructionFormList Form items on the stack into a list. The 2 bytes after the instructions are the amount of // InstructionFormList Form items on the stack into a list. The 2 bytes after the instructions are the amount of
// items to include minus one. (value of 0 => 1 item, value of 1 => 2 items, etc.) The order is reversed compared // items to include) The order is reversed compared to on the stack; the top value on the stack is the last in the
// to on the stack; the top value on the stack is the last in the list. // list.
InstructionFormList InstructionFormList
// InstructionConcatLists concatenate lists, producing a new list with the values of both lists. Pops two lists. // InstructionConcatLists concatenate lists, producing a new list with the values of both lists. Pops two lists.
InstructionConcatLists InstructionConcatLists
@ -136,6 +134,13 @@ const (
// on the stack is the last value in the tuple. // on the stack is the last value in the tuple.
InstructionFormTuple InstructionFormTuple
// InstructionIndexList index into a list. The lower item is the container, and the top item
// is the index. [..., container, index] -> [..., item]
InstructionIndexList
// InstructionIndexTuple index into a tuple. The lower item is the container, and the top item
// is the index. [..., container, index] -> [..., item]
InstructionIndexTuple
// InstructionBreakpoint for debugging purposes // InstructionBreakpoint for debugging purposes
InstructionBreakpoint InstructionBreakpoint
) )
@ -220,7 +225,7 @@ func (b Bytecode) String() string {
return "ASCEND" return "ASCEND"
case InstructionStringConversion: case InstructionStringConversion:
return "STRING_CONVERSION" return "STRING_CONVERSION"
case InstructionStringConcatenation: case InstructionConcatStrings:
return "STRING_CONCATENATION" return "STRING_CONCATENATION"
case InstructionSwap: case InstructionSwap:
return "SWAP" return "SWAP"
@ -232,8 +237,6 @@ func (b Bytecode) String() string {
return "FORM_LIST" return "FORM_LIST"
case InstructionBreakpoint: case InstructionBreakpoint:
return "BREAKPOINT" return "BREAKPOINT"
case InstructionNewList:
return "NEW_LIST"
case InstructionAppend: case InstructionAppend:
return "APPEND" return "APPEND"
case InstructionAccessProperty: case InstructionAccessProperty:
@ -242,6 +245,12 @@ func (b Bytecode) String() string {
return "CONCAT_LISTS" return "CONCAT_LISTS"
case InstructionDuplicate: case InstructionDuplicate:
return "DUPLICATE" return "DUPLICATE"
case InstructionFormTuple:
return "FORM_TUPLE"
case InstructionIndexList:
return "INDEX_LIST"
case InstructionIndexTuple:
return "INDEX_TUPLE"
} }
return "UNDEFINED" return "UNDEFINED"
} }
@ -251,7 +260,7 @@ type Chunk struct {
Constants []Value Constants []Value
} }
func (c Chunk) String() string { func (c *Chunk) String() string {
b := strings.Builder{} b := strings.Builder{}
b.WriteString("=v= chunk =v=\n") b.WriteString("=v= chunk =v=\n")
@ -324,7 +333,7 @@ func RegisterGOBTypes() {
} }
func (c Chunk) Serialize() []byte { func (c *Chunk) Serialize() []byte {
b := bytes.Buffer{} b := bytes.Buffer{}
e := gob.NewEncoder(&b) e := gob.NewEncoder(&b)
@ -622,8 +631,8 @@ var DefaultGlobals = map[string]Value{
nil, nil,
true, true,
}, },
"type": &BuiltinFunctionValue{ "typeof": &BuiltinFunctionValue{
Name: "type", Name: "typeof",
Signature: &FunctionSignature{ Signature: &FunctionSignature{
In: []TypeSignature{&AnySignature{}}, In: []TypeSignature{&AnySignature{}},
Out: &StringSignature{}, Out: &StringSignature{},
@ -979,9 +988,6 @@ func (vm *VM) Next() bool {
items, items,
}) })
case InstructionNewList:
vm.Stack.Push(&ListValue{[]Value{}})
case InstructionAppend: case InstructionAppend:
value := vm.Stack.Pop() value := vm.Stack.Pop()
list := vm.Stack.Pop().(*ListValue) list := vm.Stack.Pop().(*ListValue)
@ -1018,7 +1024,7 @@ func (vm *VM) Next() bool {
v := vm.Stack.Pop() v := vm.Stack.Pop()
vm.Stack.Push(&StringValue{v.String()}) vm.Stack.Push(&StringValue{v.String()})
case InstructionStringConcatenation: case InstructionConcatStrings:
r := vm.Stack.Pop().(*StringValue).Text r := vm.Stack.Pop().(*StringValue).Text
l := vm.Stack.Pop().(*StringValue).Text l := vm.Stack.Pop().(*StringValue).Text
@ -1051,6 +1057,30 @@ func (vm *VM) Next() bool {
vm.Stack.Push(member) vm.Stack.Push(member)
case InstructionIndexList:
i := vm.Stack.Pop().(*IntegerValue)
l := vm.Stack.Pop().(*ListValue)
n := int(i.Number.Int64())
if 0 < n || n >= 10 {
vm.error(fmt.Sprintf("index %d out of bounds", n))
}
vm.Stack.Push(l.Items[n].Clone())
case InstructionIndexTuple:
i := vm.Stack.Pop().(*IntegerValue)
l := vm.Stack.Pop().(*TupleValue)
n := int(i.Number.Int64())
if 0 < n || n >= len(l.Items) {
vm.error(fmt.Sprintf("index %d out of bounds", n))
}
vm.Stack.Push(l.Items[n].Clone())
case InstructionBreakpoint: case InstructionBreakpoint:
/* /*
// I'm keeping this // I'm keeping this

View file

@ -1,7 +1,7 @@
assertEq((1, 2), (1, 2)) assertEq((1, 2), (1, 2))
assertEq(type((1,)), type((1,))) assertEq(typeof((1,)), typeof((1,)))
assertEq((1,), (1,)) assertEq((1,), (1,))
fn neighbours(n: int) -> (int, int) { fn neighbours(n: int) -> (int, int) {

View file

@ -1,8 +1,15 @@
assertEq(type(1), "int") assertEq(typeof(1), "int")
assertEq(type("Hello"), "string") assertEq(typeof("Hello"), "string")
assertEq(type(true), "boolean") assertEq(typeof(true), "boolean")
# lists # lists
assertEq(type(["Hello", "world"]), "list[string]") assertEq(typeof(["Hello", "world"]), "[string]")
assertEq(type([0, 1]), "list[int]") assertEq(typeof([0, 1]), "[int]")
assertEq(typeof((0, 1)), "(int, int)")
type Vec2 = (int, int)
fn add(a: Vec2, b: Vec2) -> Vec2 {
(a[0] + b[0], a[1] + b[1])
}