Basic type system working
This commit is contained in:
parent
3f260e7ffd
commit
84cc845748
19 changed files with 900 additions and 131 deletions
|
|
@ -22,13 +22,22 @@ func GetAllTestCases() map[string]AllTestCase {
|
|||
},
|
||||
},
|
||||
"func": {
|
||||
"func sum(a, b) {\n\treturn a + b\n}\nsum(1, 2)",
|
||||
"func sum(a: number, b: number) {\n\treturn a + b\n}\nsum(1, 2)",
|
||||
[]Value{
|
||||
&VariableValue{
|
||||
"sum",
|
||||
&FunctionValue{
|
||||
Name: "sum",
|
||||
Params: []string{"a", "b"},
|
||||
Params: []FunctionParameter{
|
||||
{
|
||||
"a",
|
||||
&NumberSignature{},
|
||||
},
|
||||
{
|
||||
"b",
|
||||
&NumberSignature{},
|
||||
},
|
||||
},
|
||||
Chunk: &Chunk{
|
||||
Bytecode: []Bytecode{
|
||||
InstructionDescend,
|
||||
|
|
|
|||
171
core/compiler.go
171
core/compiler.go
|
|
@ -1,6 +1,7 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
|
|
@ -21,16 +22,18 @@ type ImportsResolver interface {
|
|||
|
||||
type LocalVariable struct {
|
||||
name string
|
||||
signature TypeSignature
|
||||
scope int
|
||||
}
|
||||
|
||||
func NewCompiler() *Compiler {
|
||||
c := &Compiler{
|
||||
Chunk: NewChunk(make([]Bytecode, 0), make([]Value, 0)),
|
||||
ip: 0,
|
||||
scope: 0,
|
||||
stack: NewStack[LocalVariable](256),
|
||||
imports: make(map[string]Node),
|
||||
NewChunk(make([]Bytecode, 0), make([]Value, 0)),
|
||||
0,
|
||||
0,
|
||||
make(map[string]Node),
|
||||
nil,
|
||||
NewStack[LocalVariable](256),
|
||||
}
|
||||
|
||||
return c
|
||||
|
|
@ -253,8 +256,8 @@ func (c *Compiler) Compile(tree Node) error {
|
|||
// reset instruction pointer (ip)
|
||||
c.ip = 0
|
||||
|
||||
for _, p := range n.params {
|
||||
c.registerVar(p)
|
||||
for _, p := range n.parameters {
|
||||
c.registerVar(p.name, p.signature)
|
||||
}
|
||||
|
||||
err := c.Compile(n.logic)
|
||||
|
|
@ -268,7 +271,7 @@ func (c *Compiler) Compile(tree Node) error {
|
|||
|
||||
mc.Constants[fi] = &FunctionValue{
|
||||
n.name,
|
||||
n.params,
|
||||
n.parameters,
|
||||
c.Chunk,
|
||||
nil,
|
||||
}
|
||||
|
|
@ -365,6 +368,149 @@ func (c *Compiler) compileBinary(binary *BinaryNode) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
||||
switch tree.Type() {
|
||||
case StringNodeType:
|
||||
return &StringSignature{}, nil
|
||||
case NumberNodeType:
|
||||
return &NumberSignature{}, nil
|
||||
case ReferenceNodeType:
|
||||
return c.getVarSignature(tree.(*ReferenceNode).name)
|
||||
case BooleanNodeType:
|
||||
return &BooleanSignature{}, nil
|
||||
case NilNodeType:
|
||||
return &NilSignature{}, nil
|
||||
case ListNodeType:
|
||||
return &ListSignature{}, nil
|
||||
case BinaryNodeType:
|
||||
n := tree.(*BinaryNode)
|
||||
l, err := c.deduceSignature(n.Left)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, err := c.deduceSignature(n.Right)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if l != r {
|
||||
return nil, errors.New(fmt.Sprintf("cannot perform binary %s on different types: %s and %s", n.BinaryOperation, l, r))
|
||||
}
|
||||
|
||||
switch n.BinaryOperation {
|
||||
case BinarySubtraction, BinaryMultiplication, BinaryDivision:
|
||||
if l.Type() != TypeNumber {
|
||||
return nil, errors.New(fmt.Sprintf("cannot perform binary %s non-number type %s", n.BinaryOperation, l))
|
||||
}
|
||||
|
||||
return &NumberSignature{}, nil
|
||||
case BinaryAddition:
|
||||
switch l.Type() {
|
||||
case TypeString:
|
||||
return &StringSignature{}, nil
|
||||
case TypeNumber:
|
||||
return &NumberSignature{}, nil
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintf("cannot perform binary addition on type %s", l))
|
||||
}
|
||||
case BinaryAnd, BinaryOr:
|
||||
if l.Type() != TypeBoolean {
|
||||
return nil, errors.New(fmt.Sprintf("cannot perform binary %s on type %s", l, n.BinaryOperation))
|
||||
}
|
||||
|
||||
return &BooleanSignature{}, nil
|
||||
case BinaryEquality, BinaryInequality:
|
||||
return &BooleanSignature{}, nil
|
||||
case BinaryLess, BinaryGreater, BinaryLessEqual, BinaryGreaterEqual:
|
||||
if l.Type() != TypeNumber {
|
||||
return nil, errors.New(fmt.Sprintf("cannot perform number comparison (%s) on type %s", l, n.BinaryOperation))
|
||||
}
|
||||
|
||||
return &BooleanSignature{}, nil
|
||||
}
|
||||
case AccessNodeType:
|
||||
n := tree.(*AccessNode)
|
||||
sig, err := c.deduceSignature(n.source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch sig.Type() {
|
||||
case TypeString, TypeList:
|
||||
case TypeObject:
|
||||
return sig.(*ObjectSignature).members[n.property], nil
|
||||
|
||||
case TypeNumber, TypeBoolean, TypeNil, TypeFunction:
|
||||
default:
|
||||
panic(fmt.Sprintf("cannot access property from value of type %s", sig))
|
||||
}
|
||||
|
||||
case CallNodeType:
|
||||
n := tree.(*CallNode)
|
||||
sig, err := c.deduceSignature(n.source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if sig.Type() != TypeFunction {
|
||||
return nil, errors.New(fmt.Sprintf("cannot call value of type %s", sig.Type()))
|
||||
}
|
||||
|
||||
f := sig.(*FunctionSignature)
|
||||
|
||||
if len(n.args) != len(f.in) {
|
||||
return nil, errors.New(fmt.Sprintf("bad argument count (expected %v, got %v)", len(f.in), len(n.args)))
|
||||
}
|
||||
|
||||
// type check arguments
|
||||
for i, arg := range n.args {
|
||||
sig, err := c.deduceSignature(arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !sig.Matches(f.in[i]) {
|
||||
}
|
||||
}
|
||||
|
||||
return f.out, nil
|
||||
|
||||
case FunctionNodeType:
|
||||
n := tree.(*FunctionNode)
|
||||
|
||||
sigs := make([]TypeSignature, len(n.parameters))
|
||||
|
||||
for i, p := range n.parameters {
|
||||
sigs[i] = p.signature
|
||||
}
|
||||
|
||||
return &FunctionSignature{
|
||||
sigs,
|
||||
n.yield,
|
||||
}, nil
|
||||
|
||||
default:
|
||||
panic("unhandled default case")
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("impossible to deduce signature of %s", tree.Type()))
|
||||
}
|
||||
|
||||
func (c *Compiler) getVarSignature(name string) (TypeSignature, error) {
|
||||
if c.isGlobal(name) {
|
||||
return SignatureOf(DefaultGlobals[name]), nil
|
||||
}
|
||||
|
||||
for i := c.stack.Current - 1; i >= 0; i-- {
|
||||
v := c.stack.items[i]
|
||||
if v.name == name {
|
||||
return v.signature, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New(fmt.Sprintf("variable %s not defined", name))
|
||||
}
|
||||
|
||||
func (c *Compiler) getVar(name string) {
|
||||
if c.isGlobal(name) {
|
||||
c.add(InstructionGetGlobal)
|
||||
|
|
@ -387,7 +533,11 @@ func (c *Compiler) setVar(name string, value Node, declare bool) error {
|
|||
|
||||
if declare {
|
||||
c.add(InstructionDeclareLocal)
|
||||
c.registerVar(name)
|
||||
t, err := c.deduceSignature(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.registerVar(name, t)
|
||||
} else {
|
||||
c.add(InstructionSetLocal)
|
||||
}
|
||||
|
|
@ -400,9 +550,10 @@ func (c *Compiler) setVar(name string, value Node, declare bool) error {
|
|||
}
|
||||
|
||||
// keep track that a variable is declared but doesn't necessarily have a deducible type
|
||||
func (c *Compiler) registerVar(name string) {
|
||||
func (c *Compiler) registerVar(name string, t TypeSignature) {
|
||||
c.stack.Push(LocalVariable{
|
||||
name,
|
||||
t,
|
||||
int(c.scope),
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -231,7 +231,17 @@ func GetCompileTestData() map[string]CompileTestData {
|
|||
"sum",
|
||||
&FunctionNode{
|
||||
"sum",
|
||||
[]string{"a", "b"},
|
||||
[]FunctionParameter{
|
||||
{
|
||||
"a",
|
||||
&NumberSignature{},
|
||||
},
|
||||
{
|
||||
"b",
|
||||
&NumberSignature{},
|
||||
},
|
||||
},
|
||||
&NumberSignature{},
|
||||
&BlockNode{
|
||||
[]Node{
|
||||
&ReturnNode{
|
||||
|
|
@ -254,7 +264,16 @@ func GetCompileTestData() map[string]CompileTestData {
|
|||
|
||||
&FunctionValue{
|
||||
"sum",
|
||||
[]string{"a", "b"},
|
||||
[]FunctionParameter{
|
||||
{
|
||||
"a",
|
||||
&NumberSignature{},
|
||||
},
|
||||
{
|
||||
"b",
|
||||
&NumberSignature{},
|
||||
},
|
||||
},
|
||||
NewChunk(
|
||||
[]Bytecode{
|
||||
InstructionDescend,
|
||||
|
|
@ -281,7 +300,8 @@ func GetCompileTestData() map[string]CompileTestData {
|
|||
"a",
|
||||
&FunctionNode{
|
||||
"a",
|
||||
[]string{},
|
||||
[]FunctionParameter{},
|
||||
&NilSignature{},
|
||||
&BlockNode{
|
||||
[]Node{
|
||||
&AssignNode{
|
||||
|
|
@ -311,7 +331,7 @@ func GetCompileTestData() map[string]CompileTestData {
|
|||
"a",
|
||||
&FunctionValue{
|
||||
"a",
|
||||
[]string{},
|
||||
[]FunctionParameter{},
|
||||
NewChunk(
|
||||
[]Bytecode{
|
||||
InstructionDescend,
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ const (
|
|||
|
||||
TokenComma
|
||||
TokenDot
|
||||
TokenColon
|
||||
|
||||
TokenAssign
|
||||
TokenDeclare
|
||||
|
|
@ -153,6 +154,8 @@ func (t TokenType) String() string {
|
|||
return "close bracket"
|
||||
case TokenImport:
|
||||
return "import"
|
||||
case TokenColon:
|
||||
return "colon"
|
||||
}
|
||||
|
||||
return "UNDEFINED TOKENTYPE STRING CONVERSION"
|
||||
|
|
@ -234,11 +237,11 @@ func (l *Lexer) NextToken() (Token, error) {
|
|||
case '.':
|
||||
return l.makeToken(TokenDot), nil
|
||||
case ':':
|
||||
if !l.accept('=') {
|
||||
return l.makeToken(TokenError), errors.New("malformed token (got ':', expected '=' to follow)")
|
||||
if l.accept('=') {
|
||||
return l.makeToken(TokenDeclare), nil
|
||||
}
|
||||
|
||||
return l.makeToken(TokenDeclare), nil
|
||||
return l.makeToken(TokenColon), nil
|
||||
case '!':
|
||||
if l.accept('=') {
|
||||
return l.makeToken(TokenBangEquals), nil
|
||||
|
|
|
|||
|
|
@ -331,10 +331,16 @@ func (n CallNode) String() string {
|
|||
// FunctionNode definition of function
|
||||
type FunctionNode struct {
|
||||
name string
|
||||
params []string
|
||||
parameters []FunctionParameter
|
||||
yield TypeSignature
|
||||
logic Node
|
||||
}
|
||||
|
||||
type FunctionParameter struct {
|
||||
name string
|
||||
signature TypeSignature
|
||||
}
|
||||
|
||||
func (n FunctionNode) Type() NodeType {
|
||||
return FunctionNodeType
|
||||
}
|
||||
|
|
|
|||
108
core/parser.go
108
core/parser.go
|
|
@ -243,6 +243,11 @@ func (p *Parser) factor() (Node, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
sig, err := p.parseSignature()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, err := p.block(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -251,6 +256,7 @@ func (p *Parser) factor() (Node, error) {
|
|||
return &FunctionNode{
|
||||
"*",
|
||||
params,
|
||||
sig,
|
||||
b,
|
||||
}, nil
|
||||
|
||||
|
|
@ -564,6 +570,8 @@ func (p *Parser) statement() (Node, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
yield, err := p.parseSignature()
|
||||
|
||||
b, err := p.block(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -574,6 +582,7 @@ func (p *Parser) statement() (Node, error) {
|
|||
&FunctionNode{
|
||||
name,
|
||||
params,
|
||||
yield,
|
||||
b,
|
||||
},
|
||||
true,
|
||||
|
|
@ -678,15 +687,27 @@ func (p *Parser) parseArgs() ([]Node, error) {
|
|||
}
|
||||
|
||||
// parseParams parse parameters and parentheses
|
||||
func (p *Parser) parseParams() ([]string, error) {
|
||||
func (p *Parser) parseParams() ([]FunctionParameter, error) {
|
||||
if err := p.expect(TokenOpenParenthesis); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params := make([]string, 0)
|
||||
params := make([]FunctionParameter, 0)
|
||||
|
||||
if p.accept(TokenName) {
|
||||
name := (*p.prev).Lexeme
|
||||
params = append(params, name)
|
||||
if err := p.expect(TokenColon); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t, err := p.parseSignature()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params = append(params, FunctionParameter{
|
||||
name,
|
||||
t,
|
||||
})
|
||||
for !p.accept(TokenCloseParenthesis) {
|
||||
if err := p.expect(TokenComma); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -695,7 +716,19 @@ func (p *Parser) parseParams() ([]string, error) {
|
|||
return nil, err
|
||||
}
|
||||
name = (*p.prev).Lexeme
|
||||
params = append(params, name)
|
||||
if err := p.expect(TokenColon); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t, err := p.parseSignature()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params = append(params, FunctionParameter{
|
||||
name,
|
||||
t,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if err := p.expect(TokenCloseParenthesis); err != nil {
|
||||
|
|
@ -705,3 +738,70 @@ func (p *Parser) parseParams() ([]string, error) {
|
|||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseSignature() (TypeSignature, error) {
|
||||
if p.accept(TokenFunc) {
|
||||
if err := p.expect(TokenCloseParenthesis); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var in []TypeSignature
|
||||
|
||||
for !p.accept(TokenCloseParenthesis) && (len(in) == 0 || p.accept(TokenComma)) {
|
||||
sig, err := p.parseSignature()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
in = append(in, sig)
|
||||
}
|
||||
|
||||
if err := p.expect(TokenCloseParenthesis); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out, err := p.parseSignature()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &FunctionSignature{
|
||||
in,
|
||||
out,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if err := p.expect(TokenName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name := (*p.prev).Lexeme
|
||||
|
||||
switch name {
|
||||
case "string":
|
||||
return &StringSignature{}, nil
|
||||
case "number":
|
||||
return &NumberSignature{}, nil
|
||||
case "boolean":
|
||||
return &BooleanSignature{}, nil
|
||||
case "list":
|
||||
if err := p.expect(TokenOpenBracket); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contents, err := p.parseSignature()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := p.expect(TokenCloseBracket); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ListSignature{
|
||||
contents,
|
||||
}, nil
|
||||
}
|
||||
|
||||
panic("unsupported type: " + name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
|
@ -338,9 +340,14 @@ func GetTokenTestData() map[string]TokenTestData {
|
|||
NewToken(TokenFunc, 3, 4, 0, "func"),
|
||||
NewToken(TokenOpenParenthesis, 7, 1, 0, "("),
|
||||
NewToken(TokenName, 8, 1, 0, "a"),
|
||||
NewToken(TokenColon, 9, 1, 0, ":"),
|
||||
NewToken(TokenName, 10, 5, 0, "number"),
|
||||
NewToken(TokenComma, 9, 1, 0, ","),
|
||||
NewToken(TokenName, 10, 1, 0, "b"),
|
||||
NewToken(TokenColon, 9, 1, 0, ":"),
|
||||
NewToken(TokenName, 10, 5, 0, "number"),
|
||||
NewToken(TokenCloseParenthesis, 11, 1, 0, ")"),
|
||||
NewToken(TokenName, 10, 5, 0, "number"),
|
||||
|
||||
NewToken(TokenOpenBrace, 12, 1, 1, "{"),
|
||||
NewToken(TokenReturn, 13, 6, 1, "return"),
|
||||
|
|
@ -357,7 +364,17 @@ func GetTokenTestData() map[string]TokenTestData {
|
|||
"a",
|
||||
&FunctionNode{
|
||||
"*",
|
||||
[]string{"a", "b"},
|
||||
[]FunctionParameter{
|
||||
{
|
||||
"a",
|
||||
&NumberSignature{},
|
||||
},
|
||||
{
|
||||
"b",
|
||||
&NumberSignature{},
|
||||
},
|
||||
},
|
||||
&NumberSignature{},
|
||||
&BlockNode{
|
||||
[]Node{
|
||||
&ReturnNode{
|
||||
|
|
@ -404,7 +421,17 @@ func GetTokenTestData() map[string]TokenTestData {
|
|||
"a",
|
||||
&FunctionNode{
|
||||
"a",
|
||||
[]string{"a", "b"},
|
||||
[]FunctionParameter{
|
||||
{
|
||||
"a",
|
||||
&NumberSignature{},
|
||||
},
|
||||
{
|
||||
"b",
|
||||
&NumberSignature{},
|
||||
},
|
||||
},
|
||||
&NumberSignature{},
|
||||
&BlockNode{
|
||||
[]Node{
|
||||
&ReturnNode{
|
||||
|
|
@ -642,15 +669,17 @@ func NodeEquality(t *testing.T, n1 Node, n2 Node) {
|
|||
t.Logf("Function node names match (%s)", n.name)
|
||||
}
|
||||
|
||||
if len(n.params) != len(m.params) {
|
||||
t.Fatalf("Function node parameters count does not match (%d and %d)", len(n.params), len(m.params))
|
||||
if len(n.parameters) != len(m.parameters) {
|
||||
t.Fatalf("Function node parameters count does not match (%d and %d)", len(n.parameters), len(m.parameters))
|
||||
} else {
|
||||
t.Logf("Function node parameters count is equal (%d) ", len(n.params))
|
||||
t.Logf("Function node parameters count is equal (%d) ", len(n.parameters))
|
||||
}
|
||||
|
||||
for i, p := range m.params {
|
||||
if n.params[i] != p {
|
||||
t.Errorf("Function node parameter %d does not match: %s and %s", i, p, m.params)
|
||||
for i, p := range m.parameters {
|
||||
if !n.parameters[i].signature.Matches(p.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 {
|
||||
t.Errorf("Function node parameter name %d does not match: %s and %s", i, p.name, n.parameters[i].name)
|
||||
} else {
|
||||
t.Logf("Function node parameter %d matches (%s)", i, p)
|
||||
}
|
||||
|
|
@ -665,6 +694,101 @@ func NodeEquality(t *testing.T, n1 Node, n2 Node) {
|
|||
}
|
||||
}
|
||||
|
||||
func SerializeTokens(tokens []Token) string {
|
||||
out := strings.Builder{}
|
||||
level := 0
|
||||
|
||||
for _, token := range tokens {
|
||||
switch token.Type {
|
||||
case TokenPlus:
|
||||
out.WriteString(" + ")
|
||||
case TokenMinus:
|
||||
out.WriteString(" - ")
|
||||
case TokenStar:
|
||||
out.WriteString("*")
|
||||
case TokenSlash:
|
||||
out.WriteString("/")
|
||||
case TokenBang:
|
||||
out.WriteString("!")
|
||||
case TokenSemicolon:
|
||||
out.WriteString(";")
|
||||
case TokenNumber:
|
||||
out.WriteString(token.Lexeme)
|
||||
case TokenString:
|
||||
out.WriteString(fmt.Sprintf("\"%s\"", token.Lexeme))
|
||||
case TokenName:
|
||||
out.WriteString(token.Lexeme)
|
||||
case TokenOpenParenthesis:
|
||||
out.WriteString("(")
|
||||
case TokenCloseParenthesis:
|
||||
out.WriteString(")")
|
||||
case TokenOpenBracket:
|
||||
out.WriteString("[")
|
||||
case TokenCloseBracket:
|
||||
out.WriteString("]")
|
||||
case TokenOpenBrace:
|
||||
out.WriteString("{")
|
||||
level = level + 1
|
||||
case TokenCloseBrace:
|
||||
out.WriteString("}")
|
||||
level = level - 1
|
||||
case TokenTrue:
|
||||
out.WriteString("true")
|
||||
case TokenFalse:
|
||||
out.WriteString("false")
|
||||
case TokenNil:
|
||||
out.WriteString("nil")
|
||||
case TokenFunc:
|
||||
out.WriteString("func")
|
||||
case TokenReturn:
|
||||
out.WriteString("return ")
|
||||
case TokenWhile:
|
||||
out.WriteString("while ")
|
||||
case TokenVar:
|
||||
out.WriteString("var ")
|
||||
case TokenIf:
|
||||
out.WriteString("if ")
|
||||
case TokenElse:
|
||||
out.WriteString(" else ")
|
||||
case TokenImport:
|
||||
out.WriteString("import ")
|
||||
case TokenComma:
|
||||
out.WriteString(", ")
|
||||
case TokenDot:
|
||||
out.WriteString(".")
|
||||
case TokenColon:
|
||||
out.WriteString(": ")
|
||||
case TokenAssign:
|
||||
out.WriteString(" = ")
|
||||
case TokenDeclare:
|
||||
out.WriteString(" := ")
|
||||
case TokenBangEquals:
|
||||
out.WriteString(" != ")
|
||||
case TokenEquals:
|
||||
out.WriteString(" == ")
|
||||
case TokenGreaterThan:
|
||||
out.WriteString(" > ")
|
||||
case TokenLessThan:
|
||||
out.WriteString(" < ")
|
||||
case TokenGreaterThanOrEqual:
|
||||
out.WriteString(" >= ")
|
||||
case TokenLessThanOrEqual:
|
||||
out.WriteString(" <= ")
|
||||
case TokenDoubleAmpersand:
|
||||
out.WriteString(" && ")
|
||||
case TokenDoublePipe:
|
||||
out.WriteString(" || ")
|
||||
case TokenBreakpoint:
|
||||
out.WriteString("breakpoint")
|
||||
case TokenEOF:
|
||||
out.WriteString(fmt.Sprintf("<error: \"%s\">", token.Lexeme))
|
||||
case TokenError:
|
||||
}
|
||||
}
|
||||
|
||||
return out.String()
|
||||
}
|
||||
|
||||
func TestParser_Parse(t *testing.T) {
|
||||
t.Logf("Getting test data")
|
||||
tokenData := GetTokenTestData()
|
||||
|
|
@ -682,7 +806,7 @@ func TestParser_Parse(t *testing.T) {
|
|||
tree, err := p.Parse()
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error(s): %s", err.(*ParsingError).Format([]rune{}))
|
||||
t.Fatalf("Unexpected error(s): %s", err.(*ParsingError).Format([]rune(SerializeTokens(data.tokens))))
|
||||
}
|
||||
|
||||
t.Logf("Checking parsed tree")
|
||||
|
|
|
|||
224
core/types.go
Normal file
224
core/types.go
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
package core
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Type int
|
||||
|
||||
const (
|
||||
TypeString Type = iota
|
||||
TypeNumber
|
||||
TypeBoolean
|
||||
TypeNil
|
||||
TypeList
|
||||
TypeObject
|
||||
TypeFunction
|
||||
TypeAny
|
||||
)
|
||||
|
||||
func (t Type) String() string {
|
||||
switch t {
|
||||
case TypeString:
|
||||
return "String"
|
||||
case TypeNumber:
|
||||
return "Number"
|
||||
case TypeBoolean:
|
||||
return "Boolean"
|
||||
case TypeNil:
|
||||
return "Nil"
|
||||
case TypeList:
|
||||
return "List"
|
||||
case TypeObject:
|
||||
return "Object"
|
||||
case TypeFunction:
|
||||
return "Function"
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("unsupported string conversion for type %v", int(t)))
|
||||
}
|
||||
|
||||
func TypeOf(v Value) Type {
|
||||
switch v.(type) {
|
||||
case *StringValue:
|
||||
return TypeString
|
||||
case *NumberValue:
|
||||
return TypeNumber
|
||||
case *BoolValue:
|
||||
return TypeBoolean
|
||||
case *ListValue:
|
||||
return TypeList
|
||||
case *ObjectValue:
|
||||
return TypeObject
|
||||
case *FunctionValue:
|
||||
return TypeFunction
|
||||
case *BuiltinFunctionValue:
|
||||
return TypeFunction
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("unsupported value (of type %T)", v))
|
||||
}
|
||||
|
||||
func SignatureOf(v Value) TypeSignature {
|
||||
switch t := v.(type) {
|
||||
case *StringValue:
|
||||
return &StringSignature{}
|
||||
case *NumberValue:
|
||||
return &NumberSignature{}
|
||||
case *BoolValue:
|
||||
return &BooleanSignature{}
|
||||
case *ListValue:
|
||||
return &ListSignature{}
|
||||
case *ObjectValue:
|
||||
return &ObjectSignature{}
|
||||
case *FunctionValue:
|
||||
return &FunctionSignature{}
|
||||
case *BuiltinFunctionValue:
|
||||
return t.Signature
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("unknown value; cannot get signature of %s", v))
|
||||
}
|
||||
|
||||
type TypeSignature interface {
|
||||
Type() Type
|
||||
|
||||
// Matches check if this type signature matches another.
|
||||
Matches(TypeSignature) bool
|
||||
}
|
||||
|
||||
type NilSignature struct{}
|
||||
|
||||
func (*NilSignature) Type() Type {
|
||||
return TypeNil
|
||||
}
|
||||
|
||||
func (*NilSignature) Matches(other TypeSignature) bool {
|
||||
return other.Type() == TypeAny || other.Type() == TypeNil
|
||||
}
|
||||
|
||||
type StringSignature struct{}
|
||||
|
||||
func (*StringSignature) Type() Type {
|
||||
return TypeString
|
||||
}
|
||||
|
||||
func (*StringSignature) Matches(other TypeSignature) bool {
|
||||
return other.Type() == TypeAny || other.Type() == TypeString
|
||||
}
|
||||
|
||||
type NumberSignature struct{}
|
||||
|
||||
func (*NumberSignature) Type() Type {
|
||||
return TypeNumber
|
||||
}
|
||||
|
||||
func (*NumberSignature) Matches(other TypeSignature) bool {
|
||||
return other.Type() == TypeAny || other.Type() == TypeNumber
|
||||
}
|
||||
|
||||
type BooleanSignature struct{}
|
||||
|
||||
func (*BooleanSignature) Type() Type {
|
||||
return TypeBoolean
|
||||
}
|
||||
|
||||
func (*BooleanSignature) Matches(other TypeSignature) bool {
|
||||
return other.Type() == TypeAny || other.Type() == TypeBoolean
|
||||
}
|
||||
|
||||
type ListSignature struct {
|
||||
contents TypeSignature
|
||||
}
|
||||
|
||||
func (*ListSignature) Type() Type {
|
||||
return TypeList
|
||||
}
|
||||
|
||||
func (s *ListSignature) Matches(other TypeSignature) bool {
|
||||
return other.Type() == TypeAny || other.Type() == TypeList && other.(*ListSignature).contents.Matches(s.contents)
|
||||
}
|
||||
|
||||
type ObjectSignature struct {
|
||||
members map[string]TypeSignature
|
||||
}
|
||||
|
||||
func (*ObjectSignature) Type() Type {
|
||||
return TypeObject
|
||||
}
|
||||
|
||||
func (s *ObjectSignature) Matches(other TypeSignature) bool {
|
||||
if other.Type() == TypeAny {
|
||||
return true
|
||||
}
|
||||
|
||||
if other.Type() != TypeObject {
|
||||
return false
|
||||
}
|
||||
|
||||
o := other.(*ObjectSignature)
|
||||
|
||||
if len(o.members) != len(s.members) {
|
||||
return false
|
||||
}
|
||||
|
||||
for name, member := range s.members {
|
||||
v, ok := o.members[name]
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.Matches(member) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type FunctionSignature struct {
|
||||
in []TypeSignature
|
||||
out TypeSignature
|
||||
}
|
||||
|
||||
func (*FunctionSignature) Type() Type {
|
||||
return TypeFunction
|
||||
}
|
||||
|
||||
func (s *FunctionSignature) Matches(other TypeSignature) bool {
|
||||
if other.Type() == TypeAny {
|
||||
return true
|
||||
}
|
||||
|
||||
if other.Type() != TypeFunction {
|
||||
return false
|
||||
}
|
||||
|
||||
f := other.(*FunctionSignature)
|
||||
|
||||
if !s.out.Matches(f.out) {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(f.in) != len(s.in) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, p := range s.in {
|
||||
v := f.in[i]
|
||||
if !p.Matches(v) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type AnySignature struct{}
|
||||
|
||||
func (AnySignature) Type() Type {
|
||||
return TypeAny
|
||||
}
|
||||
|
||||
func (AnySignature) Matches(_ TypeSignature) bool {
|
||||
return true
|
||||
}
|
||||
|
|
@ -203,12 +203,15 @@ func (v *ObjectValue) Equals(other Value) bool {
|
|||
var ObjectPrototype = map[string]Value{
|
||||
"set": &BuiltinFunctionValue{
|
||||
"set",
|
||||
[]string{"property", "value"},
|
||||
func(vm *VM, _this Value, params map[string]Value) (Value, error) {
|
||||
&FunctionSignature{
|
||||
[]TypeSignature{&StringSignature{}, &ListSignature{}},
|
||||
&NilSignature{},
|
||||
},
|
||||
func(vm *VM, _this Value, params []Value) (Value, error) {
|
||||
this := _this.(*ObjectValue)
|
||||
|
||||
p := params["property"]
|
||||
v, ok := params["value"].(*StringValue)
|
||||
p := params[1]
|
||||
v, ok := params[0].(*StringValue)
|
||||
if !ok {
|
||||
return nil, errors.New("property is not a string")
|
||||
}
|
||||
|
|
@ -282,10 +285,13 @@ func (v *StringValue) Equals(other Value) bool {
|
|||
var StringPrototype = map[string]*BuiltinFunctionValue{
|
||||
"split": {
|
||||
"split",
|
||||
[]string{"seperator"},
|
||||
func(vm *VM, this Value, m map[string]Value) (Value, error) {
|
||||
&FunctionSignature{
|
||||
[]TypeSignature{&StringSignature{}},
|
||||
&NilSignature{},
|
||||
},
|
||||
func(vm *VM, this Value, v []Value) (Value, error) {
|
||||
str := this.(*StringValue).String()
|
||||
sep := m["seperator"].(*StringValue).String()
|
||||
sep := v[0].(*StringValue).String()
|
||||
|
||||
var out []string
|
||||
tmp := strings.Builder{}
|
||||
|
|
@ -361,19 +367,27 @@ func (v *ListValue) Equals(other Value) bool {
|
|||
var ListPrototype = map[string]*BuiltinFunctionValue{
|
||||
"append": {
|
||||
"append",
|
||||
[]string{"item"},
|
||||
func(_ *VM, this Value, p map[string]Value) (Value, error) {
|
||||
this.(*ListValue).items = append(this.(*ListValue).items, p["item"])
|
||||
&FunctionSignature{
|
||||
[]TypeSignature{&AnySignature{}},
|
||||
&NilSignature{},
|
||||
},
|
||||
func(_ *VM, this Value, v []Value) (Value, error) {
|
||||
this.(*ListValue).items = append(this.(*ListValue).items, v[0])
|
||||
return &NilValue{}, nil
|
||||
},
|
||||
nil,
|
||||
},
|
||||
"at": {
|
||||
"at",
|
||||
[]string{"index"},
|
||||
func(_ *VM, this Value, p map[string]Value) (Value, error) {
|
||||
&FunctionSignature{
|
||||
[]TypeSignature{
|
||||
&NumberSignature{},
|
||||
},
|
||||
&AnySignature{},
|
||||
},
|
||||
func(_ *VM, this Value, p []Value) (Value, error) {
|
||||
items := this.(*ListValue).items
|
||||
index := int(p["index"].(*NumberValue).float64)
|
||||
index := int(p[0].(*NumberValue).float64)
|
||||
|
||||
if index >= len(items) {
|
||||
return nil, errors.New(fmt.Sprintf("list index %x out of range", index))
|
||||
|
|
@ -385,19 +399,32 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
|
|||
},
|
||||
"length": {
|
||||
"length",
|
||||
[]string{},
|
||||
func(_ *VM, this Value, p map[string]Value) (Value, error) {
|
||||
&FunctionSignature{
|
||||
[]TypeSignature{},
|
||||
&NumberSignature{},
|
||||
},
|
||||
func(_ *VM, this Value, _ []Value) (Value, error) {
|
||||
return GoToValue(len(this.(*ListValue).items)), nil
|
||||
},
|
||||
nil,
|
||||
},
|
||||
"map": {
|
||||
"map",
|
||||
[]string{"f"},
|
||||
func(vm *VM, value Value, m map[string]Value) (Value, error) {
|
||||
&FunctionSignature{
|
||||
[]TypeSignature{
|
||||
&FunctionSignature{
|
||||
[]TypeSignature{
|
||||
&AnySignature{},
|
||||
},
|
||||
&AnySignature{},
|
||||
},
|
||||
},
|
||||
&ListSignature{},
|
||||
},
|
||||
func(vm *VM, value Value, m []Value) (Value, error) {
|
||||
list := value.(*ListValue)
|
||||
|
||||
v := m["f"]
|
||||
v := m[0]
|
||||
var f Value
|
||||
f, ok := v.(*FunctionValue)
|
||||
if !ok {
|
||||
|
|
@ -425,11 +452,23 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
|
|||
},
|
||||
"reduce": {
|
||||
"reduce",
|
||||
[]string{"f", "start"},
|
||||
func(vm *VM, value Value, m map[string]Value) (Value, error) {
|
||||
&FunctionSignature{
|
||||
[]TypeSignature{
|
||||
&FunctionSignature{
|
||||
[]TypeSignature{
|
||||
&AnySignature{},
|
||||
&AnySignature{},
|
||||
},
|
||||
&AnySignature{},
|
||||
},
|
||||
&AnySignature{},
|
||||
},
|
||||
&AnySignature{},
|
||||
},
|
||||
func(vm *VM, value Value, m []Value) (Value, error) {
|
||||
list := value.(*ListValue)
|
||||
f := m["f"]
|
||||
sum := m["start"]
|
||||
f := m[0]
|
||||
sum := m[1]
|
||||
|
||||
for _, v := range list.items {
|
||||
result, err := vm.Call(f, []Value{sum, v})
|
||||
|
|
@ -455,7 +494,7 @@ func (v *ListValue) Get(key string) (Value, error) {
|
|||
|
||||
type FunctionValue struct {
|
||||
Name string
|
||||
Params []string
|
||||
Params []FunctionParameter
|
||||
Chunk *Chunk
|
||||
Parent Value
|
||||
}
|
||||
|
|
@ -484,8 +523,8 @@ func (v *FunctionValue) Get(_ string) (Value, error) {
|
|||
|
||||
type BuiltinFunctionValue struct {
|
||||
Name string
|
||||
Parameters []string
|
||||
F func(*VM, Value, map[string]Value) (Value, error)
|
||||
Signature *FunctionSignature
|
||||
F func(*VM, Value, []Value) (Value, error)
|
||||
Parent Value
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -60,19 +60,10 @@ func CompareValues(t *testing.T, got Value, want Value) {
|
|||
t.Errorf("builtin function name mismatch: got %v, want %v", n.Name, m.Name)
|
||||
}
|
||||
|
||||
if len(n.Parameters) != len(m.Parameters) {
|
||||
t.Errorf("builtin function parameter count mismatch: got %v, want %v", n.Parameters, m.Parameters)
|
||||
if !n.Signature.Matches(m.Signature) {
|
||||
t.Errorf("builtin function parameter count mismatch: got %v, want %v", n, m)
|
||||
}
|
||||
|
||||
for i, v := range n.Parameters {
|
||||
if v != m.Parameters[i] {
|
||||
t.Errorf("builtin function parameter %d mismatch: got %v, want %v", i, v, m.Parameters[i])
|
||||
}
|
||||
}
|
||||
|
||||
if &n.F != &m.F {
|
||||
t.Errorf("builtin function f mismatch: got %v, want %v", &n.F, &m.F)
|
||||
}
|
||||
case VariableValueType:
|
||||
n := got.(*VariableValue)
|
||||
m := want.(*VariableValue)
|
||||
|
|
|
|||
78
core/vm.go
78
core/vm.go
|
|
@ -287,38 +287,56 @@ type Call struct {
|
|||
var DefaultGlobals = map[string]Value{
|
||||
"write": &BuiltinFunctionValue{
|
||||
"write", // always remember where you come from...
|
||||
[]string{"value"},
|
||||
func(_ *VM, this Value, v map[string]Value) (Value, error) {
|
||||
println(v["value"].String())
|
||||
&FunctionSignature{
|
||||
[]TypeSignature{&StringSignature{}},
|
||||
&NilSignature{},
|
||||
},
|
||||
func(_ *VM, this Value, v []Value) (Value, error) {
|
||||
println(v[0].String())
|
||||
return nil, nil
|
||||
},
|
||||
nil,
|
||||
},
|
||||
"print": &BuiltinFunctionValue{
|
||||
"print",
|
||||
[]string{"value"},
|
||||
func(_ *VM, this Value, v map[string]Value) (Value, error) {
|
||||
print(v["value"].String())
|
||||
&FunctionSignature{
|
||||
[]TypeSignature{&StringSignature{}},
|
||||
&NilSignature{},
|
||||
},
|
||||
func(_ *VM, this Value, v []Value) (Value, error) {
|
||||
print(v[0].String())
|
||||
return nil, nil
|
||||
},
|
||||
nil,
|
||||
},
|
||||
"format": &BuiltinFunctionValue{
|
||||
"format",
|
||||
[]string{"format_string", "values"},
|
||||
func(vm *VM, value Value, m map[string]Value) (Value, error) {
|
||||
valuies := m["values"].(*ListValue).items
|
||||
&FunctionSignature{
|
||||
[]TypeSignature{
|
||||
&StringSignature{},
|
||||
&StringSignature{},
|
||||
},
|
||||
&StringSignature{},
|
||||
},
|
||||
func(vm *VM, value Value, m []Value) (Value, error) {
|
||||
valuies := m[1].(*ListValue).items
|
||||
|
||||
return GoToValue(fmt.Sprintf(m["format_string"].String(), valuies)), nil
|
||||
return GoToValue(fmt.Sprintf(m[0].String(), valuies)), nil
|
||||
},
|
||||
nil,
|
||||
},
|
||||
"assertEq": &BuiltinFunctionValue{
|
||||
"assertEq",
|
||||
[]string{"a", "b"},
|
||||
func(vm *VM, this Value, params map[string]Value) (Value, error) {
|
||||
a := params["a"]
|
||||
b := params["b"]
|
||||
&FunctionSignature{
|
||||
[]TypeSignature{
|
||||
&AnySignature{},
|
||||
&AnySignature{},
|
||||
},
|
||||
&NilSignature{},
|
||||
},
|
||||
func(vm *VM, this Value, params []Value) (Value, error) {
|
||||
a := params[0]
|
||||
b := params[1]
|
||||
|
||||
if !a.Equals(b) {
|
||||
return nil, errors.New(fmt.Sprintf("assertion failed: %s does not equal %s", a, b))
|
||||
|
|
@ -330,10 +348,16 @@ var DefaultGlobals = map[string]Value{
|
|||
},
|
||||
"assertNotEq": &BuiltinFunctionValue{
|
||||
"assertNotEq",
|
||||
[]string{"a", "b"},
|
||||
func(vm *VM, this Value, params map[string]Value) (Value, error) {
|
||||
a := params["a"]
|
||||
b := params["b"]
|
||||
&FunctionSignature{
|
||||
[]TypeSignature{
|
||||
&AnySignature{},
|
||||
&AnySignature{},
|
||||
},
|
||||
&NilSignature{},
|
||||
},
|
||||
func(vm *VM, this Value, params []Value) (Value, error) {
|
||||
a := params[0]
|
||||
b := params[1]
|
||||
|
||||
if a.Equals(b) {
|
||||
return nil, errors.New(fmt.Sprintf("assertion failed: %s does not equal %s", a, b))
|
||||
|
|
@ -479,7 +503,7 @@ func (vm *VM) Next() bool {
|
|||
for i := len(f.Params) - 1; i >= 0; i-- {
|
||||
p := vm.stack.Current - Pos(len(f.Params)) + Pos(i)
|
||||
vm.stack.items[p] = &VariableValue{
|
||||
f.Params[i],
|
||||
f.Params[i].name,
|
||||
vm.stack.items[p],
|
||||
vm.scope,
|
||||
}
|
||||
|
|
@ -494,10 +518,10 @@ func (vm *VM) Next() bool {
|
|||
vm.chunk = f.Chunk
|
||||
vm.ip = 0
|
||||
case *BuiltinFunctionValue:
|
||||
args := map[string]Value{}
|
||||
args := make([]Value, len(f.Signature.in))
|
||||
|
||||
for i := len(f.Parameters) - 1; i >= 0; i-- {
|
||||
args[f.Parameters[i]] = vm.stack.Pop()
|
||||
for i := len(f.Signature.in) - 1; i >= 0; i-- {
|
||||
args[i] = vm.stack.Pop()
|
||||
}
|
||||
|
||||
v, err := f.F(vm, f.Parent, args)
|
||||
|
|
@ -645,7 +669,7 @@ func (vm *VM) Call(v Value, args []Value) (Value, error) {
|
|||
})
|
||||
|
||||
for i := 0; i < len(f.Params); i++ {
|
||||
vm.addVar(f.Params[i], args[i])
|
||||
vm.addVar(f.Params[i].name, args[i])
|
||||
}
|
||||
|
||||
if f.Parent != nil {
|
||||
|
|
@ -667,13 +691,7 @@ func (vm *VM) Call(v Value, args []Value) (Value, error) {
|
|||
return vm.stack.Pop(), nil
|
||||
|
||||
case *BuiltinFunctionValue:
|
||||
argies := map[string]Value{}
|
||||
|
||||
for i, arg := range args {
|
||||
argies[f.Parameters[i]] = arg
|
||||
}
|
||||
|
||||
return f.F(vm, f.Parent, argies)
|
||||
return f.F(vm, f.Parent, args)
|
||||
}
|
||||
|
||||
return nil, errors.New(fmt.Sprintf("value is not a function (%s)", v.DebugString()))
|
||||
|
|
|
|||
|
|
@ -442,7 +442,16 @@ func GetExecutionTestData() map[string]struct {
|
|||
&NumberValue{2},
|
||||
&FunctionValue{
|
||||
Name: "sum",
|
||||
Params: []string{"a", "b"},
|
||||
Params: []FunctionParameter{
|
||||
{
|
||||
"a",
|
||||
&NumberSignature{},
|
||||
},
|
||||
{
|
||||
"b",
|
||||
&NumberSignature{},
|
||||
},
|
||||
},
|
||||
Chunk: NewChunk(
|
||||
[]Bytecode{
|
||||
InstructionGetLocal, 0,
|
||||
|
|
@ -476,7 +485,16 @@ func GetExecutionTestData() map[string]struct {
|
|||
&NumberValue{2},
|
||||
&FunctionValue{
|
||||
Name: "sum",
|
||||
Params: []string{"a", "b"},
|
||||
Params: []FunctionParameter{
|
||||
{
|
||||
"a",
|
||||
&NumberSignature{},
|
||||
},
|
||||
{
|
||||
"b",
|
||||
&NumberSignature{},
|
||||
},
|
||||
},
|
||||
Chunk: NewChunk(
|
||||
[]Bytecode{
|
||||
InstructionGetLocal, 0,
|
||||
|
|
@ -493,7 +511,12 @@ func GetExecutionTestData() map[string]struct {
|
|||
},
|
||||
&FunctionValue{
|
||||
Name: "square",
|
||||
Params: []string{"n"},
|
||||
Params: []FunctionParameter{
|
||||
{
|
||||
"n",
|
||||
&NumberSignature{},
|
||||
},
|
||||
},
|
||||
Chunk: NewChunk(
|
||||
[]Bytecode{
|
||||
InstructionGetLocal, 0,
|
||||
|
|
@ -514,7 +537,12 @@ func GetExecutionTestData() map[string]struct {
|
|||
"square",
|
||||
&FunctionValue{
|
||||
Name: "square",
|
||||
Params: []string{"n"},
|
||||
Params: []FunctionParameter{
|
||||
{
|
||||
"n",
|
||||
&NumberSignature{},
|
||||
},
|
||||
},
|
||||
Chunk: NewChunk(
|
||||
[]Bytecode{
|
||||
InstructionGetLocal, 0,
|
||||
|
|
|
|||
36
examples/pi-approx.py
Normal file
36
examples/pi-approx.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
terms = 100000
|
||||
|
||||
tot = 0
|
||||
|
||||
n = 1
|
||||
while n <= terms:
|
||||
tot = tot + 1 / (n*n)
|
||||
n = n + 1
|
||||
|
||||
tot = tot * 6
|
||||
|
||||
# get the absolute value of a number
|
||||
def abs(x):
|
||||
if x < 0:
|
||||
return -x
|
||||
return x
|
||||
|
||||
# calculate an approximation of the square root of tot using
|
||||
# newton's method.
|
||||
# see: https://en.wikipedia.org/wiki/Newton's_method
|
||||
# The required accuracy
|
||||
SQRT_ACC = 0.00000001
|
||||
def sqrt(x):
|
||||
pg = 0 # previous guess
|
||||
g = 1 # current guess
|
||||
|
||||
while abs(pg - g) >= SQRT_ACC:
|
||||
pg = g
|
||||
g = (pg + tot/pg)/2
|
||||
|
||||
return g
|
||||
|
||||
tot = sqrt(tot)
|
||||
|
||||
# output the result
|
||||
print(tot)
|
||||
16
examples/pøck.ang
Normal file
16
examples/pøck.ang
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import "math.ang"
|
||||
|
||||
func r_x(t) {
|
||||
return 8*(exp(-t) - t)
|
||||
}
|
||||
|
||||
func r_y(t) {
|
||||
return 5*(exp(-t) - t)
|
||||
}
|
||||
|
||||
func r(t) {
|
||||
return format("(%s, %s)", [r_x(t), r_y(t)])
|
||||
}
|
||||
|
||||
write(r(1))
|
||||
write()
|
||||
4
lib/util.ang
Normal file
4
lib/util.ang
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
func memoize(f) {
|
||||
|
||||
}
|
||||
|
|
@ -3,14 +3,14 @@ list := []
|
|||
x := 1
|
||||
while x <= 1000 {
|
||||
list.append(x)
|
||||
assertEq(list.reduce(func(tot, a){
|
||||
assertEq(list.reduce(func(tot: number, a: number) number {
|
||||
return tot + a
|
||||
}, 0), x*(x + 1)/2)
|
||||
|
||||
x = x + 1
|
||||
}
|
||||
|
||||
func sum(a, b) {
|
||||
func sum(a: number, b: number) number {
|
||||
return a + b
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ E := 2.718281828459045235360287471352
|
|||
# Get the absolute value of a number. If x is negative, the returned
|
||||
# value is positive and equal to `-x`. If x is positive or zero, the
|
||||
# returned value is x.
|
||||
func abs(x) {
|
||||
func abs(x: number) number {
|
||||
# if the number is negative
|
||||
if x < 0 {
|
||||
# negate it so it's positive
|
||||
|
|
@ -23,7 +23,7 @@ MAX_SQRT_DX := 0.0000001
|
|||
# x: number
|
||||
# Calculate the approximate square root using newton's method until
|
||||
# the accuracy has increased by less than the variable `MAX_SQRT_DX`.
|
||||
func sqrt(x) {
|
||||
func sqrt(x: number) number {
|
||||
ng := x
|
||||
g := 1
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ func sqrt(x) {
|
|||
# Return the whole number part of the number. if x is a whole number,
|
||||
# the returned value is x. If x is not a whole number, the closest
|
||||
# whole number which is less than or equal to x is returned.
|
||||
func floor(x) {
|
||||
func floor(x: number) number {
|
||||
# todo
|
||||
}
|
||||
|
||||
|
|
@ -51,14 +51,14 @@ func floor(x) {
|
|||
# Return the whole number part of the number. if x is a whole number,
|
||||
# the returned value is x. If x is not a whole number, the closest
|
||||
# whole number which is greater than or equal to x is returned.
|
||||
func ceil(x) {
|
||||
func ceil(x: number) number {
|
||||
# todo
|
||||
}
|
||||
|
||||
# round(x)
|
||||
# x: number
|
||||
# Return the closest whole number to the value x.
|
||||
func round(x) {
|
||||
func round(x: number) number {
|
||||
f := floor(x)
|
||||
|
||||
if x - f > 0.5 {
|
||||
|
|
@ -72,7 +72,7 @@ func round(x) {
|
|||
# x: number; an angle in radians
|
||||
# Get the sine of an angle (in radians). https://en.wikipedia.org/wiki/Sine_and_cosine
|
||||
# TODO: use hashmap with precomputed values and linear interpolation
|
||||
func sin(x) {
|
||||
func sin(x: number) number {
|
||||
f := 1
|
||||
x = mod(x, 2*PI)
|
||||
if x > PI {
|
||||
|
|
@ -101,14 +101,14 @@ func sin(x) {
|
|||
# cos(x)
|
||||
# x: number; an angle in radians
|
||||
# Get the cosine of an angle (in radians). https://en.wikipedia.org/wiki/Sine_and_cosine
|
||||
func cos(x) {
|
||||
func cos(x: number) number {
|
||||
# todo
|
||||
}
|
||||
|
||||
# tan(x)
|
||||
# x: number; an angle in radians
|
||||
# Get the tangent of an angle. https://en.wikipedia.org/wiki/Tangent
|
||||
func tan(x) {
|
||||
func tan(x: number) number {
|
||||
# todo
|
||||
}
|
||||
|
||||
|
|
@ -116,7 +116,7 @@ func tan(x) {
|
|||
# x: number; any number
|
||||
# n: number; the number to divide by
|
||||
# Return the rest from a division of x by n.
|
||||
func mod(x, n) {
|
||||
func mod(x: number, n: number) {
|
||||
if x == 0 {
|
||||
return 0
|
||||
}
|
||||
|
|
@ -139,7 +139,7 @@ func mod(x, n) {
|
|||
# Get the approximate value of the natural logarithm
|
||||
# This function uses newton's method to approximate.
|
||||
LN_ACC := 0.000000001
|
||||
func ln(x) {
|
||||
func ln(x: number) number {
|
||||
pg := 0
|
||||
g := 1
|
||||
|
||||
|
|
@ -157,7 +157,7 @@ func ln(x) {
|
|||
# This value is only reasonable if 0<x<1.
|
||||
# It is approximated using the taylor series of e**x.
|
||||
SM_EXP_ACC := 0.00000000001
|
||||
func sm_exp(x) {
|
||||
func sm_exp(x: number) number {
|
||||
p_tot := 0
|
||||
tot := 1
|
||||
n := 1
|
||||
|
|
@ -178,7 +178,7 @@ func sm_exp(x) {
|
|||
# exp(x)
|
||||
# x: number; any number
|
||||
# Get an approximate value of e raised to the power of x.
|
||||
func exp(x) {
|
||||
func exp(x: number) number {
|
||||
n := abs(x)
|
||||
tot := 1
|
||||
while n >= 1 {
|
||||
|
|
@ -201,6 +201,6 @@ func exp(x) {
|
|||
# x: number; any number. The base
|
||||
# p: number; the value of the exponent
|
||||
# Raise any number to any power (x^p)
|
||||
func pow(x, p) {
|
||||
func pow(x: number, p: number) number {
|
||||
return exp(p*ln(x))
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ fibonacci_numbers := [
|
|||
1346269, 2178309, 3524578, 5702887, 9227465, 14930352
|
||||
]
|
||||
|
||||
func fib(n) {
|
||||
func fib(n: number) number {
|
||||
if n < 2 {
|
||||
return n
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
func sum(a, b) {
|
||||
func sum(a: number, b: number) number {
|
||||
return a + b
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue