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())
}
// 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
type EchoStatement struct {
Value Expression

View File

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

View File

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