rangetype

package module
v0.0.0-...-60bd746 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 17, 2018 License: MIT Imports: 6 Imported by: 0

README

Rangetype

Build Status GoDoc License Report Card

A mini-language for defining numeric types by defining ranges.

The idea is to provide a DSL for defining and validating numeric types for implementations of programming languages.

It can also be used for iterating over ranges, generating lists of numbers or slicing a given slice (like slices in Python).

ForEach

Looping over a range can be done by providing a function that takes a float64:

r.New("1..10").ForEach(func(x float64) {
	fmt.Println(int(x))
})

Join

Collecting integers to a comma separated string can be done with Join:

r.New("1..10").Join(", ", 0)

Or for floats, with 2 digits after the period, separated by semicolons:

r.New("1..3 step 0.5").Join(";", 2)

Syntax

Expressions can optionally start with:

  • [ for including the first value in the range, or
  • ( for excluding the first value in the range

And can end with:

  • ] for including the last value in the range, or
  • ) for excluding the last value in the range

Numbers can be suffixed by:

  • ~ for subtracting 1 from the preceding number. Any number of ~ is possible.

The ranges can be Python-style:

[0:10]

Python-style with a step:

[1:20:-1]

Ruby-style:

1..10

Ruby-style with a step:

1..10 step 2

Math-style:

[1,5)

Math-style with a step:

[1,5) step 2

Math-style with negative steps:

(5,1] step -0.1

Here's an expression for specifying the range for a 16-bit unsigned integer:

0..65535

This can also be defined by dropping the initial 0 and using ~ for -1:

..65536~

This can be made clearer (when defining many numbers of many bit-sizes) by using 2**16 instead of 65536:

..2**16~

This can be used for validating if a given number fits a 16-bit unsigned type:

IntType := r.New("..2**16~")     // from 0 up to and including 65536-1
IntType.Valid(42)              // true

Examples

An int with a range from 1 to 3 that includes both 1, 2 and 3:

1,3

A float with a range from 1 to 3 that includes 1.0, 1.1 etc up to 3.0:

1,3 step 0.1

Inclusivity is specified with square brackets:

[1,3]

Exclusivity is specified with parenthesis:

(1,3)

Ranges inspired by Ruby also work:

1..3

These are inclusive, unless parenthesis are used.

Ruby-style range which will exclude 1 and 3 and only keep 2:

(1..3)

Python style ranges are also supported, where the start value is inclusive and the end value is exclusive:

1:3

Adding square brackets makes the range inclusive:

[1:3]

Brackets and parenthesis does not have to be balanced. This works too:

1:3]

Adding an iteration step is also possible:

1..5 step 2

This is a range with the numbers 1, 3 and 5.

The Python-style syntax also supports steps:

[3:1:-1]

This is 3, 2, 1.

Steps does not have to be integers:

[3:1:-0.1]

This steps from 3 (inclusive) down to 1 (inclusive) in step sizes of 0.1.

More Examples

Defining a SmallInt type and checking if a given number is valid
package main

import (
	"fmt"

	r "github.com/xyproto/rangetype"
)

func main() {
	// Define a new type that can hold numbers from 0 up to and including 99
	SmallInt := r.New("0..99")

	// Another way to define a number type from 0 up to and excluding 100
	//SmallInt := r.New("[0,100)")

	// Another way to define a number type from 0 up to and excluding 100
	//SmallInt := r.New("..10**2~")

	// Is 42 a valid SmallInt?
	fmt.Println("0 is a valid SmallInt value:", SmallInt.Valid(0))
	fmt.Println("2 is a valid SmallInt value:", SmallInt.Valid(2))
	fmt.Println("-1 is a valid SmallInt value:", SmallInt.Valid(-1))
	fmt.Println("99 is a valid SmallInt value:", SmallInt.Valid(99))
	fmt.Println("100 is a valid SmallInt value:", SmallInt.Valid(100))

	// How many integers are there room for?
	fmt.Printf("SmallInt can hold %d different numbers.\n", SmallInt.Len())
	fmt.Printf("Storage required for SmallInt: a %d-bit int\n", SmallInt.Bits())

	// All possible SmallInt values, comma separated:
	fmt.Println("All possible values for SmallInt:\n" + SmallInt.Join(",", 0))
}

Example 2

Slicing slices and looping with the ForEach method
package main

import (
	"fmt"

	r "github.com/xyproto/rangetype"
)

func main() {
	// Outputs 1 to 10, with 0 digits after "."
	fmt.Println(r.New("1..10").Join(", ", 0))

	// Outputs 2 and 4
	for _, x := range r.Slice([]float64{1.0, 2.0, 3.0, 4.0}, "1..3 step 2") {
		fmt.Print(x, " ")
	}
	fmt.Println()

	// Also outputs 2 and 4
	r.New("(0:6:2)").ForEach(func(x float64) {
		fmt.Print(x, " ")
	})
	fmt.Println()
}

There are more examples in the range_test.go file.

Features and Limitations

  • Can handle very large ranges without storing the actual numbers in the ranges, but iterating over large ranges may be slow.
  • Only ** and ~ are supported for manipulating numbers in the range expressions.
  • It's not a general language, it's only a DSL for expressing ranges of integers or floating point numbers, with an optional step size.

Error Handling

  • New2 and Slice2 will return both a value and an error (if the expression failed to evaluate) and are the recommended functions to use.
  • New and Slice are fine to use for expressions that are already known to evaluate, but may panic if there are errors in the given expression.

