basic records support
This commit is contained in:
parent
19011e67da
commit
ff11d7e0ed
11 changed files with 522 additions and 138 deletions
|
|
@ -265,6 +265,30 @@ func (c *Compiler) compile(tree Node) (TypeSignature, error) {
|
|||
|
||||
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:
|
||||
l := tree.(*ListNode)
|
||||
|
||||
|
|
@ -1196,6 +1220,13 @@ func (c *Compiler) getPropertySignature(source TypeSignature, property string) (
|
|||
return &CompositeSignature{
|
||||
at, bt,
|
||||
}, 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:
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import (
|
|||
)
|
||||
|
||||
type Token struct {
|
||||
Type TokenKind
|
||||
Kind TokenKind
|
||||
Start Pos
|
||||
End Pos
|
||||
Line Pos
|
||||
|
|
@ -19,7 +19,7 @@ func (t Token) Bounds() (Pos, Pos) {
|
|||
}
|
||||
|
||||
func (t Token) String() string {
|
||||
return fmt.Sprintf("token %s, '%s' %d -> %d, line %d", t.Type.String(), t.Lexeme, t.Start, t.End, t.Line)
|
||||
return fmt.Sprintf("token %s, '%s' %d -> %d, line %d", t.Kind.String(), t.Lexeme, t.Start, t.End, t.Line)
|
||||
}
|
||||
|
||||
type TokenKind uint64
|
||||
|
|
@ -415,7 +415,7 @@ func (l *Lexer) NextToken() (Token, error) {
|
|||
|
||||
func NewToken(t TokenKind, start Pos, end Pos, line Pos, lexeme string) Token {
|
||||
return Token{
|
||||
Type: t,
|
||||
Kind: t,
|
||||
Start: start,
|
||||
End: end,
|
||||
Line: line,
|
||||
|
|
@ -430,7 +430,7 @@ func (l *Lexer) Tokenize() ([]Token, error) {
|
|||
for ; err == nil; tok, err = l.NextToken() {
|
||||
tokens = append(tokens, tok)
|
||||
|
||||
if tok.Type == TokenEOF {
|
||||
if tok.Kind == TokenEOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,8 +148,8 @@ func TestLexer_NextToken(t *testing.T) {
|
|||
continue
|
||||
}
|
||||
|
||||
if tok.Type != expectedType {
|
||||
t.Errorf("Expected token type '%s' but got '%s'", expectedType, tok.Type)
|
||||
if tok.Kind != expectedType {
|
||||
t.Errorf("Expected token type '%s' but got '%s'", expectedType, tok.Kind)
|
||||
} else {
|
||||
t.Logf("Got expected token type '%s'", expectedType)
|
||||
}
|
||||
|
|
@ -193,7 +193,7 @@ func TestLexer_NextTokenErrors(t *testing.T) {
|
|||
lex := NewLexer(code)
|
||||
tok, err := lex.NextToken()
|
||||
|
||||
for err == nil && tok.Type != TokenEOF {
|
||||
for err == nil && tok.Kind != TokenEOF {
|
||||
tok, err = lex.NextToken()
|
||||
}
|
||||
|
||||
|
|
@ -220,7 +220,7 @@ func BenchmarkLexer_NextToken(b *testing.B) {
|
|||
lex := NewLexer(tc.source)
|
||||
tok, err := lex.NextToken()
|
||||
|
||||
for err == nil && tok.Type != TokenEOF {
|
||||
for err == nil && tok.Kind != TokenEOF {
|
||||
tok, err = lex.NextToken()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ const (
|
|||
NilNodeType
|
||||
ListNodeType
|
||||
TupleNodeType
|
||||
RecordNodeType
|
||||
BinaryNodeType
|
||||
UnaryNodeType
|
||||
BlockNodeType
|
||||
|
|
@ -93,6 +94,8 @@ func (n NodeType) String() string {
|
|||
return "Alias"
|
||||
case IndexNodeType:
|
||||
return "Index"
|
||||
case RecordNodeType:
|
||||
return "Record"
|
||||
}
|
||||
return "Invalid Node Type"
|
||||
}
|
||||
|
|
@ -237,6 +240,36 @@ func (n TupleNode) Bounds() (Pos, Pos) {
|
|||
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 {
|
||||
source Node
|
||||
property *Token
|
||||
|
|
@ -269,6 +302,8 @@ func (n BinaryOperation) String() string {
|
|||
return "multiply"
|
||||
case BinaryDivision:
|
||||
return "divide"
|
||||
case BinaryModulo:
|
||||
return "modulo"
|
||||
case BinaryEquality:
|
||||
return "equality"
|
||||
case BinaryInequality:
|
||||
|
|
|
|||
340
core/parser.go
340
core/parser.go
|
|
@ -76,9 +76,13 @@ func (p ParsingError) Format() string {
|
|||
}
|
||||
|
||||
type Parser struct {
|
||||
source string
|
||||
trace []string
|
||||
tokens []Token
|
||||
source string
|
||||
trace []string
|
||||
tokens []Token
|
||||
state ParserState
|
||||
}
|
||||
|
||||
type ParserState struct {
|
||||
prev *Token
|
||||
curr *Token
|
||||
pos Pos
|
||||
|
|
@ -90,7 +94,9 @@ func NewParser(source string, trace []string, tokens []Token) *Parser {
|
|||
source: source,
|
||||
trace: trace,
|
||||
tokens: tokens,
|
||||
pos: 0,
|
||||
state: ParserState{
|
||||
pos: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -116,11 +122,11 @@ func (p *Parser) Parse(path string) (*Program, error) {
|
|||
// initialize current
|
||||
p.advance()
|
||||
|
||||
for int(p.pos) < len(p.tokens) && p.curr.Type != TokenEOF {
|
||||
for int(p.state.pos) < len(p.tokens) && p.state.curr.Kind != TokenEOF {
|
||||
for p.accept(TokenNewLine) {
|
||||
}
|
||||
|
||||
if p.curr.Type == TokenEOF {
|
||||
if p.state.curr.Kind == TokenEOF {
|
||||
break
|
||||
}
|
||||
|
||||
|
|
@ -139,25 +145,25 @@ func (p *Parser) Parse(path string) (*Program, error) {
|
|||
&BlockNode{
|
||||
statements,
|
||||
0,
|
||||
p.curr.End,
|
||||
p.state.curr.End,
|
||||
},
|
||||
path,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Parser) accept(tokenType TokenKind) bool {
|
||||
if p.curr == nil {
|
||||
if p.state.curr == nil {
|
||||
log.Fatal("unexpected current token nil")
|
||||
return false
|
||||
}
|
||||
|
||||
if p.ignoreNewLine && tokenType != TokenNewLine {
|
||||
for p.curr.Type == TokenNewLine {
|
||||
if p.state.ignoreNewLine && tokenType != TokenNewLine {
|
||||
for p.state.curr.Kind == TokenNewLine {
|
||||
p.advance()
|
||||
}
|
||||
}
|
||||
|
||||
if (*p.curr).Type == tokenType {
|
||||
if (*p.state.curr).Kind == tokenType {
|
||||
p.advance()
|
||||
return true
|
||||
}
|
||||
|
|
@ -165,46 +171,52 @@ func (p *Parser) accept(tokenType TokenKind) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (p *Parser) acceptAll(tokenTypes ...TokenKind) bool {
|
||||
if int(p.pos)+len(tokenTypes) > len(p.tokens) {
|
||||
return false
|
||||
}
|
||||
func (p *Parser) getState() ParserState {
|
||||
return p.state
|
||||
}
|
||||
|
||||
for i, tokenType := range tokenTypes {
|
||||
if p.tokens[int(p.pos)+i].Type != tokenType {
|
||||
func (p *Parser) restoreState(state ParserState) {
|
||||
p.state = state
|
||||
}
|
||||
|
||||
func (p *Parser) acceptSeq(tokenTypes ...TokenKind) bool {
|
||||
state := p.getState()
|
||||
|
||||
for _, t := range tokenTypes {
|
||||
if !p.accept(t) {
|
||||
p.restoreState(state)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
p.pos += Pos(len(tokenTypes))
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *Parser) expect(tokenType TokenKind, reason string) error {
|
||||
if !p.accept(tokenType) {
|
||||
return p.error(fmt.Sprintf("Expected token %s, got %s; %s", tokenType, p.curr.Type, reason), p.curr)
|
||||
return p.error(fmt.Sprintf("Expected token %s, got %s; %s", tokenType, p.state.curr.Kind, reason), p.state.curr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Parser) peek() (Token, error) {
|
||||
if p.pos >= Pos(len(p.tokens)) {
|
||||
if p.state.pos >= Pos(len(p.tokens)) {
|
||||
return Token{}, errors.New("cannot peek beyond tokens")
|
||||
}
|
||||
|
||||
return p.tokens[p.pos], nil
|
||||
return p.tokens[p.state.pos], nil
|
||||
}
|
||||
|
||||
func (p *Parser) advance() {
|
||||
p.prev = p.curr
|
||||
p.state.prev = p.state.curr
|
||||
|
||||
if p.pos < Pos(len(p.tokens)) {
|
||||
p.curr = &p.tokens[p.pos]
|
||||
if p.state.pos < Pos(len(p.tokens)) {
|
||||
p.state.curr = &p.tokens[p.state.pos]
|
||||
} else {
|
||||
p.curr = nil
|
||||
p.state.curr = nil
|
||||
}
|
||||
|
||||
p.pos++
|
||||
p.state.pos++
|
||||
}
|
||||
|
||||
func (p *Parser) error(error string, causer *Token) error {
|
||||
|
|
@ -224,10 +236,10 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
|||
}
|
||||
}
|
||||
|
||||
oldIgnoreNewline := p.ignoreNewLine
|
||||
p.ignoreNewLine = false
|
||||
oldIgnoreNewline := p.state.ignoreNewLine
|
||||
p.state.ignoreNewLine = false
|
||||
|
||||
start := p.prev.Start
|
||||
start := p.state.prev.Start
|
||||
|
||||
var statements []Node
|
||||
for !p.accept(TokenCloseBrace) {
|
||||
|
|
@ -251,22 +263,22 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
|||
}
|
||||
}
|
||||
|
||||
p.ignoreNewLine = oldIgnoreNewline
|
||||
p.state.ignoreNewLine = oldIgnoreNewline
|
||||
|
||||
return &BlockNode{statements, start, p.prev.End}, nil
|
||||
return &BlockNode{statements, start, p.state.prev.End}, nil
|
||||
}
|
||||
|
||||
t := p.curr
|
||||
switch t.Type {
|
||||
t := p.state.curr
|
||||
switch t.Kind {
|
||||
case TokenType:
|
||||
p.advance()
|
||||
start := p.prev.Start
|
||||
start := p.state.prev.Start
|
||||
|
||||
if err := p.expect(TokenName, "types must have a name"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name := p.prev
|
||||
name := p.state.prev
|
||||
|
||||
if err := p.expect(TokenAssign, "type aliases must be defined with an assign"); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -282,7 +294,7 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
|||
sig,
|
||||
|
||||
start,
|
||||
p.prev.End,
|
||||
p.state.prev.End,
|
||||
}, nil
|
||||
|
||||
case TokenIf:
|
||||
|
|
@ -300,7 +312,7 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
|||
|
||||
var otherwise Node
|
||||
if p.accept(TokenElse) {
|
||||
otherwise, err = p.expression(p.curr.Type != TokenIf)
|
||||
otherwise, err = p.expression(p.state.curr.Kind != TokenIf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -316,7 +328,7 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
|||
|
||||
case TokenReturn:
|
||||
p.advance()
|
||||
start := p.prev.Start
|
||||
start := p.state.prev.Start
|
||||
|
||||
v, err := p.expression(false)
|
||||
if err != nil {
|
||||
|
|
@ -326,12 +338,12 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
|||
return &ReturnNode{
|
||||
v,
|
||||
start,
|
||||
p.prev.End,
|
||||
p.state.prev.End,
|
||||
}, nil
|
||||
|
||||
case TokenWhile:
|
||||
p.advance()
|
||||
start := p.prev.Start
|
||||
start := p.state.prev.Start
|
||||
|
||||
cond, err := p.expression(false)
|
||||
if err != nil {
|
||||
|
|
@ -347,12 +359,12 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
|||
cond,
|
||||
logic,
|
||||
start,
|
||||
p.prev.End,
|
||||
p.state.prev.End,
|
||||
}, nil
|
||||
|
||||
case TokenFor:
|
||||
p.advance()
|
||||
start := p.prev.Start
|
||||
start := p.state.prev.Start
|
||||
|
||||
counter, err := p.expression(false)
|
||||
if err != nil {
|
||||
|
|
@ -379,12 +391,12 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
|||
logic,
|
||||
|
||||
start,
|
||||
p.prev.End,
|
||||
p.state.prev.End,
|
||||
}, nil
|
||||
|
||||
case TokenInclude:
|
||||
p.advance()
|
||||
start := p.prev.Start
|
||||
start := p.state.prev.Start
|
||||
|
||||
if err := p.expect(TokenString, "import requires a path/name to include"); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -392,13 +404,13 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
|||
|
||||
return &IncludeNode{
|
||||
&StringNode{
|
||||
p.prev.Lexeme[1 : len(p.prev.Lexeme)-1],
|
||||
p.prev.Lexeme,
|
||||
p.prev.Start,
|
||||
p.prev.End,
|
||||
p.state.prev.Lexeme[1 : len(p.state.prev.Lexeme)-1],
|
||||
p.state.prev.Lexeme,
|
||||
p.state.prev.Start,
|
||||
p.state.prev.End,
|
||||
},
|
||||
start,
|
||||
p.prev.End,
|
||||
p.state.prev.End,
|
||||
}, nil
|
||||
|
||||
default:
|
||||
|
|
@ -408,7 +420,7 @@ func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
|||
}
|
||||
|
||||
if p.accept(TokenDeclare) || p.accept(TokenAssign) {
|
||||
isDeclaration := p.prev.Type == TokenDeclare
|
||||
isDeclaration := p.state.prev.Kind == TokenDeclare
|
||||
|
||||
// possibly assign tuples; not implemented yet
|
||||
v, err := p.expression(false)
|
||||
|
|
@ -509,7 +521,7 @@ func (p *Parser) binary() (Node, error) {
|
|||
r := values.Pop()
|
||||
l := values.Pop()
|
||||
opToken := ops.Pop()
|
||||
op := tokenToBinaryOperation(opToken.Type)
|
||||
op := tokenToBinaryOperation(opToken.Kind)
|
||||
|
||||
start, _ := l.Bounds()
|
||||
_, end := r.Bounds()
|
||||
|
|
@ -524,12 +536,12 @@ func (p *Parser) binary() (Node, error) {
|
|||
})
|
||||
}
|
||||
|
||||
for isBinaryOperator(p.curr.Type) {
|
||||
for ops.Current > 0 && binaryPrecedence(p.curr.Type) <= binaryPrecedence(ops.Peek().Type) {
|
||||
for isBinaryOperator(p.state.curr.Kind) {
|
||||
for ops.Current > 0 && binaryPrecedence(p.state.curr.Kind) <= binaryPrecedence(ops.Peek().Kind) {
|
||||
reduce()
|
||||
}
|
||||
|
||||
ops.Push(p.curr)
|
||||
ops.Push(p.state.curr)
|
||||
p.advance()
|
||||
|
||||
v, err := p.chain()
|
||||
|
|
@ -558,16 +570,16 @@ func (p *Parser) chain() (Node, error) {
|
|||
if err = p.expect(TokenName, "can only access properties by name"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name := p.prev
|
||||
name := p.state.prev
|
||||
|
||||
f = &AccessNode{
|
||||
f,
|
||||
p.prev,
|
||||
p.state.prev,
|
||||
name.Start,
|
||||
name.End,
|
||||
}
|
||||
|
||||
if p.curr.Type == TokenOpenParenthesis {
|
||||
if p.state.curr.Kind == TokenOpenParenthesis {
|
||||
args, err := p.parseArgs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -577,11 +589,11 @@ func (p *Parser) chain() (Node, error) {
|
|||
f,
|
||||
args,
|
||||
name.Start,
|
||||
p.prev.End,
|
||||
p.state.prev.End,
|
||||
}
|
||||
}
|
||||
} else if p.curr.Type == TokenOpenParenthesis {
|
||||
start := p.curr.Start
|
||||
} else if p.state.curr.Kind == TokenOpenParenthesis {
|
||||
start := p.state.curr.Start
|
||||
args, err := p.parseArgs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -592,10 +604,10 @@ func (p *Parser) chain() (Node, error) {
|
|||
args,
|
||||
|
||||
start,
|
||||
p.prev.End,
|
||||
p.state.prev.End,
|
||||
}
|
||||
} else if p.accept(TokenOpenBracket) {
|
||||
start := p.prev.Start
|
||||
start := p.state.prev.Start
|
||||
|
||||
index, err := p.expression(false)
|
||||
if err != nil {
|
||||
|
|
@ -610,7 +622,7 @@ func (p *Parser) chain() (Node, error) {
|
|||
f,
|
||||
index,
|
||||
start,
|
||||
p.prev.End,
|
||||
p.state.prev.End,
|
||||
}
|
||||
} else {
|
||||
break
|
||||
|
|
@ -621,71 +633,71 @@ func (p *Parser) chain() (Node, error) {
|
|||
}
|
||||
|
||||
func (p *Parser) factor() (Node, error) {
|
||||
switch (*p.curr).Type {
|
||||
switch (*p.state.curr).Kind {
|
||||
case TokenString:
|
||||
p.advance()
|
||||
return &StringNode{
|
||||
(*p.prev).Lexeme[1 : len((*p.prev).Lexeme)-1],
|
||||
(*p.prev).Lexeme,
|
||||
p.prev.Start,
|
||||
p.prev.End,
|
||||
(*p.state.prev).Lexeme[1 : len((*p.state.prev).Lexeme)-1],
|
||||
(*p.state.prev).Lexeme,
|
||||
p.state.prev.Start,
|
||||
p.state.prev.End,
|
||||
}, nil
|
||||
|
||||
case TokenInteger:
|
||||
p.advance()
|
||||
|
||||
num, success := new(big.Int).SetString(p.prev.Lexeme, 10)
|
||||
num, success := new(big.Int).SetString(p.state.prev.Lexeme, 10)
|
||||
if !success {
|
||||
return nil, p.error(fmt.Sprintf("cannot parse integer base 10: %s", p.prev.Lexeme), p.prev)
|
||||
return nil, p.error(fmt.Sprintf("cannot parse integer base 10: %s", p.state.prev.Lexeme), p.state.prev)
|
||||
}
|
||||
|
||||
return &IntegerNode{
|
||||
num,
|
||||
p.prev.Start,
|
||||
p.prev.End,
|
||||
p.state.prev.Start,
|
||||
p.state.prev.End,
|
||||
}, nil
|
||||
|
||||
case TokenFloat:
|
||||
p.advance()
|
||||
num, err := strconv.ParseFloat((*p.prev).Lexeme, FloatSize)
|
||||
num, err := strconv.ParseFloat((*p.state.prev).Lexeme, FloatSize)
|
||||
|
||||
if err != nil {
|
||||
return nil, p.error(fmt.Sprintf("Error parsing number: %v", err), p.prev)
|
||||
return nil, p.error(fmt.Sprintf("Error parsing number: %v", err), p.state.prev)
|
||||
}
|
||||
|
||||
return &FloatNode{
|
||||
num,
|
||||
p.prev.Start,
|
||||
p.prev.End,
|
||||
p.state.prev.Start,
|
||||
p.state.prev.End,
|
||||
}, nil
|
||||
|
||||
case TokenHexadecimal:
|
||||
p.advance()
|
||||
start := (*p.prev).Start
|
||||
num, ok := new(big.Int).SetString(p.prev.Lexeme[2:], 16)
|
||||
start := (*p.state.prev).Start
|
||||
num, ok := new(big.Int).SetString(p.state.prev.Lexeme[2:], 16)
|
||||
if !ok {
|
||||
return nil, p.error(fmt.Sprintf("cannot parse hexadecimal: %v", p.prev.Lexeme), p.prev)
|
||||
return nil, p.error(fmt.Sprintf("cannot parse hexadecimal: %v", p.state.prev.Lexeme), p.state.prev)
|
||||
}
|
||||
|
||||
return &IntegerNode{
|
||||
num,
|
||||
start,
|
||||
p.prev.End,
|
||||
p.state.prev.End,
|
||||
}, nil
|
||||
|
||||
case TokenTrue:
|
||||
p.advance()
|
||||
return &BooleanNode{
|
||||
true,
|
||||
p.prev.Start,
|
||||
p.prev.End,
|
||||
p.state.prev.Start,
|
||||
p.state.prev.End,
|
||||
}, nil
|
||||
case TokenFalse:
|
||||
p.advance()
|
||||
return &BooleanNode{
|
||||
false,
|
||||
p.prev.Start,
|
||||
p.prev.End,
|
||||
p.state.prev.Start,
|
||||
p.state.prev.End,
|
||||
}, nil
|
||||
|
||||
case TokenNil:
|
||||
|
|
@ -694,7 +706,7 @@ func (p *Parser) factor() (Node, error) {
|
|||
|
||||
case TokenOpenBracket:
|
||||
p.advance()
|
||||
start := p.prev.Start
|
||||
start := p.state.prev.Start
|
||||
|
||||
// TODO: find better solution; current one is messy
|
||||
// Maybe perform better analysis to determine the kind of the list...
|
||||
|
|
@ -708,12 +720,12 @@ func (p *Parser) factor() (Node, error) {
|
|||
[]Node{},
|
||||
s,
|
||||
start,
|
||||
p.prev.End,
|
||||
p.state.prev.End,
|
||||
}, nil
|
||||
}
|
||||
|
||||
oldIgnoreNewline := p.ignoreNewLine
|
||||
p.ignoreNewLine = true
|
||||
oldIgnoreNewline := p.state.ignoreNewLine
|
||||
p.state.ignoreNewLine = true
|
||||
|
||||
var values []Node
|
||||
for !p.accept(TokenCloseBracket) {
|
||||
|
|
@ -731,19 +743,19 @@ func (p *Parser) factor() (Node, error) {
|
|||
values = append(values, value)
|
||||
}
|
||||
|
||||
p.ignoreNewLine = oldIgnoreNewline
|
||||
p.state.ignoreNewLine = oldIgnoreNewline
|
||||
|
||||
return &ListNode{
|
||||
values,
|
||||
nil,
|
||||
start,
|
||||
p.prev.End,
|
||||
p.state.prev.End,
|
||||
}, nil
|
||||
|
||||
// unary minus
|
||||
case TokenMinus:
|
||||
p.advance()
|
||||
op := p.prev
|
||||
op := p.state.prev
|
||||
|
||||
f, err := p.factor()
|
||||
if err != nil {
|
||||
|
|
@ -754,12 +766,12 @@ func (p *Parser) factor() (Node, error) {
|
|||
f,
|
||||
op,
|
||||
op.Start,
|
||||
p.prev.End,
|
||||
p.state.prev.End,
|
||||
}, nil
|
||||
|
||||
case TokenBang:
|
||||
p.advance()
|
||||
op := p.prev
|
||||
op := p.state.prev
|
||||
|
||||
v, err := p.factor()
|
||||
if err != nil {
|
||||
|
|
@ -771,16 +783,16 @@ func (p *Parser) factor() (Node, error) {
|
|||
v,
|
||||
op,
|
||||
op.Start,
|
||||
p.prev.End,
|
||||
p.state.prev.End,
|
||||
}, nil
|
||||
|
||||
case TokenName:
|
||||
p.advance()
|
||||
name := (*p.prev).Lexeme
|
||||
start := p.prev.Start
|
||||
nameEnd := p.prev.End
|
||||
name := (*p.state.prev).Lexeme
|
||||
start := p.state.prev.Start
|
||||
nameEnd := p.state.prev.End
|
||||
|
||||
if p.curr.Type == TokenOpenParenthesis {
|
||||
if p.state.curr.Kind == TokenOpenParenthesis {
|
||||
args, err := p.parseArgs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -794,7 +806,7 @@ func (p *Parser) factor() (Node, error) {
|
|||
},
|
||||
args,
|
||||
start,
|
||||
p.prev.End,
|
||||
p.state.prev.End,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -806,11 +818,11 @@ func (p *Parser) factor() (Node, error) {
|
|||
|
||||
case TokenFunc:
|
||||
p.advance()
|
||||
start := p.prev.Start
|
||||
start := p.state.prev.Start
|
||||
|
||||
var name *Token
|
||||
if p.accept(TokenName) { // can be unnamed, but accept name if it is named
|
||||
name = p.prev
|
||||
name = p.state.prev
|
||||
}
|
||||
|
||||
params, err := p.parseParams()
|
||||
|
|
@ -842,7 +854,7 @@ func (p *Parser) factor() (Node, error) {
|
|||
yield,
|
||||
logic,
|
||||
start,
|
||||
p.prev.End,
|
||||
p.state.prev.End,
|
||||
}
|
||||
|
||||
if name != nil {
|
||||
|
|
@ -851,7 +863,7 @@ func (p *Parser) factor() (Node, error) {
|
|||
fn,
|
||||
true,
|
||||
start,
|
||||
p.prev.End,
|
||||
p.state.prev.End,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -859,11 +871,67 @@ func (p *Parser) factor() (Node, error) {
|
|||
|
||||
case TokenOpenParenthesis:
|
||||
p.advance()
|
||||
start := p.prev.Start
|
||||
start := p.state.prev.Start
|
||||
oldCare := p.state.ignoreNewLine
|
||||
p.state.ignoreNewLine = true
|
||||
p.skipNewLines()
|
||||
|
||||
// we're inside an object
|
||||
if p.acceptAll(TokenName, TokenColon) {
|
||||
return nil, p.error("objects are not implemented yet (TBD)", p.prev)
|
||||
key := p.state.curr
|
||||
if p.acceptSeq(TokenName, TokenColon) {
|
||||
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)
|
||||
|
|
@ -876,6 +944,7 @@ func (p *Parser) factor() (Node, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
p.state.ignoreNewLine = oldCare
|
||||
return v, nil
|
||||
}
|
||||
|
||||
|
|
@ -897,26 +966,27 @@ func (p *Parser) factor() (Node, error) {
|
|||
}
|
||||
}
|
||||
|
||||
p.state.ignoreNewLine = oldCare
|
||||
return &TupleNode{
|
||||
items,
|
||||
|
||||
start,
|
||||
p.prev.End,
|
||||
p.state.prev.End,
|
||||
}, nil
|
||||
|
||||
case TokenBreakpoint:
|
||||
p.advance()
|
||||
|
||||
return &BreakpointNode{
|
||||
p.prev.Start,
|
||||
p.prev.End,
|
||||
p.state.prev.Start,
|
||||
p.state.prev.End,
|
||||
}, nil
|
||||
|
||||
case TokenOpenBrace:
|
||||
return p.expression(true)
|
||||
|
||||
default:
|
||||
return nil, p.error(fmt.Sprintf("invalid factor %s", p.curr), p.curr)
|
||||
return nil, p.error(fmt.Sprintf("invalid factor %s", p.state.curr), p.state.curr)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -956,7 +1026,7 @@ func (p *Parser) parseParams() ([]FunctionParameter, error) {
|
|||
params := make([]FunctionParameter, 0)
|
||||
|
||||
if p.accept(TokenName) {
|
||||
name := (*p.prev).Lexeme
|
||||
name := (*p.state.prev).Lexeme
|
||||
if err := p.expect(TokenColon, "parameters must have a type separated by a colon"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -977,7 +1047,7 @@ func (p *Parser) parseParams() ([]FunctionParameter, error) {
|
|||
if err := p.expect(TokenName, "parameters must have a name (cannot have trailing comma)"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name = (*p.prev).Lexeme
|
||||
name = (*p.state.prev).Lexeme
|
||||
if err := p.expect(TokenColon, "parameters must have a type separated by a colon"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -1005,9 +1075,42 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
|
|||
var s TypeSignature
|
||||
|
||||
if p.accept(TokenOpenParenthesis) {
|
||||
oldCare := p.state.ignoreNewLine
|
||||
p.state.ignoreNewLine = true
|
||||
p.skipNewLines()
|
||||
|
||||
// we're inside an object
|
||||
if p.acceptAll(TokenName, TokenColon) {
|
||||
return nil, p.error("objects are not implemented yet (TBD)", p.prev)
|
||||
name := p.state.curr
|
||||
if p.acceptSeq(TokenName, TokenColon) {
|
||||
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()
|
||||
|
|
@ -1043,6 +1146,8 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
|
|||
items,
|
||||
}
|
||||
}
|
||||
|
||||
p.state.ignoreNewLine = oldCare
|
||||
} else if p.accept(TokenFunc) {
|
||||
if err := p.expect(TokenOpenParenthesis, "func signature must have parentheses for parameters"); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -1095,7 +1200,7 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
|
|||
if err := p.expect(TokenName, "type must be a name"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name := (*p.prev).Lexeme
|
||||
name := (*p.state.prev).Lexeme
|
||||
|
||||
switch name {
|
||||
case "str":
|
||||
|
|
@ -1130,3 +1235,8 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
|
|||
|
||||
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")
|
||||
}
|
||||
|
||||
if p.pos != 0 {
|
||||
if p.state.pos != 0 {
|
||||
t.Error("parser should initialize position at 0")
|
||||
}
|
||||
|
||||
|
|
@ -715,6 +715,95 @@ func GetTokenTestData() map[string]TokenTestData {
|
|||
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,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -912,6 +1001,23 @@ func NodeEquality(t *testing.T, n1 Node, n2 Node) {
|
|||
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:
|
||||
panic("unimplemented node equality")
|
||||
}
|
||||
|
|
@ -939,25 +1045,55 @@ func TestParser_Parse(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestParser_AcceptAll(t *testing.T) {
|
||||
func TestParser_AcceptSeq(t *testing.T) {
|
||||
p := NewParser("a:", []string{}, []Token{
|
||||
NewToken(TokenName, 0, 1, 0, "a"),
|
||||
NewToken(TokenColon, 1, 2, 0, "a"),
|
||||
NewToken(TokenColon, 1, 2, 0, ":"),
|
||||
NewToken(TokenEOF, 2, 2, 0, ""),
|
||||
})
|
||||
|
||||
if !p.acceptAll(TokenName, TokenColon) {
|
||||
// initialize
|
||||
p.advance()
|
||||
|
||||
if !p.acceptSeq(TokenName, TokenColon) {
|
||||
t.Fatalf("tokens were not accepted")
|
||||
}
|
||||
|
||||
t.Logf("tokens were accepted")
|
||||
}
|
||||
|
||||
func TestParser_AcceptAll_TooFew(t *testing.T) {
|
||||
p := NewParser("a", []string{}, []Token{
|
||||
func TestParser_AcceptSeqAndAfter(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, ""),
|
||||
})
|
||||
|
||||
if p.acceptAll(TokenName, TokenColon) {
|
||||
// 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{
|
||||
NewToken(TokenName, 0, 1, 0, "a"),
|
||||
NewToken(TokenEOF, 1, 1, 0, ""),
|
||||
})
|
||||
|
||||
// initialize
|
||||
p.advance()
|
||||
|
||||
if p.acceptSeq(TokenName, TokenColon) {
|
||||
t.Fatalf("tokens were incorrectly accepted")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,9 +50,11 @@ func (t Type) String() string {
|
|||
return "composite"
|
||||
case TypeInner:
|
||||
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 {
|
||||
|
|
@ -560,7 +562,7 @@ func (s *RecordSignature) Contains(t TypeSignature) bool {
|
|||
}
|
||||
|
||||
func (s *RecordSignature) Equal(t TypeSignature) bool {
|
||||
return s.Contains(t) && t.Contains(s)
|
||||
return s.Contains(t) && len(t.(*RecordSignature).Entries) == len(s.Entries)
|
||||
}
|
||||
|
||||
func (s *RecordSignature) String() string {
|
||||
|
|
|
|||
|
|
@ -777,7 +777,30 @@ func (v *RecordValue) Type() ValueType {
|
|||
}
|
||||
|
||||
func (v *RecordValue) String() string {
|
||||
return ""
|
||||
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 {
|
||||
|
|
|
|||
20
core/vm.go
20
core/vm.go
|
|
@ -126,7 +126,7 @@ const (
|
|||
// InstructionAppend Append to a list. stack: (... > list > item) => (... > list)
|
||||
InstructionAppend
|
||||
// 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.
|
||||
InstructionFormList
|
||||
// InstructionConcatLists concatenate lists, producing a new list with the values of both lists. Pops two lists.
|
||||
|
|
@ -139,6 +139,12 @@ const (
|
|||
// being the last item in the tuple.
|
||||
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
|
||||
// is the index. [..., container, index] -> [..., item]
|
||||
InstructionIndexList
|
||||
|
|
@ -1076,6 +1082,18 @@ func (vm *VM) Next() bool {
|
|||
|
||||
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:
|
||||
i := vm.Stack.Pop().(*IntegerValue)
|
||||
l := vm.Stack.Pop().(*ListValue)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue