Added some frontend stuff
This commit is contained in:
parent
3df9bdc88f
commit
29df939df3
|
@ -0,0 +1,195 @@
|
|||
# Copyright 2021 Mattia Giambirtone & All Contributors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# Implementation of a custom list data type for JAPL objects (used also internally by the VM)
|
||||
|
||||
{.experimental: "implicitDeref".}
|
||||
import iterable
|
||||
import ../../memory/allocator
|
||||
import base
|
||||
import strformat
|
||||
|
||||
|
||||
type
|
||||
ArrayList*[T] = object of Iterable
|
||||
## Implementation of a simple dynamic
|
||||
## array with amortized O(1) append complexity
|
||||
## and O(1) complexity when popping/deleting
|
||||
## the last element
|
||||
container: ptr UncheckedArray[T]
|
||||
ArrayListIterator*[T] = object of Iterator
|
||||
list: ArrayList[T]
|
||||
current: int
|
||||
|
||||
|
||||
proc newArrayList*[T](): ptr ArrayList[T] =
|
||||
## Allocates a new, empty array list
|
||||
result = allocateObj(ArrayList[T], ObjectType.List)
|
||||
result.capacity = 0
|
||||
result.container = nil
|
||||
result.length = 0
|
||||
|
||||
|
||||
proc append*[T](self: ptr ArrayList[T], elem: T) =
|
||||
## Appends an object to the end of the list
|
||||
## in amortized constant time (~O(1))
|
||||
if self.capacity <= self.length:
|
||||
self.capacity = growCapacity(self.capacity)
|
||||
self.container = resizeArray(T, self.container, self.length, self.capacity)
|
||||
self.container[self.length] = elem
|
||||
self.length += 1
|
||||
|
||||
|
||||
proc pop*[T](self: ptr ArrayList[T], idx: int = -1): T =
|
||||
## Pops an item from the list. By default, the last
|
||||
## element is popped, in which case the operation's
|
||||
## time complexity is O(1). When an arbitrary element
|
||||
## is popped, the complexity rises to O(k) where k
|
||||
## is the number of elements that had to be shifted
|
||||
## by 1 to avoid empty slots
|
||||
var idx = idx
|
||||
if self.length == 0:
|
||||
raise newException(IndexDefect, "pop from empty ArrayList")
|
||||
if idx == -1:
|
||||
idx = self.length - 1
|
||||
if idx notin 0..self.length - 1:
|
||||
raise newException(IndexDefect, &"ArrayList index out of bounds: {idx} notin 0..{self.length - 1}")
|
||||
result = self.container[idx]
|
||||
if idx != self.length - 1:
|
||||
for i in countup(idx, self.length - 1):
|
||||
self.container[i] = self.container[i + 1]
|
||||
self.capacity -= 1
|
||||
self.length -= 1
|
||||
|
||||
|
||||
proc `[]`*[T](self: ptr ArrayList[T], idx: int): T =
|
||||
## Retrieves an item from the list, in constant
|
||||
## time
|
||||
if self.length == 0:
|
||||
raise newException(IndexDefect, &"ArrayList index out of bounds: : {idx} notin 0..{self.length - 1}")
|
||||
if idx notin 0..self.length - 1:
|
||||
raise newException(IndexDefect, &"ArrayList index out of bounds: {idx} notin 0..{self.length - 1}")
|
||||
result = self.container[idx]
|
||||
|
||||
|
||||
proc `[]`*[T](self: ptr ArrayList[T], slice: Hslice[int, int]): ptr ArrayList[T] =
|
||||
## Retrieves a subset of the list, in O(k) time where k is the size
|
||||
## of the slice
|
||||
if self.length == 0:
|
||||
raise newException(IndexDefect, "ArrayList index out of bounds")
|
||||
if slice.a notin 0..self.length - 1 or slice.b notin 0..self.length:
|
||||
raise newException(IndexDefect, "ArrayList index out of bounds")
|
||||
result = newArrayList[T]()
|
||||
for i in countup(slice.a, slice.b - 1):
|
||||
result.append(self.container[i])
|
||||
|
||||
|
||||
proc `[]=`*[T](self: ptr ArrayList[T], idx: int, obj: T) =
|
||||
## Assigns an object to the given index, in constant
|
||||
## time
|
||||
if self.length == 0:
|
||||
raise newException(IndexDefect, "ArrayList is empty")
|
||||
if idx notin 0..self.length - 1:
|
||||
raise newException(IndexDefect, "ArrayList index out of bounds")
|
||||
self.container[idx] = obj
|
||||
|
||||
|
||||
proc delete*[T](self: ptr ArrayList[T], idx: int) =
|
||||
## Deletes an object from the given index.
|
||||
## This method shares the time complexity
|
||||
## of self.pop()
|
||||
if self.length == 0:
|
||||
raise newException(IndexDefect, "delete from empty ArrayList")
|
||||
if idx notin 0..self.length - 1:
|
||||
raise newException(IndexDefect, &"ArrayList index out of bounds: {idx} notin 0..{self.length - 1}")
|
||||
discard self.pop(idx)
|
||||
|
||||
|
||||
proc contains*[T](self: ptr ArrayList[T], elem: T): bool =
|
||||
## Returns true if the given object is present
|
||||
## in the list, false otherwise. O(n) complexity
|
||||
if self.length > 0:
|
||||
for i in 0..self.length - 1:
|
||||
if self[i] == elem:
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
proc high*[T](self: ptr ArrayList[T]): int =
|
||||
## Returns the index of the last
|
||||
## element in the list, in constant time
|
||||
if self.length == 0:
|
||||
raise newException(IndexDefect, "ArrayList is empty")
|
||||
result = self.length - 1
|
||||
|
||||
|
||||
proc len*[T](self: ptr ArrayList[T]): int =
|
||||
## Returns the length of the list
|
||||
## in constant time
|
||||
result = self.length
|
||||
|
||||
|
||||
iterator pairs*[T](self: ptr ArrayList[T]): tuple[key: int, val: T] =
|
||||
## Implements pairwise iteration (similar to python's enumerate)
|
||||
for i in countup(0, self.length - 1):
|
||||
yield (key: i, val: self[i])
|
||||
|
||||
|
||||
iterator items*[T](self: ptr ArrayList[T]): T =
|
||||
## Implements iteration
|
||||
for i in countup(0, self.length - 1):
|
||||
yield self[i]
|
||||
|
||||
|
||||
proc reversed*[T](self: ptr ArrayList[T], first: int = -1, last: int = 0): ptr ArrayList[T] =
|
||||
## Returns a reversed version of the given list, from first to last.
|
||||
## First defaults to -1 (the end of the list) and last defaults to 0 (the
|
||||
## beginning of the list)
|
||||
var first = first
|
||||
if first == -1:
|
||||
first = self.length - 1
|
||||
result = newArrayList[T]()
|
||||
for i in countdown(first, last):
|
||||
result.append(self[i])
|
||||
|
||||
|
||||
proc extend*[T](self: ptr ArrayList[T], other: seq[T]) =
|
||||
## Iteratively calls self.append() with the elements
|
||||
## from a nim sequence
|
||||
for elem in other:
|
||||
self.append(elem)
|
||||
|
||||
|
||||
proc extend*[T](self: ptr ArrayList[T], other: ptr ArrayList[T]) =
|
||||
## Iteratively calls self.append() with the elements
|
||||
## from another ArrayList
|
||||
for elem in other:
|
||||
self.append(elem)
|
||||
|
||||
|
||||
proc `$`*[T](self: ptr ArrayList[T]): string =
|
||||
## Returns a string representation
|
||||
## of self
|
||||
result = "["
|
||||
if self.length > 0:
|
||||
for i in 0..self.length - 1:
|
||||
result = result & $self.container[i]
|
||||
if i < self.length - 1:
|
||||
result = result & ", "
|
||||
result = result & "]"
|
||||
|
||||
|
||||
proc getIter*[T](self: ptr ArrayList[T]): Iterator =
|
||||
## Returns the iterator object of the
|
||||
## arraylist
|
||||
result = allocate(ArrayListIterator, ) # TODO
|
|
@ -0,0 +1,60 @@
|
|||
# Copyright 2021 Mattia Giambirtone & All Contributors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import ../../memory/allocator
|
||||
|
||||
|
||||
type
|
||||
ObjectType* {.pure.} = enum
|
||||
## All the possible object types
|
||||
String, Exception, Function,
|
||||
Class, Module, BaseObject,
|
||||
Native, Integer, Float,
|
||||
Bool, NotANumber, Infinity,
|
||||
Nil, List, Dict, Set, Tuple
|
||||
Obj* = object of RootObj
|
||||
## The base object for all
|
||||
## JAPL types. Every object
|
||||
## in JAPL implicitly inherits
|
||||
## from this base type
|
||||
kind*: ObjectType
|
||||
hashValue*: uint64
|
||||
|
||||
|
||||
## Object constructors and allocators
|
||||
|
||||
proc allocateObject*(size: int, kind: ObjectType): ptr Obj =
|
||||
## Wrapper around memory.reallocate to create a new generic JAPL object
|
||||
result = cast[ptr Obj](reallocate(nil, 0, size))
|
||||
result.kind = kind
|
||||
|
||||
|
||||
template allocateObj*(kind: untyped, objType: ObjectType): untyped =
|
||||
## Wrapper around allocateObject to cast a generic object
|
||||
## to a more specific type
|
||||
cast[ptr kind](allocateObject(sizeof kind, objType))
|
||||
|
||||
|
||||
proc newObj*(): ptr Obj =
|
||||
## Allocates a generic JAPL object
|
||||
result = allocateObj(Obj, ObjectType.BaseObject)
|
||||
|
||||
|
||||
proc asObj*(self: ptr Obj): ptr Obj =
|
||||
## Casts a specific JAPL object into a generic
|
||||
## pointer to Obj
|
||||
result = cast[ptr Obj](self)
|
||||
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
# Copyright 2021 Mattia Giambirtone & All Contributors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import ../../memory/allocator
|
||||
import ../../config
|
||||
import base
|
||||
import iterable
|
||||
|
||||
|
||||
type
|
||||
Entry = object
|
||||
key: ptr Obj
|
||||
value: ptr Obj
|
||||
tombstone: bool
|
||||
HashMap* = object of Iterable
|
||||
entries: ptr UncheckedArray[ptr Entry]
|
||||
actual_length: int
|
||||
|
||||
|
||||
proc newHashMap*(): ptr HashMap =
|
||||
result = allocateObj(HashMap, ObjectType.Dict)
|
||||
result.actual_length = 0
|
||||
result.entries = nil
|
||||
result.capacity = 0
|
||||
result.length = 0
|
||||
|
||||
|
||||
proc freeHashMap*(self: ptr HashMap) =
|
||||
discard freeArray(UncheckedArray[ptr Entry], self.entries, self.capacity)
|
||||
self.length = 0
|
||||
self.actual_length = 0
|
||||
self.capacity = 0
|
||||
self.entries = nil
|
||||
|
||||
|
||||
proc findEntry(self: ptr UncheckedArray[ptr Entry], key: ptr Obj, capacity: int): ptr Entry =
|
||||
var capacity = uint64(capacity)
|
||||
var idx = uint64(key.hash()) mod capacity
|
||||
while true:
|
||||
result = self[idx]
|
||||
if system.`==`(result.key, nil):
|
||||
break
|
||||
elif result.tombstone:
|
||||
if result.key == key:
|
||||
break
|
||||
elif result.key == key:
|
||||
break
|
||||
idx = (idx + 1) mod capacity
|
||||
|
||||
|
||||
proc adjustCapacity(self: ptr HashMap) =
|
||||
var newCapacity = growCapacity(self.capacity)
|
||||
var entries = allocate(UncheckedArray[ptr Entry], Entry, newCapacity)
|
||||
var oldEntry: ptr Entry
|
||||
var newEntry: ptr Entry
|
||||
self.length = 0
|
||||
for x in countup(0, newCapacity - 1):
|
||||
entries[x] = allocate(Entry, Entry, 1)
|
||||
entries[x].tombstone = false
|
||||
entries[x].key = nil
|
||||
entries[x].value = nil
|
||||
for x in countup(0, self.capacity - 1):
|
||||
oldEntry = self.entries[x]
|
||||
if not system.`==`(oldEntry.key, nil):
|
||||
newEntry = entries.findEntry(oldEntry.key, newCapacity)
|
||||
newEntry.key = oldEntry.key
|
||||
newEntry.value = oldEntry.value
|
||||
self.length += 1
|
||||
discard freeArray(UncheckedArray[ptr Entry], self.entries, self.capacity)
|
||||
self.entries = entries
|
||||
self.capacity = newCapacity
|
||||
|
||||
|
||||
proc setEntry(self: ptr HashMap, key: ptr Obj, value: ptr Obj): bool =
|
||||
if float64(self.length + 1) >= float64(self.capacity) * MAP_LOAD_FACTOR:
|
||||
self.adjustCapacity()
|
||||
var entry = findEntry(self.entries, key, self.capacity)
|
||||
result = system.`==`(entry.key, nil)
|
||||
if result:
|
||||
self.actual_length += 1
|
||||
self.length += 1
|
||||
entry.key = key
|
||||
entry.value = value
|
||||
entry.tombstone = false
|
||||
|
||||
|
||||
proc `[]`*(self: ptr HashMap, key: ptr Obj): ptr Obj =
|
||||
var entry = findEntry(self.entries, key, self.capacity)
|
||||
if system.`==`(entry.key, nil) or entry.tombstone:
|
||||
raise newException(KeyError, "Key not found: " & $key)
|
||||
result = entry.value
|
||||
|
||||
|
||||
proc `[]=`*(self: ptr HashMap, key: ptr Obj, value: ptr Obj) =
|
||||
discard self.setEntry(key, value)
|
||||
|
||||
|
||||
proc len*(self: ptr HashMap): int =
|
||||
result = self.actual_length
|
||||
|
||||
|
||||
proc del*(self: ptr HashMap, key: ptr Obj) =
|
||||
if self.len() == 0:
|
||||
raise newException(KeyError, "delete from empty hashmap")
|
||||
var entry = findEntry(self.entries, key, self.capacity)
|
||||
if not system.`==`(entry.key, nil):
|
||||
self.actual_length -= 1
|
||||
entry.tombstone = true
|
||||
else:
|
||||
raise newException(KeyError, "Key not found: " & $key)
|
||||
|
||||
|
||||
proc contains*(self: ptr HashMap, key: ptr Obj): bool =
|
||||
let entry = findEntry(self.entries, key, self.capacity)
|
||||
if not system.`==`(entry.key, nil) and not entry.tombstone:
|
||||
result = true
|
||||
else:
|
||||
result = false
|
||||
|
||||
|
||||
iterator keys*(self: ptr HashMap): ptr Obj =
|
||||
var entry: ptr Entry
|
||||
for i in countup(0, self.capacity - 1):
|
||||
entry = self.entries[i]
|
||||
if not system.`==`(entry.key, nil) and not entry.tombstone:
|
||||
yield entry.key
|
||||
|
||||
|
||||
iterator values*(self: ptr HashMap): ptr Obj =
|
||||
for key in self.keys():
|
||||
yield self[key]
|
||||
|
||||
|
||||
iterator pairs*(self: ptr HashMap): tuple[key: ptr Obj, val: ptr Obj] =
|
||||
for key in self.keys():
|
||||
yield (key: key, val: self[key])
|
||||
|
||||
|
||||
iterator items*(self: ptr HashMap): ptr Obj =
|
||||
for k in self.keys():
|
||||
yield k
|
||||
|
||||
|
||||
proc `$`*(self: ptr HashMap): string =
|
||||
var i = 0
|
||||
result &= "{"
|
||||
for key, value in self.pairs():
|
||||
result &= $key & ": " & $value
|
||||
if i < self.len() - 1:
|
||||
result &= ", "
|
||||
i += 1
|
||||
result &= "}"
|
|
@ -0,0 +1,44 @@
|
|||
# Copyright 2021 Mattia Giambirtone & All Contributors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# Implementation of iterable types and iterators in JAPL
|
||||
|
||||
import base
|
||||
|
||||
|
||||
type
|
||||
Iterable* = object of Obj
|
||||
## Defines the standard interface
|
||||
## for iterable types in JAPL
|
||||
length*: int
|
||||
capacity*: int
|
||||
Iterator* = object of Iterable
|
||||
## This object drives iteration
|
||||
## for every iterable type in JAPL except
|
||||
## generators
|
||||
iterable*: ptr Obj
|
||||
iterCount*: int
|
||||
|
||||
|
||||
proc getIter*(self: Iterable): ptr Iterator =
|
||||
## Returns the iterator object of an
|
||||
## iterable, which drives foreach
|
||||
## loops
|
||||
return nil
|
||||
|
||||
|
||||
proc next*(self: Iterator): ptr Obj =
|
||||
## Returns the next element from
|
||||
## the iterator or nil if the
|
||||
## iterator has been consumed
|
||||
return nil
|
Loading…
Reference in New Issue