funcs as expression

This commit is contained in:
Sky Johnson 2025-06-11 10:37:36 -05:00
parent e98e0643e2
commit 90eaf081a7
3 changed files with 71 additions and 42 deletions

View File

@ -66,6 +66,16 @@ func (as *AssignStatement) String() string {
return fmt.Sprintf("%s%s = %s", prefix, nameStr, as.Value.String()) return fmt.Sprintf("%s%s = %s", prefix, nameStr, as.Value.String())
} }
// ExpressionStatement represents expressions used as statements
type ExpressionStatement struct {
Expression Expression
}
func (es *ExpressionStatement) statementNode() {}
func (es *ExpressionStatement) String() string {
return es.Expression.String()
}
// EchoStatement represents echo output statements // EchoStatement represents echo output statements
type EchoStatement struct { type EchoStatement struct {
Value Expression Value Expression

View File

@ -201,7 +201,7 @@ func (p *Parser) ParseProgram() *Program {
func (p *Parser) parseStatement() Statement { func (p *Parser) parseStatement() Statement {
switch p.curToken.Type { switch p.curToken.Type {
case IDENT: case IDENT:
return p.parseAssignStatement() return p.parseIdentifierStatement()
case IF: case IF:
return p.parseIfStatement() return p.parseIfStatement()
case FOR: case FOR:
@ -230,54 +230,68 @@ func (p *Parser) parseStatement() Statement {
} }
} }
// parseAssignStatement parses variable assignment with optional type hint // parseIdentifierStatement handles both assignments and expression statements starting with identifiers
func (p *Parser) parseAssignStatement() *AssignStatement { func (p *Parser) parseIdentifierStatement() Statement {
stmt := &AssignStatement{} // Parse the left-hand side expression first
expr := p.ParseExpression(LOWEST)
// Parse left-hand side expression if expr == nil {
stmt.Name = p.ParseExpression(LOWEST)
if stmt.Name == nil {
p.addError("expected expression for assignment left-hand side")
return nil return nil
} }
// Check for type hint on simple identifiers // Check for type hint (only valid on simple identifiers)
if _, ok := stmt.Name.(*Identifier); ok { var typeHint *TypeInfo
stmt.TypeHint = p.parseTypeHint() if _, ok := expr.(*Identifier); ok {
typeHint = p.parseTypeHint()
} }
// Check if next token is assignment operator // Check if this is an assignment
if !p.peekTokenIs(ASSIGN) { if p.peekTokenIs(ASSIGN) {
p.addError("unexpected identifier, expected assignment or declaration") // Convert to assignment statement
return nil stmt := &AssignStatement{
} Name: expr,
TypeHint: typeHint,
// Validate assignment target and check if it's a declaration
switch name := stmt.Name.(type) {
case *Identifier:
stmt.IsDeclaration = !p.isVariableDeclared(name.Value)
if stmt.IsDeclaration {
p.declareVariable(name.Value)
} }
case *DotExpression, *IndexExpression:
stmt.IsDeclaration = false // Validate assignment target and check if it's a declaration
default: switch name := expr.(type) {
p.addError("invalid assignment target") case *Identifier:
stmt.IsDeclaration = !p.isVariableDeclared(name.Value)
if stmt.IsDeclaration {
p.declareVariable(name.Value)
}
case *DotExpression, *IndexExpression:
stmt.IsDeclaration = false
default:
p.addError("invalid assignment target")
return nil
}
if !p.expectPeek(ASSIGN) {
return nil
}
p.nextToken()
stmt.Value = p.ParseExpression(LOWEST)
if stmt.Value == nil {
p.addError("expected expression after assignment operator")
return nil
}
return stmt
} else {
// This is an expression statement
return &ExpressionStatement{Expression: expr}
}
}
// parseExpressionStatement parses expressions used as statements
func (p *Parser) parseExpressionStatement() *ExpressionStatement {
stmt := &ExpressionStatement{}
stmt.Expression = p.ParseExpression(LOWEST)
if stmt.Expression == nil {
return nil return nil
} }
if !p.expectPeek(ASSIGN) {
return nil
}
p.nextToken()
stmt.Value = p.ParseExpression(LOWEST)
if stmt.Value == nil {
p.addError("expected expression after assignment operator")
return nil
}
return stmt return stmt
} }
@ -298,6 +312,11 @@ func (p *Parser) parseEchoStatement() *EchoStatement {
// parseBreakStatement parses break statements // parseBreakStatement parses break statements
func (p *Parser) parseBreakStatement() *BreakStatement { func (p *Parser) parseBreakStatement() *BreakStatement {
// Check if break is followed by an identifier (invalid)
if p.peekTokenIs(IDENT) {
p.addError("unexpected identifier")
return nil
}
return &BreakStatement{} return &BreakStatement{}
} }

View File

@ -20,7 +20,7 @@ func TestParsingErrors(t *testing.T) {
{"+ 5", "unexpected operator '+'", 1, 1}, {"+ 5", "unexpected operator '+'", 1, 1},
{"1 +", "expected expression after operator '+'", 1, 3}, {"1 +", "expected expression after operator '+'", 1, 3},
{"@", "unexpected token '@'", 1, 1}, {"@", "unexpected token '@'", 1, 1},
{"invalid@", "unexpected identifier", 1, 1}, {"invalid@", "unexpected token '@'", 1, 1},
{"{1, 2", "expected next token to be }", 1, 6}, {"{1, 2", "expected next token to be }", 1, 6},
{"{a =", "expected expression after assignment operator", 1, 4}, {"{a =", "expected expression after assignment operator", 1, 4},
{"{a = 1,", "expected next token to be }", 1, 8}, {"{a = 1,", "expected next token to be }", 1, 8},