static type analysis, formatted compiler errors, hex support, composite types, updated cli, more global builtins, fix list form, fix value refs
This commit is contained in:
parent
84cc845748
commit
954308b29f
18 changed files with 1352 additions and 285 deletions
315
core/compiler.go
315
core/compiler.go
|
|
@ -1,8 +1,8 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Compiler struct {
|
||||
|
|
@ -26,6 +26,61 @@ type LocalVariable struct {
|
|||
scope int
|
||||
}
|
||||
|
||||
type CompilerError struct {
|
||||
Description string
|
||||
Causer Node
|
||||
}
|
||||
|
||||
func (e CompilerError) Error() string {
|
||||
return e.Description
|
||||
}
|
||||
|
||||
func (e CompilerError) Format(src []rune) string {
|
||||
b := strings.Builder{}
|
||||
|
||||
b.WriteString(e.Description)
|
||||
b.WriteString("\n")
|
||||
|
||||
// highlight offending area
|
||||
start, end := e.Causer.Bounds()
|
||||
|
||||
lineStart := 0
|
||||
line := 1
|
||||
pos := 0
|
||||
for i := Pos(0); i < start; i++ {
|
||||
pos++
|
||||
|
||||
if src[i] == '\n' {
|
||||
line++
|
||||
lineStart = int(i)
|
||||
pos = 0
|
||||
}
|
||||
}
|
||||
|
||||
lineEnd := lineStart
|
||||
for lineEnd < len(src) {
|
||||
lineEnd++
|
||||
if src[lineEnd] == '\n' {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
lineDescriptor := fmt.Sprintf("%d:%d~%d", line, pos, int(end-start)+pos)
|
||||
|
||||
b.WriteString(lineDescriptor)
|
||||
b.WriteString("\t | ")
|
||||
|
||||
b.WriteString(string(src[lineStart+1 : lineEnd]))
|
||||
b.WriteString("\n")
|
||||
|
||||
b.WriteString(strings.Repeat(" ", len(lineDescriptor)))
|
||||
b.WriteString("\t ")
|
||||
b.WriteString(strings.Repeat(" ", int(start)-lineStart-1))
|
||||
b.WriteString(strings.Repeat("^", int(end-start)))
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func NewCompiler() *Compiler {
|
||||
c := &Compiler{
|
||||
NewChunk(make([]Bytecode, 0), make([]Value, 0)),
|
||||
|
|
@ -113,6 +168,29 @@ func (c *Compiler) Compile(tree Node) error {
|
|||
return err
|
||||
}
|
||||
|
||||
case UnaryNodeType:
|
||||
if c.isTreeConstant(tree.(*UnaryNode).value) {
|
||||
v, err := c.compute(tree)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.add(InstructionConstant)
|
||||
c.addConstant(v)
|
||||
} else {
|
||||
err := c.Compile(tree.(*UnaryNode).value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch tree.(*UnaryNode).UnaryOperation {
|
||||
case UnaryNegate:
|
||||
c.add(InstructionNegate)
|
||||
case UnaryNot:
|
||||
c.add(InstructionNot)
|
||||
}
|
||||
}
|
||||
|
||||
case BooleanNodeType:
|
||||
if tree.(*BooleanNode).value {
|
||||
c.add(InstructionTrue)
|
||||
|
|
@ -219,14 +297,34 @@ func (c *Compiler) Compile(tree Node) error {
|
|||
case CallNodeType:
|
||||
n := tree.(*CallNode)
|
||||
|
||||
for _, arg := range n.args {
|
||||
err := c.Compile(arg)
|
||||
s, err := c.deduceSignature(n.source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, ok := s.(*FunctionSignature)
|
||||
if !ok {
|
||||
return c.error(fmt.Sprintf("cannot call non-function value of type %s", s), n)
|
||||
}
|
||||
|
||||
for i, arg := range n.args {
|
||||
sig, err := c.deduceSignature(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check that arg type is as required
|
||||
if !f.in[i].Matches(sig) {
|
||||
return c.error(fmt.Sprintf("argument #%d does not have expected type signature: got %s, requires %s", i, sig, f.in[i]), arg)
|
||||
}
|
||||
|
||||
err = c.Compile(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := c.Compile(n.source)
|
||||
err = c.Compile(n.source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -246,6 +344,13 @@ func (c *Compiler) Compile(tree Node) error {
|
|||
c.add(InstructionConstant)
|
||||
c.add(Bytecode(fi))
|
||||
|
||||
// allow self-referencing
|
||||
sig, err := c.deduceSignature(n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.registerVar(n.name, sig)
|
||||
|
||||
// keep track of main chunk
|
||||
mc := c.Chunk
|
||||
// and ip
|
||||
|
|
@ -257,10 +362,10 @@ func (c *Compiler) Compile(tree Node) error {
|
|||
c.ip = 0
|
||||
|
||||
for _, p := range n.parameters {
|
||||
c.registerVar(p.name, p.signature)
|
||||
c.registerVar(p.Name, p.Signature)
|
||||
}
|
||||
|
||||
err := c.Compile(n.logic)
|
||||
err = c.Compile(n.logic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -312,6 +417,9 @@ func (c *Compiler) Compile(tree Node) error {
|
|||
|
||||
case BreakpointNodeType:
|
||||
c.add(InstructionBreakpoint)
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unimplemented compiling of %s", tree.Type()))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -340,7 +448,16 @@ func (c *Compiler) compileBinary(binary *BinaryNode) error {
|
|||
|
||||
switch binary.BinaryOperation {
|
||||
case BinaryAddition:
|
||||
c.add(InstructionAdd)
|
||||
res, err := c.deduceSignature(binary)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if res.Type() == TypeString {
|
||||
c.add(InstructionStringConcatenation)
|
||||
} else {
|
||||
c.add(InstructionAdd)
|
||||
}
|
||||
case BinarySubtraction:
|
||||
c.add(InstructionSub)
|
||||
case BinaryMultiplication:
|
||||
|
|
@ -375,13 +492,45 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
|||
case NumberNodeType:
|
||||
return &NumberSignature{}, nil
|
||||
case ReferenceNodeType:
|
||||
return c.getVarSignature(tree.(*ReferenceNode).name)
|
||||
n := tree.(*ReferenceNode)
|
||||
if c.isGlobal(n.name) {
|
||||
return SignatureOf(DefaultGlobals[n.name]), nil
|
||||
}
|
||||
|
||||
for i := c.stack.Current - 1; i >= 0; i-- {
|
||||
v := c.stack.items[i]
|
||||
if v.name == n.name {
|
||||
return v.signature, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, c.error(fmt.Sprintf("variable %s not defined", n.name), n)
|
||||
case BooleanNodeType:
|
||||
return &BooleanSignature{}, nil
|
||||
case NilNodeType:
|
||||
return &NilSignature{}, nil
|
||||
case ListNodeType:
|
||||
return &ListSignature{}, nil
|
||||
n := tree.(*ListNode)
|
||||
|
||||
var contents TypeSignature
|
||||
// check for contents type
|
||||
for _, v := range n.items {
|
||||
sig, err := c.deduceSignature(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if contents == nil {
|
||||
contents = sig
|
||||
} else {
|
||||
contents = &AnySignature{}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return &ListSignature{
|
||||
contents,
|
||||
}, nil
|
||||
case BinaryNodeType:
|
||||
n := tree.(*BinaryNode)
|
||||
l, err := c.deduceSignature(n.Left)
|
||||
|
|
@ -394,13 +543,13 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
|||
}
|
||||
|
||||
if l != r {
|
||||
return nil, errors.New(fmt.Sprintf("cannot perform binary %s on different types: %s and %s", n.BinaryOperation, l, r))
|
||||
return nil, c.error(fmt.Sprintf("cannot perform binary %s on different types: %s and %s", n.BinaryOperation, l, r), n)
|
||||
}
|
||||
|
||||
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 nil, c.error(fmt.Sprintf("cannot perform binary %s non-number type %s", n.BinaryOperation, l), n)
|
||||
}
|
||||
|
||||
return &NumberSignature{}, nil
|
||||
|
|
@ -411,11 +560,11 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
|||
case TypeNumber:
|
||||
return &NumberSignature{}, nil
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintf("cannot perform binary addition on type %s", l))
|
||||
return nil, c.error(fmt.Sprintf("cannot perform binary addition on type %s", l), n)
|
||||
}
|
||||
case BinaryAnd, BinaryOr:
|
||||
if l.Type() != TypeBoolean {
|
||||
return nil, errors.New(fmt.Sprintf("cannot perform binary %s on type %s", l, n.BinaryOperation))
|
||||
return nil, c.error(fmt.Sprintf("cannot perform binary %s on type %s", l, n.BinaryOperation), n)
|
||||
}
|
||||
|
||||
return &BooleanSignature{}, nil
|
||||
|
|
@ -423,11 +572,13 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
|||
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 nil, c.error(fmt.Sprintf("cannot perform number comparison (%s) on type %s", l, n.BinaryOperation), n)
|
||||
}
|
||||
|
||||
return &BooleanSignature{}, nil
|
||||
}
|
||||
|
||||
return nil, c.error(fmt.Sprintf("cannot deduce result type of binary %s", n.BinaryOperation), n)
|
||||
case AccessNodeType:
|
||||
n := tree.(*AccessNode)
|
||||
sig, err := c.deduceSignature(n.source)
|
||||
|
|
@ -436,13 +587,19 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
|||
}
|
||||
|
||||
switch sig.Type() {
|
||||
case TypeString, TypeList:
|
||||
case TypeString:
|
||||
return SignatureOf(StringPrototype[n.property]), nil
|
||||
case TypeList:
|
||||
return SignatureOf(ListPrototype[n.property]), nil
|
||||
case TypeObject:
|
||||
if v, ok := ObjectPrototype[n.property]; ok {
|
||||
return SignatureOf(v), nil
|
||||
}
|
||||
|
||||
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))
|
||||
return nil, c.error(fmt.Sprintf("cannot access property from value of type %s", sig), n)
|
||||
}
|
||||
|
||||
case CallNodeType:
|
||||
|
|
@ -453,13 +610,13 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
|||
}
|
||||
|
||||
if sig.Type() != TypeFunction {
|
||||
return nil, errors.New(fmt.Sprintf("cannot call value of type %s", sig.Type()))
|
||||
return nil, c.error(fmt.Sprintf("cannot call value of type %s", sig.Type()), n.source)
|
||||
}
|
||||
|
||||
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)))
|
||||
return nil, c.error(fmt.Sprintf("bad argument count (expected %v, got %v)", len(f.in), len(n.args)), n)
|
||||
}
|
||||
|
||||
// type check arguments
|
||||
|
|
@ -470,6 +627,7 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
|||
}
|
||||
|
||||
if !sig.Matches(f.in[i]) {
|
||||
return nil, c.error(fmt.Sprintf("argument #%d has wrong type signature. requires %s, got %s", i, f.in[i], sig), arg)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -481,7 +639,7 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
|||
sigs := make([]TypeSignature, len(n.parameters))
|
||||
|
||||
for i, p := range n.parameters {
|
||||
sigs[i] = p.signature
|
||||
sigs[i] = p.Signature
|
||||
}
|
||||
|
||||
return &FunctionSignature{
|
||||
|
|
@ -489,26 +647,76 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
|||
n.yield,
|
||||
}, nil
|
||||
|
||||
default:
|
||||
panic("unhandled default case")
|
||||
}
|
||||
case UnaryNodeType:
|
||||
n := tree.(*UnaryNode)
|
||||
|
||||
panic(fmt.Sprintf("impossible to deduce signature of %s", tree.Type()))
|
||||
sig, err := c.deduceSignature(n.value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch n.UnaryOperation {
|
||||
case UnaryNegate:
|
||||
if sig.Type() != TypeNumber {
|
||||
return nil, c.error(fmt.Sprintf("cannot perform negation on type %s (must be number)", n.UnaryOperation), n)
|
||||
}
|
||||
return &NumberSignature{}, nil
|
||||
case UnaryNot:
|
||||
if sig.Type() != TypeBoolean {
|
||||
return nil, c.error(fmt.Sprintf("cannot perform negation on type %s (must be boolean)", n.UnaryOperation), n)
|
||||
}
|
||||
return &BooleanSignature{}, nil
|
||||
}
|
||||
|
||||
return nil, c.error(fmt.Sprintf("unimplemented result type deduction for unary %s", n.UnaryOperation), n)
|
||||
default:
|
||||
return nil, c.error(fmt.Sprintf("impossible to deduce signature of %s", tree.Type()), tree)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
func (c *Compiler) affirmReturnSignature(tree Node, sig TypeSignature) error {
|
||||
switch tree.Type() {
|
||||
case BlockNodeType:
|
||||
for _, stmt := range tree.(*BlockNode).statements {
|
||||
if err := c.affirmReturnSignature(stmt, sig); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
case ReturnNodeType:
|
||||
n := tree.(*ReturnNode)
|
||||
v, err := c.deduceSignature(n.value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !sig.Matches(v) {
|
||||
return c.error(fmt.Sprintf("function cannot return a value with type %s. must be %s", v, sig), n.value)
|
||||
}
|
||||
|
||||
case ConditionalNodeType:
|
||||
n := tree.(*ConditionalNode)
|
||||
|
||||
if err := c.affirmReturnSignature(n.do, sig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if n.otherwise != nil {
|
||||
if err := c.affirmReturnSignature(n.otherwise, sig); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
case LoopNodeType:
|
||||
n := tree.(*LoopNode)
|
||||
|
||||
if err := c.affirmReturnSignature(n.do, sig); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
return nil, errors.New(fmt.Sprintf("variable %s not defined", name))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Compiler) getVar(name string) {
|
||||
|
|
@ -628,6 +836,16 @@ func (c *Compiler) compute(tree Node) (Value, error) {
|
|||
case *BinaryNode:
|
||||
return c.computeBinary(n)
|
||||
|
||||
case *UnaryNode:
|
||||
v, err := c.compute(n.value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &NumberValue{
|
||||
-v.(*NumberValue).Number,
|
||||
}, nil
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected node %s, %T", tree.String(), tree))
|
||||
}
|
||||
|
|
@ -646,29 +864,29 @@ func (c *Compiler) computeBinary(n *BinaryNode) (Value, error) {
|
|||
var v interface{}
|
||||
switch n.BinaryOperation {
|
||||
case BinaryAddition:
|
||||
v = l.(*NumberValue).float64 + r.(*NumberValue).float64
|
||||
v = l.(*NumberValue).Number + r.(*NumberValue).Number
|
||||
case BinarySubtraction:
|
||||
v = l.(*NumberValue).float64 - r.(*NumberValue).float64
|
||||
v = l.(*NumberValue).Number - r.(*NumberValue).Number
|
||||
case BinaryMultiplication:
|
||||
v = l.(*NumberValue).float64 * r.(*NumberValue).float64
|
||||
v = l.(*NumberValue).Number * r.(*NumberValue).Number
|
||||
case BinaryDivision:
|
||||
v = l.(*NumberValue).float64 / r.(*NumberValue).float64
|
||||
v = l.(*NumberValue).Number / r.(*NumberValue).Number
|
||||
case BinaryAnd:
|
||||
v = l.(*BoolValue).bool && r.(*BoolValue).bool
|
||||
v = l.(*BoolValue).Boolean && r.(*BoolValue).Boolean
|
||||
case BinaryOr:
|
||||
v = l.(*BoolValue).bool && r.(*BoolValue).bool
|
||||
v = l.(*BoolValue).Boolean && r.(*BoolValue).Boolean
|
||||
case BinaryEquality:
|
||||
v = l.Equals(r)
|
||||
case BinaryInequality:
|
||||
v = !l.Equals(r.(*BoolValue))
|
||||
v = !l.Equals(r)
|
||||
case BinaryLess:
|
||||
v = l.(*NumberValue).float64 < r.(*NumberValue).float64
|
||||
v = l.(*NumberValue).Number < r.(*NumberValue).Number
|
||||
case BinaryGreater:
|
||||
v = l.(*NumberValue).float64 > r.(*NumberValue).float64
|
||||
v = l.(*NumberValue).Number > r.(*NumberValue).Number
|
||||
case BinaryLessEqual:
|
||||
v = l.(*NumberValue).float64 <= r.(*NumberValue).float64
|
||||
v = l.(*NumberValue).Number <= r.(*NumberValue).Number
|
||||
case BinaryGreaterEqual:
|
||||
v = l.(*NumberValue).float64 >= r.(*NumberValue).float64
|
||||
v = l.(*NumberValue).Number >= r.(*NumberValue).Number
|
||||
}
|
||||
|
||||
return GoToValue(v), nil
|
||||
|
|
@ -697,6 +915,13 @@ func (c *Compiler) descend() {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) error(msg string, causer Node) CompilerError {
|
||||
return CompilerError{
|
||||
msg,
|
||||
causer,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) resolveImport(path string) Node {
|
||||
if chunk, ok := c.imports[path]; ok {
|
||||
return chunk
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ func GetCompileTestData() map[string]CompileTestData {
|
|||
&StringNode{
|
||||
"Hello world!",
|
||||
"\"Hello world!\"",
|
||||
0, 0,
|
||||
},
|
||||
[]Value{
|
||||
&StringValue{"Hello world!"},
|
||||
|
|
@ -50,12 +51,15 @@ func GetCompileTestData() map[string]CompileTestData {
|
|||
"a",
|
||||
&NumberNode{
|
||||
0,
|
||||
0, 0,
|
||||
},
|
||||
true,
|
||||
0, 0,
|
||||
},
|
||||
&ConditionalNode{
|
||||
&BooleanNode{
|
||||
false,
|
||||
0, 0,
|
||||
},
|
||||
&BlockNode{
|
||||
[]Node{
|
||||
|
|
@ -63,14 +67,19 @@ func GetCompileTestData() map[string]CompileTestData {
|
|||
"a",
|
||||
&NumberNode{
|
||||
1,
|
||||
0, 0,
|
||||
},
|
||||
false,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
nil,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
[]Value{
|
||||
&VariableValue{
|
||||
|
|
@ -87,12 +96,15 @@ func GetCompileTestData() map[string]CompileTestData {
|
|||
"a",
|
||||
&NumberNode{
|
||||
0,
|
||||
0, 0,
|
||||
},
|
||||
true,
|
||||
0, 0,
|
||||
},
|
||||
&ConditionalNode{
|
||||
&BooleanNode{
|
||||
true,
|
||||
0, 0,
|
||||
},
|
||||
&BlockNode{
|
||||
[]Node{
|
||||
|
|
@ -100,14 +112,19 @@ func GetCompileTestData() map[string]CompileTestData {
|
|||
"a",
|
||||
&NumberNode{
|
||||
1,
|
||||
0, 0,
|
||||
},
|
||||
false,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
nil,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
[]Value{
|
||||
&VariableValue{
|
||||
|
|
@ -124,12 +141,15 @@ func GetCompileTestData() map[string]CompileTestData {
|
|||
"a",
|
||||
&NumberNode{
|
||||
0,
|
||||
0, 0,
|
||||
},
|
||||
true,
|
||||
0, 0,
|
||||
},
|
||||
&ConditionalNode{
|
||||
&BooleanNode{
|
||||
false,
|
||||
0, 0,
|
||||
},
|
||||
&BlockNode{
|
||||
[]Node{
|
||||
|
|
@ -137,10 +157,13 @@ func GetCompileTestData() map[string]CompileTestData {
|
|||
"a",
|
||||
&NumberNode{
|
||||
1,
|
||||
0, 0,
|
||||
},
|
||||
false,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
&BlockNode{
|
||||
[]Node{
|
||||
|
|
@ -148,13 +171,18 @@ func GetCompileTestData() map[string]CompileTestData {
|
|||
"a",
|
||||
&NumberNode{
|
||||
2,
|
||||
0, 0,
|
||||
},
|
||||
false,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
[]Value{
|
||||
&VariableValue{
|
||||
|
|
@ -171,12 +199,15 @@ func GetCompileTestData() map[string]CompileTestData {
|
|||
"a",
|
||||
&NumberNode{
|
||||
0,
|
||||
0, 0,
|
||||
},
|
||||
true,
|
||||
0, 0,
|
||||
},
|
||||
&ConditionalNode{
|
||||
&BooleanNode{
|
||||
true,
|
||||
0, 0,
|
||||
},
|
||||
&BlockNode{
|
||||
[]Node{
|
||||
|
|
@ -184,10 +215,13 @@ func GetCompileTestData() map[string]CompileTestData {
|
|||
"a",
|
||||
&NumberNode{
|
||||
1,
|
||||
0, 0,
|
||||
},
|
||||
false,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
&BlockNode{
|
||||
[]Node{
|
||||
|
|
@ -195,13 +229,18 @@ func GetCompileTestData() map[string]CompileTestData {
|
|||
"a",
|
||||
&NumberNode{
|
||||
2,
|
||||
0, 0,
|
||||
},
|
||||
false,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
[]Value{
|
||||
&VariableValue{
|
||||
|
|
@ -216,10 +255,13 @@ func GetCompileTestData() map[string]CompileTestData {
|
|||
BinaryAddition,
|
||||
&NumberNode{
|
||||
1,
|
||||
0, 0,
|
||||
},
|
||||
&NumberNode{
|
||||
2,
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
[]Value{
|
||||
&NumberValue{3},
|
||||
|
|
@ -247,16 +289,28 @@ func GetCompileTestData() map[string]CompileTestData {
|
|||
&ReturnNode{
|
||||
&BinaryNode{
|
||||
BinaryAddition,
|
||||
&ReferenceNode{"a"},
|
||||
&ReferenceNode{"b"},
|
||||
&ReferenceNode{
|
||||
"a",
|
||||
0, 0,
|
||||
},
|
||||
&ReferenceNode{
|
||||
"b",
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
true,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
[]Value{
|
||||
&VariableValue{
|
||||
|
|
@ -306,25 +360,39 @@ func GetCompileTestData() map[string]CompileTestData {
|
|||
[]Node{
|
||||
&AssignNode{
|
||||
"b",
|
||||
&NumberNode{1},
|
||||
&NumberNode{
|
||||
1,
|
||||
0, 0,
|
||||
},
|
||||
true,
|
||||
0, 0,
|
||||
},
|
||||
&ReturnNode{
|
||||
&ReferenceNode{"b"},
|
||||
&ReferenceNode{
|
||||
"b",
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
true,
|
||||
0, 0,
|
||||
},
|
||||
&CallNode{
|
||||
&ReferenceNode{
|
||||
"a",
|
||||
0, 0,
|
||||
},
|
||||
[]Node{},
|
||||
false,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
[]Value{
|
||||
&VariableValue{
|
||||
|
|
@ -363,7 +431,7 @@ func printChunk(t *testing.T, name string, chunk *Chunk) {
|
|||
t.Logf("=-= constants =-=")
|
||||
|
||||
for i, ct := range chunk.Constants {
|
||||
t.Logf("c=%d \t%s", i, ct)
|
||||
t.Logf("c=%d \t%s", i, ct.DebugString())
|
||||
|
||||
f, ok := ct.(*FunctionValue)
|
||||
if ok {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ const (
|
|||
TokenSemicolon
|
||||
|
||||
TokenNumber
|
||||
TokenHexadecimal
|
||||
TokenString
|
||||
TokenName
|
||||
|
||||
|
|
@ -65,6 +66,7 @@ const (
|
|||
TokenLessThanOrEqual
|
||||
|
||||
TokenDoubleAmpersand
|
||||
TokenPipe
|
||||
TokenDoublePipe
|
||||
|
||||
TokenBreakpoint
|
||||
|
|
@ -156,6 +158,10 @@ func (t TokenType) String() string {
|
|||
return "import"
|
||||
case TokenColon:
|
||||
return "colon"
|
||||
case TokenPipe:
|
||||
return "pipe"
|
||||
case TokenHexadecimal:
|
||||
return "hexadecimal"
|
||||
}
|
||||
|
||||
return "UNDEFINED TOKENTYPE STRING CONVERSION"
|
||||
|
|
@ -279,7 +285,7 @@ func (l *Lexer) NextToken() (Token, error) {
|
|||
return l.makeToken(TokenDoublePipe), nil
|
||||
}
|
||||
|
||||
return l.makeToken(TokenError), errors.New("malformed token (got '|', expected '|' to follow)")
|
||||
return l.makeToken(TokenPipe), nil
|
||||
|
||||
case '"':
|
||||
// include ending quote
|
||||
|
|
@ -330,6 +336,19 @@ func (l *Lexer) NextToken() (Token, error) {
|
|||
default:
|
||||
return l.makeToken(TokenName), nil
|
||||
}
|
||||
} else if c == '0' && l.peek() != '.' {
|
||||
if l.peek() == 'x' {
|
||||
l.advance()
|
||||
|
||||
// hex
|
||||
for unicode.In(l.peek(), unicode.Hex_Digit) {
|
||||
l.advance()
|
||||
}
|
||||
|
||||
return l.makeToken(TokenHexadecimal), nil
|
||||
}
|
||||
|
||||
return l.makeToken(TokenNumber), nil
|
||||
} else if unicode.IsDigit(c) {
|
||||
for unicode.IsDigit(l.peek()) {
|
||||
l.advance()
|
||||
|
|
|
|||
214
core/nodes.go
214
core/nodes.go
|
|
@ -11,6 +11,8 @@ type NodeType int
|
|||
type Node interface {
|
||||
Type() NodeType
|
||||
String() string
|
||||
|
||||
Bounds() (Pos, Pos)
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
@ -21,6 +23,7 @@ const (
|
|||
NilNodeType
|
||||
ListNodeType
|
||||
BinaryNodeType
|
||||
UnaryNodeType
|
||||
BlockNodeType
|
||||
ConditionalNodeType
|
||||
LoopNodeType
|
||||
|
|
@ -69,6 +72,8 @@ func (n NodeType) String() string {
|
|||
return "Breakpoint"
|
||||
case ImportNodeType:
|
||||
return "Import"
|
||||
case UnaryNodeType:
|
||||
return "Unary"
|
||||
}
|
||||
return "Invalid Node Type"
|
||||
}
|
||||
|
|
@ -76,6 +81,9 @@ func (n NodeType) String() string {
|
|||
// ReferenceNode a reference to a variable on the stack
|
||||
type ReferenceNode struct {
|
||||
name string
|
||||
|
||||
start Pos
|
||||
end Pos
|
||||
}
|
||||
|
||||
func (n ReferenceNode) Type() NodeType {
|
||||
|
|
@ -86,10 +94,17 @@ func (n ReferenceNode) String() string {
|
|||
return n.name
|
||||
}
|
||||
|
||||
func (n ReferenceNode) Bounds() (Pos, Pos) {
|
||||
return n.start, n.end
|
||||
}
|
||||
|
||||
// StringNode string/text values
|
||||
type StringNode struct {
|
||||
value string
|
||||
quoted string
|
||||
|
||||
start Pos
|
||||
end Pos
|
||||
}
|
||||
|
||||
func (n StringNode) Type() NodeType {
|
||||
|
|
@ -100,8 +115,15 @@ func (n StringNode) String() string {
|
|||
return n.quoted
|
||||
}
|
||||
|
||||
func (n StringNode) Bounds() (Pos, Pos) {
|
||||
return n.start, n.end
|
||||
}
|
||||
|
||||
type NumberNode struct {
|
||||
value float64
|
||||
|
||||
start Pos
|
||||
end Pos
|
||||
}
|
||||
|
||||
func (n NumberNode) Type() NodeType {
|
||||
|
|
@ -112,9 +134,16 @@ func (n NumberNode) String() string {
|
|||
return strconv.FormatFloat(n.value, 'g', -1, NumberSize)
|
||||
}
|
||||
|
||||
func (n NumberNode) Bounds() (Pos, Pos) {
|
||||
return n.start, n.end
|
||||
}
|
||||
|
||||
// ListNode a list or sequence of values (items)
|
||||
type ListNode struct {
|
||||
items []Node
|
||||
|
||||
start Pos
|
||||
end Pos
|
||||
}
|
||||
|
||||
func (n ListNode) Type() NodeType {
|
||||
|
|
@ -125,18 +154,25 @@ func (n ListNode) String() string {
|
|||
sb := strings.Builder{}
|
||||
sb.WriteString("[")
|
||||
for i, item := range n.items {
|
||||
sb.WriteString(item.String())
|
||||
if i > 0 {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
sb.WriteString(item.String())
|
||||
}
|
||||
sb.WriteString("]")
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (n ListNode) Bounds() (Pos, Pos) {
|
||||
return n.start, n.end
|
||||
}
|
||||
|
||||
type AccessNode struct {
|
||||
source Node
|
||||
property string
|
||||
|
||||
start Pos
|
||||
end Pos
|
||||
}
|
||||
|
||||
func (n AccessNode) Type() NodeType {
|
||||
|
|
@ -147,6 +183,10 @@ func (n AccessNode) String() string {
|
|||
return fmt.Sprintf("(%s from %s)", n.property, n.source)
|
||||
}
|
||||
|
||||
func (n AccessNode) Bounds() (Pos, Pos) {
|
||||
return n.start, n.end
|
||||
}
|
||||
|
||||
type BinaryOperation uint
|
||||
|
||||
func (n BinaryOperation) String() string {
|
||||
|
|
@ -198,11 +238,45 @@ const (
|
|||
BinaryGreaterEqual
|
||||
)
|
||||
|
||||
func (n BinaryOperation) Symbol() string {
|
||||
switch n {
|
||||
case BinaryAddition:
|
||||
return "+"
|
||||
case BinarySubtraction:
|
||||
return "-"
|
||||
case BinaryMultiplication:
|
||||
return "*"
|
||||
case BinaryDivision:
|
||||
return "/"
|
||||
case BinaryEquality:
|
||||
return "=="
|
||||
case BinaryInequality:
|
||||
return "!="
|
||||
case BinaryLess:
|
||||
return "<"
|
||||
case BinaryGreater:
|
||||
return ">"
|
||||
case BinaryAnd:
|
||||
return "&&"
|
||||
case BinaryOr:
|
||||
return "||"
|
||||
case BinaryLessEqual:
|
||||
return "<="
|
||||
case BinaryGreaterEqual:
|
||||
return ">="
|
||||
}
|
||||
|
||||
panic("unsupported binary operation to symbol conversion for " + n.String())
|
||||
}
|
||||
|
||||
// BinaryNode All operations which take 2 variables
|
||||
type BinaryNode struct {
|
||||
BinaryOperation
|
||||
Left Node
|
||||
Right Node
|
||||
|
||||
start Pos
|
||||
end Pos
|
||||
}
|
||||
|
||||
func (n BinaryNode) Type() NodeType {
|
||||
|
|
@ -213,9 +287,65 @@ func (n BinaryNode) String() string {
|
|||
return fmt.Sprintf("%s %s %s", n.Left.String(), n.BinaryOperation.String(), n.Right.String())
|
||||
}
|
||||
|
||||
func (n BinaryNode) Bounds() (Pos, Pos) {
|
||||
return n.start, n.end
|
||||
}
|
||||
|
||||
type UnaryOperation int
|
||||
|
||||
const (
|
||||
UnaryNegate UnaryOperation = iota
|
||||
UnaryNot
|
||||
)
|
||||
|
||||
func (op UnaryOperation) String() string {
|
||||
switch op {
|
||||
case UnaryNegate:
|
||||
return "negate"
|
||||
case UnaryNot:
|
||||
return "not"
|
||||
}
|
||||
|
||||
panic("unimplemented unary operation to string conversion")
|
||||
}
|
||||
|
||||
func (op UnaryOperation) Symbol() string {
|
||||
switch op {
|
||||
case UnaryNegate:
|
||||
return "-"
|
||||
case UnaryNot:
|
||||
return "!"
|
||||
}
|
||||
|
||||
panic("unimplemented unary operation to symbol conversion")
|
||||
}
|
||||
|
||||
type UnaryNode struct {
|
||||
UnaryOperation
|
||||
value Node
|
||||
|
||||
start Pos
|
||||
end Pos
|
||||
}
|
||||
|
||||
func (n UnaryNode) Type() NodeType {
|
||||
return UnaryNodeType
|
||||
}
|
||||
|
||||
func (n UnaryNode) String() string {
|
||||
return fmt.Sprintf("%s %s", n.UnaryOperation.String(), n.value.String())
|
||||
}
|
||||
|
||||
func (n UnaryNode) Bounds() (Pos, Pos) {
|
||||
return n.start, n.end
|
||||
}
|
||||
|
||||
// BooleanNode boolean value
|
||||
type BooleanNode struct {
|
||||
value bool
|
||||
|
||||
start Pos
|
||||
end Pos
|
||||
}
|
||||
|
||||
func (n BooleanNode) Type() NodeType {
|
||||
|
|
@ -226,8 +356,15 @@ func (n BooleanNode) String() string {
|
|||
return strconv.FormatBool(n.value)
|
||||
}
|
||||
|
||||
func (n BooleanNode) Bounds() (Pos, Pos) {
|
||||
return n.start, n.end
|
||||
}
|
||||
|
||||
// NilNode nil value
|
||||
type NilNode struct{}
|
||||
type NilNode struct {
|
||||
start Pos
|
||||
end Pos
|
||||
}
|
||||
|
||||
func (n NilNode) Type() NodeType {
|
||||
return NilNodeType
|
||||
|
|
@ -237,9 +374,16 @@ func (n NilNode) String() string {
|
|||
return "nil"
|
||||
}
|
||||
|
||||
func (n NilNode) Bounds() (Pos, Pos) {
|
||||
return n.start, n.end
|
||||
}
|
||||
|
||||
// BlockNode block node with statements
|
||||
type BlockNode struct {
|
||||
statements []Node
|
||||
|
||||
start Pos
|
||||
end Pos
|
||||
}
|
||||
|
||||
func (n BlockNode) Type() NodeType {
|
||||
|
|
@ -257,8 +401,15 @@ func (n BlockNode) String() string {
|
|||
return builder.String()
|
||||
}
|
||||
|
||||
func (n BlockNode) Bounds() (Pos, Pos) {
|
||||
return n.start, n.end
|
||||
}
|
||||
|
||||
type ImportNode struct {
|
||||
path string
|
||||
|
||||
start Pos
|
||||
end Pos
|
||||
}
|
||||
|
||||
func (n ImportNode) Type() NodeType {
|
||||
|
|
@ -269,11 +420,18 @@ func (n ImportNode) String() string {
|
|||
return fmt.Sprintf("import %s", n.path)
|
||||
}
|
||||
|
||||
func (n ImportNode) Bounds() (Pos, Pos) {
|
||||
return n.start, n.end
|
||||
}
|
||||
|
||||
// ConditionalNode conditionals (if statements)
|
||||
type ConditionalNode struct {
|
||||
condition Node
|
||||
do Node
|
||||
otherwise Node
|
||||
|
||||
start Pos
|
||||
end Pos
|
||||
}
|
||||
|
||||
func (n ConditionalNode) Type() NodeType {
|
||||
|
|
@ -284,10 +442,17 @@ func (n ConditionalNode) String() string {
|
|||
return fmt.Sprintf("if %s then %s otheriwise %s", n.condition.String(), n.do.String(), n.otherwise.String())
|
||||
}
|
||||
|
||||
func (n ConditionalNode) Bounds() (Pos, Pos) {
|
||||
return n.start, n.end
|
||||
}
|
||||
|
||||
// LoopNode Loops (for/while)
|
||||
type LoopNode struct {
|
||||
condition Node
|
||||
do Node
|
||||
|
||||
start Pos
|
||||
end Pos
|
||||
}
|
||||
|
||||
func (n LoopNode) Type() NodeType {
|
||||
|
|
@ -298,11 +463,18 @@ func (n LoopNode) String() string {
|
|||
return fmt.Sprintf("while %s loop %s", n.condition.String(), n.do.String())
|
||||
}
|
||||
|
||||
func (n LoopNode) Bounds() (Pos, Pos) {
|
||||
return n.start, n.end
|
||||
}
|
||||
|
||||
// AssignNode assignment
|
||||
type AssignNode struct {
|
||||
name string
|
||||
value Node
|
||||
declare bool
|
||||
|
||||
start Pos
|
||||
end Pos
|
||||
}
|
||||
|
||||
func (n AssignNode) Type() NodeType {
|
||||
|
|
@ -313,11 +485,18 @@ func (n AssignNode) String() string {
|
|||
return fmt.Sprintf("set %s to %s", n.name, n.value)
|
||||
}
|
||||
|
||||
func (n AssignNode) Bounds() (Pos, Pos) {
|
||||
return n.start, n.end
|
||||
}
|
||||
|
||||
// CallNode function call
|
||||
type CallNode struct {
|
||||
source Node
|
||||
args []Node
|
||||
keep bool
|
||||
|
||||
start Pos
|
||||
end Pos
|
||||
}
|
||||
|
||||
func (n CallNode) Type() NodeType {
|
||||
|
|
@ -328,17 +507,24 @@ func (n CallNode) String() string {
|
|||
return fmt.Sprintf("call %s with args (%s)", n.source.String(), n.args)
|
||||
}
|
||||
|
||||
func (n CallNode) Bounds() (Pos, Pos) {
|
||||
return n.start, n.end
|
||||
}
|
||||
|
||||
// FunctionNode definition of function
|
||||
type FunctionNode struct {
|
||||
name string
|
||||
parameters []FunctionParameter
|
||||
yield TypeSignature
|
||||
logic Node
|
||||
|
||||
start Pos
|
||||
end Pos
|
||||
}
|
||||
|
||||
type FunctionParameter struct {
|
||||
name string
|
||||
signature TypeSignature
|
||||
Name string
|
||||
Signature TypeSignature
|
||||
}
|
||||
|
||||
func (n FunctionNode) Type() NodeType {
|
||||
|
|
@ -349,9 +535,16 @@ func (n FunctionNode) String() string {
|
|||
return fmt.Sprintf("definition of %s, do %s", n.name, n.logic.String())
|
||||
}
|
||||
|
||||
func (n FunctionNode) Bounds() (Pos, Pos) {
|
||||
return n.start, n.end
|
||||
}
|
||||
|
||||
// ReturnNode return a value out of this context
|
||||
type ReturnNode struct {
|
||||
value Node
|
||||
|
||||
start Pos
|
||||
end Pos
|
||||
}
|
||||
|
||||
func (n ReturnNode) Type() NodeType {
|
||||
|
|
@ -362,7 +555,14 @@ func (n ReturnNode) String() string {
|
|||
return fmt.Sprintf("return %s", n.value)
|
||||
}
|
||||
|
||||
type BreakpointNode struct{}
|
||||
func (n ReturnNode) Bounds() (Pos, Pos) {
|
||||
return n.start, n.end
|
||||
}
|
||||
|
||||
type BreakpointNode struct {
|
||||
start Pos
|
||||
end Pos
|
||||
}
|
||||
|
||||
func (n BreakpointNode) Type() NodeType {
|
||||
return BreakpointNodeType
|
||||
|
|
@ -371,3 +571,7 @@ func (n BreakpointNode) Type() NodeType {
|
|||
func (n BreakpointNode) String() string {
|
||||
return "breakpoint"
|
||||
}
|
||||
|
||||
func (n BreakpointNode) Bounds() (Pos, Pos) {
|
||||
return n.start, n.end
|
||||
}
|
||||
|
|
|
|||
168
core/parser.go
168
core/parser.go
|
|
@ -147,6 +147,8 @@ func (p *Parser) factor() (Node, error) {
|
|||
return &StringNode{
|
||||
(*p.prev).Lexeme[1 : len((*p.prev).Lexeme)-1],
|
||||
(*p.prev).Lexeme,
|
||||
p.prev.Start,
|
||||
p.prev.Length,
|
||||
}, nil
|
||||
|
||||
case TokenNumber:
|
||||
|
|
@ -159,17 +161,37 @@ func (p *Parser) factor() (Node, error) {
|
|||
|
||||
return &NumberNode{
|
||||
num,
|
||||
p.prev.Start,
|
||||
p.prev.Length,
|
||||
}, nil
|
||||
|
||||
case TokenHexadecimal:
|
||||
p.advance()
|
||||
start := (*p.prev).Start
|
||||
num, err := strconv.ParseUint((*p.prev).Lexeme[2:], 16, NumberSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &NumberNode{
|
||||
float64(num),
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}, nil
|
||||
|
||||
case TokenTrue:
|
||||
p.advance()
|
||||
return &BooleanNode{
|
||||
true,
|
||||
p.prev.Start,
|
||||
p.prev.Length,
|
||||
}, nil
|
||||
case TokenFalse:
|
||||
p.advance()
|
||||
return &BooleanNode{
|
||||
false,
|
||||
p.prev.Start,
|
||||
p.prev.Length,
|
||||
}, nil
|
||||
|
||||
case TokenNil:
|
||||
|
|
@ -179,6 +201,8 @@ func (p *Parser) factor() (Node, error) {
|
|||
case TokenOpenBracket:
|
||||
p.advance()
|
||||
|
||||
start := p.prev.Start
|
||||
|
||||
var values []Node
|
||||
for !p.accept(TokenCloseBracket) {
|
||||
if len(values) > 0 {
|
||||
|
|
@ -198,24 +222,47 @@ func (p *Parser) factor() (Node, error) {
|
|||
|
||||
return &ListNode{
|
||||
values,
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}, nil
|
||||
|
||||
// unary minus
|
||||
case TokenMinus:
|
||||
p.advance()
|
||||
first := p.prev
|
||||
|
||||
f, err := p.factor()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &BinaryNode{
|
||||
BinarySubtraction,
|
||||
&NumberNode{0},
|
||||
return &UnaryNode{
|
||||
UnaryNegate,
|
||||
f,
|
||||
first.Start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}, nil
|
||||
|
||||
case TokenBang:
|
||||
p.advance()
|
||||
start := p.prev.Start
|
||||
|
||||
v, err := p.factor()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &UnaryNode{
|
||||
UnaryNot,
|
||||
v,
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}, nil
|
||||
|
||||
case TokenName:
|
||||
p.advance()
|
||||
name := (*p.prev).Lexeme
|
||||
start := p.prev.Start
|
||||
nameEnd := start + p.prev.Length
|
||||
|
||||
if p.curr.Type == TokenOpenParenthesis {
|
||||
args, err := p.parseArgs()
|
||||
|
|
@ -226,26 +273,37 @@ func (p *Parser) factor() (Node, error) {
|
|||
return &CallNode{
|
||||
&ReferenceNode{
|
||||
name,
|
||||
start,
|
||||
nameEnd,
|
||||
},
|
||||
args,
|
||||
true,
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &ReferenceNode{
|
||||
name,
|
||||
start,
|
||||
nameEnd,
|
||||
}, nil
|
||||
|
||||
case TokenFunc:
|
||||
p.advance()
|
||||
start := p.prev.Start
|
||||
|
||||
params, err := p.parseParams()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sig, err := p.parseSignature()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var sig TypeSignature = &NilSignature{}
|
||||
if p.curr.Type != TokenOpenBrace {
|
||||
sig, err = p.parseSignature()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
b, err := p.block(false)
|
||||
|
|
@ -258,6 +316,8 @@ func (p *Parser) factor() (Node, error) {
|
|||
params,
|
||||
sig,
|
||||
b,
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}, nil
|
||||
|
||||
case TokenOpenParenthesis:
|
||||
|
|
@ -280,6 +340,8 @@ 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
|
||||
|
|
@ -295,6 +357,8 @@ func (p *Parser) prop() (Node, error) {
|
|||
v = &AccessNode{
|
||||
v,
|
||||
property,
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}
|
||||
|
||||
// if called, also add
|
||||
|
|
@ -308,6 +372,8 @@ func (p *Parser) prop() (Node, error) {
|
|||
v,
|
||||
args,
|
||||
true,
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -316,6 +382,7 @@ func (p *Parser) prop() (Node, error) {
|
|||
}
|
||||
|
||||
func (p *Parser) product() (Node, error) {
|
||||
start := p.curr.Start
|
||||
left, err := p.prop()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -337,6 +404,8 @@ func (p *Parser) product() (Node, error) {
|
|||
op,
|
||||
left,
|
||||
f,
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -344,6 +413,8 @@ func (p *Parser) product() (Node, error) {
|
|||
}
|
||||
|
||||
func (p *Parser) term() (Node, error) {
|
||||
start := p.curr.Start
|
||||
|
||||
left, err := p.product()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -365,6 +436,8 @@ func (p *Parser) term() (Node, error) {
|
|||
op,
|
||||
left,
|
||||
pr,
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -372,6 +445,7 @@ func (p *Parser) term() (Node, error) {
|
|||
}
|
||||
|
||||
func (p *Parser) comparison() (Node, error) {
|
||||
start := p.curr.Start
|
||||
left, err := p.term()
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -409,10 +483,13 @@ func (p *Parser) comparison() (Node, error) {
|
|||
op,
|
||||
left,
|
||||
t,
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Parser) condition() (Node, error) {
|
||||
start := p.curr.Start
|
||||
left, err := p.comparison()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -440,12 +517,15 @@ func (p *Parser) condition() (Node, error) {
|
|||
op,
|
||||
left,
|
||||
c,
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Parser) statement() (Node, error) {
|
||||
switch (*p.curr).Type {
|
||||
case TokenIf:
|
||||
start := p.curr.Start
|
||||
p.advance()
|
||||
|
||||
condition, err := p.condition()
|
||||
|
|
@ -476,15 +556,20 @@ func (p *Parser) statement() (Node, error) {
|
|||
condition,
|
||||
then,
|
||||
otherwise,
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}, nil
|
||||
|
||||
case TokenName:
|
||||
p.advance()
|
||||
start := p.prev.Start
|
||||
name := (*p.prev).Lexeme
|
||||
|
||||
if (*p.curr).Type == TokenDot {
|
||||
var v Node = &ReferenceNode{
|
||||
name,
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}
|
||||
|
||||
// parse chains of prop-getting ( "".split().join().length.round() )
|
||||
|
|
@ -497,6 +582,8 @@ func (p *Parser) statement() (Node, error) {
|
|||
v = &AccessNode{
|
||||
v,
|
||||
property,
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}
|
||||
|
||||
// if called, also add
|
||||
|
|
@ -510,6 +597,8 @@ func (p *Parser) statement() (Node, error) {
|
|||
v,
|
||||
args,
|
||||
(*p.curr).Type == TokenDot, // if the chain is continued, keep the value.
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -524,9 +613,13 @@ func (p *Parser) statement() (Node, error) {
|
|||
return &CallNode{
|
||||
&ReferenceNode{
|
||||
name,
|
||||
start,
|
||||
start + Pos(len(name)),
|
||||
},
|
||||
args,
|
||||
false,
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}, nil
|
||||
} else if p.accept(TokenAssign) || p.accept(TokenDeclare) {
|
||||
isDeclaration := p.prev.Type == TokenDeclare
|
||||
|
|
@ -539,6 +632,8 @@ func (p *Parser) statement() (Node, error) {
|
|||
name,
|
||||
c,
|
||||
isDeclaration,
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}, nil
|
||||
} else {
|
||||
return p.condition()
|
||||
|
|
@ -546,6 +641,7 @@ func (p *Parser) statement() (Node, error) {
|
|||
|
||||
case TokenImport:
|
||||
p.advance()
|
||||
start := p.prev.Start
|
||||
|
||||
if err := p.expect(TokenString); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -555,11 +651,15 @@ func (p *Parser) statement() (Node, error) {
|
|||
|
||||
return &ImportNode{
|
||||
path,
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}, nil
|
||||
|
||||
case TokenFunc:
|
||||
p.advance()
|
||||
|
||||
funcStart := p.prev.Start
|
||||
|
||||
if err := p.expect(TokenName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -570,7 +670,13 @@ func (p *Parser) statement() (Node, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
yield, err := p.parseSignature()
|
||||
var yield TypeSignature = &NilSignature{}
|
||||
if p.curr.Type != TokenOpenBrace {
|
||||
yield, err = p.parseSignature()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
b, err := p.block(false)
|
||||
if err != nil {
|
||||
|
|
@ -584,12 +690,17 @@ func (p *Parser) statement() (Node, error) {
|
|||
params,
|
||||
yield,
|
||||
b,
|
||||
funcStart,
|
||||
p.prev.Start + p.prev.Length,
|
||||
},
|
||||
true,
|
||||
funcStart,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}, nil
|
||||
|
||||
case TokenWhile:
|
||||
p.advance()
|
||||
start := p.prev.Start
|
||||
|
||||
c, err := p.condition()
|
||||
if err != nil {
|
||||
|
|
@ -604,10 +715,13 @@ func (p *Parser) statement() (Node, error) {
|
|||
return &LoopNode{
|
||||
c,
|
||||
b,
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}, nil
|
||||
|
||||
case TokenReturn:
|
||||
p.advance()
|
||||
start := p.prev.Start
|
||||
|
||||
c, err := p.condition()
|
||||
if err != nil {
|
||||
|
|
@ -616,6 +730,8 @@ func (p *Parser) statement() (Node, error) {
|
|||
|
||||
return &ReturnNode{
|
||||
c,
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}, nil
|
||||
|
||||
case TokenBreakpoint:
|
||||
|
|
@ -641,6 +757,8 @@ func (p *Parser) block(canBeStatement bool) (Node, error) {
|
|||
}
|
||||
}
|
||||
|
||||
start := p.prev.Start
|
||||
|
||||
statements := make([]Node, 0)
|
||||
|
||||
for !p.accept(TokenCloseBrace) {
|
||||
|
|
@ -655,6 +773,8 @@ func (p *Parser) block(canBeStatement bool) (Node, error) {
|
|||
|
||||
return &BlockNode{
|
||||
statements,
|
||||
start,
|
||||
p.prev.Start + p.prev.Length,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -740,6 +860,8 @@ func (p *Parser) parseParams() ([]FunctionParameter, error) {
|
|||
}
|
||||
|
||||
func (p *Parser) parseSignature() (TypeSignature, error) {
|
||||
var s TypeSignature
|
||||
|
||||
if p.accept(TokenFunc) {
|
||||
if err := p.expect(TokenCloseParenthesis); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -766,10 +888,10 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return &FunctionSignature{
|
||||
s = &FunctionSignature{
|
||||
in,
|
||||
out,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
if err := p.expect(TokenName); err != nil {
|
||||
|
|
@ -779,11 +901,11 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
|
|||
|
||||
switch name {
|
||||
case "string":
|
||||
return &StringSignature{}, nil
|
||||
s = &StringSignature{}
|
||||
case "number":
|
||||
return &NumberSignature{}, nil
|
||||
s = &NumberSignature{}
|
||||
case "boolean":
|
||||
return &BooleanSignature{}, nil
|
||||
s = &BooleanSignature{}
|
||||
case "list":
|
||||
if err := p.expect(TokenOpenBracket); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -798,10 +920,28 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return &ListSignature{
|
||||
s = &ListSignature{
|
||||
contents,
|
||||
}
|
||||
|
||||
case "any":
|
||||
s = &AnySignature{}
|
||||
}
|
||||
|
||||
if p.accept(TokenPipe) {
|
||||
other, err := p.parseSignature()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CompositeSignature{
|
||||
s,
|
||||
other,
|
||||
}, nil
|
||||
}
|
||||
|
||||
panic("unsupported type: " + name)
|
||||
if s == nil {
|
||||
return nil, p.error("unsupported type: "+name, p.prev)
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,14 +68,19 @@ func GetTokenTestData() map[string]TokenTestData {
|
|||
BinaryAddition,
|
||||
&NumberNode{
|
||||
1,
|
||||
0, 0,
|
||||
},
|
||||
&NumberNode{
|
||||
2,
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
false,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
"assignment": {
|
||||
|
|
@ -92,10 +97,13 @@ func GetTokenTestData() map[string]TokenTestData {
|
|||
&StringNode{
|
||||
"Hello world!",
|
||||
"\"Hello world!\"",
|
||||
0, 0,
|
||||
},
|
||||
false,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
"declaration": {
|
||||
|
|
@ -115,14 +123,19 @@ func GetTokenTestData() map[string]TokenTestData {
|
|||
BinaryAddition,
|
||||
&NumberNode{
|
||||
1,
|
||||
0, 0,
|
||||
},
|
||||
&ReferenceNode{
|
||||
"b",
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
true,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
// (2 + 1) * 5 + 3 / (6 - 2) - 10 / 2
|
||||
|
|
@ -169,30 +182,63 @@ func GetTokenTestData() map[string]TokenTestData {
|
|||
BinaryMultiplication,
|
||||
&BinaryNode{
|
||||
BinaryAddition,
|
||||
&NumberNode{2},
|
||||
&NumberNode{1},
|
||||
&NumberNode{
|
||||
2,
|
||||
0, 0,
|
||||
},
|
||||
&NumberNode{
|
||||
1,
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
&NumberNode{5},
|
||||
&NumberNode{
|
||||
5,
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
&BinaryNode{
|
||||
BinaryDivision,
|
||||
&NumberNode{3},
|
||||
&NumberNode{
|
||||
3,
|
||||
0, 0,
|
||||
},
|
||||
&BinaryNode{
|
||||
BinarySubtraction,
|
||||
&NumberNode{6},
|
||||
&NumberNode{2},
|
||||
&NumberNode{
|
||||
6,
|
||||
0, 0,
|
||||
},
|
||||
&NumberNode{
|
||||
2,
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
&BinaryNode{
|
||||
BinaryDivision,
|
||||
&NumberNode{10},
|
||||
&NumberNode{2},
|
||||
&NumberNode{
|
||||
10,
|
||||
0, 0,
|
||||
},
|
||||
&NumberNode{
|
||||
2,
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
false,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
"condition_equal": {
|
||||
|
|
@ -212,14 +258,19 @@ func GetTokenTestData() map[string]TokenTestData {
|
|||
BinaryEquality,
|
||||
&NumberNode{
|
||||
20,
|
||||
0, 0,
|
||||
},
|
||||
&NumberNode{
|
||||
15,
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
false,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
"if_statement": {
|
||||
|
|
@ -242,10 +293,13 @@ func GetTokenTestData() map[string]TokenTestData {
|
|||
BinaryEquality,
|
||||
&ReferenceNode{
|
||||
"a",
|
||||
0, 0,
|
||||
},
|
||||
&NumberNode{
|
||||
0,
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
do: &BlockNode{
|
||||
[]Node{
|
||||
|
|
@ -253,13 +307,17 @@ func GetTokenTestData() map[string]TokenTestData {
|
|||
"b",
|
||||
&NumberNode{
|
||||
1,
|
||||
0, 0,
|
||||
},
|
||||
false,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
"if_else_statement": {
|
||||
|
|
@ -288,10 +346,13 @@ func GetTokenTestData() map[string]TokenTestData {
|
|||
BinaryEquality,
|
||||
&ReferenceNode{
|
||||
"a",
|
||||
0, 0,
|
||||
},
|
||||
&NumberNode{
|
||||
0,
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
do: &BlockNode{
|
||||
[]Node{
|
||||
|
|
@ -299,10 +360,13 @@ func GetTokenTestData() map[string]TokenTestData {
|
|||
"b",
|
||||
&NumberNode{
|
||||
1,
|
||||
0, 0,
|
||||
},
|
||||
false,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
otherwise: &BlockNode{
|
||||
[]Node{
|
||||
|
|
@ -310,13 +374,17 @@ func GetTokenTestData() map[string]TokenTestData {
|
|||
"b",
|
||||
&NumberNode{
|
||||
0,
|
||||
0, 0,
|
||||
},
|
||||
false,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
"empty_block": {
|
||||
|
|
@ -329,8 +397,10 @@ func GetTokenTestData() map[string]TokenTestData {
|
|||
[]Node{
|
||||
&BlockNode{
|
||||
[]Node{},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
"lambda": { // a := func(a, b) { return a + b }
|
||||
|
|
@ -382,18 +452,26 @@ func GetTokenTestData() map[string]TokenTestData {
|
|||
BinaryAddition,
|
||||
&ReferenceNode{
|
||||
"a",
|
||||
0, 0,
|
||||
},
|
||||
&ReferenceNode{
|
||||
"b",
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
true,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
"function_declaration": {
|
||||
|
|
@ -439,18 +517,26 @@ func GetTokenTestData() map[string]TokenTestData {
|
|||
BinaryAddition,
|
||||
&ReferenceNode{
|
||||
"a",
|
||||
0, 0,
|
||||
},
|
||||
&ReferenceNode{
|
||||
"b",
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
true,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
"prop_getting": {
|
||||
|
|
@ -470,12 +556,16 @@ func GetTokenTestData() map[string]TokenTestData {
|
|||
&AccessNode{
|
||||
&ReferenceNode{
|
||||
"a",
|
||||
0, 0,
|
||||
},
|
||||
"b",
|
||||
0, 0,
|
||||
},
|
||||
true,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
"list_init": {
|
||||
|
|
@ -515,27 +605,41 @@ func GetTokenTestData() map[string]TokenTestData {
|
|||
[]Node{
|
||||
&ReferenceNode{
|
||||
"a",
|
||||
0, 0,
|
||||
},
|
||||
&NumberNode{
|
||||
3.141,
|
||||
0, 0,
|
||||
},
|
||||
&StringNode{
|
||||
"Hello world!",
|
||||
"\"Hello world!\"",
|
||||
0, 0,
|
||||
},
|
||||
&BooleanNode{
|
||||
true,
|
||||
0, 0,
|
||||
},
|
||||
&ListNode{
|
||||
[]Node{
|
||||
&NumberNode{2}, &NumberNode{3},
|
||||
&NumberNode{
|
||||
2,
|
||||
0, 0,
|
||||
}, &NumberNode{
|
||||
3,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
true,
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
0, 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -676,10 +780,10 @@ func NodeEquality(t *testing.T, n1 Node, n2 Node) {
|
|||
}
|
||||
|
||||
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)
|
||||
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)
|
||||
}
|
||||
|
|
@ -782,6 +886,10 @@ func SerializeTokens(tokens []Token) string {
|
|||
out.WriteString("breakpoint")
|
||||
case TokenEOF:
|
||||
out.WriteString(fmt.Sprintf("<error: \"%s\">", token.Lexeme))
|
||||
case TokenHexadecimal:
|
||||
out.WriteString(token.Lexeme)
|
||||
case TokenPipe:
|
||||
out.WriteString(" | ")
|
||||
case TokenError:
|
||||
}
|
||||
}
|
||||
|
|
|
|||
153
core/types.go
153
core/types.go
|
|
@ -1,6 +1,9 @@
|
|||
package core
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Type int
|
||||
|
||||
|
|
@ -13,50 +16,34 @@ const (
|
|||
TypeObject
|
||||
TypeFunction
|
||||
TypeAny
|
||||
TypeComposite
|
||||
)
|
||||
|
||||
func (t Type) String() string {
|
||||
switch t {
|
||||
case TypeString:
|
||||
return "String"
|
||||
return "string"
|
||||
case TypeNumber:
|
||||
return "Number"
|
||||
return "number"
|
||||
case TypeBoolean:
|
||||
return "Boolean"
|
||||
return "boolean"
|
||||
case TypeNil:
|
||||
return "Nil"
|
||||
return "nil"
|
||||
case TypeList:
|
||||
return "List"
|
||||
return "list"
|
||||
case TypeObject:
|
||||
return "Object"
|
||||
return "object"
|
||||
case TypeFunction:
|
||||
return "Function"
|
||||
return "func"
|
||||
case TypeAny:
|
||||
return "any"
|
||||
case TypeComposite:
|
||||
return "composite"
|
||||
}
|
||||
|
||||
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:
|
||||
|
|
@ -83,6 +70,9 @@ type TypeSignature interface {
|
|||
|
||||
// Matches check if this type signature matches another.
|
||||
Matches(TypeSignature) bool
|
||||
|
||||
// String create a human-readable string version of the value type.
|
||||
String() string
|
||||
}
|
||||
|
||||
type NilSignature struct{}
|
||||
|
|
@ -91,40 +81,72 @@ func (*NilSignature) Type() Type {
|
|||
return TypeNil
|
||||
}
|
||||
|
||||
func (*NilSignature) Matches(other TypeSignature) bool {
|
||||
func (s *NilSignature) Matches(other TypeSignature) bool {
|
||||
if other.Type() == TypeComposite {
|
||||
return other.Matches(s)
|
||||
}
|
||||
|
||||
return other.Type() == TypeAny || other.Type() == TypeNil
|
||||
}
|
||||
|
||||
func (*NilSignature) String() string {
|
||||
return "nil"
|
||||
}
|
||||
|
||||
type StringSignature struct{}
|
||||
|
||||
func (*StringSignature) Type() Type {
|
||||
return TypeString
|
||||
}
|
||||
|
||||
func (*StringSignature) Matches(other TypeSignature) bool {
|
||||
func (s *StringSignature) Matches(other TypeSignature) bool {
|
||||
if other.Type() == TypeComposite {
|
||||
return other.Matches(s)
|
||||
}
|
||||
|
||||
return other.Type() == TypeAny || other.Type() == TypeString
|
||||
}
|
||||
|
||||
func (*StringSignature) String() string {
|
||||
return "string"
|
||||
}
|
||||
|
||||
type NumberSignature struct{}
|
||||
|
||||
func (*NumberSignature) Type() Type {
|
||||
return TypeNumber
|
||||
}
|
||||
|
||||
func (*NumberSignature) Matches(other TypeSignature) bool {
|
||||
func (s *NumberSignature) Matches(other TypeSignature) bool {
|
||||
if other.Type() == TypeComposite {
|
||||
return other.Matches(s)
|
||||
}
|
||||
|
||||
return other.Type() == TypeAny || other.Type() == TypeNumber
|
||||
}
|
||||
|
||||
func (*NumberSignature) String() string {
|
||||
return "number"
|
||||
}
|
||||
|
||||
type BooleanSignature struct{}
|
||||
|
||||
func (*BooleanSignature) Type() Type {
|
||||
return TypeBoolean
|
||||
}
|
||||
|
||||
func (*BooleanSignature) Matches(other TypeSignature) bool {
|
||||
func (s *BooleanSignature) Matches(other TypeSignature) bool {
|
||||
if other.Type() == TypeComposite {
|
||||
return other.Matches(s)
|
||||
}
|
||||
|
||||
return other.Type() == TypeAny || other.Type() == TypeBoolean
|
||||
}
|
||||
|
||||
func (*BooleanSignature) String() string {
|
||||
return "boolean"
|
||||
}
|
||||
|
||||
type ListSignature struct {
|
||||
contents TypeSignature
|
||||
}
|
||||
|
|
@ -134,7 +156,15 @@ func (*ListSignature) Type() Type {
|
|||
}
|
||||
|
||||
func (s *ListSignature) Matches(other TypeSignature) bool {
|
||||
return other.Type() == TypeAny || other.Type() == TypeList && other.(*ListSignature).contents.Matches(s.contents)
|
||||
if other.Type() == TypeComposite {
|
||||
return other.Matches(s)
|
||||
}
|
||||
|
||||
return other.Type() == TypeAny || (other.Type() == TypeList && other.(*ListSignature).contents.Matches(s.contents))
|
||||
}
|
||||
|
||||
func (s *ListSignature) String() string {
|
||||
return fmt.Sprintf("list[%s]", s.contents)
|
||||
}
|
||||
|
||||
type ObjectSignature struct {
|
||||
|
|
@ -146,6 +176,10 @@ func (*ObjectSignature) Type() Type {
|
|||
}
|
||||
|
||||
func (s *ObjectSignature) Matches(other TypeSignature) bool {
|
||||
if other.Type() == TypeComposite {
|
||||
return other.Matches(s)
|
||||
}
|
||||
|
||||
if other.Type() == TypeAny {
|
||||
return true
|
||||
}
|
||||
|
|
@ -175,6 +209,10 @@ func (s *ObjectSignature) Matches(other TypeSignature) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (s *ObjectSignature) String() string {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
type FunctionSignature struct {
|
||||
in []TypeSignature
|
||||
out TypeSignature
|
||||
|
|
@ -185,6 +223,10 @@ func (*FunctionSignature) Type() Type {
|
|||
}
|
||||
|
||||
func (s *FunctionSignature) Matches(other TypeSignature) bool {
|
||||
if other.Type() == TypeComposite {
|
||||
return other.Matches(s)
|
||||
}
|
||||
|
||||
if other.Type() == TypeAny {
|
||||
return true
|
||||
}
|
||||
|
|
@ -213,12 +255,51 @@ func (s *FunctionSignature) Matches(other TypeSignature) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (s *FunctionSignature) String() string {
|
||||
b := strings.Builder{}
|
||||
|
||||
b.WriteString("func(")
|
||||
|
||||
for i, t := range s.in {
|
||||
if i > 0 {
|
||||
b.WriteString(", ")
|
||||
}
|
||||
b.WriteString(t.String())
|
||||
}
|
||||
|
||||
b.WriteString(") ")
|
||||
b.WriteString(s.out.String())
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
type AnySignature struct{}
|
||||
|
||||
func (AnySignature) Type() Type {
|
||||
func (*AnySignature) Type() Type {
|
||||
return TypeAny
|
||||
}
|
||||
|
||||
func (AnySignature) Matches(_ TypeSignature) bool {
|
||||
func (*AnySignature) Matches(_ TypeSignature) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (*AnySignature) String() string {
|
||||
return "any"
|
||||
}
|
||||
|
||||
type CompositeSignature struct {
|
||||
A TypeSignature
|
||||
B TypeSignature
|
||||
}
|
||||
|
||||
func (*CompositeSignature) Type() Type {
|
||||
return TypeComposite
|
||||
}
|
||||
|
||||
func (s *CompositeSignature) Matches(other TypeSignature) bool {
|
||||
return s.A.Matches(other) || s.B.Matches(other)
|
||||
}
|
||||
|
||||
func (s *CompositeSignature) String() string {
|
||||
return fmt.Sprintf("%s|%s", s.A, s.B)
|
||||
}
|
||||
|
|
|
|||
141
core/values.go
141
core/values.go
|
|
@ -106,6 +106,9 @@ type Value interface {
|
|||
|
||||
// Get a member from the value. An error is returned if the member does not exist
|
||||
Get(string) (Value, error)
|
||||
|
||||
// Clone create a clone of the value. The returned value is a pointer to a new value of the same type as the value.
|
||||
Clone() Value
|
||||
}
|
||||
|
||||
type NilValue struct{}
|
||||
|
|
@ -130,8 +133,12 @@ func (v *NilValue) Get(_ string) (Value, error) {
|
|||
return nil, errors.New("nil has no properties")
|
||||
}
|
||||
|
||||
func (v *NilValue) Clone() Value {
|
||||
return &NilValue{}
|
||||
}
|
||||
|
||||
type BoolValue struct {
|
||||
bool
|
||||
Boolean bool
|
||||
}
|
||||
|
||||
func (v *BoolValue) Type() ValueType {
|
||||
|
|
@ -139,7 +146,7 @@ func (v *BoolValue) Type() ValueType {
|
|||
}
|
||||
|
||||
func (v *BoolValue) String() string {
|
||||
if v.bool {
|
||||
if v.Boolean {
|
||||
return "true"
|
||||
} else {
|
||||
return "false"
|
||||
|
|
@ -151,16 +158,22 @@ func (v *BoolValue) DebugString() string {
|
|||
}
|
||||
|
||||
func (v *BoolValue) Equals(other Value) bool {
|
||||
return other.Type() == BoolValueType && other.(*BoolValue).bool == v.bool
|
||||
return other.Type() == BoolValueType && other.(*BoolValue).Boolean == v.Boolean
|
||||
}
|
||||
|
||||
func (v *BoolValue) Get(_ string) (Value, error) {
|
||||
return nil, errors.New("booleans have no properties")
|
||||
}
|
||||
|
||||
func (v *BoolValue) Clone() Value {
|
||||
return &BoolValue{
|
||||
v.Boolean,
|
||||
}
|
||||
}
|
||||
|
||||
// ObjectValue An object with any number of members (key-value pairs)
|
||||
type ObjectValue struct {
|
||||
members map[string]Value
|
||||
Members map[string]Value
|
||||
}
|
||||
|
||||
func (v *ObjectValue) Type() ValueType {
|
||||
|
|
@ -169,7 +182,7 @@ func (v *ObjectValue) Type() ValueType {
|
|||
|
||||
func (v *ObjectValue) String() string {
|
||||
out := "{"
|
||||
for key, value := range v.members {
|
||||
for key, value := range v.Members {
|
||||
if out != "{" {
|
||||
out += ", "
|
||||
}
|
||||
|
|
@ -191,8 +204,8 @@ func (v *ObjectValue) Equals(other Value) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
for key, value := range v.members {
|
||||
if !object.members[key].Equals(value) {
|
||||
for key, value := range v.Members {
|
||||
if !object.Members[key].Equals(value) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -216,7 +229,7 @@ var ObjectPrototype = map[string]Value{
|
|||
return nil, errors.New("property is not a string")
|
||||
}
|
||||
|
||||
this.members[v.string] = p
|
||||
this.Members[v.Text] = p
|
||||
|
||||
return &NilValue{}, nil
|
||||
},
|
||||
|
|
@ -225,7 +238,7 @@ var ObjectPrototype = map[string]Value{
|
|||
}
|
||||
|
||||
func (v *ObjectValue) Get(key string) (Value, error) {
|
||||
if member, ok := v.members[key]; ok {
|
||||
if member, ok := v.Members[key]; ok {
|
||||
return member, nil
|
||||
} else if p, ok := ObjectPrototype[key]; ok {
|
||||
return p, nil
|
||||
|
|
@ -234,9 +247,21 @@ func (v *ObjectValue) Get(key string) (Value, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func (v *ObjectValue) Clone() Value {
|
||||
m := make(map[string]Value, len(v.Members))
|
||||
|
||||
for name, mem := range v.Members {
|
||||
m[name] = mem.Clone()
|
||||
}
|
||||
|
||||
return &ObjectValue{
|
||||
m,
|
||||
}
|
||||
}
|
||||
|
||||
// NumberValue Integer or floating-point values
|
||||
type NumberValue struct {
|
||||
float64
|
||||
Number float64
|
||||
}
|
||||
|
||||
const NumberSize int = 64
|
||||
|
|
@ -246,7 +271,7 @@ func (v *NumberValue) Type() ValueType {
|
|||
}
|
||||
|
||||
func (v *NumberValue) String() string {
|
||||
return strconv.FormatFloat(v.float64, 'g', -1, NumberSize)
|
||||
return strconv.FormatFloat(v.Number, 'g', -1, NumberSize)
|
||||
}
|
||||
|
||||
func (v *NumberValue) DebugString() string {
|
||||
|
|
@ -254,7 +279,7 @@ func (v *NumberValue) DebugString() string {
|
|||
}
|
||||
|
||||
func (v *NumberValue) Equals(other Value) bool {
|
||||
return other.Type() == NumberValueType && other.(*NumberValue).float64 == v.float64
|
||||
return other.Type() == NumberValueType && other.(*NumberValue).Number == v.Number
|
||||
}
|
||||
|
||||
func (v *NumberValue) Get(_ string) (Value, error) {
|
||||
|
|
@ -262,8 +287,14 @@ func (v *NumberValue) Get(_ string) (Value, error) {
|
|||
return nil, errors.New("numbers have no properties")
|
||||
}
|
||||
|
||||
func (v *NumberValue) Clone() Value {
|
||||
return &NumberValue{
|
||||
v.Number,
|
||||
}
|
||||
}
|
||||
|
||||
type StringValue struct {
|
||||
string
|
||||
Text string
|
||||
}
|
||||
|
||||
func (v *StringValue) Type() ValueType {
|
||||
|
|
@ -271,7 +302,7 @@ func (v *StringValue) Type() ValueType {
|
|||
}
|
||||
|
||||
func (v *StringValue) String() string {
|
||||
return v.string
|
||||
return v.Text
|
||||
}
|
||||
|
||||
func (v *StringValue) DebugString() string {
|
||||
|
|
@ -279,7 +310,7 @@ func (v *StringValue) DebugString() string {
|
|||
}
|
||||
|
||||
func (v *StringValue) Equals(other Value) bool {
|
||||
return other.Type() == StringValueType && other.(*StringValue).string == v.string
|
||||
return other.Type() == StringValueType && other.(*StringValue).Text == v.Text
|
||||
}
|
||||
|
||||
var StringPrototype = map[string]*BuiltinFunctionValue{
|
||||
|
|
@ -318,9 +349,15 @@ func (v *StringValue) Get(key string) (Value, error) {
|
|||
return nil, errors.New(fmt.Sprintf("string has no property \"%s\"", key))
|
||||
}
|
||||
|
||||
func (v *StringValue) Clone() Value {
|
||||
return &StringValue{
|
||||
v.Text,
|
||||
}
|
||||
}
|
||||
|
||||
// ListValue a dynamic list of values
|
||||
type ListValue struct {
|
||||
items []Value
|
||||
Items []Value
|
||||
}
|
||||
|
||||
func (v *ListValue) Type() ValueType {
|
||||
|
|
@ -329,7 +366,7 @@ func (v *ListValue) Type() ValueType {
|
|||
|
||||
func (v *ListValue) String() string {
|
||||
out := "["
|
||||
for i, item := range v.items {
|
||||
for i, item := range v.Items {
|
||||
if i != 0 {
|
||||
out += ", "
|
||||
}
|
||||
|
|
@ -351,12 +388,12 @@ func (v *ListValue) Equals(other Value) bool {
|
|||
|
||||
l := other.(*ListValue)
|
||||
|
||||
if len(v.items) != len(l.items) {
|
||||
if len(v.Items) != len(l.Items) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, item := range l.items {
|
||||
if !item.Equals(l.items[i]) {
|
||||
for i, item := range l.Items {
|
||||
if !item.Equals(l.Items[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -372,7 +409,7 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
|
|||
&NilSignature{},
|
||||
},
|
||||
func(_ *VM, this Value, v []Value) (Value, error) {
|
||||
this.(*ListValue).items = append(this.(*ListValue).items, v[0])
|
||||
this.(*ListValue).Items = append(this.(*ListValue).Items, v[0])
|
||||
return &NilValue{}, nil
|
||||
},
|
||||
nil,
|
||||
|
|
@ -386,8 +423,8 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
|
|||
&AnySignature{},
|
||||
},
|
||||
func(_ *VM, this Value, p []Value) (Value, error) {
|
||||
items := this.(*ListValue).items
|
||||
index := int(p[0].(*NumberValue).float64)
|
||||
items := this.(*ListValue).Items
|
||||
index := int(p[0].(*NumberValue).Number)
|
||||
|
||||
if index >= len(items) {
|
||||
return nil, errors.New(fmt.Sprintf("list index %x out of range", index))
|
||||
|
|
@ -404,7 +441,7 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
|
|||
&NumberSignature{},
|
||||
},
|
||||
func(_ *VM, this Value, _ []Value) (Value, error) {
|
||||
return GoToValue(len(this.(*ListValue).items)), nil
|
||||
return GoToValue(len(this.(*ListValue).Items)), nil
|
||||
},
|
||||
nil,
|
||||
},
|
||||
|
|
@ -426,18 +463,16 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
|
|||
|
||||
v := m[0]
|
||||
var f Value
|
||||
f, ok := v.(*FunctionValue)
|
||||
if !ok {
|
||||
f, ok = v.(*BuiltinFunctionValue)
|
||||
|
||||
if !ok {
|
||||
return nil, errors.New(fmt.Sprintf("not a function to apply: %s", v))
|
||||
}
|
||||
switch a := v.(type) {
|
||||
case *FunctionValue, *BuiltinFunctionValue:
|
||||
f = a
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintf("not a function to apply: %s", v))
|
||||
}
|
||||
|
||||
for i, item := range list.items {
|
||||
for i, item := range list.Items {
|
||||
var err error
|
||||
list.items[i], err = vm.Call(f, []Value{
|
||||
list.Items[i], err = vm.Call(f, []Value{
|
||||
item,
|
||||
})
|
||||
|
||||
|
|
@ -470,7 +505,7 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
|
|||
f := m[0]
|
||||
sum := m[1]
|
||||
|
||||
for _, v := range list.items {
|
||||
for _, v := range list.Items {
|
||||
result, err := vm.Call(f, []Value{sum, v})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -492,6 +527,18 @@ func (v *ListValue) Get(key string) (Value, error) {
|
|||
return nil, errors.New(fmt.Sprintf("list has no property \"%s\"", key))
|
||||
}
|
||||
|
||||
func (v *ListValue) Clone() Value {
|
||||
n := make([]Value, len(v.Items))
|
||||
|
||||
for i, item := range v.Items {
|
||||
n[i] = item.Clone()
|
||||
}
|
||||
|
||||
return &ListValue{
|
||||
n,
|
||||
}
|
||||
}
|
||||
|
||||
type FunctionValue struct {
|
||||
Name string
|
||||
Params []FunctionParameter
|
||||
|
|
@ -521,6 +568,15 @@ func (v *FunctionValue) Get(_ string) (Value, error) {
|
|||
return nil, errors.New("functions have no properties")
|
||||
}
|
||||
|
||||
func (v *FunctionValue) Clone() Value {
|
||||
return &FunctionValue{
|
||||
v.Name,
|
||||
v.Params,
|
||||
v.Chunk,
|
||||
v.Parent,
|
||||
}
|
||||
}
|
||||
|
||||
type BuiltinFunctionValue struct {
|
||||
Name string
|
||||
Signature *FunctionSignature
|
||||
|
|
@ -549,6 +605,15 @@ func (v *BuiltinFunctionValue) Get(_ string) (Value, error) {
|
|||
return nil, errors.New("functions have no properties")
|
||||
}
|
||||
|
||||
func (v *BuiltinFunctionValue) Clone() Value {
|
||||
return &BuiltinFunctionValue{
|
||||
v.Name,
|
||||
v.Signature,
|
||||
v.F,
|
||||
v.Parent,
|
||||
}
|
||||
}
|
||||
|
||||
// VariableValue a value wrapper for variables kept on the stack
|
||||
type VariableValue struct {
|
||||
name string
|
||||
|
|
@ -580,3 +645,11 @@ func (v *VariableValue) Equals(other Value) bool {
|
|||
func (v *VariableValue) Get(_ string) (Value, error) {
|
||||
return nil, errors.New("variables have no properties")
|
||||
}
|
||||
|
||||
func (v *VariableValue) Clone() Value {
|
||||
return &VariableValue{
|
||||
v.name,
|
||||
v.value.Clone(),
|
||||
v.scope,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,19 +16,19 @@ func CompareValues(t *testing.T, got Value, want Value) {
|
|||
t.Logf("Both are nil")
|
||||
return
|
||||
case BoolValueType:
|
||||
if got.(*BoolValue).bool != want.(*BoolValue).bool {
|
||||
if got.(*BoolValue).Boolean != want.(*BoolValue).Boolean {
|
||||
t.Errorf("bool value mismatch: got %v, want %v", got.(*BoolValue), want.(*BoolValue))
|
||||
} else {
|
||||
t.Logf("Both are same boolean (%s)", want.(*BoolValue).String())
|
||||
}
|
||||
case NumberValueType:
|
||||
if got.(*NumberValue).float64 != want.(*NumberValue).float64 {
|
||||
if got.(*NumberValue).Number != want.(*NumberValue).Number {
|
||||
t.Errorf("number value mismatch: got %v, want %v", got.(*NumberValue), want.(*NumberValue))
|
||||
} else {
|
||||
t.Logf("Both are same number (%s)", got.(*NumberValue).String())
|
||||
}
|
||||
case StringValueType:
|
||||
if got.(*StringValue).string != want.(*StringValue).string {
|
||||
if got.(*StringValue).Text != want.(*StringValue).Text {
|
||||
t.Errorf("string value mismatch: got %v, want %v", got.(*StringValue), want.(*StringValue))
|
||||
} else {
|
||||
t.Logf("Both are same string (%s)", got.(*StringValue).String())
|
||||
|
|
|
|||
162
core/vm.go
162
core/vm.go
|
|
@ -6,6 +6,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
|
@ -26,6 +27,8 @@ const (
|
|||
InstructionMul
|
||||
// InstructionDiv pop two and divide the second by the first
|
||||
InstructionDiv
|
||||
// InstructionNegate negate the value; if it was positive, make it negative, and vice versa.
|
||||
InstructionNegate
|
||||
// InstructionEquals whether the two top values on the stack are equal
|
||||
InstructionEquals
|
||||
// InstructionNotEqual whether the two top values on the stack are not equal
|
||||
|
|
@ -118,6 +121,8 @@ func (b Bytecode) String() string {
|
|||
return "MUL"
|
||||
case InstructionDiv:
|
||||
return "DIV"
|
||||
case InstructionNegate:
|
||||
return "NEGATE"
|
||||
case InstructionEquals:
|
||||
return "EQUALS"
|
||||
case InstructionNotEqual:
|
||||
|
|
@ -202,7 +207,7 @@ func (c Chunk) String() string {
|
|||
b.WriteString("=-= constants =-=\n")
|
||||
|
||||
for i, ct := range c.Constants {
|
||||
b.WriteString(fmt.Sprintf("c=%d \t%s\n", i, ct))
|
||||
b.WriteString(fmt.Sprintf("c=%d \t%s\n", i, ct.DebugString()))
|
||||
|
||||
f, ok := ct.(*FunctionValue)
|
||||
if ok {
|
||||
|
|
@ -228,6 +233,16 @@ func RegisterGOBTypes() {
|
|||
Params: nil,
|
||||
Chunk: nil,
|
||||
})
|
||||
|
||||
// Signatures
|
||||
gob.Register(&NilSignature{})
|
||||
gob.Register(&NumberSignature{})
|
||||
gob.Register(&StringSignature{})
|
||||
gob.Register(&FunctionSignature{})
|
||||
gob.Register(&ListSignature{})
|
||||
gob.Register(&ObjectSignature{})
|
||||
gob.Register(&BooleanSignature{})
|
||||
|
||||
}
|
||||
|
||||
func (c Chunk) Serialize() []byte {
|
||||
|
|
@ -314,14 +329,47 @@ var DefaultGlobals = map[string]Value{
|
|||
&FunctionSignature{
|
||||
[]TypeSignature{
|
||||
&StringSignature{},
|
||||
&StringSignature{},
|
||||
&ListSignature{
|
||||
&AnySignature{},
|
||||
},
|
||||
},
|
||||
&StringSignature{},
|
||||
},
|
||||
func(vm *VM, value Value, m []Value) (Value, error) {
|
||||
valuies := m[1].(*ListValue).items
|
||||
b := strings.Builder{}
|
||||
template := m[0].(*StringValue).Text
|
||||
valuies := m[1].(*ListValue).Items
|
||||
|
||||
return GoToValue(fmt.Sprintf(m[0].String(), valuies)), nil
|
||||
vi := 0
|
||||
last := 0
|
||||
for i := 0; i < len(template); i++ {
|
||||
if template[i] == '%' {
|
||||
b.WriteString(template[last:i])
|
||||
b.WriteString(valuies[vi].String())
|
||||
vi++
|
||||
last = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
b.WriteString(template[last:])
|
||||
|
||||
return GoToValue(b.String()), nil
|
||||
},
|
||||
nil,
|
||||
},
|
||||
"char": &BuiltinFunctionValue{
|
||||
"char",
|
||||
&FunctionSignature{
|
||||
[]TypeSignature{&NumberSignature{}},
|
||||
&StringSignature{},
|
||||
},
|
||||
func(vm *VM, this Value, args []Value) (Value, error) {
|
||||
n := args[0].(*NumberValue).Number
|
||||
b := byte(n)
|
||||
|
||||
return &StringValue{
|
||||
string([]byte{b}),
|
||||
}, nil
|
||||
},
|
||||
nil,
|
||||
},
|
||||
|
|
@ -367,6 +415,29 @@ var DefaultGlobals = map[string]Value{
|
|||
},
|
||||
nil,
|
||||
},
|
||||
"str": &BuiltinFunctionValue{
|
||||
"str",
|
||||
&FunctionSignature{
|
||||
[]TypeSignature{&AnySignature{}},
|
||||
&StringSignature{},
|
||||
},
|
||||
func(vm *VM, _ Value, args []Value) (Value, error) {
|
||||
return GoToValue(args[0].String()), nil
|
||||
},
|
||||
nil,
|
||||
},
|
||||
"exit": &BuiltinFunctionValue{
|
||||
"exit",
|
||||
&FunctionSignature{
|
||||
[]TypeSignature{&NumberSignature{}},
|
||||
&NilSignature{},
|
||||
},
|
||||
func(vm *VM, this Value, args []Value) (Value, error) {
|
||||
os.Exit(int(args[0].(*NumberValue).Number))
|
||||
return &NilValue{}, nil
|
||||
},
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
func NewVM(chunk *Chunk, stackSize Pos, callstackSize Pos) *VM {
|
||||
|
|
@ -417,29 +488,34 @@ func (vm *VM) Next() bool {
|
|||
vm.stack.Push(vm.ReadConstant())
|
||||
|
||||
case InstructionAdd:
|
||||
r := vm.stack.Pop().(*NumberValue).float64
|
||||
l := vm.stack.Pop().(*NumberValue).float64
|
||||
r := vm.stack.Pop().(*NumberValue).Number
|
||||
l := vm.stack.Pop().(*NumberValue).Number
|
||||
|
||||
vm.stack.Push(&NumberValue{l + r})
|
||||
|
||||
case InstructionSub:
|
||||
r := vm.stack.Pop().(*NumberValue).float64
|
||||
l := vm.stack.Pop().(*NumberValue).float64
|
||||
r := vm.stack.Pop().(*NumberValue).Number
|
||||
l := vm.stack.Pop().(*NumberValue).Number
|
||||
|
||||
vm.stack.Push(&NumberValue{l - r})
|
||||
|
||||
case InstructionMul:
|
||||
r := vm.stack.Pop().(*NumberValue).float64
|
||||
l := vm.stack.Pop().(*NumberValue).float64
|
||||
r := vm.stack.Pop().(*NumberValue).Number
|
||||
l := vm.stack.Pop().(*NumberValue).Number
|
||||
|
||||
vm.stack.Push(&NumberValue{l * r})
|
||||
|
||||
case InstructionDiv:
|
||||
r := vm.stack.Pop().(*NumberValue).float64
|
||||
l := vm.stack.Pop().(*NumberValue).float64
|
||||
r := vm.stack.Pop().(*NumberValue).Number
|
||||
l := vm.stack.Pop().(*NumberValue).Number
|
||||
|
||||
vm.stack.Push(&NumberValue{l / r})
|
||||
|
||||
case InstructionNegate:
|
||||
v := vm.stack.Pop().(*NumberValue).Number
|
||||
|
||||
vm.stack.Push(&NumberValue{-v})
|
||||
|
||||
case InstructionEquals:
|
||||
vm.stack.Push(
|
||||
&BoolValue{vm.stack.Pop().Equals(vm.stack.Pop())},
|
||||
|
|
@ -451,40 +527,40 @@ func (vm *VM) Next() bool {
|
|||
)
|
||||
|
||||
case InstructionNot:
|
||||
b := vm.stack.Pop().(*BoolValue).bool
|
||||
b := vm.stack.Pop().(*BoolValue).Boolean
|
||||
vm.stack.Push(&BoolValue{!b})
|
||||
|
||||
case InstructionAnd:
|
||||
r := vm.stack.Pop().(*BoolValue).bool
|
||||
l := vm.stack.Pop().(*BoolValue).bool
|
||||
r := vm.stack.Pop().(*BoolValue).Boolean
|
||||
l := vm.stack.Pop().(*BoolValue).Boolean
|
||||
vm.stack.Push(&BoolValue{l && r})
|
||||
|
||||
case InstructionOr:
|
||||
r := vm.stack.Pop().(*BoolValue).bool
|
||||
l := vm.stack.Pop().(*BoolValue).bool
|
||||
r := vm.stack.Pop().(*BoolValue).Boolean
|
||||
l := vm.stack.Pop().(*BoolValue).Boolean
|
||||
vm.stack.Push(&BoolValue{l || r})
|
||||
|
||||
case InstructionLess:
|
||||
r := vm.stack.Pop().(*NumberValue).float64
|
||||
l := vm.stack.Pop().(*NumberValue).float64
|
||||
r := vm.stack.Pop().(*NumberValue).Number
|
||||
l := vm.stack.Pop().(*NumberValue).Number
|
||||
|
||||
vm.stack.Push(&BoolValue{l < r})
|
||||
|
||||
case InstructionLessOrEqual:
|
||||
r := vm.stack.Pop().(*NumberValue).float64
|
||||
l := vm.stack.Pop().(*NumberValue).float64
|
||||
r := vm.stack.Pop().(*NumberValue).Number
|
||||
l := vm.stack.Pop().(*NumberValue).Number
|
||||
|
||||
vm.stack.Push(&BoolValue{l <= r})
|
||||
|
||||
case InstructionGreater:
|
||||
r := vm.stack.Pop().(*NumberValue).float64
|
||||
l := vm.stack.Pop().(*NumberValue).float64
|
||||
r := vm.stack.Pop().(*NumberValue).Number
|
||||
l := vm.stack.Pop().(*NumberValue).Number
|
||||
|
||||
vm.stack.Push(&BoolValue{l > r})
|
||||
|
||||
case InstructionGreaterOrEqual:
|
||||
r := vm.stack.Pop().(*NumberValue).float64
|
||||
l := vm.stack.Pop().(*NumberValue).float64
|
||||
r := vm.stack.Pop().(*NumberValue).Number
|
||||
l := vm.stack.Pop().(*NumberValue).Number
|
||||
|
||||
vm.stack.Push(&BoolValue{l >= r})
|
||||
|
||||
|
|
@ -503,7 +579,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].name,
|
||||
f.Params[i].Name,
|
||||
vm.stack.items[p],
|
||||
vm.scope,
|
||||
}
|
||||
|
|
@ -543,12 +619,12 @@ func (vm *VM) Next() bool {
|
|||
|
||||
case InstructionJumpFalse:
|
||||
n := vm.NextU16()
|
||||
if !vm.stack.Pop().(*BoolValue).bool {
|
||||
if !vm.stack.Pop().(*BoolValue).Boolean {
|
||||
vm.ip += Pos(n)
|
||||
}
|
||||
|
||||
case InstructionGetLocal:
|
||||
name := vm.GetConstant(vm.NextByte()).(*StringValue).string
|
||||
name := vm.GetConstant(vm.NextByte()).(*StringValue).Text
|
||||
v := vm.getVar(name)
|
||||
|
||||
if v == nil {
|
||||
|
|
@ -560,7 +636,7 @@ func (vm *VM) Next() bool {
|
|||
|
||||
case InstructionSetLocal:
|
||||
value := vm.stack.Pop().(Value)
|
||||
name := vm.GetConstant(vm.NextByte()).(*StringValue).string
|
||||
name := vm.GetConstant(vm.NextByte()).(*StringValue).Text
|
||||
|
||||
v := vm.getVar(name)
|
||||
|
||||
|
|
@ -568,19 +644,19 @@ func (vm *VM) Next() bool {
|
|||
vm.error(fmt.Sprintf("cannot set local: undefined variable %s", name))
|
||||
}
|
||||
|
||||
v.value = value
|
||||
v.value = value.Clone()
|
||||
|
||||
case InstructionDeclareLocal:
|
||||
vm.addVar(
|
||||
vm.GetConstant(vm.NextByte()).(*StringValue).string,
|
||||
vm.stack.Pop().(Value),
|
||||
vm.GetConstant(vm.NextByte()).(*StringValue).Text,
|
||||
vm.stack.Pop().Clone(),
|
||||
)
|
||||
|
||||
case InstructionGetGlobal:
|
||||
vm.stack.Push(vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).string])
|
||||
vm.stack.Push(vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).Text])
|
||||
|
||||
case InstructionSetGlobal:
|
||||
vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).string] = vm.stack.Pop()
|
||||
vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).Text] = vm.stack.Pop()
|
||||
|
||||
case InstructionTrue:
|
||||
vm.stack.Push(&BoolValue{true})
|
||||
|
|
@ -594,18 +670,22 @@ func (vm *VM) Next() bool {
|
|||
case InstructionFormList:
|
||||
n := int(vm.NextU16())
|
||||
|
||||
items := make([]Value, n+1)
|
||||
for i := 0; i <= n; i++ {
|
||||
items[n-i] = vm.stack.Pop()
|
||||
items := make([]Value, n)
|
||||
for i := n - 1; i >= 0; i-- {
|
||||
items[i] = vm.stack.Pop()
|
||||
}
|
||||
|
||||
vm.stack.Push(&ListValue{
|
||||
items,
|
||||
})
|
||||
|
||||
case InstructionNewList:
|
||||
vm.stack.Push(&ListValue{[]Value{}})
|
||||
|
||||
case InstructionAppend:
|
||||
value := vm.stack.Pop()
|
||||
list := vm.stack.Pop().(*ListValue)
|
||||
list.items = append(list.items, value)
|
||||
list.Items = append(list.Items, value)
|
||||
vm.stack.Push(list)
|
||||
|
||||
case InstructionDescend:
|
||||
|
|
@ -619,8 +699,8 @@ func (vm *VM) Next() bool {
|
|||
vm.stack.Push(&StringValue{v.String()})
|
||||
|
||||
case InstructionStringConcatenation:
|
||||
r := vm.stack.Pop().(*StringValue).string
|
||||
l := vm.stack.Pop().(*StringValue).string
|
||||
r := vm.stack.Pop().(*StringValue).Text
|
||||
l := vm.stack.Pop().(*StringValue).Text
|
||||
|
||||
vm.stack.Push(&StringValue{l + r})
|
||||
|
||||
|
|
@ -669,7 +749,7 @@ func (vm *VM) Call(v Value, args []Value) (Value, error) {
|
|||
})
|
||||
|
||||
for i := 0; i < len(f.Params); i++ {
|
||||
vm.addVar(f.Params[i].name, args[i])
|
||||
vm.addVar(f.Params[i].Name, args[i])
|
||||
}
|
||||
|
||||
if f.Parent != nil {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue