Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
671 lines (565 sloc) 14.1 KB
from webidl`core` import Array
from webidl`core` import Number
from webidl`core` import Object
from webidl`core` import String as NativeString
from helpertypes import SimpleError
from helpertypes import Tuple
from primitives import Integer
/**
* listStream defines a stream over the contents of a list.
*/
class listStream<I> {
var list List<I>
var index int = 0
/**
* forList creates a new listStream over the items in the given `list`.
*/
constructor forList(list List<I>) {
return listStream<I>{list: list}
}
function Next() Tuple<I, bool> {
if this.index >= this.list.Count {
return Tuple<I, bool>.Build(null, false)
}
this.index = this.index + 1
return Tuple<I, bool>.Build(this.list[this.index - 1], true)
}
}
/**
* sliceStream defines a stream over the contents of a slice.
*/
class sliceStream<I> {
var slice Slice<I>
var index int = 0
/**
* forStream returns a new sliceStream over the items in the given `slice`.
*/
constructor forStream(slice Slice<I>) {
return sliceStream<I>{slice: slice}
}
function Next() Tuple<I, bool> {
if this.index >= this.slice.Length {
return Tuple<I, bool>.Build(null, false)
}
this.index = this.index + 1
return Tuple<I, bool>.Build(this.slice[this.index - 1], true)
}
}
/**
* Mapping defines a well-typed static (read-only) map between string keys to items.
*/
@•typealias('mapping')
type Mapping<T> : Object {
/**
* Empty returns an empty mapping.
*/
constructor Empty() {
return Mapping<T>(Object.new())
}
/**
* overObject returns a Mapping over the native object `obj`.
*/
constructor overObject(obj Object) {
return Mapping<T>(obj)
}
/**
* Keys returns a stream of the keys currently in the Mapping.
*/
property Keys string* {
get {
nativeKeys := Object.keys(Object(this))
for index in 0 ..< int(nativeKeys.length) {
yield string(nativeKeys[index].(NativeString))
}
yield break
}
}
/**
* IsEmpty returns true if the mapping is empty.
*/
property IsEmpty bool {
get {
return int(Object.keys(Object(this)).length) == 0
}
}
/**
* Clone makes a copy of the Mapping.
*/
function Clone() Mapping<T> {
copy := Object.new()
for existingKey in this.Keys {
copy[&existingKey] = this[existingKey]
}
return Mapping<T>(copy)
}
/**
* WithEntry returns a copy of the Mapping, with the given `key` set to the given `value`.
*/
function WithEntry(key string, value T) Mapping<T> {
copy := this.Clone()
var o = Object(copy)
o[&key] = value
return copy
}
operator Bool(value Mapping<T>) {
return !value.IsEmpty
}
operator Index(key string) T? {
var value = Object(this)[&key]
if value is null { return null }
return value.(T)
}
}
/**
* Slice defines a well-typed static (read-only) list of items.
*/
@•typealias('slice')
type Slice<T> : Array {
/**
* Empty returns an empty slice.
*/
constructor Empty() { return Slice<T>(Array.new()) }
/**
* From returns a slice representing the `items` in the stream.
*/
constructor From(items T*) {
var s = Slice<T>.Empty()
for i in items {
Array(s).push(i)
}
return s
}
/**
* ForArray returns a slice over the specified `arr`.
*/
constructor ForArray(arr Array) {
return Slice<T>.overArray(arr)
}
/**
* overArray returns a slice over the specified `arr`.
*/
constructor overArray(arr Array) {
return Slice<T>(arr)
}
/**
* CopyOfList returns a slice from the given `list`.
*/
constructor CopyOfList(list List<T>) {
return Slice<T>.overArray(list.internalArray.slice(0))
}
/**
* Concat returns a slice that is the concatination of the `first` and `second` slices.
*/
constructor Concat(first Slice<T>, second Slice<T>) {
return Slice<T>.overArray(Array(first).concat(&second))
}
/**
* Plus concatinates the two slices.
*/
operator Plus(left Slice<T>, right Slice<T>) {
return Slice<T>.Concat(left, right)
}
operator Slice(startindex int?, endindex int?) Slice<T> {
var start = startindex ?? 0
var end = endindex ?? this.Length
if start < 0 {
start = start + this.Length
}
if end < 0 {
end = end + this.Length
}
if start >= end { return Slice<T>.Empty() }
return Slice<T>.overArray(Array(this).slice(Number(start), Number(end)))
}
operator Index(index int) T {
var finalIndex = index
if finalIndex < 0 {
finalIndex = this.Length + index
}
if finalIndex >= this.Length || finalIndex < 0 {
reject SimpleError.WithMessage('Index is out of bounds')
}
return Array(this)[Number(finalIndex)].(T)
}
/**
* WithElementAtIndex returns a copy of this slice with the element at `index` replaced
* with the specified `element`. Raises an error if the index it out of bounds.
*/
function WithElementAtIndex(index int, element T) []T {
var finalIndex = index
if finalIndex < 0 {
finalIndex = this.Length + index
}
if finalIndex >= this.Length || finalIndex < 0 {
reject SimpleError.WithMessage('Index is out of bounds')
}
copy := Array(this).slice()
copy[finalIndex] = element
return Slice<T>(copy)
}
/**
* Stream returns a stream of all the items in the slice.
*/
function Stream() T* {
return sliceStream<T>.forStream(this)
}
/**
* Length returns the length of the slice.
*/
property Length int {
get { return int(Array(this).length) }
}
/**
* IsEmpty returns true if the slice is empty.
*/
property IsEmpty bool {
get { return this.Length == 0 }
}
/**
* IndexOf returns the index of the `element` in this slice or null if none.
*/
function IndexOf(element T) int? {
// Check directly.
var foundIndex = int(Array(this).indexOf(&element, &0))
if foundIndex >= 0 { return foundIndex }
// Unbox the contents of the slice, so we can compare by value if necessary.
normalized := Array.new()
for index in 0 ..< this.Length {
normalized.push(&this[index])
}
// Check unboxed.
foundIndex = int(normalized.indexOf(&element, &0))
if foundIndex < 0 { return null }
return foundIndex
}
/**
* WithElement returns a copy of this slice with the `element` appended.
*/
function WithElement(element T) Slice<T> {
return this + [element]
}
operator Contains(value T) {
return this.IndexOf(value) ?? -1 >= 0
}
operator Bool(value Slice<T>) {
return !value.IsEmpty
}
}
/**
* List defines a well-typed list of items.
*/
@•typealias('list')
class List<T> {
var internalArray Array = Array.new()
var indexArray Array = Array.new()
/**
* forArray creates a List for the given `arr`.
*/
constructor forArray(arr Array) {
var l = List<T>.Empty()
for i in 0 .. Integer(arr.length) - 1 {
l.Add(arr[Number(i)].(T))
}
return l
}
/**
* Empty returns an empty list.
*/
constructor Empty() { return List<T>.new() }
/**
* CopyOf returns a List which is a copy of the `other` list.
*/
constructor CopyOf(other List<T>) {
var l = List<T>.Empty()
l.internalArray = other.internalArray.slice(0)
l.indexArray = other.indexArray.slice(0)
return l
}
/**
* Of returns a List populated by the contents of the `stream`.
*/
constructor Of(stream T*) {
var l = List<T>.Empty()
for item in stream {
l.Add(item)
}
return l
}
/**
* Concat returns a list whose contents are the concatenated contents of the `first` list and
* the `second` list.
*/
constructor Concat(first List<T>, second List<T>) {
var l = List<T>.Empty()
l.internalArray = first.internalArray.slice(0).concat(second.internalArray)
l.indexArray = first.indexArray.slice(0).concat(second.indexArray)
return l
}
/**
* FromSlice returns a list populated from the given `slice`.
*/
constructor FromSlice(slice Slice<T>) {
return List<T>.Of(slice.Stream())
}
operator Bool(value List<T>) {
return !value.IsEmpty
}
operator Plus(left List<T>, right List<T>) {
return List<T>.Concat(left, right)
}
operator Contains(value T) {
return this.IndexOf(value) ?? -1 >= 0
}
operator Slice(startindex int?, endindex int?) Slice<T> {
var start = startindex ?? 0
var end = endindex ?? this.Count
if start < 0 {
start = start + this.Count
}
if end < 0 {
end = end + this.Count
}
if start >= end { return Slice<T>.Empty() }
return Slice<T>.overArray(this.internalArray.slice(Number(start), Number(end)))
}
operator Index(index int) T {
var finalIndex = index
if finalIndex < 0 {
finalIndex = this.Count + index
}
if finalIndex >= this.Count || finalIndex < 0 {
reject SimpleError.WithMessage('Index is out of bounds')
}
return this.internalArray[Number(finalIndex)].(T)
}
operator SetIndex(index int, value T) {
var finalIndex = index
if finalIndex < 0 {
finalIndex = this.Count + index
}
if finalIndex >= this.Count || finalIndex < 0 {
reject SimpleError.WithMessage('Index is out of bounds')
}
this.internalArray[Number(finalIndex)] = value
this.indexArray[Number(finalIndex)] = &value
}
/**
* Stream returns a stream of all the items in the list.
*/
function Stream() stream<T> {
return listStream<T>.forList(this)
}
/**
* Slice returns this list as a slice.
*/
function Slice() []T {
return Slice<T>(this.internalArray.slice(0))
}
/**
* Add adds the given `element` to the list.
*/
function Add(element T) {
this.internalArray.push(element)
this.indexArray.push(&element)
}
/**
* Remove removes the given `element` from the list, if it is present. Note that this
* will only remove the first instance found (if any).
*/
function Remove(element T) {
var index = this.IndexOf(element)
if index is null { return }
this.indexArray.splice(Number(index), Number(1))
this.internalArray.splice(Number(index), Number(1))
}
/**
* IndexOf returns the index of the `element` in this list or null if none.
*/
function IndexOf(element T, startIndex int?) int? {
var finalIndex = startIndex ?? 0
var foundIndex = int(this.indexArray.indexOf(&element, Number(finalIndex)))
if foundIndex < 0 { return null }
return foundIndex
}
/**
* Count returns the number of elements in the list.
*/
property Count int {
get { return Integer(this.internalArray.length) }
}
/**
* IsEmpty returns true if the list is empty.
*/
property IsEmpty bool {
get { return this.Count == 0 }
}
}
/**
* Set defines a well-typed set of items.
*/
@•typealias('set')
class Set<T : mappable> {
var keyMap Object = Object.new()
var keys List<T> = List<T>.Empty()
/**
* Empty returns an empty set.
*/
constructor Empty() { return Set<T>.new() }
/**
* Of returns a set containing the elements found in the given `stream`.
*/
constructor Of(stream T*) {
s := Set<T>.Empty()
for element in stream {
s.Add(element)
}
return s
}
/**
* From returns a set containing the elements found in the given `streamable`.
*/
constructor From(streamable streamable<T>) {
return Set<T>.Of(streamable.Stream())
}
/**
* Add adds the given `item` to the set (if not already present). Returns true if the `item`
* was added.
*/
function Add(item T) bool {
if this.Contains(item) { return false }
this.keys.Add(item)
this.keyMap[NativeString(item.MapKey.String())] = true
return true
}
/**
* Remove removes the given `item` from the set (if present). Returns true if the `item`
* was removed.
*/
function Remove(item T) bool {
if !this.Contains(item) { return false }
this.keys.Remove(item)
this.keyMap[NativeString(item.MapKey.String())] = null
return true
}
/**
* Contains returns true if the set contains the specified `item`.
*/
function Contains(item T) bool {
return !(this.keyMap[NativeString(item.MapKey.String())] is null)
}
/**
* Stream returns a stream of all the items in the set.
*/
function Stream() T* { return this.keys.Stream() }
/**
* IsEmpty returns true if the set is empty.
*/
property IsEmpty bool {
get { return this.keys.IsEmpty }
}
operator Bool(value Set<T>) {
return !value.IsEmpty
}
operator Contains(value T) {
return this.Contains(value)
}
}
/**
* Map defines a map from keys to values.
*/
@•typealias('map')
class Map<T : mappable, Q> {
var internalObject Object = Object.new()
var keys Set<T> = Set<T>.Empty()
/**
* Empty returns an empty map.
*/
constructor Empty() { return Map<T, Q>.new() }
/**
* forArrays constructs and returns a Map from the given `keys` and `values` arrays.
*/
constructor forArrays(keys Array, values Array) {
var map = Map<T, Q>.new()
var len = int(keys.length)
for i in 0 .. len - 1 {
var tKey = keys[Number(i)].(T)
map[tKey] = values[Number(i)].(Q)
}
return map
}
/**
* Mapping returns the a Mapping of this Map's values. Note that the keys will be the
* MapKey for the items.
*/
function Mapping() Mapping<Q> {
return Mapping<Q>(this.internalObject).Clone()
}
/**
* Keys returns a stream of all the keys in the map.
*/
property Keys T* {
get { return this.keys.Stream() }
}
/**
* Values returns a stream of all the values in the map.
*/
property Values Q* {
get {
for key in this.Keys {
value := this[key]
if value is not null {
yield value
}
}
}
}
/**
* Pairs returns all the key-value pairs in the map.
*/
property Pairs Tuple<T, Q>* {
get {
for key in this.Keys {
value := this[key]
if value is not null {
yield Tuple<T, Q>.Build(key, value)
}
}
}
}
/**
* RemoveKey removes the the specified `key` from the map (if present).
*/
function RemoveKey(key T) {
var keyString = key.MapKey.String()
if this.keys.Remove(key) {
this.internalObject[NativeString(keyString)] = null
}
}
/**
* HasKey returns true if the map contains the specified `key`.
*/
function HasKey(key T) bool {
return this.keys.Contains(key)
}
operator Index(key T) Q? {
var keyString = key.MapKey.String()
var value = this.internalObject[NativeString(keyString)]
if value is null { return null }
return value.(Q)
}
operator SetIndex(key T, value Q) {
var keyString = key.MapKey.String()
this.keys.Add(key)
this.internalObject[NativeString(keyString)] = value
}
/**
* IsEmpty returns true if the Map is empty.
*/
property IsEmpty bool {
get { return this.keys.IsEmpty }
}
operator Contains(key T) { return this.HasKey(key) }
operator Bool(value Map<T, Q>) {
return !value.IsEmpty
}
}
You can’t perform that action at this time.