Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
| 677808fa0e |
46 changed files with 520 additions and 1109 deletions
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
14
.idea/anglais.iml
generated
Normal file
14
.idea/anglais.iml
generated
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="Go" enabled="true">
|
||||||
|
<buildTags>
|
||||||
|
<option name="os" value="js" />
|
||||||
|
<option name="arch" value="wasm" />
|
||||||
|
</buildTags>
|
||||||
|
</component>
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
10
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
10
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="GoDfaErrorMayBeNotNil" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<methods>
|
||||||
|
<method importPath="neemek.com/anglais/src" receiver="*Lexer" name="NextToken" />
|
||||||
|
</methods>
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/anglais.iml" filepath="$PROJECT_DIR$/.idea/anglais.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -3,7 +3,6 @@ package core
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -117,7 +116,8 @@ func (e CompilerError) Format() string {
|
||||||
|
|
||||||
b.WriteString("\nsource trace:")
|
b.WriteString("\nsource trace:")
|
||||||
// print import stack trace
|
// print import stack trace
|
||||||
for i, p := range slices.Backward(e.Trace) {
|
for i := len(e.Trace) - 1; i >= 0; i-- {
|
||||||
|
p := e.Trace[i]
|
||||||
b.WriteString(fmt.Sprintf("\n[%d] %s", i, p))
|
b.WriteString(fmt.Sprintf("\n[%d] %s", i, p))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,20 +180,20 @@ func (c *Compiler) Compile(p *Program) (TypeSignature, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func EscapeString(in string) string {
|
func EscapeString(in string) string {
|
||||||
var out strings.Builder
|
out := ""
|
||||||
escaped := false
|
escaped := false
|
||||||
|
|
||||||
for _, ch := range in {
|
for _, ch := range in {
|
||||||
if escaped {
|
if escaped {
|
||||||
switch ch {
|
switch ch {
|
||||||
case 'n':
|
case 'n':
|
||||||
out.WriteRune('\n')
|
out += "\n"
|
||||||
case 't':
|
case 't':
|
||||||
out.WriteRune('\t')
|
out += "\t"
|
||||||
case 'r':
|
case 'r':
|
||||||
out.WriteRune('\r')
|
out += "\r"
|
||||||
default:
|
default:
|
||||||
out.WriteString(string(ch))
|
out += string(ch)
|
||||||
}
|
}
|
||||||
escaped = false
|
escaped = false
|
||||||
continue
|
continue
|
||||||
|
|
@ -203,11 +203,11 @@ func EscapeString(in string) string {
|
||||||
case '\\':
|
case '\\':
|
||||||
escaped = true
|
escaped = true
|
||||||
default:
|
default:
|
||||||
out.WriteString(string(ch))
|
out += string(ch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return out.String()
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) resolveType(value TypeSignature) TypeSignature {
|
func (c *Compiler) resolveType(value TypeSignature) TypeSignature {
|
||||||
|
|
@ -265,30 +265,6 @@ func (c *Compiler) compile(tree Node) (TypeSignature, error) {
|
||||||
|
|
||||||
return &TupleSignature{contents}, nil
|
return &TupleSignature{contents}, nil
|
||||||
|
|
||||||
case RecordNodeType:
|
|
||||||
n := tree.(*RecordNode)
|
|
||||||
|
|
||||||
c.add(InstructionNewRecord)
|
|
||||||
|
|
||||||
contents := map[string]TypeSignature{}
|
|
||||||
for k, v := range n.entries {
|
|
||||||
t, err := c.compile(v)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.add(InstructionSetRecordItem)
|
|
||||||
c.addConstant(&StringValue{
|
|
||||||
k,
|
|
||||||
})
|
|
||||||
|
|
||||||
contents[k] = t
|
|
||||||
}
|
|
||||||
|
|
||||||
return &RecordSignature{
|
|
||||||
contents,
|
|
||||||
}, nil
|
|
||||||
|
|
||||||
case ListNodeType:
|
case ListNodeType:
|
||||||
l := tree.(*ListNode)
|
l := tree.(*ListNode)
|
||||||
|
|
||||||
|
|
@ -968,8 +944,8 @@ func (c *Compiler) compileAssignFromStack(to Node, sig TypeSignature, declare bo
|
||||||
c.add(InstructionDestructureTuple)
|
c.add(InstructionDestructureTuple)
|
||||||
|
|
||||||
// iterate from top to bottom
|
// iterate from top to bottom
|
||||||
for i, v := range slices.Backward(t.items) {
|
for i := len(t.items) - 1; i >= 0; i-- {
|
||||||
_, err := c.compileAssignFromStack(v, tsig.Contents[i], declare)
|
_, err := c.compileAssignFromStack(t.items[i], tsig.Contents[i], declare)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -977,41 +953,6 @@ func (c *Compiler) compileAssignFromStack(to Node, sig TypeSignature, declare bo
|
||||||
}
|
}
|
||||||
|
|
||||||
return sig, nil
|
return sig, nil
|
||||||
case IndexNodeType:
|
|
||||||
if declare {
|
|
||||||
return nil, c.error(fmt.Sprintf("cannot declare an indexed item"), to)
|
|
||||||
}
|
|
||||||
|
|
||||||
n := to.(*IndexNode)
|
|
||||||
|
|
||||||
ssig, err := c.compile(n.source)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
isig, err := c.compile(n.index)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if isig.Type() != TypeInteger {
|
|
||||||
return nil, c.error(fmt.Sprintf("cannot index into list with non-integer (%s)", isig), n.index)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ssig.Type() {
|
|
||||||
case TypeList:
|
|
||||||
lsig := ssig.(*ListSignature)
|
|
||||||
if !c.typeMatches(sig, lsig.Contents) {
|
|
||||||
return nil, c.error(fmt.Sprintf("cannot assign value of type %s to list with items of type %s", sig, lsig.Contents), to)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.add(InstructionSetIndexList)
|
|
||||||
default:
|
|
||||||
return nil, c.error(fmt.Sprintf("cannot set index of %s", ssig.Type()), n.source)
|
|
||||||
}
|
|
||||||
|
|
||||||
return sig, nil
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, c.error(fmt.Sprintf("cannot assign to %s", to.Type()), to)
|
return nil, c.error(fmt.Sprintf("cannot assign to %s", to.Type()), to)
|
||||||
}
|
}
|
||||||
|
|
@ -1255,13 +1196,6 @@ func (c *Compiler) getPropertySignature(source TypeSignature, property string) (
|
||||||
return &CompositeSignature{
|
return &CompositeSignature{
|
||||||
at, bt,
|
at, bt,
|
||||||
}, nil
|
}, nil
|
||||||
case TypeRecord:
|
|
||||||
prop, ok := sig.(*RecordSignature).Entries[property]
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New(fmt.Sprintf("cannot record has no property \"%s\"", property))
|
|
||||||
}
|
|
||||||
|
|
||||||
return prop, nil
|
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Token struct {
|
type Token struct {
|
||||||
Kind TokenKind
|
Type TokenKind
|
||||||
Start Pos
|
Start Pos
|
||||||
End Pos
|
End Pos
|
||||||
Line Pos
|
Line Pos
|
||||||
|
|
@ -19,7 +19,7 @@ func (t Token) Bounds() (Pos, Pos) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t Token) String() string {
|
func (t Token) String() string {
|
||||||
return fmt.Sprintf("token %s, '%s' %d -> %d, line %d", t.Kind.String(), t.Lexeme, t.Start, t.End, t.Line)
|
return fmt.Sprintf("token %s, '%s' %d -> %d, line %d", t.Type.String(), t.Lexeme, t.Start, t.End, t.Line)
|
||||||
}
|
}
|
||||||
|
|
||||||
type TokenKind uint64
|
type TokenKind uint64
|
||||||
|
|
@ -185,8 +185,6 @@ func (t TokenKind) String() string {
|
||||||
return "for"
|
return "for"
|
||||||
case TokenIn:
|
case TokenIn:
|
||||||
return "in"
|
return "in"
|
||||||
case TokenPercent:
|
|
||||||
return "percent"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
panic("UNDEFINED TOKENTYPE STRING CONVERSION")
|
panic("UNDEFINED TOKENTYPE STRING CONVERSION")
|
||||||
|
|
@ -417,7 +415,7 @@ func (l *Lexer) NextToken() (Token, error) {
|
||||||
|
|
||||||
func NewToken(t TokenKind, start Pos, end Pos, line Pos, lexeme string) Token {
|
func NewToken(t TokenKind, start Pos, end Pos, line Pos, lexeme string) Token {
|
||||||
return Token{
|
return Token{
|
||||||
Kind: t,
|
Type: t,
|
||||||
Start: start,
|
Start: start,
|
||||||
End: end,
|
End: end,
|
||||||
Line: line,
|
Line: line,
|
||||||
|
|
@ -432,7 +430,7 @@ func (l *Lexer) Tokenize() ([]Token, error) {
|
||||||
for ; err == nil; tok, err = l.NextToken() {
|
for ; err == nil; tok, err = l.NextToken() {
|
||||||
tokens = append(tokens, tok)
|
tokens = append(tokens, tok)
|
||||||
|
|
||||||
if tok.Kind == TokenEOF {
|
if tok.Type == TokenEOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -148,8 +148,8 @@ func TestLexer_NextToken(t *testing.T) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if tok.Kind != expectedType {
|
if tok.Type != expectedType {
|
||||||
t.Errorf("Expected token type '%s' but got '%s'", expectedType, tok.Kind)
|
t.Errorf("Expected token type '%s' but got '%s'", expectedType, tok.Type)
|
||||||
} else {
|
} else {
|
||||||
t.Logf("Got expected token type '%s'", expectedType)
|
t.Logf("Got expected token type '%s'", expectedType)
|
||||||
}
|
}
|
||||||
|
|
@ -193,7 +193,7 @@ func TestLexer_NextTokenErrors(t *testing.T) {
|
||||||
lex := NewLexer(code)
|
lex := NewLexer(code)
|
||||||
tok, err := lex.NextToken()
|
tok, err := lex.NextToken()
|
||||||
|
|
||||||
for err == nil && tok.Kind != TokenEOF {
|
for err == nil && tok.Type != TokenEOF {
|
||||||
tok, err = lex.NextToken()
|
tok, err = lex.NextToken()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -220,7 +220,7 @@ func BenchmarkLexer_NextToken(b *testing.B) {
|
||||||
lex := NewLexer(tc.source)
|
lex := NewLexer(tc.source)
|
||||||
tok, err := lex.NextToken()
|
tok, err := lex.NextToken()
|
||||||
|
|
||||||
for err == nil && tok.Kind != TokenEOF {
|
for err == nil && tok.Type != TokenEOF {
|
||||||
tok, err = lex.NextToken()
|
tok, err = lex.NextToken()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ const (
|
||||||
NilNodeType
|
NilNodeType
|
||||||
ListNodeType
|
ListNodeType
|
||||||
TupleNodeType
|
TupleNodeType
|
||||||
RecordNodeType
|
|
||||||
BinaryNodeType
|
BinaryNodeType
|
||||||
UnaryNodeType
|
UnaryNodeType
|
||||||
BlockNodeType
|
BlockNodeType
|
||||||
|
|
@ -94,12 +93,6 @@ func (n NodeType) String() string {
|
||||||
return "Alias"
|
return "Alias"
|
||||||
case IndexNodeType:
|
case IndexNodeType:
|
||||||
return "Index"
|
return "Index"
|
||||||
case RecordNodeType:
|
|
||||||
return "Record"
|
|
||||||
case ForNodeType:
|
|
||||||
return "For"
|
|
||||||
case IncludeNodeType:
|
|
||||||
return "Include"
|
|
||||||
}
|
}
|
||||||
return "Invalid Node Type"
|
return "Invalid Node Type"
|
||||||
}
|
}
|
||||||
|
|
@ -244,36 +237,6 @@ func (n TupleNode) Bounds() (Pos, Pos) {
|
||||||
return n.start, n.end
|
return n.start, n.end
|
||||||
}
|
}
|
||||||
|
|
||||||
type RecordNode struct {
|
|
||||||
entries map[string]Node
|
|
||||||
|
|
||||||
start Pos
|
|
||||||
end Pos
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n RecordNode) Type() NodeType {
|
|
||||||
return RecordNodeType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n RecordNode) String() string {
|
|
||||||
sb := strings.Builder{}
|
|
||||||
|
|
||||||
sb.WriteString("(")
|
|
||||||
for name, item := range n.entries {
|
|
||||||
sb.WriteString(name)
|
|
||||||
sb.WriteString(": ")
|
|
||||||
sb.WriteString(item.String())
|
|
||||||
sb.WriteString(",")
|
|
||||||
}
|
|
||||||
sb.WriteString(")")
|
|
||||||
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n RecordNode) Bounds() (Pos, Pos) {
|
|
||||||
return n.start, n.end
|
|
||||||
}
|
|
||||||
|
|
||||||
type AccessNode struct {
|
type AccessNode struct {
|
||||||
source Node
|
source Node
|
||||||
property *Token
|
property *Token
|
||||||
|
|
@ -306,8 +269,6 @@ func (n BinaryOperation) String() string {
|
||||||
return "multiply"
|
return "multiply"
|
||||||
case BinaryDivision:
|
case BinaryDivision:
|
||||||
return "divide"
|
return "divide"
|
||||||
case BinaryModulo:
|
|
||||||
return "modulo"
|
|
||||||
case BinaryEquality:
|
case BinaryEquality:
|
||||||
return "equality"
|
return "equality"
|
||||||
case BinaryInequality:
|
case BinaryInequality:
|
||||||
|
|
@ -538,7 +499,7 @@ func (n ConditionalNode) String() string {
|
||||||
return fmt.Sprintf("if %s then %s", n.condition.String(), n.do.String())
|
return fmt.Sprintf("if %s then %s", n.condition.String(), n.do.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("if %s then %s otherwise %s", n.condition.String(), n.do.String(), n.otherwise.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) {
|
func (n ConditionalNode) Bounds() (Pos, Pos) {
|
||||||
|
|
|
||||||
250
core/optimizer.go
Normal file
250
core/optimizer.go
Normal file
|
|
@ -0,0 +1,250 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TreeOptimizer struct{}
|
||||||
|
|
||||||
|
func (t *TreeOptimizer) Optimize(node *Node) {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TreeOptimizer) error(message string, causer Bounded) error {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// isTreeConstant check if a node tree is constant (predictable)
|
||||||
|
func (t *TreeOptimizer) isTreeConstant(tree Node) bool {
|
||||||
|
switch tree.Type() {
|
||||||
|
case StringNodeType, FloatNodeType, IntegerNodeType, BooleanNodeType, NilNodeType:
|
||||||
|
return true
|
||||||
|
case ListNodeType:
|
||||||
|
for _, item := range tree.(*ListNode).items {
|
||||||
|
if !t.isTreeConstant(item) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
case UnaryNodeType:
|
||||||
|
return t.isTreeConstant(tree.(*UnaryNode).value)
|
||||||
|
case BinaryNodeType:
|
||||||
|
return t.isTreeConstant(tree.(*BinaryNode).Left) && t.isTreeConstant(tree.(*BinaryNode).Right)
|
||||||
|
case InvokeNodeType:
|
||||||
|
for _, arg := range tree.(*InvokeNode).args {
|
||||||
|
if !t.isTreeConstant(arg) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t.isTreeConstant(tree.(*InvokeNode).source)
|
||||||
|
case BlockNodeType, ConditionalNodeType, LoopNodeType, AssignNodeType, FunctionNodeType,
|
||||||
|
ReturnNodeType, AccessNodeType, BreakpointNodeType, ReferenceNodeType:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unexpected node %s", tree))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TreeOptimizer) compute(tree Node) (Value, error) {
|
||||||
|
switch n := tree.(type) {
|
||||||
|
case *StringNode:
|
||||||
|
return &StringValue{
|
||||||
|
EscapeString(n.value),
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
case *FloatNode:
|
||||||
|
return &FloatValue{
|
||||||
|
n.value,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
case *IntegerNode:
|
||||||
|
return &IntegerValue{
|
||||||
|
n.value,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
case *BooleanNode:
|
||||||
|
return &BoolValue{
|
||||||
|
n.Boolean,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
case *NilNode:
|
||||||
|
return &NilValue{}, nil
|
||||||
|
|
||||||
|
case *ListNode:
|
||||||
|
items := make([]Value, len(n.items))
|
||||||
|
var err error
|
||||||
|
for i, item := range n.items {
|
||||||
|
items[i], err = t.compute(item)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &ListValue{
|
||||||
|
items,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
case *BinaryNode:
|
||||||
|
return t.computeBinary(n)
|
||||||
|
|
||||||
|
case *UnaryNode:
|
||||||
|
v, err := t.compute(n.value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch n.UnaryOperation {
|
||||||
|
case UnaryNegate:
|
||||||
|
if v.Type() == FloatValueType {
|
||||||
|
return &FloatValue{
|
||||||
|
-v.(*FloatValue).Number,
|
||||||
|
}, nil
|
||||||
|
} else if v.Type() == IntegerValueType {
|
||||||
|
return &IntegerValue{
|
||||||
|
new(big.Int).Neg(v.(*IntegerValue).Number),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, t.error(fmt.Sprintf("cannot negate %s value (not a number)", v.Type()), n)
|
||||||
|
case UnaryNot:
|
||||||
|
if v.Type() != BoolValueType {
|
||||||
|
return nil, t.error(fmt.Sprintf("cannot invert %s value (not a boolean)", v.Type()), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &BoolValue{
|
||||||
|
!v.(*BoolValue).Boolean,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, t.error(fmt.Sprintf("unimplemented unary %s", v.Type()), n)
|
||||||
|
|
||||||
|
case *InvokeNode:
|
||||||
|
source, err := t.compute(n.source)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
f, ok := source.(*BuiltinFunctionValue)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.Constant {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
args := make([]Value, len(f.Signature.In))
|
||||||
|
for i, arg := range n.args {
|
||||||
|
args[i], err = t.compute(arg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.F(nil, nil, args)
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unexpected node %s, %T", tree.String(), tree))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TreeOptimizer) computeBinary(n *BinaryNode) (Value, error) {
|
||||||
|
l, err := t.compute(n.Left)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r, err := t.compute(n.Right)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.Type() != r.Type() {
|
||||||
|
return nil, t.error(fmt.Sprintf("cannot %s different types %s and %s", n.BinaryOperation, l.Type(), r.Type()), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// perform type check
|
||||||
|
switch n.BinaryOperation {
|
||||||
|
case BinarySubtraction, BinaryMultiplication, BinaryDivision, BinaryLess, BinaryGreater, BinaryLessEqual, BinaryGreaterEqual:
|
||||||
|
if l.Type() != FloatValueType && l.Type() != IntegerValueType {
|
||||||
|
return nil, t.error(fmt.Sprintf("cannot %s values of non-number type %s", n.BinaryOperation, l.Type()), n)
|
||||||
|
}
|
||||||
|
case BinaryBooleanAnd, BinaryBooleanOr:
|
||||||
|
if l.Type() != BoolValueType {
|
||||||
|
return nil, t.error(fmt.Sprintf("cannot %s values of non-boolean type %s", n.BinaryOperation, l.Type()), n)
|
||||||
|
}
|
||||||
|
case BinaryEquality, BinaryInequality:
|
||||||
|
// can compare all types with themselves
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
var v interface{}
|
||||||
|
switch n.BinaryOperation {
|
||||||
|
case BinaryAddition:
|
||||||
|
switch l.Type() {
|
||||||
|
case FloatValueType:
|
||||||
|
v = l.(*FloatValue).Number + r.(*FloatValue).Number
|
||||||
|
case StringValueType:
|
||||||
|
v = l.(*StringValue).Text + r.(*StringValue).Text
|
||||||
|
case ListValueType:
|
||||||
|
v = append(l.(*ListValue).Items, r.(*ListValue).Items...)
|
||||||
|
case IntegerValueType:
|
||||||
|
v = new(big.Int).Add(l.(*IntegerValue).Number, r.(*IntegerValue).Number)
|
||||||
|
default:
|
||||||
|
return nil, t.error(fmt.Sprintf("cannot add values of type %s", l.Type()), n)
|
||||||
|
}
|
||||||
|
case BinarySubtraction:
|
||||||
|
if l.Type() == FloatValueType {
|
||||||
|
v = l.(*FloatValue).Number - r.(*FloatValue).Number
|
||||||
|
} else {
|
||||||
|
v = new(big.Int).Sub(l.(*IntegerValue).Number, r.(*IntegerValue).Number)
|
||||||
|
}
|
||||||
|
case BinaryMultiplication:
|
||||||
|
if l.Type() == FloatValueType {
|
||||||
|
v = l.(*FloatValue).Number * r.(*FloatValue).Number
|
||||||
|
} else {
|
||||||
|
v = new(big.Int).Mul(l.(*IntegerValue).Number, r.(*IntegerValue).Number)
|
||||||
|
}
|
||||||
|
case BinaryDivision:
|
||||||
|
if l.Type() == FloatValueType {
|
||||||
|
v = l.(*FloatValue).Number / r.(*FloatValue).Number
|
||||||
|
} else {
|
||||||
|
v = new(big.Int).Div(l.(*IntegerValue).Number, r.(*IntegerValue).Number)
|
||||||
|
}
|
||||||
|
case BinaryBooleanAnd:
|
||||||
|
v = l.(*BoolValue).Boolean && r.(*BoolValue).Boolean
|
||||||
|
case BinaryBooleanOr:
|
||||||
|
v = l.(*BoolValue).Boolean || r.(*BoolValue).Boolean
|
||||||
|
case BinaryEquality:
|
||||||
|
v = l.Equals(r)
|
||||||
|
case BinaryInequality:
|
||||||
|
v = !l.Equals(r)
|
||||||
|
case BinaryLess:
|
||||||
|
if l.Type() == FloatValueType {
|
||||||
|
v = l.(*FloatValue).Number < r.(*FloatValue).Number
|
||||||
|
} else {
|
||||||
|
v = l.(*IntegerValue).Number.Cmp(r.(*IntegerValue).Number) == -1
|
||||||
|
}
|
||||||
|
case BinaryGreater:
|
||||||
|
if l.Type() == FloatValueType {
|
||||||
|
v = l.(*FloatValue).Number > r.(*FloatValue).Number
|
||||||
|
} else {
|
||||||
|
v = l.(*IntegerValue).Number.Cmp(r.(*IntegerValue).Number) == 1
|
||||||
|
}
|
||||||
|
case BinaryLessEqual:
|
||||||
|
if l.Type() == FloatValueType {
|
||||||
|
v = l.(*FloatValue).Number <= r.(*FloatValue).Number
|
||||||
|
} else {
|
||||||
|
v = l.(*IntegerValue).Number.Cmp(r.(*IntegerValue).Number) != 1
|
||||||
|
}
|
||||||
|
case BinaryGreaterEqual:
|
||||||
|
if l.Type() == FloatValueType {
|
||||||
|
v = l.(*FloatValue).Number >= r.(*FloatValue).Number
|
||||||
|
} else {
|
||||||
|
v = l.(*IntegerValue).Number.Cmp(r.(*IntegerValue).Number) != 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GoToValue(v), nil
|
||||||
|
}
|
||||||
335
core/parser.go
335
core/parser.go
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"math/big"
|
"math/big"
|
||||||
"slices"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
@ -69,8 +68,8 @@ func (p ParsingError) Format() string {
|
||||||
b.WriteRune('\n')
|
b.WriteRune('\n')
|
||||||
b.WriteRune('\n')
|
b.WriteRune('\n')
|
||||||
|
|
||||||
for i, v := range slices.Backward(p.Trace) {
|
for i := len(p.Trace) - 1; i >= 0; i-- {
|
||||||
b.WriteString(fmt.Sprintf("[%d] %s\n", i, v))
|
b.WriteString(fmt.Sprintf("[%d] %s\n", i, p.Trace[i]))
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.String()
|
return b.String()
|
||||||
|
|
@ -80,10 +79,6 @@ type Parser struct {
|
||||||
source string
|
source string
|
||||||
trace []string
|
trace []string
|
||||||
tokens []Token
|
tokens []Token
|
||||||
state ParserState
|
|
||||||
}
|
|
||||||
|
|
||||||
type ParserState struct {
|
|
||||||
prev *Token
|
prev *Token
|
||||||
curr *Token
|
curr *Token
|
||||||
pos Pos
|
pos Pos
|
||||||
|
|
@ -95,9 +90,7 @@ func NewParser(source string, trace []string, tokens []Token) *Parser {
|
||||||
source: source,
|
source: source,
|
||||||
trace: trace,
|
trace: trace,
|
||||||
tokens: tokens,
|
tokens: tokens,
|
||||||
state: ParserState{
|
|
||||||
pos: 0,
|
pos: 0,
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,11 +116,11 @@ func (p *Parser) Parse(path string) (*Program, error) {
|
||||||
// initialize current
|
// initialize current
|
||||||
p.advance()
|
p.advance()
|
||||||
|
|
||||||
for int(p.state.pos) < len(p.tokens) && p.state.curr.Kind != TokenEOF {
|
for int(p.pos) < len(p.tokens) && p.curr.Type != TokenEOF {
|
||||||
for p.accept(TokenNewLine) {
|
for p.accept(TokenNewLine) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.state.curr.Kind == TokenEOF {
|
if p.curr.Type == TokenEOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,25 +139,25 @@ func (p *Parser) Parse(path string) (*Program, error) {
|
||||||
&BlockNode{
|
&BlockNode{
|
||||||
statements,
|
statements,
|
||||||
0,
|
0,
|
||||||
p.state.curr.End,
|
p.curr.End,
|
||||||
},
|
},
|
||||||
path,
|
path,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) accept(tokenType TokenKind) bool {
|
func (p *Parser) accept(tokenType TokenKind) bool {
|
||||||
if p.state.curr == nil {
|
if p.curr == nil {
|
||||||
log.Fatal("unexpected current token nil")
|
log.Fatal("unexpected current token nil")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.state.ignoreNewLine && tokenType != TokenNewLine {
|
if p.ignoreNewLine && tokenType != TokenNewLine {
|
||||||
for p.state.curr.Kind == TokenNewLine {
|
for p.curr.Type == TokenNewLine {
|
||||||
p.advance()
|
p.advance()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*p.state.curr).Kind == tokenType {
|
if (*p.curr).Type == tokenType {
|
||||||
p.advance()
|
p.advance()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -172,52 +165,46 @@ func (p *Parser) accept(tokenType TokenKind) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) getState() ParserState {
|
func (p *Parser) acceptAll(tokenTypes ...TokenKind) bool {
|
||||||
return p.state
|
if int(p.pos)+len(tokenTypes) > len(p.tokens) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) restoreState(state ParserState) {
|
for i, tokenType := range tokenTypes {
|
||||||
p.state = state
|
if p.tokens[int(p.pos)+i].Type != tokenType {
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) acceptSeq(tokenTypes ...TokenKind) bool {
|
|
||||||
state := p.getState()
|
|
||||||
|
|
||||||
for _, t := range tokenTypes {
|
|
||||||
if !p.accept(t) {
|
|
||||||
p.restoreState(state)
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.pos += Pos(len(tokenTypes))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) expect(tokenType TokenKind, reason string) error {
|
func (p *Parser) expect(tokenType TokenKind, reason string) error {
|
||||||
if !p.accept(tokenType) {
|
if !p.accept(tokenType) {
|
||||||
return p.error(fmt.Sprintf("Expected token %s, got %s; %s", tokenType, p.state.curr.Kind, reason), p.state.curr)
|
return p.error(fmt.Sprintf("Expected token %s, got %s; %s", tokenType, p.curr.Type, reason), p.curr)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) peek() (Token, error) {
|
func (p *Parser) peek() (Token, error) {
|
||||||
if p.state.pos >= Pos(len(p.tokens)) {
|
if p.pos >= Pos(len(p.tokens)) {
|
||||||
return Token{}, errors.New("cannot peek beyond tokens")
|
return Token{}, errors.New("cannot peek beyond tokens")
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.tokens[p.state.pos], nil
|
return p.tokens[p.pos], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) advance() {
|
func (p *Parser) advance() {
|
||||||
p.state.prev = p.state.curr
|
p.prev = p.curr
|
||||||
|
|
||||||
if p.state.pos < Pos(len(p.tokens)) {
|
if p.pos < Pos(len(p.tokens)) {
|
||||||
p.state.curr = &p.tokens[p.state.pos]
|
p.curr = &p.tokens[p.pos]
|
||||||
} else {
|
} else {
|
||||||
p.state.curr = nil
|
p.curr = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
p.state.pos++
|
p.pos++
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) error(error string, causer *Token) error {
|
func (p *Parser) error(error string, causer *Token) error {
|
||||||
|
|
@ -237,10 +224,10 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
oldIgnoreNewline := p.state.ignoreNewLine
|
oldIgnoreNewline := p.ignoreNewLine
|
||||||
p.state.ignoreNewLine = false
|
p.ignoreNewLine = false
|
||||||
|
|
||||||
start := p.state.prev.Start
|
start := p.prev.Start
|
||||||
|
|
||||||
var statements []Node
|
var statements []Node
|
||||||
for !p.accept(TokenCloseBrace) {
|
for !p.accept(TokenCloseBrace) {
|
||||||
|
|
@ -264,22 +251,22 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.state.ignoreNewLine = oldIgnoreNewline
|
p.ignoreNewLine = oldIgnoreNewline
|
||||||
|
|
||||||
return &BlockNode{statements, start, p.state.prev.End}, nil
|
return &BlockNode{statements, start, p.prev.End}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
t := p.state.curr
|
t := p.curr
|
||||||
switch t.Kind {
|
switch t.Type {
|
||||||
case TokenType:
|
case TokenType:
|
||||||
p.advance()
|
p.advance()
|
||||||
start := p.state.prev.Start
|
start := p.prev.Start
|
||||||
|
|
||||||
if err := p.expect(TokenName, "types must have a name"); err != nil {
|
if err := p.expect(TokenName, "types must have a name"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
name := p.state.prev
|
name := p.prev
|
||||||
|
|
||||||
if err := p.expect(TokenAssign, "type aliases must be defined with an assign"); err != nil {
|
if err := p.expect(TokenAssign, "type aliases must be defined with an assign"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -295,7 +282,7 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
||||||
sig,
|
sig,
|
||||||
|
|
||||||
start,
|
start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenIf:
|
case TokenIf:
|
||||||
|
|
@ -313,7 +300,7 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
||||||
|
|
||||||
var otherwise Node
|
var otherwise Node
|
||||||
if p.accept(TokenElse) {
|
if p.accept(TokenElse) {
|
||||||
otherwise, err = p.expression(p.state.curr.Kind != TokenIf)
|
otherwise, err = p.expression(p.curr.Type != TokenIf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -329,7 +316,7 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
||||||
|
|
||||||
case TokenReturn:
|
case TokenReturn:
|
||||||
p.advance()
|
p.advance()
|
||||||
start := p.state.prev.Start
|
start := p.prev.Start
|
||||||
|
|
||||||
v, err := p.expression(false)
|
v, err := p.expression(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -339,12 +326,12 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
||||||
return &ReturnNode{
|
return &ReturnNode{
|
||||||
v,
|
v,
|
||||||
start,
|
start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenWhile:
|
case TokenWhile:
|
||||||
p.advance()
|
p.advance()
|
||||||
start := p.state.prev.Start
|
start := p.prev.Start
|
||||||
|
|
||||||
cond, err := p.expression(false)
|
cond, err := p.expression(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -360,12 +347,12 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
||||||
cond,
|
cond,
|
||||||
logic,
|
logic,
|
||||||
start,
|
start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenFor:
|
case TokenFor:
|
||||||
p.advance()
|
p.advance()
|
||||||
start := p.state.prev.Start
|
start := p.prev.Start
|
||||||
|
|
||||||
counter, err := p.expression(false)
|
counter, err := p.expression(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -392,12 +379,12 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
||||||
logic,
|
logic,
|
||||||
|
|
||||||
start,
|
start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenInclude:
|
case TokenInclude:
|
||||||
p.advance()
|
p.advance()
|
||||||
start := p.state.prev.Start
|
start := p.prev.Start
|
||||||
|
|
||||||
if err := p.expect(TokenString, "import requires a path/name to include"); err != nil {
|
if err := p.expect(TokenString, "import requires a path/name to include"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -405,13 +392,13 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
||||||
|
|
||||||
return &IncludeNode{
|
return &IncludeNode{
|
||||||
&StringNode{
|
&StringNode{
|
||||||
p.state.prev.Lexeme[1 : len(p.state.prev.Lexeme)-1],
|
p.prev.Lexeme[1 : len(p.prev.Lexeme)-1],
|
||||||
p.state.prev.Lexeme,
|
p.prev.Lexeme,
|
||||||
p.state.prev.Start,
|
p.prev.Start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
},
|
},
|
||||||
start,
|
start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -421,7 +408,7 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.accept(TokenDeclare) || p.accept(TokenAssign) {
|
if p.accept(TokenDeclare) || p.accept(TokenAssign) {
|
||||||
isDeclaration := p.state.prev.Kind == TokenDeclare
|
isDeclaration := p.prev.Type == TokenDeclare
|
||||||
|
|
||||||
// possibly assign tuples; not implemented yet
|
// possibly assign tuples; not implemented yet
|
||||||
v, err := p.expression(false)
|
v, err := p.expression(false)
|
||||||
|
|
@ -522,7 +509,7 @@ func (p *Parser) binary() (Node, error) {
|
||||||
r := values.Pop()
|
r := values.Pop()
|
||||||
l := values.Pop()
|
l := values.Pop()
|
||||||
opToken := ops.Pop()
|
opToken := ops.Pop()
|
||||||
op := tokenToBinaryOperation(opToken.Kind)
|
op := tokenToBinaryOperation(opToken.Type)
|
||||||
|
|
||||||
start, _ := l.Bounds()
|
start, _ := l.Bounds()
|
||||||
_, end := r.Bounds()
|
_, end := r.Bounds()
|
||||||
|
|
@ -537,12 +524,12 @@ func (p *Parser) binary() (Node, error) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
for isBinaryOperator(p.state.curr.Kind) {
|
for isBinaryOperator(p.curr.Type) {
|
||||||
for ops.Current > 0 && binaryPrecedence(p.state.curr.Kind) <= binaryPrecedence(ops.Peek().Kind) {
|
for ops.Current > 0 && binaryPrecedence(p.curr.Type) <= binaryPrecedence(ops.Peek().Type) {
|
||||||
reduce()
|
reduce()
|
||||||
}
|
}
|
||||||
|
|
||||||
ops.Push(p.state.curr)
|
ops.Push(p.curr)
|
||||||
p.advance()
|
p.advance()
|
||||||
|
|
||||||
v, err := p.chain()
|
v, err := p.chain()
|
||||||
|
|
@ -571,16 +558,16 @@ func (p *Parser) chain() (Node, error) {
|
||||||
if err = p.expect(TokenName, "can only access properties by name"); err != nil {
|
if err = p.expect(TokenName, "can only access properties by name"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
name := p.state.prev
|
name := p.prev
|
||||||
|
|
||||||
f = &AccessNode{
|
f = &AccessNode{
|
||||||
f,
|
f,
|
||||||
p.state.prev,
|
p.prev,
|
||||||
name.Start,
|
name.Start,
|
||||||
name.End,
|
name.End,
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.state.curr.Kind == TokenOpenParenthesis {
|
if p.curr.Type == TokenOpenParenthesis {
|
||||||
args, err := p.parseArgs()
|
args, err := p.parseArgs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -590,11 +577,11 @@ func (p *Parser) chain() (Node, error) {
|
||||||
f,
|
f,
|
||||||
args,
|
args,
|
||||||
name.Start,
|
name.Start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if p.state.curr.Kind == TokenOpenParenthesis {
|
} else if p.curr.Type == TokenOpenParenthesis {
|
||||||
start := p.state.curr.Start
|
start := p.curr.Start
|
||||||
args, err := p.parseArgs()
|
args, err := p.parseArgs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -605,10 +592,10 @@ func (p *Parser) chain() (Node, error) {
|
||||||
args,
|
args,
|
||||||
|
|
||||||
start,
|
start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}
|
}
|
||||||
} else if p.accept(TokenOpenBracket) {
|
} else if p.accept(TokenOpenBracket) {
|
||||||
start := p.state.prev.Start
|
start := p.prev.Start
|
||||||
|
|
||||||
index, err := p.expression(false)
|
index, err := p.expression(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -623,7 +610,7 @@ func (p *Parser) chain() (Node, error) {
|
||||||
f,
|
f,
|
||||||
index,
|
index,
|
||||||
start,
|
start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
|
|
@ -634,71 +621,71 @@ func (p *Parser) chain() (Node, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) factor() (Node, error) {
|
func (p *Parser) factor() (Node, error) {
|
||||||
switch (*p.state.curr).Kind {
|
switch (*p.curr).Type {
|
||||||
case TokenString:
|
case TokenString:
|
||||||
p.advance()
|
p.advance()
|
||||||
return &StringNode{
|
return &StringNode{
|
||||||
(*p.state.prev).Lexeme[1 : len((*p.state.prev).Lexeme)-1],
|
(*p.prev).Lexeme[1 : len((*p.prev).Lexeme)-1],
|
||||||
(*p.state.prev).Lexeme,
|
(*p.prev).Lexeme,
|
||||||
p.state.prev.Start,
|
p.prev.Start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenInteger:
|
case TokenInteger:
|
||||||
p.advance()
|
p.advance()
|
||||||
|
|
||||||
num, success := new(big.Int).SetString(p.state.prev.Lexeme, 10)
|
num, success := new(big.Int).SetString(p.prev.Lexeme, 10)
|
||||||
if !success {
|
if !success {
|
||||||
return nil, p.error(fmt.Sprintf("cannot parse integer base 10: %s", p.state.prev.Lexeme), p.state.prev)
|
return nil, p.error(fmt.Sprintf("cannot parse integer base 10: %s", p.prev.Lexeme), p.prev)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &IntegerNode{
|
return &IntegerNode{
|
||||||
num,
|
num,
|
||||||
p.state.prev.Start,
|
p.prev.Start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenFloat:
|
case TokenFloat:
|
||||||
p.advance()
|
p.advance()
|
||||||
num, err := strconv.ParseFloat((*p.state.prev).Lexeme, FloatSize)
|
num, err := strconv.ParseFloat((*p.prev).Lexeme, FloatSize)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, p.error(fmt.Sprintf("Error parsing number: %v", err), p.state.prev)
|
return nil, p.error(fmt.Sprintf("Error parsing number: %v", err), p.prev)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &FloatNode{
|
return &FloatNode{
|
||||||
num,
|
num,
|
||||||
p.state.prev.Start,
|
p.prev.Start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenHexadecimal:
|
case TokenHexadecimal:
|
||||||
p.advance()
|
p.advance()
|
||||||
start := (*p.state.prev).Start
|
start := (*p.prev).Start
|
||||||
num, ok := new(big.Int).SetString(p.state.prev.Lexeme[2:], 16)
|
num, ok := new(big.Int).SetString(p.prev.Lexeme[2:], 16)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, p.error(fmt.Sprintf("cannot parse hexadecimal: %v", p.state.prev.Lexeme), p.state.prev)
|
return nil, p.error(fmt.Sprintf("cannot parse hexadecimal: %v", p.prev.Lexeme), p.prev)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &IntegerNode{
|
return &IntegerNode{
|
||||||
num,
|
num,
|
||||||
start,
|
start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenTrue:
|
case TokenTrue:
|
||||||
p.advance()
|
p.advance()
|
||||||
return &BooleanNode{
|
return &BooleanNode{
|
||||||
true,
|
true,
|
||||||
p.state.prev.Start,
|
p.prev.Start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}, nil
|
}, nil
|
||||||
case TokenFalse:
|
case TokenFalse:
|
||||||
p.advance()
|
p.advance()
|
||||||
return &BooleanNode{
|
return &BooleanNode{
|
||||||
false,
|
false,
|
||||||
p.state.prev.Start,
|
p.prev.Start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenNil:
|
case TokenNil:
|
||||||
|
|
@ -707,7 +694,7 @@ func (p *Parser) factor() (Node, error) {
|
||||||
|
|
||||||
case TokenOpenBracket:
|
case TokenOpenBracket:
|
||||||
p.advance()
|
p.advance()
|
||||||
start := p.state.prev.Start
|
start := p.prev.Start
|
||||||
|
|
||||||
// TODO: find better solution; current one is messy
|
// TODO: find better solution; current one is messy
|
||||||
// Maybe perform better analysis to determine the kind of the list...
|
// Maybe perform better analysis to determine the kind of the list...
|
||||||
|
|
@ -721,12 +708,12 @@ func (p *Parser) factor() (Node, error) {
|
||||||
[]Node{},
|
[]Node{},
|
||||||
s,
|
s,
|
||||||
start,
|
start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
oldIgnoreNewline := p.state.ignoreNewLine
|
oldIgnoreNewline := p.ignoreNewLine
|
||||||
p.state.ignoreNewLine = true
|
p.ignoreNewLine = true
|
||||||
|
|
||||||
var values []Node
|
var values []Node
|
||||||
for !p.accept(TokenCloseBracket) {
|
for !p.accept(TokenCloseBracket) {
|
||||||
|
|
@ -744,19 +731,19 @@ func (p *Parser) factor() (Node, error) {
|
||||||
values = append(values, value)
|
values = append(values, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
p.state.ignoreNewLine = oldIgnoreNewline
|
p.ignoreNewLine = oldIgnoreNewline
|
||||||
|
|
||||||
return &ListNode{
|
return &ListNode{
|
||||||
values,
|
values,
|
||||||
nil,
|
nil,
|
||||||
start,
|
start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
// unary minus
|
// unary minus
|
||||||
case TokenMinus:
|
case TokenMinus:
|
||||||
p.advance()
|
p.advance()
|
||||||
op := p.state.prev
|
op := p.prev
|
||||||
|
|
||||||
f, err := p.factor()
|
f, err := p.factor()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -767,12 +754,12 @@ func (p *Parser) factor() (Node, error) {
|
||||||
f,
|
f,
|
||||||
op,
|
op,
|
||||||
op.Start,
|
op.Start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenBang:
|
case TokenBang:
|
||||||
p.advance()
|
p.advance()
|
||||||
op := p.state.prev
|
op := p.prev
|
||||||
|
|
||||||
v, err := p.factor()
|
v, err := p.factor()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -784,16 +771,16 @@ func (p *Parser) factor() (Node, error) {
|
||||||
v,
|
v,
|
||||||
op,
|
op,
|
||||||
op.Start,
|
op.Start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenName:
|
case TokenName:
|
||||||
p.advance()
|
p.advance()
|
||||||
name := (*p.state.prev).Lexeme
|
name := (*p.prev).Lexeme
|
||||||
start := p.state.prev.Start
|
start := p.prev.Start
|
||||||
nameEnd := p.state.prev.End
|
nameEnd := p.prev.End
|
||||||
|
|
||||||
if p.state.curr.Kind == TokenOpenParenthesis {
|
if p.curr.Type == TokenOpenParenthesis {
|
||||||
args, err := p.parseArgs()
|
args, err := p.parseArgs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -807,7 +794,7 @@ func (p *Parser) factor() (Node, error) {
|
||||||
},
|
},
|
||||||
args,
|
args,
|
||||||
start,
|
start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -819,11 +806,11 @@ func (p *Parser) factor() (Node, error) {
|
||||||
|
|
||||||
case TokenFunc:
|
case TokenFunc:
|
||||||
p.advance()
|
p.advance()
|
||||||
start := p.state.prev.Start
|
start := p.prev.Start
|
||||||
|
|
||||||
var name *Token
|
var name *Token
|
||||||
if p.accept(TokenName) { // can be unnamed, but accept name if it is named
|
if p.accept(TokenName) { // can be unnamed, but accept name if it is named
|
||||||
name = p.state.prev
|
name = p.prev
|
||||||
}
|
}
|
||||||
|
|
||||||
params, err := p.parseParams()
|
params, err := p.parseParams()
|
||||||
|
|
@ -855,7 +842,7 @@ func (p *Parser) factor() (Node, error) {
|
||||||
yield,
|
yield,
|
||||||
logic,
|
logic,
|
||||||
start,
|
start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}
|
}
|
||||||
|
|
||||||
if name != nil {
|
if name != nil {
|
||||||
|
|
@ -864,7 +851,7 @@ func (p *Parser) factor() (Node, error) {
|
||||||
fn,
|
fn,
|
||||||
true,
|
true,
|
||||||
start,
|
start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -872,67 +859,11 @@ func (p *Parser) factor() (Node, error) {
|
||||||
|
|
||||||
case TokenOpenParenthesis:
|
case TokenOpenParenthesis:
|
||||||
p.advance()
|
p.advance()
|
||||||
start := p.state.prev.Start
|
start := p.prev.Start
|
||||||
oldCare := p.state.ignoreNewLine
|
|
||||||
p.state.ignoreNewLine = true
|
|
||||||
p.skipNewLines()
|
|
||||||
|
|
||||||
// we're inside an object
|
// we're inside an object
|
||||||
key := p.state.curr
|
if p.acceptAll(TokenName, TokenColon) {
|
||||||
if p.acceptSeq(TokenName, TokenColon) {
|
return nil, p.error("objects are not implemented yet (TBD)", p.prev)
|
||||||
entries := map[string]Node{}
|
|
||||||
|
|
||||||
for len(entries) == 0 || !p.accept(TokenCloseParenthesis) {
|
|
||||||
if len(entries) != 0 {
|
|
||||||
p.skipNewLines()
|
|
||||||
key = p.state.curr
|
|
||||||
if !p.acceptSeq(TokenName, TokenColon) {
|
|
||||||
return nil, p.error("expected a record name", key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
name := key.Lexeme
|
|
||||||
|
|
||||||
if _, ok := entries[name]; ok {
|
|
||||||
return nil, p.error("duplicate key; already defined.", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.accept(TokenComma) || p.accept(TokenCloseParenthesis) {
|
|
||||||
entries[name] = &ReferenceNode{
|
|
||||||
name,
|
|
||||||
key.Start,
|
|
||||||
key.End,
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.state.prev.Kind == TokenCloseParenthesis {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
x, err := p.expression(false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
entries[name] = x
|
|
||||||
}
|
|
||||||
|
|
||||||
if !p.accept(TokenComma) {
|
|
||||||
if err := p.expect(TokenCloseParenthesis, "record must be closed"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.state.ignoreNewLine = oldCare
|
|
||||||
return &RecordNode{
|
|
||||||
entries,
|
|
||||||
start,
|
|
||||||
p.state.prev.End,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err := p.expression(false)
|
v, err := p.expression(false)
|
||||||
|
|
@ -945,7 +876,6 @@ func (p *Parser) factor() (Node, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
p.state.ignoreNewLine = oldCare
|
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -967,27 +897,26 @@ func (p *Parser) factor() (Node, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.state.ignoreNewLine = oldCare
|
|
||||||
return &TupleNode{
|
return &TupleNode{
|
||||||
items,
|
items,
|
||||||
|
|
||||||
start,
|
start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenBreakpoint:
|
case TokenBreakpoint:
|
||||||
p.advance()
|
p.advance()
|
||||||
|
|
||||||
return &BreakpointNode{
|
return &BreakpointNode{
|
||||||
p.state.prev.Start,
|
p.prev.Start,
|
||||||
p.state.prev.End,
|
p.prev.End,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenOpenBrace:
|
case TokenOpenBrace:
|
||||||
return p.expression(true)
|
return p.expression(true)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, p.error(fmt.Sprintf("invalid factor %s", p.state.curr), p.state.curr)
|
return nil, p.error(fmt.Sprintf("invalid factor %s", p.curr), p.curr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1027,7 +956,7 @@ func (p *Parser) parseParams() ([]FunctionParameter, error) {
|
||||||
params := make([]FunctionParameter, 0)
|
params := make([]FunctionParameter, 0)
|
||||||
|
|
||||||
if p.accept(TokenName) {
|
if p.accept(TokenName) {
|
||||||
name := (*p.state.prev).Lexeme
|
name := (*p.prev).Lexeme
|
||||||
if err := p.expect(TokenColon, "parameters must have a type separated by a colon"); err != nil {
|
if err := p.expect(TokenColon, "parameters must have a type separated by a colon"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -1048,7 +977,7 @@ func (p *Parser) parseParams() ([]FunctionParameter, error) {
|
||||||
if err := p.expect(TokenName, "parameters must have a name (cannot have trailing comma)"); err != nil {
|
if err := p.expect(TokenName, "parameters must have a name (cannot have trailing comma)"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
name = (*p.state.prev).Lexeme
|
name = (*p.prev).Lexeme
|
||||||
if err := p.expect(TokenColon, "parameters must have a type separated by a colon"); err != nil {
|
if err := p.expect(TokenColon, "parameters must have a type separated by a colon"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -1076,42 +1005,9 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
|
||||||
var s TypeSignature
|
var s TypeSignature
|
||||||
|
|
||||||
if p.accept(TokenOpenParenthesis) {
|
if p.accept(TokenOpenParenthesis) {
|
||||||
oldCare := p.state.ignoreNewLine
|
|
||||||
p.state.ignoreNewLine = true
|
|
||||||
p.skipNewLines()
|
|
||||||
|
|
||||||
// we're inside an object
|
// we're inside an object
|
||||||
name := p.state.curr
|
if p.acceptAll(TokenName, TokenColon) {
|
||||||
if p.acceptSeq(TokenName, TokenColon) {
|
return nil, p.error("objects are not implemented yet (TBD)", p.prev)
|
||||||
entries := map[string]TypeSignature{}
|
|
||||||
|
|
||||||
for len(entries) == 0 || !p.accept(TokenCloseParenthesis) {
|
|
||||||
if len(entries) != 0 {
|
|
||||||
p.skipNewLines()
|
|
||||||
name = p.state.curr
|
|
||||||
if !p.acceptSeq(TokenName, TokenColon) {
|
|
||||||
return nil, p.error("expected record member", p.state.curr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sig, err := p.parseSignature()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
entries[name.Lexeme] = sig
|
|
||||||
|
|
||||||
if !p.accept(TokenComma) {
|
|
||||||
if err := p.expect(TokenCloseParenthesis, "record must be closed"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &RecordSignature{
|
|
||||||
entries,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err := p.parseSignature()
|
v, err := p.parseSignature()
|
||||||
|
|
@ -1147,8 +1043,6 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
|
||||||
items,
|
items,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.state.ignoreNewLine = oldCare
|
|
||||||
} else if p.accept(TokenFunc) {
|
} else if p.accept(TokenFunc) {
|
||||||
if err := p.expect(TokenOpenParenthesis, "func signature must have parentheses for parameters"); err != nil {
|
if err := p.expect(TokenOpenParenthesis, "func signature must have parentheses for parameters"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -1201,7 +1095,7 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
|
||||||
if err := p.expect(TokenName, "type must be a name"); err != nil {
|
if err := p.expect(TokenName, "type must be a name"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
name := (*p.state.prev).Lexeme
|
name := (*p.prev).Lexeme
|
||||||
|
|
||||||
switch name {
|
switch name {
|
||||||
case "str":
|
case "str":
|
||||||
|
|
@ -1236,8 +1130,3 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) skipNewLines() {
|
|
||||||
for p.accept(TokenNewLine) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ func TestNewParser(t *testing.T) {
|
||||||
t.Fatal("parser should not be nil")
|
t.Fatal("parser should not be nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.state.pos != 0 {
|
if p.pos != 0 {
|
||||||
t.Error("parser should initialize position at 0")
|
t.Error("parser should initialize position at 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -715,95 +715,6 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"record/single": {
|
|
||||||
[]Token{
|
|
||||||
NewToken(TokenOpenParenthesis, 0, 0, 0, "("),
|
|
||||||
NewToken(TokenName, 0, 0, 1, "a"),
|
|
||||||
NewToken(TokenColon, 0, 0, 0, ":"),
|
|
||||||
NewToken(TokenInteger, 0, 0, 0, "2"),
|
|
||||||
NewToken(TokenCloseParenthesis, 0, 0, 0, ")"),
|
|
||||||
NewToken(TokenEOF, 0, 0, 0, ""),
|
|
||||||
},
|
|
||||||
&BlockNode{
|
|
||||||
[]Node{
|
|
||||||
&RecordNode{
|
|
||||||
map[string]Node{
|
|
||||||
"a": &IntegerNode{
|
|
||||||
big.NewInt(2),
|
|
||||||
0, 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
0, 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
0, 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"record/multiple": {
|
|
||||||
[]Token{
|
|
||||||
NewToken(TokenOpenParenthesis, 0, 0, 0, "("),
|
|
||||||
NewToken(TokenName, 0, 0, 1, "a"),
|
|
||||||
NewToken(TokenColon, 0, 0, 0, ":"),
|
|
||||||
NewToken(TokenInteger, 0, 0, 0, "2"),
|
|
||||||
NewToken(TokenComma, 0, 0, 0, ","),
|
|
||||||
NewToken(TokenName, 0, 0, 1, "b"),
|
|
||||||
NewToken(TokenColon, 0, 0, 0, ":"),
|
|
||||||
NewToken(TokenInteger, 0, 0, 0, "4"),
|
|
||||||
NewToken(TokenCloseParenthesis, 0, 0, 0, ")"),
|
|
||||||
NewToken(TokenEOF, 0, 0, 0, ""),
|
|
||||||
},
|
|
||||||
&BlockNode{
|
|
||||||
[]Node{
|
|
||||||
&RecordNode{
|
|
||||||
map[string]Node{
|
|
||||||
"a": &IntegerNode{
|
|
||||||
big.NewInt(2),
|
|
||||||
0, 0,
|
|
||||||
},
|
|
||||||
"b": &IntegerNode{
|
|
||||||
big.NewInt(4),
|
|
||||||
0, 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
0, 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
0, 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"record/shorthand": {
|
|
||||||
[]Token{
|
|
||||||
NewToken(TokenOpenParenthesis, 0, 0, 0, "("),
|
|
||||||
NewToken(TokenName, 0, 0, 1, "a"),
|
|
||||||
NewToken(TokenColon, 0, 0, 0, ":"),
|
|
||||||
NewToken(TokenComma, 0, 0, 0, ","),
|
|
||||||
NewToken(TokenName, 0, 0, 1, "b"),
|
|
||||||
NewToken(TokenColon, 0, 0, 0, ":"),
|
|
||||||
NewToken(TokenCloseParenthesis, 0, 0, 0, ")"),
|
|
||||||
NewToken(TokenEOF, 0, 0, 0, ""),
|
|
||||||
},
|
|
||||||
&BlockNode{
|
|
||||||
[]Node{
|
|
||||||
&RecordNode{
|
|
||||||
map[string]Node{
|
|
||||||
"a": &ReferenceNode{
|
|
||||||
"a",
|
|
||||||
0, 0,
|
|
||||||
},
|
|
||||||
"b": &ReferenceNode{
|
|
||||||
"b",
|
|
||||||
0, 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
0, 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
0, 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1001,23 +912,6 @@ func NodeEquality(t *testing.T, n1 Node, n2 Node) {
|
||||||
NodeEquality(t, v1, t2.items[i])
|
NodeEquality(t, v1, t2.items[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
case RecordNodeType:
|
|
||||||
r1 := n1.(*RecordNode)
|
|
||||||
r2 := n2.(*RecordNode)
|
|
||||||
|
|
||||||
if len(r1.entries) != len(r2.entries) {
|
|
||||||
t.Fatalf("Record node entries count does not match")
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, v1 := range r1.entries {
|
|
||||||
t.Logf("Checking item %s", i)
|
|
||||||
if v2, ok := r2.entries[i]; ok {
|
|
||||||
NodeEquality(t, v1, v2)
|
|
||||||
} else {
|
|
||||||
t.Errorf("Record node entry %s from first does not exist in other", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic("unimplemented node equality")
|
panic("unimplemented node equality")
|
||||||
}
|
}
|
||||||
|
|
@ -1045,55 +939,25 @@ func TestParser_Parse(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParser_AcceptSeq(t *testing.T) {
|
func TestParser_AcceptAll(t *testing.T) {
|
||||||
p := NewParser("a:", []string{}, []Token{
|
p := NewParser("a:", []string{}, []Token{
|
||||||
NewToken(TokenName, 0, 1, 0, "a"),
|
NewToken(TokenName, 0, 1, 0, "a"),
|
||||||
NewToken(TokenColon, 1, 2, 0, ":"),
|
NewToken(TokenColon, 1, 2, 0, "a"),
|
||||||
NewToken(TokenEOF, 2, 2, 0, ""),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// initialize
|
if !p.acceptAll(TokenName, TokenColon) {
|
||||||
p.advance()
|
|
||||||
|
|
||||||
if !p.acceptSeq(TokenName, TokenColon) {
|
|
||||||
t.Fatalf("tokens were not accepted")
|
t.Fatalf("tokens were not accepted")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("tokens were accepted")
|
t.Logf("tokens were accepted")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParser_AcceptSeqAndAfter(t *testing.T) {
|
func TestParser_AcceptAll_TooFew(t *testing.T) {
|
||||||
p := NewParser("a: 1", []string{}, []Token{
|
|
||||||
NewToken(TokenName, 0, 1, 0, "a"),
|
|
||||||
NewToken(TokenColon, 1, 2, 0, ":"),
|
|
||||||
NewToken(TokenInteger, 3, 4, 0, "1"),
|
|
||||||
NewToken(TokenEOF, 4, 4, 0, ""),
|
|
||||||
})
|
|
||||||
|
|
||||||
// initialize
|
|
||||||
p.advance()
|
|
||||||
|
|
||||||
if !p.acceptSeq(TokenName, TokenColon) {
|
|
||||||
t.Fatalf("seq tokens were not accepted")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !p.accept(TokenInteger) {
|
|
||||||
t.Fatalf("integer was not accepted")
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("tokens were accepted")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParser_AcceptSeq_TooFew(t *testing.T) {
|
|
||||||
p := NewParser("a", []string{}, []Token{
|
p := NewParser("a", []string{}, []Token{
|
||||||
NewToken(TokenName, 0, 1, 0, "a"),
|
NewToken(TokenName, 0, 1, 0, "a"),
|
||||||
NewToken(TokenEOF, 1, 1, 0, ""),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// initialize
|
if p.acceptAll(TokenName, TokenColon) {
|
||||||
p.advance()
|
|
||||||
|
|
||||||
if p.acceptSeq(TokenName, TokenColon) {
|
|
||||||
t.Fatalf("tokens were incorrectly accepted")
|
t.Fatalf("tokens were incorrectly accepted")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,19 +2,18 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CompareScope(t *testing.T, expectedScope []map[string]Value, actualScope *Scope) {
|
func CompareScope(t *testing.T, expectedScope []map[string]Value, actualScope *Scope) {
|
||||||
s := actualScope
|
s := actualScope
|
||||||
|
|
||||||
for _, e := range slices.Backward(expectedScope) {
|
for i := len(expectedScope) - 1; i >= 0; i-- {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
t.Fatal("scope cut unexpecetantly short")
|
t.Fatal("scope cut unexpecetantly short")
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, value := range e {
|
for name, value := range expectedScope[i] {
|
||||||
v, ok := s.current[name]
|
v, ok := s.current[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("variable %s not found in correct scope", name)
|
t.Errorf("variable %s not found in correct scope", name)
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ const (
|
||||||
TypeComposite
|
TypeComposite
|
||||||
TypeInner
|
TypeInner
|
||||||
TypeNamed
|
TypeNamed
|
||||||
TypeRecord
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t Type) String() string {
|
func (t Type) String() string {
|
||||||
|
|
@ -50,11 +49,9 @@ func (t Type) String() string {
|
||||||
return "composite"
|
return "composite"
|
||||||
case TypeInner:
|
case TypeInner:
|
||||||
return "inner"
|
return "inner"
|
||||||
case TypeRecord:
|
|
||||||
return "record"
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("unsupported string conversion for type %v", int(t)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
panic(fmt.Sprintf("unsupported string conversion for type %v", int(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func SignatureOf(v Value) TypeSignature {
|
func SignatureOf(v Value) TypeSignature {
|
||||||
|
|
@ -487,27 +484,6 @@ func quickComposite(a ...TypeSignature) TypeSignature {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func simplifyComposite(a TypeSignature) TypeSignature {
|
|
||||||
var atoms []TypeSignature
|
|
||||||
|
|
||||||
s := NewStack[TypeSignature](16)
|
|
||||||
s.Push(a)
|
|
||||||
|
|
||||||
for s.Current > 0 {
|
|
||||||
i := s.Pop()
|
|
||||||
|
|
||||||
c, ok := i.(*CompositeSignature)
|
|
||||||
if !ok {
|
|
||||||
|
|
||||||
atoms = append(atoms, i)
|
|
||||||
} else {
|
|
||||||
s.Push(c.A, c.B)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return quickComposite(atoms...)
|
|
||||||
}
|
|
||||||
|
|
||||||
type InnerSignature struct{}
|
type InnerSignature struct{}
|
||||||
|
|
||||||
func (*InnerSignature) Type() Type {
|
func (*InnerSignature) Type() Type {
|
||||||
|
|
@ -545,56 +521,3 @@ func (s *NamedSignature) Equal(t TypeSignature) bool {
|
||||||
func (s *NamedSignature) String() string {
|
func (s *NamedSignature) String() string {
|
||||||
return s.Name
|
return s.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
type RecordSignature struct {
|
|
||||||
Entries map[string]TypeSignature
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*RecordSignature) Type() Type {
|
|
||||||
return TypeRecord
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *RecordSignature) Contains(t TypeSignature) bool {
|
|
||||||
if t.Type() != TypeRecord {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
rs := t.(*RecordSignature)
|
|
||||||
|
|
||||||
/*
|
|
||||||
// not quite sure about this one
|
|
||||||
if len(s.Entries) != len(rs.Entries) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
for k, v := range s.Entries {
|
|
||||||
is, ok := rs.Entries[k]
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !v.Contains(is) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *RecordSignature) Equal(t TypeSignature) bool {
|
|
||||||
return s.Contains(t) && len(t.(*RecordSignature).Entries) == len(s.Entries)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *RecordSignature) String() string {
|
|
||||||
b := strings.Builder{}
|
|
||||||
b.WriteString("(")
|
|
||||||
|
|
||||||
for name, kind := range s.Entries {
|
|
||||||
b.WriteString(fmt.Sprintf("%s: %s, ", name, kind))
|
|
||||||
}
|
|
||||||
|
|
||||||
b.WriteString(")")
|
|
||||||
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
|
|
|
||||||
161
core/values.go
161
core/values.go
|
|
@ -22,7 +22,7 @@ const (
|
||||||
ObjectValueType
|
ObjectValueType
|
||||||
FunctionValueType
|
FunctionValueType
|
||||||
BuiltinFunctionValueType
|
BuiltinFunctionValueType
|
||||||
RecordValueType
|
VariableValueType
|
||||||
)
|
)
|
||||||
|
|
||||||
func (v ValueType) String() string {
|
func (v ValueType) String() string {
|
||||||
|
|
@ -47,15 +47,15 @@ func (v ValueType) String() string {
|
||||||
return "function"
|
return "function"
|
||||||
case BuiltinFunctionValueType:
|
case BuiltinFunctionValueType:
|
||||||
return "builtin function"
|
return "builtin function"
|
||||||
case RecordValueType:
|
case VariableValueType:
|
||||||
return "record"
|
return "variable"
|
||||||
}
|
}
|
||||||
|
|
||||||
return "undefined"
|
return "undefined"
|
||||||
}
|
}
|
||||||
|
|
||||||
// GoToValue convert go values to anglais VM-values. Works for some values (nil, bool, float64, int, string, slices, maps)
|
// GoToValue convert go values to anglais VM-values. Works for some values (nil, bool, float64, int, string, slices, maps)
|
||||||
func GoToValue(gov any) Value {
|
func GoToValue(gov interface{}) Value {
|
||||||
switch v := gov.(type) {
|
switch v := gov.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
return &NilValue{}
|
return &NilValue{}
|
||||||
|
|
@ -79,7 +79,7 @@ func GoToValue(gov any) Value {
|
||||||
return &StringValue{
|
return &StringValue{
|
||||||
v,
|
v,
|
||||||
}
|
}
|
||||||
case map[string]any:
|
case map[string]interface{}:
|
||||||
values := map[string]Value{}
|
values := map[string]Value{}
|
||||||
for key, value := range v {
|
for key, value := range v {
|
||||||
values[key] = GoToValue(value)
|
values[key] = GoToValue(value)
|
||||||
|
|
@ -117,12 +117,8 @@ type Value interface {
|
||||||
// Get a member from the value. An error is returned if the member does not exist
|
// Get a member from the value. An error is returned if the member does not exist
|
||||||
Get(string) (Value, error)
|
Get(string) (Value, error)
|
||||||
|
|
||||||
// Copy create a copy of the value. A copy is a direct copy for small data (numbers) and a copy of pointer
|
// Clone create a clone of the value. The returned value is a pointer to a new value of the same type as the value.
|
||||||
// for bigger data (lists, tuples, dicts)
|
Clone() Value
|
||||||
Copy() Value
|
|
||||||
|
|
||||||
// Clone create a clone of the value. A clone is new data for all data.
|
|
||||||
//Clone() Value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type NilValue struct{}
|
type NilValue struct{}
|
||||||
|
|
@ -147,7 +143,7 @@ func (v *NilValue) Get(_ string) (Value, error) {
|
||||||
return nil, errors.New("nil has no properties")
|
return nil, errors.New("nil has no properties")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *NilValue) Copy() Value {
|
func (v *NilValue) Clone() Value {
|
||||||
return &NilValue{}
|
return &NilValue{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -179,7 +175,7 @@ func (v *BoolValue) Get(_ string) (Value, error) {
|
||||||
return nil, errors.New("booleans have no properties")
|
return nil, errors.New("booleans have no properties")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *BoolValue) Copy() Value {
|
func (v *BoolValue) Clone() Value {
|
||||||
return &BoolValue{
|
return &BoolValue{
|
||||||
v.Boolean,
|
v.Boolean,
|
||||||
}
|
}
|
||||||
|
|
@ -262,11 +258,11 @@ func (v *ObjectValue) Get(key string) (Value, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ObjectValue) Copy() Value {
|
func (v *ObjectValue) Clone() Value {
|
||||||
m := make(map[string]Value, len(v.Members))
|
m := make(map[string]Value, len(v.Members))
|
||||||
|
|
||||||
for name, mem := range v.Members {
|
for name, mem := range v.Members {
|
||||||
m[name] = mem.Copy()
|
m[name] = mem.Clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ObjectValue{
|
return &ObjectValue{
|
||||||
|
|
@ -307,7 +303,7 @@ func (v *FloatValue) Get(_ string) (Value, error) {
|
||||||
return nil, errors.New("numbers have no properties")
|
return nil, errors.New("numbers have no properties")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *FloatValue) Copy() Value {
|
func (v *FloatValue) Clone() Value {
|
||||||
return &FloatValue{
|
return &FloatValue{
|
||||||
v.Number,
|
v.Number,
|
||||||
}
|
}
|
||||||
|
|
@ -338,7 +334,7 @@ func (v *IntegerValue) Get(_ string) (Value, error) {
|
||||||
return nil, errors.New("numbers have no properties")
|
return nil, errors.New("numbers have no properties")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *IntegerValue) Copy() Value {
|
func (v *IntegerValue) Clone() Value {
|
||||||
return &IntegerValue{
|
return &IntegerValue{
|
||||||
new(big.Int).Set(v.Number),
|
new(big.Int).Set(v.Number),
|
||||||
}
|
}
|
||||||
|
|
@ -387,7 +383,7 @@ var StringPrototype = map[string]*BuiltinFunctionValue{
|
||||||
}
|
}
|
||||||
|
|
||||||
if prev != len(str) {
|
if prev != len(str) {
|
||||||
out = append(out, &StringValue{str[prev:]})
|
out = append(out, &StringValue{str[prev:len(str)]})
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ListValue{out}, nil
|
return &ListValue{out}, nil
|
||||||
|
|
@ -431,7 +427,7 @@ func (v *StringValue) Get(key string) (Value, error) {
|
||||||
return nil, errors.New(fmt.Sprintf("string has no property \"%s\"", key))
|
return nil, errors.New(fmt.Sprintf("string has no property \"%s\"", key))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *StringValue) Copy() Value {
|
func (v *StringValue) Clone() Value {
|
||||||
return &StringValue{
|
return &StringValue{
|
||||||
v.Text,
|
v.Text,
|
||||||
}
|
}
|
||||||
|
|
@ -447,17 +443,16 @@ func (v *ListValue) Type() ValueType {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ListValue) String() string {
|
func (v *ListValue) String() string {
|
||||||
var out strings.Builder
|
out := "["
|
||||||
out.WriteString("[")
|
|
||||||
for i, item := range v.Items {
|
for i, item := range v.Items {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
out.WriteString(", ")
|
out += ", "
|
||||||
}
|
}
|
||||||
out.WriteString(item.DebugString())
|
out += item.DebugString()
|
||||||
}
|
}
|
||||||
out.WriteString("]")
|
out += "]"
|
||||||
|
|
||||||
return out.String()
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ListValue) DebugString() string {
|
func (v *ListValue) DebugString() string {
|
||||||
|
|
@ -599,9 +594,15 @@ func (v *ListValue) Get(key string) (Value, error) {
|
||||||
return nil, errors.New(fmt.Sprintf("list has no property \"%s\"", key))
|
return nil, errors.New(fmt.Sprintf("list has no property \"%s\"", key))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ListValue) Copy() Value {
|
func (v *ListValue) Clone() Value {
|
||||||
|
n := make([]Value, len(v.Items))
|
||||||
|
|
||||||
|
for i, item := range v.Items {
|
||||||
|
n[i] = item.Clone()
|
||||||
|
}
|
||||||
|
|
||||||
return &ListValue{
|
return &ListValue{
|
||||||
v.Items,
|
n,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -614,30 +615,33 @@ func (v *TupleValue) Type() ValueType {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *TupleValue) String() string {
|
func (v *TupleValue) String() string {
|
||||||
var out strings.Builder
|
out := "("
|
||||||
out.WriteString("(")
|
|
||||||
for i, item := range v.Items {
|
for i, item := range v.Items {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
out.WriteString(", ")
|
out += ", "
|
||||||
}
|
}
|
||||||
out.WriteString(item.DebugString())
|
out += item.DebugString()
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(v.Items) <= 1 {
|
if len(v.Items) <= 1 {
|
||||||
out.WriteString(",")
|
out += ","
|
||||||
}
|
}
|
||||||
out.WriteString(")")
|
out += ")"
|
||||||
|
|
||||||
return out.String()
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *TupleValue) DebugString() string {
|
func (v *TupleValue) DebugString() string {
|
||||||
return v.String()
|
return v.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *TupleValue) Copy() Value {
|
func (v *TupleValue) Clone() Value {
|
||||||
|
n := make([]Value, len(v.Items))
|
||||||
|
for i, item := range v.Items {
|
||||||
|
n[i] = item.Clone()
|
||||||
|
}
|
||||||
return &TupleValue{
|
return &TupleValue{
|
||||||
v.Items,
|
n,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -657,7 +661,7 @@ func (v *TupleValue) Equals(other Value) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
var TuplePrototype = map[string]*BuiltinFunctionValue{
|
var TuplePrototype = map[string]*BuiltinFunctionValue{
|
||||||
"at": {
|
"at": &BuiltinFunctionValue{
|
||||||
"at",
|
"at",
|
||||||
&FunctionSignature{
|
&FunctionSignature{
|
||||||
[]TypeSignature{&IntegerSignature{}},
|
[]TypeSignature{&IntegerSignature{}},
|
||||||
|
|
@ -714,7 +718,7 @@ func (v *FunctionValue) Get(_ string) (Value, error) {
|
||||||
return nil, errors.New("functions have no properties")
|
return nil, errors.New("functions have no properties")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *FunctionValue) Copy() Value {
|
func (v *FunctionValue) Clone() Value {
|
||||||
return &FunctionValue{
|
return &FunctionValue{
|
||||||
v.Name,
|
v.Name,
|
||||||
v.Params,
|
v.Params,
|
||||||
|
|
@ -754,7 +758,7 @@ func (v *BuiltinFunctionValue) Get(_ string) (Value, error) {
|
||||||
return nil, errors.New("functions have no properties")
|
return nil, errors.New("functions have no properties")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *BuiltinFunctionValue) Copy() Value {
|
func (v *BuiltinFunctionValue) Clone() Value {
|
||||||
return &BuiltinFunctionValue{
|
return &BuiltinFunctionValue{
|
||||||
v.Name,
|
v.Name,
|
||||||
v.Signature,
|
v.Signature,
|
||||||
|
|
@ -763,82 +767,3 @@ func (v *BuiltinFunctionValue) Copy() Value {
|
||||||
v.Constant,
|
v.Constant,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type RecordValue struct {
|
|
||||||
Entries map[string]Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *RecordValue) Type() ValueType {
|
|
||||||
return RecordValueType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *RecordValue) String() string {
|
|
||||||
sb := strings.Builder{}
|
|
||||||
|
|
||||||
sb.WriteString("(")
|
|
||||||
|
|
||||||
n := 0
|
|
||||||
for prop, value := range v.Entries {
|
|
||||||
if n != 0 {
|
|
||||||
sb.WriteString(", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.WriteString(prop)
|
|
||||||
sb.WriteString(": ")
|
|
||||||
sb.WriteString(value.DebugString())
|
|
||||||
|
|
||||||
n += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if n == 1 {
|
|
||||||
sb.WriteString(",")
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.WriteString(")")
|
|
||||||
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *RecordValue) DebugString() string {
|
|
||||||
return v.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *RecordValue) Copy() Value {
|
|
||||||
return &RecordValue{
|
|
||||||
v.Entries,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *RecordValue) Equals(other Value) bool {
|
|
||||||
if other.Type() != RecordValueType {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
r := other.(*RecordValue)
|
|
||||||
|
|
||||||
if len(r.Entries) != len(v.Entries) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, val := range v.Entries {
|
|
||||||
oth, ok := r.Entries[key]
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !val.Equals(oth) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *RecordValue) Get(key string) (Value, error) {
|
|
||||||
val, ok := v.Entries[key]
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New(fmt.Sprintf("record has no property \"%s\"", key))
|
|
||||||
}
|
|
||||||
|
|
||||||
return val, nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
67
core/vm.go
67
core/vm.go
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"slices"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
@ -127,7 +126,7 @@ const (
|
||||||
// InstructionAppend Append to a list. stack: (... > list > item) => (... > list)
|
// InstructionAppend Append to a list. stack: (... > list > item) => (... > list)
|
||||||
InstructionAppend
|
InstructionAppend
|
||||||
// InstructionFormList Form items on the stack into a list. The 2 bytes after the instructions are the amount of
|
// InstructionFormList Form items on the stack into a list. The 2 bytes after the instructions are the amount of
|
||||||
// items to include. The order is reversed compared to on the stack; the top value on the stack is the last in the
|
// items to include) The order is reversed compared to on the stack; the top value on the stack is the last in the
|
||||||
// list.
|
// list.
|
||||||
InstructionFormList
|
InstructionFormList
|
||||||
// InstructionConcatLists concatenate lists, producing a new list with the values of both lists. Pops two lists.
|
// InstructionConcatLists concatenate lists, producing a new list with the values of both lists. Pops two lists.
|
||||||
|
|
@ -140,12 +139,6 @@ const (
|
||||||
// being the last item in the tuple.
|
// being the last item in the tuple.
|
||||||
InstructionDestructureTuple
|
InstructionDestructureTuple
|
||||||
|
|
||||||
// InstructionNewRecord Create a new empty record.
|
|
||||||
InstructionNewRecord
|
|
||||||
// InstructionSetRecordItem Set the value of an item in the record, and create it if it does not already exist.
|
|
||||||
// [..., record, item]; the following byte should be the index of a string constant with the name of the property.
|
|
||||||
InstructionSetRecordItem
|
|
||||||
|
|
||||||
// InstructionIndexList index into a list. The lower item is the container, and the top item
|
// InstructionIndexList index into a list. The lower item is the container, and the top item
|
||||||
// is the index. [..., container, index] -> [..., item]
|
// is the index. [..., container, index] -> [..., item]
|
||||||
InstructionIndexList
|
InstructionIndexList
|
||||||
|
|
@ -153,14 +146,10 @@ const (
|
||||||
// is the index. [..., container, index] -> [..., item]
|
// is the index. [..., container, index] -> [..., item]
|
||||||
InstructionIndexTuple
|
InstructionIndexTuple
|
||||||
// InstructionIndexString index into a string. The lower item is the container, and the top item
|
// InstructionIndexString index into a string. The lower item is the container, and the top item
|
||||||
// is the index. [..., container, index] -> [..., item]. Produces a new string with only the character
|
// is the index. [..., container, index] -> [..., item]. Produces a new string with the character
|
||||||
// at the indexed position
|
// at the position
|
||||||
InstructionIndexString
|
InstructionIndexString
|
||||||
|
|
||||||
// InstructionSetIndexList set the item at a given index in a list.
|
|
||||||
// [..., item, container, index] -> [..., item]
|
|
||||||
InstructionSetIndexList
|
|
||||||
|
|
||||||
// InstructionBreakpoint for debugging purposes
|
// InstructionBreakpoint for debugging purposes
|
||||||
InstructionBreakpoint
|
InstructionBreakpoint
|
||||||
)
|
)
|
||||||
|
|
@ -273,14 +262,6 @@ func (b Bytecode) String() string {
|
||||||
return "INDEX_TUPLE"
|
return "INDEX_TUPLE"
|
||||||
case InstructionDestructureTuple:
|
case InstructionDestructureTuple:
|
||||||
return "DESTRUCTURE_TUPLE"
|
return "DESTRUCTURE_TUPLE"
|
||||||
case InstructionModInt:
|
|
||||||
return "MOD_INT"
|
|
||||||
case InstructionNewRecord:
|
|
||||||
return "NEW_RECORD"
|
|
||||||
case InstructionSetRecordItem:
|
|
||||||
return "SET_PROPERTY"
|
|
||||||
case InstructionIndexString:
|
|
||||||
return "INDEX_STRING"
|
|
||||||
}
|
}
|
||||||
return "UNDEFINED"
|
return "UNDEFINED"
|
||||||
}
|
}
|
||||||
|
|
@ -643,7 +624,7 @@ var DefaultGlobals = map[string]Value{
|
||||||
n, _ := v.Number.Float64()
|
n, _ := v.Number.Float64()
|
||||||
return &FloatValue{n}, nil
|
return &FloatValue{n}, nil
|
||||||
case *FloatValue:
|
case *FloatValue:
|
||||||
return v.Copy(), nil
|
return v.Clone(), nil
|
||||||
case *StringValue:
|
case *StringValue:
|
||||||
num, err := strconv.ParseFloat(v.Text, FloatSize)
|
num, err := strconv.ParseFloat(v.Text, FloatSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -927,8 +908,8 @@ func (vm *VM) Next() bool {
|
||||||
vm.scope = f.Scope
|
vm.scope = f.Scope
|
||||||
vm.descend()
|
vm.descend()
|
||||||
|
|
||||||
for _, v := range slices.Backward(f.Params) {
|
for i := len(f.Params) - 1; i >= 0; i-- {
|
||||||
vm.addVar(v.Name, vm.Stack.Pop())
|
vm.addVar(f.Params[i].Name, vm.Stack.Pop())
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.Parent != nil {
|
if f.Parent != nil {
|
||||||
|
|
@ -983,7 +964,7 @@ func (vm *VM) Next() bool {
|
||||||
vm.Stack.Push(v)
|
vm.Stack.Push(v)
|
||||||
|
|
||||||
case InstructionSetLocal:
|
case InstructionSetLocal:
|
||||||
value := vm.Stack.Peek().Copy()
|
value := vm.Stack.Peek().Clone()
|
||||||
name := vm.GetConstant(vm.NextByte()).(*StringValue).Text
|
name := vm.GetConstant(vm.NextByte()).(*StringValue).Text
|
||||||
|
|
||||||
vm.setVar(name, value)
|
vm.setVar(name, value)
|
||||||
|
|
@ -991,7 +972,7 @@ func (vm *VM) Next() bool {
|
||||||
case InstructionDeclareLocal:
|
case InstructionDeclareLocal:
|
||||||
vm.addVar(
|
vm.addVar(
|
||||||
vm.GetConstant(vm.NextByte()).(*StringValue).Text,
|
vm.GetConstant(vm.NextByte()).(*StringValue).Text,
|
||||||
vm.Stack.Peek().Copy(),
|
vm.Stack.Peek().Clone(),
|
||||||
)
|
)
|
||||||
|
|
||||||
case InstructionGetGlobal:
|
case InstructionGetGlobal:
|
||||||
|
|
@ -1075,7 +1056,7 @@ func (vm *VM) Next() bool {
|
||||||
vm.Stack.Push(r, l)
|
vm.Stack.Push(r, l)
|
||||||
|
|
||||||
case InstructionDuplicate:
|
case InstructionDuplicate:
|
||||||
vm.Stack.Push(vm.Stack.Peek().Copy())
|
vm.Stack.Push(vm.Stack.Peek().Clone())
|
||||||
|
|
||||||
case InstructionAccessProperty:
|
case InstructionAccessProperty:
|
||||||
source := vm.Stack.Pop()
|
source := vm.Stack.Pop()
|
||||||
|
|
@ -1095,18 +1076,6 @@ func (vm *VM) Next() bool {
|
||||||
|
|
||||||
vm.Stack.Push(member)
|
vm.Stack.Push(member)
|
||||||
|
|
||||||
case InstructionSetRecordItem:
|
|
||||||
i := vm.Stack.Pop()
|
|
||||||
prop := vm.ReadConstant().(*StringValue)
|
|
||||||
r := vm.Stack.Peek().(*RecordValue)
|
|
||||||
|
|
||||||
r.Entries[prop.Text] = i
|
|
||||||
|
|
||||||
case InstructionNewRecord:
|
|
||||||
vm.Stack.Push(&RecordValue{
|
|
||||||
map[string]Value{},
|
|
||||||
})
|
|
||||||
|
|
||||||
case InstructionIndexList:
|
case InstructionIndexList:
|
||||||
i := vm.Stack.Pop().(*IntegerValue)
|
i := vm.Stack.Pop().(*IntegerValue)
|
||||||
l := vm.Stack.Pop().(*ListValue)
|
l := vm.Stack.Pop().(*ListValue)
|
||||||
|
|
@ -1117,7 +1086,7 @@ func (vm *VM) Next() bool {
|
||||||
vm.error(fmt.Sprintf("index %d out of bounds", n))
|
vm.error(fmt.Sprintf("index %d out of bounds", n))
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.Stack.Push(l.Items[n].Copy())
|
vm.Stack.Push(l.Items[n].Clone())
|
||||||
|
|
||||||
case InstructionIndexTuple:
|
case InstructionIndexTuple:
|
||||||
i := vm.Stack.Pop().(*IntegerValue)
|
i := vm.Stack.Pop().(*IntegerValue)
|
||||||
|
|
@ -1129,7 +1098,7 @@ func (vm *VM) Next() bool {
|
||||||
vm.error(fmt.Sprintf("index %d out of bounds", n))
|
vm.error(fmt.Sprintf("index %d out of bounds", n))
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.Stack.Push(t.Items[n].Copy())
|
vm.Stack.Push(t.Items[n].Clone())
|
||||||
|
|
||||||
case InstructionIndexString:
|
case InstructionIndexString:
|
||||||
i := vm.Stack.Pop().(*IntegerValue)
|
i := vm.Stack.Pop().(*IntegerValue)
|
||||||
|
|
@ -1143,18 +1112,6 @@ func (vm *VM) Next() bool {
|
||||||
|
|
||||||
vm.Stack.Push(&StringValue{string(s.Text[n])})
|
vm.Stack.Push(&StringValue{string(s.Text[n])})
|
||||||
|
|
||||||
case InstructionSetIndexList:
|
|
||||||
n := vm.Stack.Pop().(*IntegerValue)
|
|
||||||
l := vm.Stack.Pop().(*ListValue)
|
|
||||||
|
|
||||||
i := n.Number.Int64()
|
|
||||||
|
|
||||||
if i < 0 || int64(len(l.Items)) <= i {
|
|
||||||
vm.error(fmt.Sprintf("index %d out of bounds", i))
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Items[i] = vm.Stack.Peek().Copy()
|
|
||||||
|
|
||||||
case InstructionBreakpoint:
|
case InstructionBreakpoint:
|
||||||
/*
|
/*
|
||||||
// I'm keeping this
|
// I'm keeping this
|
||||||
|
|
@ -1301,7 +1258,7 @@ func (vm *VM) HasNext() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) GetConstant(id Bytecode) Value {
|
func (vm *VM) GetConstant(id Bytecode) Value {
|
||||||
return vm.chunk.Constants[id].Copy()
|
return vm.chunk.Constants[id].Clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) ReadConstant() Value {
|
func (vm *VM) ReadConstant() Value {
|
||||||
|
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
import "lib/math.ang"
|
|
||||||
|
|
||||||
primes := [2]
|
|
||||||
|
|
||||||
func is_prime(x: number) boolean {
|
|
||||||
i := 0
|
|
||||||
while i < primes.length() && primes.at(i)*primes.at(i) < x {
|
|
||||||
if mod(x, primes.at(i)) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
i = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
n := 1
|
|
||||||
max := 100000
|
|
||||||
|
|
||||||
while n < max {
|
|
||||||
n = n + 2
|
|
||||||
|
|
||||||
if is_prime(n) {
|
|
||||||
primes.append(n)
|
|
||||||
|
|
||||||
# Update counter
|
|
||||||
print(char(0x0D))
|
|
||||||
print(str(n))
|
|
||||||
print("/")
|
|
||||||
print(str(max))
|
|
||||||
print(char(0x09))
|
|
||||||
print(str(roundd(n/max*100, 2)))
|
|
||||||
print("%")
|
|
||||||
print(char(0x09))
|
|
||||||
print(str(primes.length()))
|
|
||||||
print(" primes")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write(str(primes))
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
fn fib(n: int) -> int {
|
|
||||||
if n < 2 {
|
|
||||||
n
|
|
||||||
} else {
|
|
||||||
fib(n-1) + fib(n-2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println(fib(2))
|
|
||||||
|
|
||||||
fn other(n: int) -> int {
|
|
||||||
if n <= 0 {
|
|
||||||
n
|
|
||||||
} else {
|
|
||||||
println(n)
|
|
||||||
(n - 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println(other(2))
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
|
|
||||||
MAX_WIDTH := 16
|
|
||||||
|
|
||||||
print(" ")
|
|
||||||
w := 1
|
|
||||||
n := 0x21
|
|
||||||
while n < 0xA0 {
|
|
||||||
print(char(n))
|
|
||||||
n = n + 1
|
|
||||||
w = w + 1
|
|
||||||
if w >= MAX_WIDTH {
|
|
||||||
write("")
|
|
||||||
w = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
|
|
||||||
passphrase := "Hello world!".split("")
|
|
||||||
start := [0, 0, 0]
|
|
||||||
modulus := 10
|
|
||||||
base := byte("!")
|
|
||||||
|
|
||||||
i := 0
|
|
||||||
n := 0
|
|
||||||
while n < passphrase.length() {
|
|
||||||
b := byte(passphrase.at(n))
|
|
||||||
|
|
||||||
v = start.at(i) + b - base
|
|
||||||
while v >= modulus {
|
|
||||||
v = v - modulus
|
|
||||||
}
|
|
||||||
|
|
||||||
start.put(i, v)
|
|
||||||
|
|
||||||
if i >= 3 {
|
|
||||||
i = 0
|
|
||||||
}
|
|
||||||
n = n + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
|
|
||||||
write(char(0x12) + char(0x85) + char(0x07))
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fn counter() -> (fn() -> int) {
|
|
||||||
i := 0
|
|
||||||
|
|
||||||
fn() -> int { i = i + 1 }
|
|
||||||
}
|
|
||||||
|
|
||||||
next := counter()
|
|
||||||
|
|
||||||
println(next())
|
|
||||||
println(next())
|
|
||||||
println(next())
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
import "lib/honning.ang"
|
|
||||||
|
|
||||||
write(_bell+_italic+"Hello "+_underline+"world "+_strike+"micheal"+_reset)
|
|
||||||
|
|
||||||
|
|
@ -3,9 +3,14 @@
|
||||||
|
|
||||||
fn range(from: int, to: int) -> (fn() -> (int, bool)) {
|
fn range(from: int, to: int) -> (fn() -> (int, bool)) {
|
||||||
i := from - 1
|
i := from - 1
|
||||||
|
end := to - 1
|
||||||
|
|
||||||
fn() -> (int, bool) {
|
fn() -> (int, bool) {
|
||||||
(i = i+1, i+1 < to)
|
if i < end {
|
||||||
|
(i = i+1, true)
|
||||||
|
} else {
|
||||||
|
(-1, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
|
|
||||||
fn foo(n: int) -> str {
|
|
||||||
if n % 2 == 0 {
|
|
||||||
"foo"
|
|
||||||
} else {
|
|
||||||
"bar"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
|
|
||||||
func is_cool(x: number|string) boolean {
|
|
||||||
if x == "cool" {
|
|
||||||
return true
|
|
||||||
} else if x == 69 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
write(str(is_cool("not cool")))
|
|
||||||
write(str(is_cool("cool")))
|
|
||||||
write(str(is_cool(0)))
|
|
||||||
write(str(is_cool(69)))
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
|
|
||||||
foo := include "foo.ang"
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
# Empty list
|
# Empty list
|
||||||
println([]any)
|
println([])
|
||||||
|
|
||||||
# List with items
|
# List with items
|
||||||
println([3, 1, 4, 1, 5, 9, 2, 6, 5])
|
println([3, 1, 4, 1, 5, 9, 2, 6, 5])
|
||||||
|
|
@ -8,25 +8,25 @@ println([3, 1, 4, 1, 5, 9, 2, 6, 5])
|
||||||
# List with items of different types
|
# List with items of different types
|
||||||
println(["", "私はかっこいいです。", true, nil, nil, 1, 2])
|
println(["", "私はかっこいいです。", true, nil, nil, 1, 2])
|
||||||
|
|
||||||
a := []int
|
a := []
|
||||||
|
|
||||||
a.push(1)
|
a = a + [1]
|
||||||
a.push(2)
|
a = a + [2]
|
||||||
|
|
||||||
println(a)
|
println(a)
|
||||||
|
|
||||||
|
|
||||||
list := []int
|
list := []
|
||||||
|
|
||||||
x := 0
|
x := 0
|
||||||
for n in 0..100 {
|
for n in 0..100 {
|
||||||
x = x + 2*n + 1
|
x = x + 2*n + 1
|
||||||
|
|
||||||
list.push(x)
|
list = list + [x]
|
||||||
}
|
}
|
||||||
|
|
||||||
println(list)
|
println(list)
|
||||||
println(list.map(fn(a: int) -> int {
|
println(list.map(func(a) {
|
||||||
return a - 1
|
return a - 1
|
||||||
}))
|
}))
|
||||||
println(list.length())
|
println(list.length())
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
a := fn b(n: int) -> int {
|
|
||||||
n + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
|
|
||||||
func get(url: string) string {
|
|
||||||
res := request("GET", url, "")
|
|
||||||
|
|
||||||
return res.text
|
|
||||||
}
|
|
||||||
|
|
||||||
write(get("https://www.neemek.com/hello.txt"))
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
import "../lib/math.ang"
|
|
||||||
|
|
||||||
# Inputs
|
|
||||||
c := 1.0*pow(10.0, -2.0)
|
|
||||||
|
|
||||||
println(c)
|
|
||||||
pH := -log(c, 10.0)
|
|
||||||
|
|
||||||
println(pH)
|
|
||||||
|
|
||||||
if pH == 7.0 {
|
|
||||||
println("pH-en er nøytral (=7)")
|
|
||||||
} else if pH < 7.0 {
|
|
||||||
println("pH-en er sur (<7)")
|
|
||||||
} else {
|
|
||||||
println("pH-en er basisk (>7)")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
|
|
||||||
type Vec2 = (float, float)
|
|
||||||
|
|
||||||
fn add(a: Vec2, b: Vec2) -> Vec2 {
|
|
||||||
(a[0] + b[0], a[1] + b[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sub(a: Vec2, b: Vec2) -> Vec2 {
|
|
||||||
(a[0] - b[0], a[1] - b[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dot(a: Vec2, b: Vec2) -> float {
|
|
||||||
a[0]*b[0] + a[1]*b[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
u := (0.0, 1.0)
|
|
||||||
v := (2.0, 3.0)
|
|
||||||
|
|
||||||
println(add(u, v))
|
|
||||||
println(sub(u, v))
|
|
||||||
println(dot(u, v))
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
# This program computes the fibonacci numbers using recursion (O(2^n))
|
# This program computes the fibonacci numbers using recursion (O(2^n))
|
||||||
# It is very slow
|
# It is very slow
|
||||||
fn fib(x: number) number {
|
func fib(x: number) number {
|
||||||
if x <= 1 {
|
if x <= 1 {
|
||||||
x
|
return x
|
||||||
} else {
|
|
||||||
fib(x - 1) + fib(x - 2)
|
|
||||||
}
|
}
|
||||||
|
return fib(x - 1) + fib(x - 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
n := 0
|
n := 0
|
||||||
while n < 100 {
|
while n < 100 {
|
||||||
println(fib(n))
|
write(str(fib(n)))
|
||||||
n = n + 1
|
n = n + 1
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
#!/Users/neemek/Code/anglais/cli/cli run
|
|
||||||
|
|
||||||
import "lib/math.ang"
|
|
||||||
|
|
||||||
x := sqrt(9485739448)
|
|
||||||
write(str(x))
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
|
|
||||||
a := 0
|
|
||||||
while a < 10 {
|
|
||||||
print(".")
|
|
||||||
a = a + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
write("")
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
|
|
||||||
# Aliases
|
|
||||||
type Receiver = fn() -> any
|
|
||||||
type Sender = fn(any)
|
|
||||||
|
|
||||||
fn new_channel() -> (Sender, Receiver) {
|
|
||||||
queue := []
|
|
||||||
|
|
||||||
fn send(n: any) {
|
|
||||||
queue.push(n) # mutate in-place
|
|
||||||
}
|
|
||||||
|
|
||||||
fn recv() -> any | nil {
|
|
||||||
queue.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
(send, recv)
|
|
||||||
}
|
|
||||||
|
|
||||||
(send, recv) := new_channel()
|
|
||||||
|
|
||||||
(
|
|
||||||
send:,
|
|
||||||
recv:,
|
|
||||||
)
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
import "worst.ang"
|
|
||||||
|
|
||||||
write("Hello world!")
|
|
||||||
|
|
||||||
0
foo.ang
0
foo.ang
|
|
@ -1 +0,0 @@
|
||||||
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
fn map<T, R>(list: [T], f: fn(T) -> R) -> [R] {
|
fn<T, R> ([T]) map(f: fn(T) -> R) -> [R] {
|
||||||
out := []
|
out := []
|
||||||
|
|
||||||
for v in list.iter() {
|
for v in list.iter() {
|
||||||
|
|
|
||||||
|
|
@ -249,5 +249,3 @@ fn tan(x: float) -> float {
|
||||||
# todo
|
# todo
|
||||||
0.0
|
0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
(E:, PI:, sqrt:, log:, ln:, exp:, pow:)
|
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,25 @@
|
||||||
|
|
||||||
NAMESPACE := ""
|
NAMESPACE := ""
|
||||||
|
|
||||||
fn namespace(name: str, test: fn()) {
|
func namespace(name: string, test: func()) {
|
||||||
NAMESPACE = name
|
NAMESPACE = name
|
||||||
test()
|
test()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eq<T>(a: T, b: T) {
|
func assertEqual(a: any, b: any) {
|
||||||
if a != b {
|
if a != b {
|
||||||
println(format("assertion error: % should (but doesn't) equal %", [a, b]))
|
write(format("assertion error: % should (but doesn't) equal %", [a, b]))
|
||||||
exit(1)
|
exit(1)
|
||||||
} else if env("DEBUG") != "" {
|
} else if env("DEBUG") != "" {
|
||||||
println(format("assertion success: % equals %", [a, b]))
|
write(format("assertion success: % equals %", [a, b]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn neq<T>(a: T, b: T) {
|
func assertNotEqual(a: any, b: any) {
|
||||||
if a == b {
|
if a == b {
|
||||||
println(format("assertion error: % shouldn't (but does) equal %", [a, b]))
|
write(format("assertion error: % shouldn't (but does) equal %", [a, b]))
|
||||||
exit(1)
|
exit(1)
|
||||||
} else if env("DEBUG") != "" {
|
} else if env("DEBUG") != "" {
|
||||||
println(format("assertion success: % doesn't equal %", [a, b]))
|
write(format("assertion success: % doesn't equal %", [a, b]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
29
records.ang
29
records.ang
|
|
@ -1,29 +0,0 @@
|
||||||
|
|
||||||
type User = (
|
|
||||||
id: int,
|
|
||||||
name: str,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
user := {
|
|
||||||
user_id := 0
|
|
||||||
all_users := []User
|
|
||||||
|
|
||||||
(
|
|
||||||
get_all: fn() -> [User] { all_users },
|
|
||||||
new: fn(name: str) -> User {
|
|
||||||
data := (id: { user_id = user_id + 1 }, name:)
|
|
||||||
all_users.append(data)
|
|
||||||
data
|
|
||||||
},
|
|
||||||
fmt: fn(user: User) -> str {
|
|
||||||
user.name + "(" + str(user.id) + ")"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
abe := user.new("abe")
|
|
||||||
lincoln := user.new("lincoln")
|
|
||||||
|
|
||||||
println(abe)
|
|
||||||
println(lincoln)
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
echo '=== Building WASM lib ==='
|
echo '=== Building WASM lib ==='
|
||||||
cd wasm || exit 1
|
cd wasm || exit 1
|
||||||
if ! GOOS=js GOARCH=wasm go build . "$@"; then
|
if ! GOOS=js GOARCH=wasm go build .; then
|
||||||
echo "=x= Had error building WASM lib =x="
|
echo "=x= Had error building WASM lib =x="
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
|
|
@ -12,7 +12,7 @@ cd ..
|
||||||
|
|
||||||
echo '=== Building CLI ==='
|
echo '=== Building CLI ==='
|
||||||
cd cli || exit 1
|
cd cli || exit 1
|
||||||
if ! go build . "$@"; then
|
if ! go build .; then
|
||||||
echo "=x= Had error building CLI =x="
|
echo "=x= Had error building CLI =x="
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
|
|
@ -22,7 +22,7 @@ cd ..
|
||||||
|
|
||||||
echo "=== Running go core tests ==="
|
echo "=== Running go core tests ==="
|
||||||
cd core || exit 1
|
cd core || exit 1
|
||||||
if ! go test . "$@"; then
|
if ! go test .; then
|
||||||
echo "=x= Core testing failed =x= "
|
echo "=x= Core testing failed =x= "
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
|
|
||||||
fn range(from: int, to: int) -> (fn() -> (int, bool)) {
|
|
||||||
i := from - 1
|
|
||||||
|
|
||||||
fn() -> (int, bool) {
|
|
||||||
i = i + 1
|
|
||||||
if i >= to {
|
|
||||||
(-1, false)
|
|
||||||
} else {
|
|
||||||
(i, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in range(0, 10) {
|
|
||||||
println(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn chars(s: str) -> (fn() -> (str, bool)) {
|
|
||||||
i := 0
|
|
||||||
|
|
||||||
fn() -> (str, bool) {
|
|
||||||
if i >= s.length() {
|
|
||||||
("", false)
|
|
||||||
} else {
|
|
||||||
c := s[i]
|
|
||||||
i = i + 1
|
|
||||||
|
|
||||||
(c, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for c in chars("hello") {
|
|
||||||
print(c)
|
|
||||||
print(" ")
|
|
||||||
}
|
|
||||||
|
|
||||||
println("")
|
|
||||||
|
|
||||||
fn items(l: [any]) -> (fn() -> (any, bool)) {
|
|
||||||
i := 0
|
|
||||||
|
|
||||||
fn() -> (any, bool) {
|
|
||||||
if i >= l.length() {
|
|
||||||
(nil, false)
|
|
||||||
} else {
|
|
||||||
i = i + 1
|
|
||||||
(l[i - 1], true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -34,10 +34,3 @@ assertEq("the, first, time".split(", "), ["the", "first", "time"])
|
||||||
# list indexing
|
# list indexing
|
||||||
assertEq([1, 2, 3].at(1), 2)
|
assertEq([1, 2, 3].at(1), 2)
|
||||||
assertEq(["a", "b", "c"].at(2), "c")
|
assertEq(["a", "b", "c"].at(2), "c")
|
||||||
|
|
||||||
# mutating list
|
|
||||||
a := [1, 2, 3]
|
|
||||||
assertEq(a, [1, 2, 3])
|
|
||||||
|
|
||||||
a[1] = 4
|
|
||||||
assertEq(a, [1, 4, 3])
|
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,3 @@ fn neighbours(n: int) -> (int, int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEq(neighbours(2), (1, 3))
|
assertEq(neighbours(2), (1, 3))
|
||||||
|
|
||||||
a := (1, 2)
|
|
||||||
assertEq(a, (1, 2))
|
|
||||||
|
|
||||||
(x, y) := a
|
|
||||||
assertEq(x, 1)
|
|
||||||
assertEq(y, 2)
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue