Skip to content

Commit

Permalink
adding struct field assignment to ast
Browse files Browse the repository at this point in the history
  • Loading branch information
dfirebaugh committed Jan 25, 2025
1 parent fd1c4bd commit 1c1788d
Show file tree
Hide file tree
Showing 11 changed files with 136 additions and 33 deletions.
20 changes: 20 additions & 0 deletions ast/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,23 @@ func (s *StructFieldAccess) TokenLiteral() string {
func (s *StructFieldAccess) String() string {
return s.Left.String() + "." + s.Field.String()
}

type StructFieldAssignment struct {
Token token.Token
Left *StructFieldAccess
Right Expression
}

func (sfa *StructFieldAssignment) expressionNode() {}

func (sfa *StructFieldAssignment) TokenLiteral() string {
return sfa.Token.Literal
}

func (sfa *StructFieldAssignment) String() string {
var out bytes.Buffer
out.WriteString(sfa.Left.String())
out.WriteString(" = ")
out.WriteString(sfa.Right.String())
return out.String()
}
11 changes: 11 additions & 0 deletions emitters/js/js.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ func (t *Transpiler) transpileExpression(expr ast.Expression) string {
case *ast.StructFieldAccess:
return t.transpileStructFieldAccess(expr)

case *ast.StructFieldAssignment:
return t.transpileStructFieldAssignment(expr)

case *ast.ListLiteral:
return t.transpileListLiteral(expr)

Expand Down Expand Up @@ -352,6 +355,14 @@ func (t *Transpiler) transpileStructFieldAccess(expr *ast.StructFieldAccess) str
)
}

func (t *Transpiler) transpileStructFieldAssignment(expr *ast.StructFieldAssignment) string {
return fmt.Sprintf("%s.%s = %s",
t.transpileExpression(expr.Left.Left),
expr.Left.Field.String(),
t.transpileExpression(expr.Right),
)
}

func (t *Transpiler) transpileVariableDeclaration(stmt *ast.VariableDeclaration) string {
if stmt.Type.Type == token.IDENTIFIER && t.definedStructs[stmt.Type.Literal] {
return fmt.Sprintf("const %s = new %s(%s);",
Expand Down
9 changes: 7 additions & 2 deletions examples/if.pun
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ pkg main
pub fn check_number(i32 x) {
if x > 0 {
println(x, "is positive")
} else {
println(x, "is negative")
}
}

check_number(5)
fn main() {
check_number(5)
check_number(-3)
}

check_number(-3)
main()
16 changes: 13 additions & 3 deletions examples/struct.pun
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,20 @@ message get_message() {
},
},
}
msg.sender = msg.sender + 8;

send_message()
println(msg.sender)
return msg
}

//message msg = get_message()
//println(msg)
send_message()

fn init() {
//message msg = get_message()
//println(msg)
get_message();
send_message();
}

init();

