Compare commits

..

1 commit

Author SHA1 Message Date
677808fa0e
non-functional optimizer
All checks were successful
/ test (push) Successful in 46s
2026-07-15 21:49:19 +02:00
46 changed files with 520 additions and 1109 deletions

8
.idea/.gitignore generated vendored Normal file
View 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
View 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>

View 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
View 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>

View file

@ -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:
} }

View file

@ -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
} }
} }

View file

@ -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()
} }
} }

View file

@ -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
View 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
}

View file

@ -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) {
}
}

View file

@ -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")
} }

View file

@ -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)

View file

@ -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()
}

View file

@ -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
}

View file

@ -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 {

View file

@ -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))

View file

@ -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))

View file

@ -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
}
}

View file

@ -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
}

View file

@ -1,2 +0,0 @@
write(char(0x12) + char(0x85) + char(0x07))

View file

@ -1,11 +0,0 @@
fn counter() -> (fn() -> int) {
i := 0
fn() -> int { i = i + 1 }
}
next := counter()
println(next())
println(next())
println(next())

View file

@ -1,4 +0,0 @@
import "lib/honning.ang"
write(_bell+_italic+"Hello "+_underline+"world "+_strike+"micheal"+_reset)

View file

@ -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)
}
} }
} }

View file

@ -1,8 +0,0 @@
fn foo(n: int) -> str {
if n % 2 == 0 {
"foo"
} else {
"bar"
}
}

View file

@ -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)))

View file

@ -1,2 +0,0 @@
foo := include "foo.ang"

View file

@ -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())

View file

@ -1,4 +0,0 @@
a := fn b(n: int) -> int {
n + 1
}

View file

@ -1,8 +0,0 @@
func get(url: string) string {
res := request("GET", url, "")
return res.text
}
write(get("https://www.neemek.com/hello.txt"))

View file

@ -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)")
}

View file

@ -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))

View file

@ -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
} }

View file

@ -1,6 +0,0 @@
#!/Users/neemek/Code/anglais/cli/cli run
import "lib/math.ang"
x := sqrt(9485739448)
write(str(x))

View file

@ -1,8 +0,0 @@
a := 0
while a < 10 {
print(".")
a = a + 1
}
write("")

View file

@ -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:,
)

View file

@ -1,4 +0,0 @@
import "worst.ang"
write("Hello world!")

View file

View file

@ -1 +0,0 @@
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=

View file

@ -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() {

View file

@ -249,5 +249,3 @@ fn tan(x: float) -> float {
# todo # todo
0.0 0.0
} }
(E:, PI:, sqrt:, log:, ln:, exp:, pow:)

View file

@ -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]))
} }
} }

View file

@ -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)

View file

@ -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

View file

@ -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)
}
}
}

View file

@ -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])

View file

@ -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)