add type aliases, rework compiler, remove optimization
This commit is contained in:
parent
94b12f28ab
commit
d54249cffe
12 changed files with 869 additions and 1597 deletions
1213
core/compiler.go
1213
core/compiler.go
File diff suppressed because it is too large
Load diff
|
|
@ -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]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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,6 +343,7 @@ type BinaryNode struct {
|
||||||
Left Node
|
Left Node
|
||||||
Right Node
|
Right Node
|
||||||
|
|
||||||
|
operator *Token
|
||||||
start Pos
|
start Pos
|
||||||
end Pos
|
end Pos
|
||||||
}
|
}
|
||||||
|
|
@ -386,6 +393,7 @@ type UnaryNode struct {
|
||||||
UnaryOperation
|
UnaryOperation
|
||||||
value Node
|
value Node
|
||||||
|
|
||||||
|
operator *Token
|
||||||
start Pos
|
start Pos
|
||||||
end Pos
|
end Pos
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
629
core/parser.go
629
core/parser.go
|
|
@ -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,11 +483,11 @@ func (p *Parser) binary() (Node, error) {
|
||||||
values := NewStack[Node](256)
|
values := NewStack[Node](256)
|
||||||
values.pushItem(t)
|
values.pushItem(t)
|
||||||
|
|
||||||
for isBinaryOperator(p.curr.Type) {
|
reduce := func() {
|
||||||
for ops.Current > 0 && binaryPrecedence(p.curr.Type) <= binaryPrecedence(ops.Peek().Type) {
|
|
||||||
r := values.Pop()
|
r := values.Pop()
|
||||||
l := values.Pop()
|
l := values.Pop()
|
||||||
op := tokenToBinaryOperation(ops.Pop().Type)
|
opToken := ops.Pop()
|
||||||
|
op := tokenToBinaryOperation(opToken.Type)
|
||||||
|
|
||||||
start, _ := l.Bounds()
|
start, _ := l.Bounds()
|
||||||
_, end := r.Bounds()
|
_, end := r.Bounds()
|
||||||
|
|
@ -522,11 +496,17 @@ func (p *Parser) binary() (Node, error) {
|
||||||
op,
|
op,
|
||||||
l,
|
l,
|
||||||
r,
|
r,
|
||||||
|
opToken,
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for isBinaryOperator(p.curr.Type) {
|
||||||
|
for ops.Current > 0 && binaryPrecedence(p.curr.Type) <= binaryPrecedence(ops.Peek().Type) {
|
||||||
|
reduce()
|
||||||
|
}
|
||||||
|
|
||||||
ops.Push(p.curr)
|
ops.Push(p.curr)
|
||||||
p.advance()
|
p.advance()
|
||||||
|
|
||||||
|
|
@ -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,
|
||||||
|
p.prev.End,
|
||||||
|
}
|
||||||
|
|
||||||
|
if name != nil {
|
||||||
|
return &AssignNode{
|
||||||
|
&ReferenceNode{name.Lexeme, name.Start, name.End},
|
||||||
|
fn,
|
||||||
|
true,
|
||||||
start,
|
start,
|
||||||
p.prev.End,
|
p.prev.End,
|
||||||
}, nil
|
}, 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,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
200
core/types.go
200
core/types.go
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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{}},
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
64
core/vm.go
64
core/vm.go
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue