Skip to content

Commit

Permalink
Implemented factorial-operator
Browse files Browse the repository at this point in the history
  • Loading branch information
dbaumgarten committed Dec 3, 2020
1 parent 8d31c91 commit c3278ff
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 25 deletions.
2 changes: 2 additions & 0 deletions examples/yolol/operations4.yolol
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ i="abc" j=i-- k= --i :testd=i=="a" and j=="abc" and k=="a"
:testlt="qqq"<"xyz" and not ("xyz"<"qqq") and ""<"a"
:testlte="qqq"<="xyz" and not ("xyz"<="qqq") and "qqq">="qqq"
:testra=2*3^4==162 and (2*3)^4==1296
:testfact=2!==2 and 3!==6 and 3!!=5 and 4!/4==3! and sqrt 3!==2.449
:testfact=:testfact and -2!==1 and -(2!)== -2 and 3!!==6! and 2.5!==6
:done=1
1 change: 1 addition & 0 deletions examples/yolol/operations4_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ cases:
testlt: 1
testlte: 1
testra: 1
testfact: 1


10 changes: 8 additions & 2 deletions pkg/parser/ast/tokenizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,9 @@ func (p Position) Before(other Position) bool {
return false
}

var symbols = []string{"++", "--", ">=", "<=", "!=", "==", "==", "+=", "-=", "*=", "/=", "%=",
"=", ">", "<", "+", "-", "*", "/", "^", "%", ",", "(", ")"}
// !!= is a hack, meaning "factorial followed by equal"
var symbols = []string{"!==", "++", "--", ">=", "<=", "!=", "==", "==", "+=", "-=", "*=", "/=", "%=",
"=", ">", "<", "+", "-", "*", "/", "^", "%", ",", "(", ")", "!"}

var keywordRegex = regexp.MustCompile(`(?i)^(if|else\b|end\b|then|goto|and|or|not|abs|sqrt|sin|cos|tan|asin|acos|atan)`)

Expand Down Expand Up @@ -233,6 +234,11 @@ func (t *Tokenizer) getSymbol() *Token {
for i := range t.Symbols {
symbol := []byte(t.Symbols[i])
if bytes.HasPrefix(t.remaining, symbol) {
if t.Symbols[i] == "!==" {
// this special case is needed, as otherwise !== would be parsed as "!= =", but it shold be "! =="
defer t.advance(1)
return t.newToken(TypeSymbol, "!")
}
defer t.advance(len(symbol))
return t.newToken(TypeSymbol, string(symbol))
}
Expand Down
42 changes: 41 additions & 1 deletion pkg/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ type YololParserFunctions interface {
ParseExpression() ast.Expression
ParseBinaryExpression(int) ast.Expression
ParseUnaryExpression() ast.Expression
ParseFactorioalExpression() ast.Expression
ParseNegationExpression() ast.Expression
ParseBracketExpression() ast.Expression
ParseSingleExpression() ast.Expression
ParsePreOpExpression() ast.Expression
Expand Down Expand Up @@ -533,7 +535,7 @@ func (p *Parser) ParseBinaryExpression(idx int) ast.Expression {
func (p *Parser) ParseUnaryExpression() ast.Expression {
p.Log()
preUnaryOps := []string{"not", "abs", "sqrt", "sin", "cos", "tan", "asin", "acos", "atan"}
if (p.IsCurrentValueIn(preUnaryOps) && p.IsCurrentType(ast.TypeKeyword)) || p.IsCurrent(ast.TypeSymbol, "-") {
if p.IsCurrentValueIn(preUnaryOps) && p.IsCurrentType(ast.TypeKeyword) {
unaryExp := &ast.UnaryOperation{
Operator: p.CurrentToken.Value,
Position: p.CurrentToken.Position,
Expand All @@ -546,6 +548,44 @@ func (p *Parser) ParseUnaryExpression() ast.Expression {
unaryExp.Exp = subexp
return unaryExp
}
return p.This.ParseFactorioalExpression()
}

// ParseFactorioalExpression parses a factorial
func (p *Parser) ParseFactorioalExpression() ast.Expression {
p.Log()

subexp := p.This.ParseNegationExpression()
if subexp != nil {
for p.IsCurrent(ast.TypeSymbol, "!") {
subexp = &ast.UnaryOperation{
Operator: "!",
Position: p.CurrentToken.Position,
Exp: subexp,
}
p.Advance()
}
}
return subexp
}

// ParseNegationExpression parses a negation
func (p *Parser) ParseNegationExpression() ast.Expression {
p.Log()

if p.IsCurrent(ast.TypeSymbol, "-") {
unaryExp := &ast.UnaryOperation{
Operator: p.CurrentToken.Value,
Position: p.CurrentToken.Position,
}
p.Advance()
subexp := p.This.ParseNegationExpression()
if subexp == nil {
p.ErrorExpectedExpression(fmt.Sprintf("on right side of %s", unaryExp.Operator))
}
unaryExp.Exp = subexp
return unaryExp
}
return p.This.ParseBracketExpression()
}

Expand Down
67 changes: 45 additions & 22 deletions pkg/parser/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type Printer struct {
DebugPositions bool
}

var operatorPriority = map[string]int{
var binaryOperatorPriority = map[string]int{
"and": 0,
"or": 1,
"==": 2,
Expand All @@ -55,7 +55,20 @@ var operatorPriority = map[string]int{
"/": 5,
"%": 5,
"^": 6,
"not": 7,
}

var unaryOperatorPriority = map[string]int{
"not": 7,
"abs": 7,
"sqrt": 7,
"sin": 7,
"cos": 7,
"tan": 7,
"asin": 7,
"acos": 7,
"atan": 7,
"!": 8,
"-": 9,
}

// end and else are missing here, because unlike other keywords they might require a space after them
Expand Down Expand Up @@ -212,25 +225,7 @@ func (p *Printer) Print(prog ast.Node) (string, error) {
p.printBinaryOperation(n, visitType)
break
case *ast.UnaryOperation:
_, childBinary := n.Exp.(*ast.BinaryOperation)
if visitType == ast.PreVisit {
op := n.Operator
if op == "-" {
p.Space()
p.Write(op)
} else {
p.Write(op)
p.Space()
}
if childBinary {
p.Write("(")
}
}
if visitType == ast.PostVisit {
if childBinary {
p.Write(")")
}
}
p.printUnaryOperation(n, visitType)
break
default:
return fmt.Errorf("Unknown ast-node: %T%v", node, node)
Expand Down Expand Up @@ -298,13 +293,41 @@ func (p *Printer) printBinaryOperation(o *ast.BinaryOperation, visitType int) {
}
break
}
}

func (p *Printer) printUnaryOperation(o *ast.UnaryOperation, visitType int) {
childPrio := priorityForExpression(o.Exp)
thisPrio := priorityForExpression(o)
if visitType == ast.PreVisit {
if o.Operator == "-" {
p.Space()
p.Write(o.Operator)
} else if o.Operator == "!" {
//do not write anything in PreVisit
} else {
p.Write(o.Operator)
p.Space()
}
if childPrio < thisPrio {
p.Write("(")
}
}
if visitType == ast.PostVisit {
if o.Operator == "!" {
p.Write(o.Operator)
}
if childPrio < thisPrio {
p.Write(")")
}
}
}

func priorityForExpression(e ast.Expression) int {
switch ex := e.(type) {
case *ast.BinaryOperation:
return operatorPriority[ex.Operator]
return binaryOperatorPriority[ex.Operator]
case *ast.UnaryOperation:
return unaryOperatorPriority[ex.Operator]
default:
return 10
}
Expand Down
10 changes: 10 additions & 0 deletions pkg/vm/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ func RunUnaryOperation(arg *Variable, operator string) (*Variable, error) {
case "atan":
result.Value = arg.Number().Atan()
break
case "!":
num := arg.Number()
res := 1
i := 0
for num > 0 {
i++
num = num.Sub(number.One)
res *= i
}
result.Value = number.FromInt(res)
default:
return nil, fmt.Errorf("Unknown unary operator for numbers '%s'", operator)
}
Expand Down

0 comments on commit c3278ff

Please sign in to comment.