Skip to content

Commit

Permalink
feat: tokenize, parse, and evaluate array and index expression (#16)
Browse files Browse the repository at this point in the history
## What does this PR do?

tokenize, parse, and evaluate array and index expression
  • Loading branch information
Aden-Q authored May 14, 2024
1 parent e82fcb0 commit 348e851
Show file tree
Hide file tree
Showing 12 changed files with 417 additions and 54 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ Simple if condition with an else branch:
+ [ ] feat: add quit(), exit() builtin functions to exit elegantly
+ [ ] feat: add a static/dynamic type system
+ [ ] ci: build and publish as a pkg on Docker Hub
+ [ ] feat: dot as an operator (similar to infix index expression)
+ [ ] feat: mutable array implementation for efficient push/pop
+ [ ] feat: map-reduce as an example
+ [ ] feat: iterator
## References
Expand Down
73 changes: 73 additions & 0 deletions internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ var _ Expression = (*IdentifierExpression)(nil)
var _ Expression = (*IntegerExpression)(nil)
var _ Expression = (*BooleanExpression)(nil)
var _ Expression = (*StringExpression)(nil)
var _ Expression = (*ArrayExpression)(nil)
var _ Expression = (*IndexExpression)(nil)
var _ Expression = (*IfExpression)(nil)
var _ Expression = (*FuncExpression)(nil)
var _ Expression = (*CallExpression)(nil)
Expand Down Expand Up @@ -187,6 +189,77 @@ func NewStringExpression(literal string) *StringExpression {
}
}

// ArrayExpression implements the Expression interface
type ArrayExpression struct {
// the [ token
Token token.Token
Elements []Expression
}

func (ae *ArrayExpression) expressionNode() {}

func (ae *ArrayExpression) TokenLiteral() string {
return ae.Token.Literal
}

func (ae *ArrayExpression) String() string {
builder := strings.Builder{}

elements := []string{}
for _, el := range ae.Elements {
elements = append(elements, el.String())
}

builder.WriteString("[")
builder.WriteString(strings.Join(elements, ", "))
builder.WriteString("]")

return builder.String()
}

// NewArrayExpression creates an ArrayExpression node
func NewArrayExpression(exps ...Expression) *ArrayExpression {
return &ArrayExpression{
Token: token.New(token.LBRACKET, "["),
Elements: exps,
}
}

// IndexExpression implements the Expression interface
type IndexExpression struct {
// the [ token
Token token.Token
Left Expression
Index Expression
}

func (ie *IndexExpression) expressionNode() {}

func (ie *IndexExpression) TokenLiteral() string {
return ie.Token.Literal
}

func (ie *IndexExpression) String() string {
builder := strings.Builder{}

builder.WriteString("(")
builder.WriteString(ie.Left.String())
builder.WriteString("[")
builder.WriteString(ie.Index.String())
builder.WriteString("])")

return builder.String()
}

// NewIndexExpression creates an IndexExpression node
func NewIndexExpression(left, index Expression) *IndexExpression {
return &IndexExpression{
Token: token.New(token.LBRACKET, "["),
Left: left,
Index: index,
}
}

// IfExpression implements the Expression interface
type IfExpression struct {
// the if token
Expand Down
1 change: 1 addition & 0 deletions internal/evaluator/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ var (
ErrUnexpectedOperatorType = errors.New("unexpected operator type")
ErrIdentifierNotFound = errors.New("identifier not found")
ErrNotAFunction = errors.New("not a function")
ErrIndexOutOfRange = errors.New("index out of range")
)
45 changes: 45 additions & 0 deletions internal/evaluator/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ func (e *evaluator) Eval(node ast.Node) (object.Object, error) {
return booleanConv(node.Value), nil
case *ast.StringExpression:
return object.NewString(node.Value), nil
case *ast.ArrayExpression:
return e.evalArrayExpression(node)
case *ast.IndexExpression:
return e.evalIndexExpression(node)
case *ast.IfExpression:
return e.evalIfExpression(node)
case *ast.FuncExpression:
Expand Down Expand Up @@ -124,6 +128,47 @@ func (e *evaluator) evalIdentifierExpression(ie *ast.IdentifierExpression) (obje
return object.NIL, ErrIdentifierNotFound
}

func (e *evaluator) evalArrayExpression(ae *ast.ArrayExpression) (object.Object, error) {
elements := make([]object.Object, 0, len(ae.Elements))

for _, exp := range ae.Elements {
val, err := e.Eval(exp)
if err != nil {
return object.NIL, err
}

elements = append(elements, val)
}

return object.NewArray(elements...), nil
}

func (e *evaluator) evalIndexExpression(ae *ast.IndexExpression) (object.Object, error) {
left, err := e.Eval(ae.Left)
if err != nil {
return object.NIL, err
}

index, err := e.Eval(ae.Index)
if err != nil {
return object.NIL, err
}

if left.Type() == object.ARRAY_OBJ && index.Type() == object.INTEGER_OBJ {
array := left.(*object.Array)
idx := index.(*object.Integer).Value
maxIdx := int64(len(array.Elements) - 1)

if idx < 0 || idx > maxIdx {
return object.NIL, ErrIndexOutOfRange
}

return array.Elements[idx], nil
}

return object.NIL, ErrUnexpectedObjectType
}

func (e *evaluator) evalIfExpression(ie *ast.IfExpression) (object.Object, error) {
condition, err := e.Eval(ie.Condition)
if err != nil {
Expand Down
Loading

0 comments on commit 348e851

Please sign in to comment.