Beispiel #1
0
func listFilter(context *parser.FunctionCallContext) error {
	values, err := parser.ValidateArguments(context, "list-filter", parser.VALUE_FUNCTION, parser.VALUE_LIST)

	if err != nil {
		return err
	}

	list := parser.NewListValue()

	for _, item := range values[1].L {
		block, err := execFunctionWithArguments(context, values[0].F, "list-filter", false, item)

		if err != nil {
			return err
		}

		if block.Stack.Len() == 1 {
			b := block.Stack.Pop().(*parser.Value)

			if b.Type != parser.VALUE_BOOL {
				return util.NewInvalidArgumentError("list-filter", "callback function should have a boolean value on the stack, found "+b.Type.String(), context.Token.Line, context.Token.Column, context.Token.File)
			}

			if b.B {
				list.L = append(list.L, item)
			}
		} else {
			return util.NewInvalidArgumentError("list-filter", fmt.Sprintf("callback function needs to exactly have 1 boolean value on top of the stack, found %d", block.Stack.Len()), context.Token.Line, context.Token.Column, context.Token.File)
		}
	}

	context.Block.Stack.Push(list)

	return nil
}
Beispiel #2
0
func orN(context *parser.FunctionCallContext) error {
	values, err := parser.ValidateArguments(context, "or-n", parser.VALUE_FUNCTION)

	if err != nil {
		return err
	}

	fakeBlock := parser.NewBlock(nil, nil, nil)

	runErr := values[0].F.Exec(fakeBlock, context.Token)

	if runErr != nil {
		return runErr
	}

	result := false

	if fakeBlock.Stack.Len() >= 1 {
		for fakeBlock.Stack.Len() > 0 {
			value := fakeBlock.Stack.Pop().(*parser.Value)

			if value.Type != parser.VALUE_BOOL {
				return util.NewInvalidArgumentError("or-n", "expected a boolean in the function stack", context.Token.Line, context.Token.Column, context.Token.File)
			}

			result = result || value.B
		}
	} else {
		return util.NewInvalidArgumentError("or-n", "expected at least 2 values to compare in function stack", context.Token.Line, context.Token.Column, context.Token.File)
	}

	context.Block.Stack.Push(parser.NewValueFromBool(result))

	return nil
}
Beispiel #3
0
func stringRange(context *parser.FunctionCallContext) error {
	values, err := parser.ValidateArguments(context, "string-range", parser.VALUE_NUMBER, parser.VALUE_NUMBER, parser.VALUE_STRING)

	if err != nil {
		return err
	}

	beginf, _ := values[1].N.Float64()
	endf, _ := values[0].N.Float64()

	begin := int(beginf)
	end := int(endf)

	s := values[2].S

	if begin < 0 || begin > len(s)-1 {
		return util.NewInvalidArgumentError("string-range", fmt.Sprintf("%d out of range (string length is: %d)", begin, len(s)), context.Token.Line, context.Token.Column, context.Token.File)
	}

	if end < begin {
		return util.NewInvalidArgumentError("string-range", fmt.Sprintf("%d must be bigger then %d", end, begin), context.Token.Line, context.Token.Column, context.Token.File)
	}

	if end < 0 || end > len(s)-1 {
		return util.NewInvalidArgumentError("string-range", fmt.Sprintf("%d out of range (string length is: %d)", end, len(s)), context.Token.Line, context.Token.Column, context.Token.File)
	}

	context.Block.Stack.Push(parser.NewValueFromString(s[begin : end+1]))

	return nil
}
Beispiel #4
0
func list(context *parser.FunctionCallContext) error {
	values, err := parser.ValidateArguments(context, "list", parser.VALUE_FUNCTION)

	if err != nil {
		return err
	}

	if values[0].F.Type != parser.FUNCTION_LAMBDA {
		return util.NewInvalidArgumentError("list", "expected a lambda", context.Token.Line, context.Token.Column, context.Token.File)
	}

	err = values[0].F.L.Exec()

	if err != nil {
		return err
	}

	var data []*parser.Value

	list := parser.NewListValue()

	for values[0].F.L.Stack.Len() > 0 {
		data = append(data, values[0].F.L.Stack.Pop().(*parser.Value))
	}

	for i := len(data) - 1; i >= 0; i-- {
		list.L = append(list.L, data[i])
	}

	context.Block.Stack.Push(list)

	return nil
}
Beispiel #5
0
func listRange(context *parser.FunctionCallContext) error {
	values, err := parser.ValidateArguments(context, "list-range", parser.VALUE_NUMBER, parser.VALUE_NUMBER, parser.VALUE_LIST)

	if err != nil {
		return err
	}

	newList := parser.NewListValue()
	list := values[2].L

	start, _ := values[1].N.Float64()
	end, _ := values[0].N.Float64()

	if int(start) > int(end) || int(end) >= len(list) || int(start) < 0 || int(end) < 0 {
		return util.NewInvalidArgumentError("list-range", "invalid bounds", context.Token.Line, context.Token.Column, context.Token.File)
	}

	for i := int(start); i <= int(end); i++ {
		newList.L = append(newList.L, list[i])
	}

	context.Block.Stack.Push(newList)

	return nil
}
Beispiel #6
0
func while_(context *parser.FunctionCallContext) error {
	values, err := parser.ValidateArguments(context, "while", parser.VALUE_FUNCTION, parser.VALUE_FUNCTION)

	if err != nil {
		return err
	}

	check := values[0].F

recheck:
	fakeBlock := parser.NewBlock(nil, nil, nil)

	checkErr := check.Exec(fakeBlock, context.Token)

	if checkErr != nil {
		return checkErr
	}

	if fakeBlock.Stack.Len() != 1 || fakeBlock.Stack.Peek(0).(*parser.Value).Type != parser.VALUE_BOOL {
		return util.NewInvalidArgumentError("while", "the callback function provided to while must return 1 boolean value", context.Token.Line, context.Token.Column, context.Token.File)
	}

	if fakeBlock.Stack.Peek(0).(*parser.Value).B {
		runErr := values[1].F.Exec(context.Block, context.Token)

		if runErr != nil {
			return runErr
		}

		goto recheck
	}

	return nil
}
Beispiel #7
0
func keep(context *parser.FunctionCallContext) error {
	values, err := parser.ValidateArguments(context, "keep", parser.VALUE_NUMBER)

	if err != nil {
		return err
	}

	n, _ := values[0].N.Float64()

	if int(n) < 0 || int(n) > context.Block.Stack.Len() {
		return util.NewInvalidArgumentError("keep", fmt.Sprintf("cannot keep %d values while stack height is %d", int(n), context.Block.Stack.Len()), context.Token.Line, context.Token.Column, context.Token.File)
	}

	var kept []*parser.Value

	for i := 0; i < int(n); i++ {
		kept = append(kept, context.Block.Stack.Pop().(*parser.Value))
	}

	for context.Block.Stack.Len() > 0 {
		context.Block.Stack.Pop()
	}

	for i := len(kept) - 1; i >= 0; i-- {
		context.Block.Stack.Push(kept[i])
	}

	return nil
}
Beispiel #8
0
func characterChecks(context *parser.FunctionCallContext, name string) (rune, error) {
	values, err := parser.ValidateArguments(context, name, parser.VALUE_STRING)

	if err != nil {
		return 0, err
	}

	if len(values[0].S) != 1 {
		return 0, util.NewInvalidArgumentError(name, "can only accept string with 1 character", context.Token.Line, context.Token.Column, context.Token.File)
	}

	return rune(values[0].S[0]), nil
}
Beispiel #9
0
func execFunctionWithArguments(context *parser.FunctionCallContext, function *parser.Function, name string, popExcess bool, args ...*parser.Value) (*parser.Block, error) {
	switch function.Type {
	case parser.FUNCTION_DECLARED:
		if len(function.Args) != len(args) {
			return nil, util.NewInvalidArgumentError(name, fmt.Sprintf("given function needs %d argument(s), %d specified", len(args), len(function.Args)), context.Token.Line, context.Token.Column, context.Token.File)
		}

		for i := 0; i < len(args); i++ {
			function.D.Scope.SetSymbol(function.Args[i], parser.NewSymbol(args[i], true, false))
		}

		err := function.D.Exec()

		if popExcess {
			for function.D.Stack.Len() > 0 {
				context.Block.Stack.Push(function.D.Stack.Pop())
			}
		}

		return function.D, err
	case parser.FUNCTION_LAMBDA:
		for _, arg := range args {
			function.L.Stack.Push(arg)
		}

		err := function.L.Exec()

		if popExcess {
			for function.L.Stack.Len() > 0 {
				context.Block.Stack.Push(function.L.Stack.Pop())
			}
		}

		return function.L, err
	default:
		return nil, util.NewInvalidArgumentError(name, "cannot handle "+function.Type.String()+" function", context.Token.Line, context.Token.Column, context.Token.File)
	}
}
Beispiel #10
0
func div(context *parser.FunctionCallContext) error {
	values, err := parser.ValidateArguments(context, "div", parser.VALUE_NUMBER, parser.VALUE_NUMBER)

	if err != nil {
		return err
	}

	if values[0].N.Cmp(big.NewRat(0, 1)) == 0 {
		return util.NewInvalidArgumentError("div", "division by zero", context.Token.Line, context.Token.Column, context.Token.File)
	}

	context.Block.Stack.Push(parser.NewValueFromRat(big.NewRat(0, 1).Quo(values[1].N, values[0].N)))

	return nil
}
Beispiel #11
0
func listGet(context *parser.FunctionCallContext) error {
	values, err := parser.ValidateArguments(context, "list-get", parser.VALUE_NUMBER, parser.VALUE_LIST)

	if err != nil {
		return err
	}

	n, _ := values[0].N.Float64()

	if int(n) < 0 || int(n) > len(values[1].L) {
		return util.NewInvalidArgumentError("list-get", fmt.Sprintf("index out of bounds (%d, list size is %d)", int(n), len(values[1].L)), context.Token.Line, context.Token.Column, context.Token.File)
	}

	context.Block.Stack.Push(values[1].L[int(n)])

	return nil
}
Beispiel #12
0
func stringRuneAt(context *parser.FunctionCallContext) error {
	values, err := parser.ValidateArguments(context, "string-rune-at", parser.VALUE_STRING, parser.VALUE_STRING)

	if err != nil {
		return err
	}

	s := values[0].S

	if len(s) == 0 || len(s) > 1 {
		return util.NewInvalidArgumentError("string-rune-at", "expected a string of 1 character", context.Token.Line, context.Token.Column, context.Token.File)
	}

	context.Block.Stack.Push(parser.NewValueFromInt64(int64(strings.IndexRune(values[1].S, rune(s[0])))))

	return nil
}
Beispiel #13
0
func listRemove(context *parser.FunctionCallContext) error {
	values, err := parser.ValidateArguments(context, "list-remove", parser.VALUE_NUMBER, parser.VALUE_LIST)

	if err != nil {
		return err
	}

	list := values[1]
	n, _ := values[0].N.Float64()

	if int(n) < 0 || int(n) > len(list.L) {
		return util.NewInvalidArgumentError("list-remove", fmt.Sprintf("index out of bounds (%d, list size is %d)", int(n), len(list.L)), context.Token.Line, context.Token.Column, context.Token.File)
	}

	list.L = append(list.L[:int(n)], list.L[int(n)+1:]...)

	context.Block.Stack.Push(list)

	return nil
}
Beispiel #14
0
func map_(context *parser.FunctionCallContext) error {
	values, err := parser.ValidateArguments(context, "map", parser.VALUE_LIST)

	if err != nil {
		return err
	}

	list := values[0].L

	if len(list)%2 != 0 {
		return util.NewInvalidArgumentError("map", "cannot create map of given list because the list is unbalanced, missing key or value", context.Token.Line, context.Token.Column, context.Token.File)
	}

	newMap := parser.NewMapValue()

	for i := 0; i < len(list); i += 2 {
		newMap.M[list[i]] = list[i+1]
	}

	context.Block.Stack.Push(newMap)

	return nil
}
Beispiel #15
0
func switchFunction(context *parser.FunctionCallContext, fall bool, otherwise bool) error {
	functionName := "switch"

	if fall {
		functionName += "-fallthrough"
	}

	var otherwiseFunction *parser.Function

	if otherwise {
		valuesOtherwise, errOtherwise := parser.ValidateArguments(context, functionName, parser.VALUE_FUNCTION)

		if errOtherwise != nil {
			return errOtherwise
		}

		otherwiseFunction = valuesOtherwise[0].F
	}

	values, err := parser.ValidateArguments(context, functionName, parser.VALUE_FUNCTION)

	if err != nil {
		return err
	}

	conditionFunction := values[0].F

	fakeBlock := parser.NewBlock(nil, nil, nil)

	conditionsErr := conditionFunction.Exec(fakeBlock, context.Token)

	if conditionsErr != nil {
		return conditionsErr
	}

	if fakeBlock.Stack.Len()%2 != 0 {
		return util.NewInvalidArgumentError(functionName, "cannot switch because the stack of the function is unbalanced, missing condition or callback", context.Token.Line, context.Token.Column, context.Token.File)
	}

	any := false
	conditions := 0

	for i := fakeBlock.Stack.Len() - 1; i >= 0; i-- {
		conditions++

		condition := fakeBlock.Stack.Peek(i).(*parser.Value)

		if condition.Type != parser.VALUE_BOOL {
			return util.NewInvalidArgumentError(functionName, "expected a boolean condition for condition "+strconv.Itoa(conditions), context.Token.Line, context.Token.Column, context.Token.File)
		}

		i--

		if condition.B {
			any = true

			callback := fakeBlock.Stack.Peek(i).(*parser.Value)

			if callback.Type != parser.VALUE_FUNCTION {
				return util.NewInvalidArgumentError(functionName, "expected a function callback for condition "+strconv.Itoa(conditions), context.Token.Line, context.Token.Column, context.Token.File)
			}

			runErr := callback.F.Exec(context.Block, context.Token)

			if runErr != nil {
				return runErr
			}

			if !fall {
				break
			}
		}
	}

	if otherwise && !any {
		otherwiseErr := otherwiseFunction.Exec(context.Block, context.Token)

		if otherwiseErr != nil {
			return otherwiseErr
		}
	}

	return nil
}