Skip to content

Commit

Permalink
Add support for unary operators + and -
Browse files Browse the repository at this point in the history
This adds support to the configuration interpolation syntax for + and -
as unary operators, specifically to represent negative numbers.
  • Loading branch information
Jesse Szwedko authored and braintreeps committed Dec 18, 2015
1 parent 5d91069 commit 41f9ebc
Show file tree
Hide file tree
Showing 8 changed files with 334 additions and 79 deletions.
42 changes: 42 additions & 0 deletions config/lang/ast/unary_arithmetic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package ast

import (
"fmt"
)

// UnaryArithmetic represents a node where the result is arithmetic of
// one operands
type UnaryArithmetic struct {
Op ArithmeticOp
Expr Node
Posx Pos
}

func (n *UnaryArithmetic) Accept(v Visitor) Node {
n.Expr = n.Expr.Accept(v)

return v(n)
}

func (n *UnaryArithmetic) Pos() Pos {
return n.Posx
}

func (n *UnaryArithmetic) GoString() string {
return fmt.Sprintf("*%#v", *n)
}

func (n *UnaryArithmetic) String() string {
var sign rune
switch n.Op {
case ArithmeticOpAdd:
sign = '+'
case ArithmeticOpSub:
sign = '-'
}
return fmt.Sprintf("%c%s", sign, n.Expr)
}

func (n *UnaryArithmetic) Type(Scope) (Type, error) {
return TypeInt, nil
}
42 changes: 42 additions & 0 deletions config/lang/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,53 @@ func registerBuiltins(scope *ast.BasicScope) *ast.BasicScope {
scope.FuncMap["__builtin_StringToInt"] = builtinStringToInt()

// Math operations
scope.FuncMap["__builtin_UnaryIntMath"] = builtinUnaryIntMath()
scope.FuncMap["__builtin_UnaryFloatMath"] = builtinUnaryFloatMath()
scope.FuncMap["__builtin_IntMath"] = builtinIntMath()
scope.FuncMap["__builtin_FloatMath"] = builtinFloatMath()
return scope
}

func builtinUnaryIntMath() ast.Function {
return ast.Function{
ArgTypes: []ast.Type{ast.TypeInt},
Variadic: false,
ReturnType: ast.TypeInt,
Callback: func(args []interface{}) (interface{}, error) {
op := args[0].(ast.ArithmeticOp)
result := args[1].(int)
switch op {
case ast.ArithmeticOpAdd:
result = result
case ast.ArithmeticOpSub:
result = -result
}

return result, nil
},
}
}

func builtinUnaryFloatMath() ast.Function {
return ast.Function{
ArgTypes: []ast.Type{ast.TypeFloat},
Variadic: false,
ReturnType: ast.TypeFloat,
Callback: func(args []interface{}) (interface{}, error) {
op := args[0].(ast.ArithmeticOp)
result := args[1].(float64)
switch op {
case ast.ArithmeticOpAdd:
result = result
case ast.ArithmeticOpSub:
result = -result
}

return result, nil
},
}
}

func builtinFloatMath() ast.Function {
return ast.Function{
ArgTypes: []ast.Type{ast.TypeInt},
Expand Down
45 changes: 45 additions & 0 deletions config/lang/check_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ func (v *TypeCheck) visit(raw ast.Node) ast.Node {
var result ast.Node
var err error
switch n := raw.(type) {
case *ast.UnaryArithmetic:
tc := &typeCheckUnaryArithmetic{n}
result, err = tc.TypeCheck(v)
case *ast.Arithmetic:
tc := &typeCheckArithmetic{n}
result, err = tc.TypeCheck(v)
Expand Down Expand Up @@ -89,6 +92,48 @@ func (v *TypeCheck) visit(raw ast.Node) ast.Node {
return result
}

type typeCheckUnaryArithmetic struct {
n *ast.UnaryArithmetic
}

func (tc *typeCheckUnaryArithmetic) TypeCheck(v *TypeCheck) (ast.Node, error) {
// Only support + or - as unary op
if tc.n.Op != ast.ArithmeticOpAdd && tc.n.Op != ast.ArithmeticOpSub {
fmt.Printf("%+v\n", tc.n.Op)
return nil, fmt.Errorf("only + or - supported as unary operator")
}
expr := v.StackPop()

mathFunc := "__builtin_UnaryIntMath"
mathType := ast.TypeInt
switch expr {
case ast.TypeInt:
mathFunc = "__builtin_UnaryIntMath"
mathType = expr
case ast.TypeFloat:
mathFunc = "__builtin_UnaryFloatMath"
mathType = expr
}

// Return type
v.StackPush(mathType)

args := make([]ast.Node, 2)
args[0] = &ast.LiteralNode{
Value: tc.n.Op,
Typex: ast.TypeInt,
Posx: tc.n.Pos(),
}
args[1] = tc.n.Expr
// Replace our node with a call to the proper function. This isn't
// type checked but we already verified types.
return &ast.Call{
Func: mathFunc,
Args: args,
Posx: tc.n.Pos(),
}, nil
}

type typeCheckArithmetic struct {
n *ast.Arithmetic
}
Expand Down
54 changes: 54 additions & 0 deletions config/lang/eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,60 @@ func TestEval(t *testing.T) {
"foo 43",
ast.TypeString,
},

{
"foo ${-46}",
nil,
false,
"foo -46",
ast.TypeString,
},

{
"foo ${-46 + 5}",
nil,
false,
"foo -41",
ast.TypeString,
},

{
"foo ${46 + -5}",
nil,
false,
"foo 41",
ast.TypeString,
},

{
"foo ${-bar}",
&ast.BasicScope{
VarMap: map[string]ast.Variable{
"bar": ast.Variable{
Value: 41,
Type: ast.TypeInt,
},
},
},
false,
"foo -41",
ast.TypeString,
},

{
"foo ${5 + -bar}",
&ast.BasicScope{
VarMap: map[string]ast.Variable{
"bar": ast.Variable{
Value: 41,
Type: ast.TypeInt,
},
},
},
false,
"foo -36",
ast.TypeString,
},
}

for _, tc := range cases {
Expand Down
8 changes: 8 additions & 0 deletions config/lang/lang.y
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,14 @@ expr:
Posx: $1.Pos(),
}
}
| ARITH_OP expr
{
$$ = &ast.UnaryArithmetic{
Op: $1.Value.(ast.ArithmeticOp),
Expr: $2,
Posx: $1.Pos,
}
}
| IDENTIFIER
{
$$ = &ast.VariableAccess{Name: $1.Value.(string), Posx: $1.Pos}
Expand Down
23 changes: 23 additions & 0 deletions config/lang/lex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,20 @@ func TestLex(t *testing.T) {
PROGRAM_BRACKET_RIGHT, lexEOF},
},

{
"${bar(-42)}",
[]int{PROGRAM_BRACKET_LEFT,
IDENTIFIER, PAREN_LEFT, ARITH_OP, INTEGER, PAREN_RIGHT,
PROGRAM_BRACKET_RIGHT, lexEOF},
},

{
"${bar(-42.0)}",
[]int{PROGRAM_BRACKET_LEFT,
IDENTIFIER, PAREN_LEFT, ARITH_OP, FLOAT, PAREN_RIGHT,
PROGRAM_BRACKET_RIGHT, lexEOF},
},

{
"${bar(42+1)}",
[]int{PROGRAM_BRACKET_LEFT,
Expand All @@ -72,6 +86,15 @@ func TestLex(t *testing.T) {
PROGRAM_BRACKET_RIGHT, lexEOF},
},

{
"${bar(42+-1)}",
[]int{PROGRAM_BRACKET_LEFT,
IDENTIFIER, PAREN_LEFT,
INTEGER, ARITH_OP, ARITH_OP, INTEGER,
PAREN_RIGHT,
PROGRAM_BRACKET_RIGHT, lexEOF},
},

{
"${bar(3.14159)}",
[]int{PROGRAM_BRACKET_LEFT,
Expand Down
Loading

0 comments on commit 41f9ebc

Please sign in to comment.