General Info

Documentation

Index

Constants

View Source
const (
	RANGE_MISSING       = 0
	RANGE_EXCLUDE_START = 1 << iota // Start at 1, then double
	RANGE_INCLUDE_START
	RANGE_EXCLUDE_STOP
	RANGE_INCLUDE_STOP

	// Thanks https://groups.google.com/forum/#!msg/golang-nuts/a9PitPAHSSU/ziQw1-QHw3EJ
	MaxUint = ^uint(0)
	MinUint = 0
	MaxInt  = int(MaxUint >> 1)
	MinInt  = -MaxInt - 1
)

Variables

View Source
var (
	// Unsigned integers
	U4   = New("..2**4~")
	U8   = New("..2**8~")
	U16  = New("..2**16~")
	U32  = New("..2**32~")
	U64  = New("..2**64~")
	U128 = New("..2**128~")

	// Aliases for unsigned integers
	Nibble = U4
	Char   = U8
	Byte   = U8
	Word   = U16
	Short  = U16
	Long   = U32
	Double = U32
	Quad   = U64

	// Signed integers
	I8   = New("-2**7..2**7~")
	I16  = New("-2**15..2**15~")
	I32  = New("-2**31..2**31~")
	I64  = New("-2**63..2**63~")
	I128 = New("-2**127..2**127~")
)
View Source
var (
	ErrRangeSyntax  = errors.New("INVALID RANGE SYNTAX")
	ErrMissingRange = errors.New("MISSING RANGE VALUES")
)

Functions

func Slice

func Slice(nums []float64, expression string) []float64

Slice can be used to slice a slice with a range Will panic if the given expresion is invalid

func Slice2

func Slice2(nums []float64, expression string) ([]float64, error)

Slice2 can be used to slice a slice with a range Returns an error if the given expression is invalid

Types

type Range

type Range struct {
	// contains filtered or unexported fields
}

Range can represent a number type in a programming language For example: An uint8 can be [0, 256) step 1 A float between 0 and 1 can be [0, 1] step 0.01

func New

func New(rangeExpression string) *Range

New is the same as New2, but panics if given an invalid input string

func New2

func New2(rangeExpression string) (*Range, error)

New2 evaluates the given input string and returns a Range struct and an error

func NewAda

func NewAda(adaRangedType string) *Range

NewAda is the same as NewAda2, but panics if given an invalid input string

func NewAda2

func NewAda2(adaRangeType string) (*Range, error)

NewAda evaluates an Ada range type

func NewRange

func NewRange(rangeExpression string, ada bool) (*Range, error)

NewRange evaluates the given input string and returns a Range struct

func (*Range) All

func (r *Range) All() []float64

All returns a slice of numbers, generated from the range

func (*Range) Bits

func (r *Range) Bits() int

Bits returns the number of bits required to hold the range

func (*Range) Find

func (r *Range) Find(x, threshold float64) (bool, float64)

Find searches a range for a given number The threshold is how close the float has to be a float in the range for it to be "equal" If the difference between the given float and the float in the range are less than the given threshold, they are counted as equal. The allowed difference could be 0.00001, for example. This is needed because of how floats are stored.

func (*Range) ForEach

func (r *Range) ForEach(f func(float64))

ForEach calls the given function for each iteration in the range

func (*Range) ForEachWithBreak

func (r *Range) ForEachWithBreak(f func(float64) bool)

ForEachWithBreak calls the given function for each iteration in the range If the given function returns true, the remaining iterations are skipped

func (*Range) ForN

func (r *Range) ForN(n int, f func(float64))

ForN runs the given function for the n first iterations If n is never reached, a smaller number of iterations will happen.

func (*Range) Has

func (r *Range) Has(x, threshold float64) bool

Has checks if a given number is in the range. If the difference between the given float and the float in the range are less than the given threshold, they are counted as equal.

func (*Range) Integer

func (r *Range) Integer() bool

Integer checks if the range has a step of 1 or -1

func (*Range) Join

func (r *Range) Join(sep string, digits int) string

Join returns the output from the range as a string, where elements are separated by sep digits are how many digits should be added to the fractional part of the floats, use 0 for integers

func (*Range) JoinInts

func (r *Range) JoinInts(sep string) string

JoinInts returns the output from the range as a string, where elements are separated by sep

func (*Range) Len

func (r *Range) Len() uint

Len returns the length of the range by iterating over it! May get stuck if the range is impossibly large.

func (*Range) Len64

func (r *Range) Len64() float64

Len returns the length of the range by iterating over it! May get stuck if the range is impossibly large.

func (*Range) String

func (r *Range) String() string

String returns the range as a string where "[" means inclusive and "(" means exclusive

func (*Range) Sum

func (r *Range) Sum() float64

Sum adds all numbers in a range

func (*Range) Take

func (r *Range) Take(n int) []float64

Take returns a slice of n numbers, generated from the range. It not generate the entire slice first, but return numbers as it iterates.

func (*Range) Valid

func (r *Range) Valid(x float64) bool

Valid is an alias for ValidFloat

func (*Range) ValidFloat

func (r *Range) ValidFloat(x float64) bool

ValidFloat checks if the given float is in the range, using half the range step size as the threshold for float equality

func (*Range) ValidInt

func (r *Range) ValidInt(i int) bool

ValidInt checks if the given integer is in the range

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL