Example #1
0
func builtinMath(context *runtime.FunctionCallContext) (*runtime.Value, error) {
	if err := runtime.ValidateArguments(context, runtime.NumberValue, runtime.NumberValue); err != nil {
		return nil, err
	}

	base := big.NewRat(0, 1)

	var callback func(*big.Rat, *big.Rat) *big.Rat

	switch context.Name {
	case "+":
		callback = base.Add
	case "-":
		callback = base.Sub
	case "*":
		callback = base.Mul
	case "/":
		if context.Args[1].Number.Cmp(base) == 0 {
			return nil, runtime.NewRuntimeError(context.Pos, "division by zero")
		}

		callback = base.Quo
	}

	return runtime.NewNumberValueFromRat(callback(context.Args[0].Number, context.Args[1].Number)), nil
}
Example #2
0
func listMap(context *runtime.MacroCallContext) (*runtime.Value, error) {
	list, err := context.Block.EvalNode(context.Nodes[0])

	if err != nil {
		return nil, err
	}

	if list.Type != runtime.ListValue {
		return nil, runtime.NewRuntimeError(context.Nodes[0].Pos(), "expected a list")
	}

	ident := context.Nodes[1].(*parser.IdentifierNode).Token.Data

	callback := context.Nodes[2]

	mappedList := runtime.NewListValue()

	for _, item := range list.List {
		b := runtime.NewBlock([]parser.Node{callback}, runtime.NewScope(context.Block.Scope))
		b.Scope.SetSymbolLocally(ident, runtime.NewSymbol(item))

		result, err := b.Eval()

		if err != nil {
			return nil, err
		}

		mappedList.List = append(mappedList.List, result)
	}

	return mappedList, nil
}
Example #3
0
func stringsCharacterCheck(context *runtime.FunctionCallContext) (*runtime.Value, error) {
	if err := runtime.ValidateArguments(context, runtime.StringValue); err != nil {
		return nil, err
	}

	s := context.Args[0].Str

	if len(s) != 1 {
		return nil, runtime.NewRuntimeError(context.Pos, "expected string that has 1 character, got %d character(s)", len(s))
	}

	var callback func(rune) bool

	switch context.Name {
	case "is-digit":
		callback = unicode.IsDigit
	case "is-letter":
		callback = unicode.IsLetter
	case "is-lower":
		callback = unicode.IsLower
	case "is-upper":
		callback = unicode.IsUpper
	}

	return runtime.BooleanValueFor(callback(rune(s[0]))), nil
}
Example #4
0
func builtinFor(context *runtime.MacroCallContext) (*runtime.Value, error) {
	l, err := context.Block.EvalNode(context.Nodes[0])

	if err != nil {
		return nil, err
	}

	if l.Type != runtime.ListValue {
		return nil, runtime.NewRuntimeError(context.Nodes[0].Pos(), "expected a list to iterate over")
	}

	var args []string

	for _, nameNode := range context.Nodes[1].(*parser.ListNode).Nodes {
		ident, isIdent := nameNode.(*parser.IdentifierNode)

		if isIdent {
			args = append(args, ident.Token.Data)
		} else {
			return nil, runtime.NewRuntimeError(nameNode.Pos(), "expected an identifier")
		}
	}

	if len(args) > 2 {
		return nil, runtime.NewRuntimeError(context.Nodes[1].Pos(), "too many arguments provided")
	}

	callbackBlock := runtime.NewBlock([]parser.Node{context.Nodes[2]}, runtime.NewScope(context.Block.Scope))

	for i, item := range l.List {
		if len(args) >= 1 {
			callbackBlock.Scope.SetSymbol(args[0], runtime.NewSymbol(item))
		}

		if len(args) == 2 {
			callbackBlock.Scope.SetSymbol(args[1], runtime.NewSymbol(runtime.NewNumberValueFromInt64(int64(i))))
		}

		_, err := callbackBlock.Eval()

		if err != nil {
			return nil, err
		}
	}

	return runtime.Nil, nil
}
Example #5
0
func builtinCall(context *runtime.FunctionCallContext) (*runtime.Value, error) {
	if len(context.Args) < 1 || context.Args[0].Type != runtime.FunctionValue {
		return nil, runtime.NewRuntimeError(context.Pos, "expected a function")
	}

	function := context.Args[0].Function

	return function.Call(context.Block, context.Args[1:], context.Pos)
}
Example #6
0
func builtinDefmacro(context *runtime.MacroCallContext) (*runtime.Value, error) {
	name := context.Nodes[0].(*parser.IdentifierNode).Token.Data

	if name == "_" {
		return nil, runtime.NewRuntimeError(context.Nodes[0].Pos(), "disallowed macro name")
	}

	if context.Block.Scope.GetSymbol(name) != nil && context.Block.Scope.GetSymbol(name).Const {
		return nil, runtime.NewRuntimeError(context.Nodes[0].Pos(), "%s is a constant and cannot be modified", name)
	}

	argNodes := context.Nodes[1].(*parser.ListNode)
	var args []string
	callback := context.Nodes[2].(*parser.ListNode)

	if len(callback.Nodes) == 0 {
		return nil, runtime.NewRuntimeError(callback.Pos(), "empty macro body")
	}

	for _, argNode := range argNodes.Nodes {
		ident, ok := argNode.(*parser.IdentifierNode)

		if !ok {
			return nil, runtime.NewRuntimeError(argNode.Pos(), "expected an identifier")
		}

		args = append(args, ident.Token.Data)
	}

	macro := runtime.NewMacro(func(handlerContext *runtime.MacroCallContext) (*runtime.Value, error) {
		block := runtime.NewBlock([]parser.Node{callback}, runtime.NewScope(handlerContext.Block.Scope))

		for i, arg := range args {
			block.Scope.SetSymbolLocally(arg, runtime.NewSymbol(runtime.NewQuotedValue(handlerContext.Nodes[i])))
		}

		return block.Eval()
	}, false)

	context.Block.Scope.SetMacro(name, macro)

	return runtime.Nil, nil
}
Example #7
0
func builtinExport(context *runtime.MacroCallContext) (*runtime.Value, error) {
	for _, node := range context.Nodes {
		ident, isIdent := node.(*parser.IdentifierNode)

		if !isIdent {
			return nil, runtime.NewRuntimeError(node.Pos(), "expected an identifier")
		} else {
			name := ident.Token.Data

			if !context.Block.Scope.HasSymbol(name) {
				return nil, runtime.NewRuntimeError(node.Pos(), "unknown symbol '%s'", name)
			}

			context.Block.Scope.GetSymbol(name).Exported = true
		}
	}

	return runtime.Nil, nil
}
Example #8
0
func listReduce(context *runtime.MacroCallContext) (*runtime.Value, error) {
	list, err := context.Block.EvalNode(context.Nodes[0])

	if err != nil {
		return nil, err
	}

	if list.Type != runtime.ListValue {
		return nil, runtime.NewRuntimeError(context.Nodes[0].Pos(), "expected a list")
	}

	if len(list.List) == 0 {
		return nil, runtime.NewRuntimeError(context.Nodes[0].Pos(), "empty list")
	}

	identLeft := context.Nodes[1].(*parser.IdentifierNode).Token.Data
	identRight := context.Nodes[2].(*parser.IdentifierNode).Token.Data

	callback := context.Nodes[3]

	reduced := list.List[0]

	for _, item := range list.List[1:] {
		b := runtime.NewBlock([]parser.Node{callback}, runtime.NewScope(context.Block.Scope))
		b.Scope.SetSymbolLocally(identLeft, runtime.NewSymbol(reduced))
		b.Scope.SetSymbolLocally(identRight, runtime.NewSymbol(item))

		result, err := b.Eval()

		if err != nil {
			return nil, err
		}

		reduced = result
	}

	return reduced, nil
}
Example #9
0
func stringsFormat(context *runtime.FunctionCallContext) (*runtime.Value, error) {
	if len(context.Args) < 1 {
		return nil, runtime.NewRuntimeError(context.Pos, "missing format specifier")
	}

	if context.Args[0].Type != runtime.StringValue {
		return nil, runtime.NewRuntimeError(context.Pos, "format specifier should be a string")
	}

	format := context.Args[0].Str

	args := strings.Count(format, "~")

	if len(context.Args)-1 != args {
		return nil, runtime.NewRuntimeError(context.Pos, "format specifier expected %d arguments, got %d", args, len(context.Args)-1)
	}

	for _, item := range context.Args[1:] {
		format = strings.Replace(format, "~", item.String(), 1)
	}

	return runtime.NewStringValue(format), nil
}
Example #10
0
func builtinDef(context *runtime.MacroCallContext) (*runtime.Value, error) {
	name := context.Nodes[0].(*parser.IdentifierNode).Token.Data
	value, err := context.Block.EvalNode(context.Nodes[1])

	if name == "_" {
		return nil, runtime.NewRuntimeError(context.Nodes[0].Pos(), "disallowed symbol name")
	}

	if err != nil {
		return nil, err
	}

	if context.Block.Scope.GetSymbol(name) != nil && context.Block.Scope.GetSymbol(name).Const {
		return nil, runtime.NewRuntimeError(context.Nodes[0].Pos(), "%s is a constant and cannot be modified", name)
	}

	sym := runtime.NewSymbol(value)
	sym.Const = context.Name == "defconst"

	context.Block.Scope.SetSymbol(name, sym)

	return value, nil
}
Example #11
0
func builtinFun(context *runtime.MacroCallContext) (*runtime.Value, error) {
	argNodes := context.Nodes[0].(*parser.ListNode)
	var args []string
	callback := context.Nodes[1].(*parser.ListNode)

	if len(callback.Nodes) == 0 {
		return nil, runtime.NewRuntimeError(callback.Pos(), "empty function body")
	}

	for _, argNode := range argNodes.Nodes {
		ident, ok := argNode.(*parser.IdentifierNode)

		if !ok {
			return nil, runtime.NewRuntimeError(argNode.Pos(), "expected an identifier")
		}

		args = append(args, ident.Token.Data)
	}

	function := runtime.NewLambdaFunction([]parser.Node{callback}, args)

	return runtime.NewFunctionValue(function), nil
}
Example #12
0
func listGet(context *runtime.FunctionCallContext) (*runtime.Value, error) {
	if err := runtime.ValidateArguments(context, runtime.ListValue, runtime.NumberValue); err != nil {
		return nil, err
	}

	size := int64(len(context.Args[0].List))
	index := context.Args[1].NumberToInt64()

	if index < 0 || index > size-1 {
		return nil, runtime.NewRuntimeError(context.Pos, "index %d out of bounds (list size is %d)", index, size)
	}

	return context.Args[0].List[index], nil
}
Example #13
0
func builtinDefun(context *runtime.MacroCallContext) (*runtime.Value, error) {
	name := context.Nodes[0].(*parser.IdentifierNode).Token.Data

	if name == "_" {
		return nil, runtime.NewRuntimeError(context.Nodes[0].Pos(), "disallowed function name")
	}

	if context.Block.Scope.GetSymbol(name) != nil && context.Block.Scope.GetSymbol(name).Const {
		return nil, runtime.NewRuntimeError(context.Nodes[0].Pos(), "%s is a constant and cannot be modified", name)
	}

	argNodes := context.Nodes[1].(*parser.ListNode)
	var args []string
	callback := context.Nodes[2].(*parser.ListNode)

	if len(callback.Nodes) == 0 {
		return nil, runtime.NewRuntimeError(callback.Pos(), "empty function body")
	}

	for _, argNode := range argNodes.Nodes {
		ident, ok := argNode.(*parser.IdentifierNode)

		if !ok {
			return nil, runtime.NewRuntimeError(argNode.Pos(), "expected an identifier")
		}

		args = append(args, ident.Token.Data)
	}

	function := runtime.NewDeclaredFunction([]parser.Node{callback}, name, args)
	functionValue := runtime.NewFunctionValue(function)

	context.Block.Scope.SetSymbol(name, runtime.NewSymbol(functionValue))

	return functionValue, nil
}
Example #14
0
func listDropLeft(context *runtime.FunctionCallContext) (*runtime.Value, error) {
	if err := runtime.ValidateArguments(context, runtime.ListValue); err != nil {
		return nil, err
	}

	l := context.Args[0]

	if len(l.List) == 0 {
		return nil, runtime.NewRuntimeError(context.Pos, "empty list")
	}

	l.List = l.List[1:]

	return context.Args[0], nil
}
Example #15
0
func builtinIfel(context *runtime.MacroCallContext) (*runtime.Value, error) {
	conditionNode := context.Nodes[0]
	condition, err := context.Block.EvalNode(conditionNode)

	if err != nil {
		return nil, err
	}

	if condition.Type != runtime.BooleanValue {
		return nil, runtime.NewRuntimeError(conditionNode.Pos(), "expected a boolean")
	}

	if condition.Boolean == true {
		return context.Block.EvalNode(context.Nodes[1])
	} else {
		return context.Block.EvalNode(context.Nodes[2])
	}
}
Example #16
0
func builtinQuoted2List(context *runtime.FunctionCallContext) (*runtime.Value, error) {
	if err := runtime.ValidateArguments(context, runtime.QuotedValue); err != nil {
		return nil, err
	}

	quoted := context.Args[0].Quoted

	if list, isList := quoted.(*parser.ListNode); isList {
		l := runtime.NewListValue()

		for _, listNode := range list.Nodes {
			l.List = append(l.List, runtime.NewQuotedValue(listNode))
		}

		return l, nil
	} else {
		return nil, runtime.NewRuntimeError(context.Pos, "expected a quoted list, not %s", quoted.Name())
	}
}
Example #17
0
func stringsRange(context *runtime.FunctionCallContext) (*runtime.Value, error) {
	if err := runtime.ValidateArguments(context, runtime.StringValue, runtime.NumberValue, runtime.NumberValue); err != nil {
		return nil, err
	}

	source := context.Args[0].Str
	start := context.Args[1].NumberToInt64()
	end := context.Args[2].NumberToInt64()

	sourceLen := int64(len(source))

	if start < 0 || start > sourceLen || end < 0 || end > sourceLen {
		return nil, runtime.NewRuntimeError(context.Pos, "out of bounds (length is %d, trying to access %d:%d)", sourceLen, start, end)
	}

	result := source[start:end]

	return runtime.NewStringValue(result), nil
}
Example #18
0
func listSeq(context *runtime.FunctionCallContext) (*runtime.Value, error) {
	if err := runtime.ValidateArguments(context, runtime.NumberValue, runtime.NumberValue); err != nil {
		return nil, err
	}

	low := context.Args[0].NumberToInt64()
	high := context.Args[1].NumberToInt64()

	if low > high {
		return nil, runtime.NewRuntimeError(context.Pos, "invalid argument(s), low can't be higher than high (%d > %d)", low, high)
	}

	l := runtime.NewListValue()

	for i := low; i <= high; i++ {
		l.List = append(l.List, runtime.NewNumberValueFromInt64(i))
	}

	return l, nil
}
Example #19
0
func builtinAssert(context *runtime.FunctionCallContext) (*runtime.Value, error) {
	optionalError := runtime.ValidateArguments(context, runtime.BooleanValue)

	if optionalError != nil {
		err := runtime.ValidateArguments(context, runtime.BooleanValue, runtime.StringValue)

		if err != nil {
			return nil, err
		}
	}

	if !context.Args[0].Boolean {
		message := "assertion failed"

		if len(context.Args) == 2 {
			message += ": " + context.Args[1].Str
		}

		return nil, runtime.NewRuntimeError(context.Pos, message)
	}

	return runtime.Nil, nil
}
Example #20
0
func listRemove(context *runtime.FunctionCallContext) (*runtime.Value, error) {
	if err := runtime.ValidateArguments(context, runtime.ListValue, runtime.NumberValue); err != nil {
		return nil, err
	}

	list := context.Args[0].List
	size := int64(len(list))
	index := context.Args[1].NumberToInt64()

	if index < 0 || index > size-1 {
		return nil, runtime.NewRuntimeError(context.Pos, "index %d out of bounds (list size is %d)", index, size)
	}

	newList := runtime.NewListValue()

	for i, item := range list {
		if int64(i) != index {
			newList.List = append(newList.List, item)
		}
	}

	return newList, nil
}
Example #21
0
func listRange(context *runtime.FunctionCallContext) (*runtime.Value, error) {
	var begin, end int64

	err := runtime.ValidateArguments(context, runtime.ListValue, runtime.NumberValue, runtime.NumberValue)

	if err != nil {
		optionalErr := runtime.ValidateArguments(context, runtime.ListValue, runtime.NumberValue)

		if optionalErr == nil {
			begin = context.Args[1].NumberToInt64()
			end = int64(len(context.Args[0].List)) - 1
		} else {
			return nil, optionalErr
		}
	} else {
		begin = context.Args[1].NumberToInt64()
		end = context.Args[2].NumberToInt64()
	}

	length := int64(len(context.Args[0].List))

	if begin < 0 || begin > length-1 || begin > end || end < 0 || end > length-1 {
		return nil, runtime.NewRuntimeError(context.Pos, "invalid bounds %d and %d (list length is %d)", begin, end, length)
	}

	newList := runtime.NewListValue()

	for _, item := range context.Args[0].List[begin : end+1] {
		newList.List = append(newList.List, item)
	}

	if len(newList.List) == 1 {
		return newList.List[0], nil
	} else {
		return newList, nil
	}
}
Example #22
0
func builtinWhile(context *runtime.MacroCallContext) (*runtime.Value, error) {
recheck:
	callback, err := context.Block.EvalNode(context.Nodes[0])

	if err != nil {
		return nil, err
	}

	if callback.Type != runtime.BooleanValue {
		return nil, runtime.NewRuntimeError(context.Nodes[0].Pos(), "expected a boolean")
	}

	if callback.Boolean {
		_, err := context.Block.EvalNode(context.Nodes[1])

		if err != nil {
			return nil, err
		}

		goto recheck
	}

	return runtime.Nil, nil
}
Example #23
0
func builtinCase(context *runtime.MacroCallContext) (*runtime.Value, error) {
	if len(context.Nodes) < 1 {
		return nil, runtime.NewRuntimeError(context.Pos, "missing value to compare to")
	}

	matchNode := context.Nodes[0]
	match, err := context.Block.EvalNode(matchNode)

	if err != nil {
		return nil, err
	}

	if ((len(context.Nodes) - 1) % 2) != 0 { // -1 because we can't count for the match node too
		return nil, runtime.NewRuntimeError(context.Pos, "unbalanced case call")
	}

	var elems []caseElement
	var otherwise parser.Node

	// we begin at 1 because we need to omit the match node
	for i := 1; i < len(context.Nodes); i++ {
		elem := caseElement{}

		list, isList := context.Nodes[i].(*parser.ListNode)

		if isList {
			for _, caseNode := range list.Nodes {
				result, err := context.Block.EvalNode(caseNode)

				if err != nil {
					return nil, err
				}

				elem.cases = append(elem.cases, result)
			}

			i++

			elem.callback = context.Nodes[i]

			elems = append(elems, elem)
		} else {
			ident, isIdent := context.Nodes[i].(*parser.IdentifierNode)

			if isIdent && ident.Token.Data == "_" {
				if otherwise != nil {
					return nil, runtime.NewRuntimeError(ident.Pos(), "match can only have one otherwise case")
				}

				i++

				otherwise = context.Nodes[i]
			} else {
				return nil, runtime.NewRuntimeError(context.Nodes[i].Pos(), "expected a list")
			}
		}
	}

	for _, elem := range elems {
		for _, possibility := range elem.cases {
			if possibility.Equals(match) {
				return context.Block.EvalNode(elem.callback)
			}
		}
	}

	if otherwise != nil {
		return context.Block.EvalNode(otherwise)
	}

	return runtime.Nil, nil
}