4 changes: 4 additions & 0 deletions lexer/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,12 +303,16 @@ func (l *Lexer) evaluateKeyword(literal string) token.Type {
return token.RETURN
case token.Keywords[token.IF]:
return token.IF
case token.Keywords[token.ELSE]:
return token.ELSE
case token.Keywords[token.TRUE]:
return token.TRUE
case token.Keywords[token.FOR]:
return token.FOR
case token.Keywords[token.FALSE]:
return token.FALSE
case token.Keywords[token.BOOL]:
return token.BOOL
case token.Keywords[token.PUB]:
return token.PUB
default:
Expand Down
14 changes: 7 additions & 7 deletions parser/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,13 @@ func (p *Parser) parseFunctionCallArguments() ([]ast.Expression, error) {
}
p.trace("parseFunctionCallArguments: first arg", firstArg.String(), p.curToken.Literal, p.peekToken.Literal)
args = append(args, firstArg)
if !p.curTokenIs(token.COMMA) {
// println("not comma", p.curToken.Literal, p.peekToken.Literal)
if p.curTokenIs(token.RPAREN) {
p.nextToken()
}
return args, nil
}
if !p.curTokenIs(token.COMMA) {
// println("not comma", p.curToken.Literal, p.peekToken.Literal)
if p.curTokenIs(token.RPAREN) {
p.nextToken()
}
return args, nil
}

for p.curTokenIs(token.COMMA) {
p.trace("parseFunctionCallArguments: consume COMMA", p.curToken.Literal, p.peekToken.Literal)
Expand Down
10 changes: 5 additions & 5 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func (p *Parser) registerParseRules() {
token.FALSE: {prefixFn: p.parseBooleanLiteral},
token.BANG: {prefixFn: p.parsePrefixExpression},
token.ASSIGN: {infixFn: p.parseAssignmentExpression},
token.MINUS: {infixFn: p.parseInfixExpression},
token.MINUS: {infixFn: p.parseInfixExpression, prefixFn: p.parsePrefixExpression},
token.PLUS: {infixFn: p.parseInfixExpression},
token.ASTERISK: {infixFn: p.parseInfixExpression},
token.MOD: {infixFn: p.parseInfixExpression},
Expand Down Expand Up @@ -112,7 +112,6 @@ func (p *Parser) registerParseRules() {
func (p *Parser) parseExpression(precedence int) (ast.Expression, error) {
var err error
p.trace("parseExpression", p.curToken.Literal, p.peekToken.Literal)

if p.isBooleanLiteral() {
p.trace("parsing bool", p.curToken.Literal, p.peekToken.Literal)
b, err := p.parseBooleanLiteral()
Expand Down Expand Up @@ -157,7 +156,9 @@ func (p *Parser) parseExpression(precedence int) (ast.Expression, error) {
p.nextToken()
return p.parseInfixExpression(n)
}
p.nextToken()
if p.curTokenIs(token.NUMBER) {
p.nextToken()
}
return n, nil
}

Expand Down Expand Up @@ -223,8 +224,7 @@ func (p *Parser) parseExpression(precedence int) (ast.Expression, error) {
if ident == nil {
return nil, p.error("identifier is nil")
}

if p.peekTokenIs(token.DOT) {
if p.isStructAccess() {
return p.parseStructFieldAccess(ident.(*ast.Identifier))
}

Expand Down
17 changes: 17 additions & 0 deletions parser/parsers.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,12 +185,20 @@ func (p *Parser) parseExpressionStatement() (*ast.ExpressionStatement, error) {
}
if stmt.Expression != nil {
p.trace("after parsing expression statement", stmt.Expression.String())
if p.curTokenIs(token.ASSIGN) {
assignment, err := p.parseStructFieldAssignment(stmt.Expression)
if err != nil {
p.error(err.Error())
}
stmt.Expression = assignment
}
if p.curTokenIs(token.RPAREN) {
p.nextToken()
}
if p.curTokenIs(token.SEMICOLON) {
p.nextToken()
}
return stmt, nil
}

return stmt, nil
Expand Down Expand Up @@ -289,6 +297,8 @@ func (p *Parser) parseInfixExpression(left ast.Expression) (ast.Expression, erro
}

precedence := p.curPrecedence()

// consume the operator
p.nextToken()
expression.Right, err = p.parseExpression(precedence)
return expression, err
Expand Down Expand Up @@ -318,7 +328,10 @@ func (p *Parser) parseIfStatement() (*ast.IfStatement, error) {
return nil, err
}
p.trace("parsing if statement", p.curToken.Literal, p.peekToken.Literal)
p.trace("parsing else block")

if p.peekTokenIs(token.ELSE) {
p.nextToken() // consume }
p.nextToken() // consume else

if !p.expectCurrentTokenIs(token.LBRACE) {
Expand Down Expand Up @@ -424,6 +437,9 @@ func (p *Parser) parseAssignmentExpression(left ast.Expression) (ast.Expression,

p.nextToken()
expression.Right, err = p.parseExpression(LOWEST)
if err != nil {
return nil, err
}

return expression, err
}
Expand All @@ -449,6 +465,7 @@ func (p *Parser) parseTypeBasedVariableDeclaration() (ast.Statement, error) {
return decl, nil
}
varType := p.curToken
// if not an identifier, it could be a struct member
if !p.expectPeek(token.IDENTIFIER) {
return nil, p.error("expected identifier")
}
Expand Down
6 changes: 5 additions & 1 deletion parser/predicate.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,9 @@ func (p *Parser) isNumber() bool {
}

func (p *Parser) isIndexExpression() bool {
return p.curTokenIs(token.IDENTIFIER) && p.peekTokenIs(token.LBRACKET)
return p.curTokenIs(token.IDENTIFIER) && p.peekTokenIs(token.LBRACKET)
}

func (p *Parser) isStructAccess() bool {
return p.curTokenIs(token.IDENTIFIER) && p.peekTokenIs(token.DOT) && p.peekTokenAfter(token.IDENTIFIER)
}
31 changes: 31 additions & 0 deletions parser/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ func (p *Parser) parseStructLiteral() (ast.Expression, error) {
}

func (p *Parser) parseStructFieldAccess(left ast.Expression) (ast.Expression, error) {
if !p.expectCurrentTokenIs(token.DOT) {
p.error("expected a dot, but got: ", p.curToken.Literal)
}
p.nextToken() // consume the dot

if !p.expectPeek(token.IDENTIFIER) {
Expand All @@ -154,6 +157,34 @@ func (p *Parser) parseStructFieldAccess(left ast.Expression) (ast.Expression, er
if p.peekTokenIs(token.DOT) {
return p.parseStructFieldAccess(fieldAccess)
}
if p.isBinaryOperator() {
p.nextToken()
return p.parseInfixExpression(fieldAccess)
}

return fieldAccess, nil
}

func (p *Parser) parseStructFieldAssignment(left ast.Expression) (ast.Expression, error) {
tok := p.curToken
if !p.curTokenIs(token.ASSIGN) {
p.error("we were expecting an assignment operator, but got: ", p.curToken.Literal)
}
p.nextToken()

right, err := p.parseExpression(LOWEST)
if err != nil {
return nil, p.error("expected expression after assignment operator")
}

fieldAccess, ok := left.(*ast.StructFieldAccess)
if !ok {
return nil, p.error("left-hand side of assignment must be a struct field access")
}

return &ast.StructFieldAssignment{
Token: tok,
Left: fieldAccess,
Right: right,
}, nil
}
31 changes: 16 additions & 15 deletions token/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ const (

// Literals
STRING = "STRING"
NUMBER = "number"
FLOAT = "float"
BOOL = "bool"
NUMBER = "NUMBER"
FLOAT = "FLOAT"
BOOL = "BOOL"

// Operators
ASSIGN = "="
Expand Down Expand Up @@ -76,18 +76,18 @@ const (
LET = "LET"
RETURN = "RETURN"
FOR = "FOR"
IF = "if"
ELSE = "else"
PUB = "pub"
PACKAGE = "pkg"
IMPORT = "import"
INTERFACE = "interface"
STRUCT = "struct"
TEST = "test"
ENUM = "enum"
DEFER = "defer"
APPEND = "append"
LEN = "len"
IF = "IF"
ELSE = "ELSE"
PUB = "PUB"
PACKAGE = "PKG"
IMPORT = "IMPORT"
INTERFACE = "INTERFACE"
STRUCT = "STRUCT"
TEST = "TEST"
ENUM = "ENUM"
DEFER = "DEFER"
APPEND = "APPEND"
LEN = "LEN"

IDENTIFIER = "IDENTIFIER"

Expand Down Expand Up @@ -127,6 +127,7 @@ var Keywords = map[Type]string{
TRUE: "true",
FALSE: "false",
PUB: "pub",
BOOL: "bool",
STRING: "str",
APPEND: "append",
LEN: "len",
Expand Down

0 comments on commit 1c1788d

Please sign in to comment.