mirror of https://github.com/japl-lang/japl.git
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
26f035a998
|
@ -322,7 +322,7 @@ proc strVal(self: ref Compiler, canAssign: bool) =
|
|||
var str = self.parser.previous().lexeme
|
||||
var delimiter = &"{str[0]}" # TODO: Add proper escape sequences support
|
||||
str = str.unescape(delimiter, delimiter)
|
||||
self.emitConstant(self.markObject(jstring.newString(str)))
|
||||
self.emitConstant(self.markObject(jobject.newString(str)))
|
||||
|
||||
|
||||
proc bracketAssign(self: ref Compiler, canAssign: bool) =
|
||||
|
@ -389,15 +389,13 @@ proc literal(self: ref Compiler, canAssign: bool) =
|
|||
proc number(self: ref Compiler, canAssign: bool) =
|
||||
## Parses numerical constants
|
||||
var value = self.parser.previous().lexeme
|
||||
var obj: ptr Obj
|
||||
try:
|
||||
if "." in value:
|
||||
obj = parseFloat(value).asFloat()
|
||||
self.emitConstant(parseFloat(value).asFloat())
|
||||
else:
|
||||
obj = parseInt(value).asInt()
|
||||
except OverflowError:
|
||||
self.emitConstant(parseInt(value).asInt())
|
||||
except ValueError:
|
||||
self.compileError("number literal is too big")
|
||||
self.emitConstant(obj)
|
||||
|
||||
|
||||
proc grouping(self: ref Compiler, canAssign: bool) =
|
||||
|
@ -447,13 +445,13 @@ proc synchronize(self: ref Compiler) =
|
|||
|
||||
proc identifierConstant(self: ref Compiler, tok: Token): uint8 =
|
||||
## Emits instructions for identifiers
|
||||
return self.makeConstant(self.markObject(jstring.newString(tok.lexeme)))
|
||||
return self.makeConstant(self.markObject(jobject.newString(tok.lexeme)))
|
||||
|
||||
|
||||
proc identifierLongConstant(self: ref Compiler, tok: Token): array[3, uint8] =
|
||||
## Same as identifierConstant, but this is used when the constant table is longer
|
||||
## than 255 elements
|
||||
return self.makeLongConstant(self.markObject(jstring.newString(tok.lexeme)))
|
||||
return self.makeLongConstant(self.markObject(jobject.newString(tok.lexeme)))
|
||||
|
||||
|
||||
proc addLocal(self: ref Compiler, name: Token) =
|
||||
|
@ -1123,7 +1121,7 @@ proc initCompiler*(context: FunctionType, enclosing: ref Compiler = nil, parser:
|
|||
inc(result.localCount)
|
||||
result.function = result.markObject(newFunction())
|
||||
if context != SCRIPT: # If we're compiling a function, we give it its name
|
||||
result.function.name = jstring.newString(enclosing.parser.previous().lexeme)
|
||||
result.function.name = jobject.newString(enclosing.parser.previous().lexeme)
|
||||
|
||||
# This way the compiler can be executed on its own
|
||||
# without the VM
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
const FRAMES_MAX* = 400 # TODO: Inspect why the VM crashes if this exceeds 400
|
||||
const JAPL_VERSION* = "0.2.0"
|
||||
const JAPL_RELEASE* = "alpha"
|
||||
const DEBUG_TRACE_VM* = true # Traces VM execution
|
||||
const DEBUG_TRACE_VM* = false # Traces VM execution
|
||||
const DEBUG_TRACE_GC* = true # Traces the garbage collector (TODO)
|
||||
const DEBUG_TRACE_ALLOCATION* = true # Traces memory allocation/deallocation (WIP)
|
||||
const DEBUG_TRACE_COMPILER* = true # Traces the compiler (TODO)
|
||||
|
|
|
@ -149,10 +149,10 @@ proc parseNumber(self: var Lexer) =
|
|||
## Parses numeric literals
|
||||
while isDigit(self.peek()):
|
||||
discard self.step()
|
||||
if self.peek() == '.':
|
||||
if self.peek() == '.':
|
||||
discard self.step()
|
||||
while self.peek().isDigit():
|
||||
discard self.step()
|
||||
while self.peek().isDigit():
|
||||
discard self.step()
|
||||
self.tokens.add(self.createToken(TokenType.NUMBER))
|
||||
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
import strformat
|
||||
import parseopt
|
||||
import os
|
||||
import common
|
||||
import config
|
||||
import vm
|
||||
|
||||
proc repl() =
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
# Copyright 2020 Mattia Giambirtone
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
## Defines JAPL exceptions
|
||||
|
||||
import jstring
|
||||
import jobject
|
||||
import strformat
|
||||
|
||||
|
||||
proc stringify*(self: ptr JAPLException): string =
|
||||
return &"{self.errName.stringify}: {self.message.stringify}"
|
||||
|
||||
proc isFalsey*(self: ptr JAPLException): bool =
|
||||
return false
|
||||
|
||||
proc newTypeError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = newString("TypeError")
|
||||
result.message = newString(message)
|
||||
|
||||
|
||||
proc newIndexError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = newString("IndexError")
|
||||
result.message = newString(message)
|
||||
|
||||
|
||||
proc newReferenceError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = newString("ReferenceError")
|
||||
result.message = newString(message)
|
||||
|
||||
|
||||
proc newInterruptedError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = newString("InterruptedError")
|
||||
result.message = newString(message)
|
||||
|
||||
|
||||
proc newRecursionError*(message: string): ptr JAPLException =
|
||||
result = allocateObj(JAPLException, ObjectType.Exception)
|
||||
result.errName = newString("RecursionError")
|
||||
result.message = newString(message)
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
# Copyright 2020 Mattia Giambirtone
|
||||
#
|
||||
# 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 for function objects in JAPL.
|
||||
# In JAPL, functions (like any other entity) are First Class Objects.
|
||||
# Each function owns its chunk object, which makes the implementation
|
||||
# easier and opens up for interesting features in the future, like
|
||||
# code objects that can be compiled inside the JAPL runtime, pretty much
|
||||
# like in Python
|
||||
|
||||
import jstring
|
||||
import jobject
|
||||
import strformat
|
||||
import ../meta/opcode
|
||||
|
||||
|
||||
type
|
||||
FunctionType* = enum
|
||||
FUNC, SCRIPT
|
||||
|
||||
|
||||
proc newFunction*(name: string = "", chunk: Chunk = newChunk(), arity: int = 0): ptr Function =
|
||||
result = allocateObj(Function, ObjectType.Function)
|
||||
if name.len > 1:
|
||||
result.name = newString(name)
|
||||
else:
|
||||
result.name = nil
|
||||
result.arity = arity
|
||||
result.chunk = chunk
|
||||
|
||||
|
||||
proc isFalsey*(fn: ptr Function): bool =
|
||||
return false
|
||||
|
||||
|
||||
proc stringify*(fn: ptr Function): string =
|
||||
if fn.name != nil:
|
||||
result = &"<function '{stringify(fn.name)}'>"
|
||||
else:
|
||||
result = &"<code object>"
|
||||
|
||||
|
||||
proc valuesEqual*(a, b: ptr Function): bool =
|
||||
result = a.name.stringify == b.name.stringify
|
||||
|
||||
|
||||
proc typeName*(self: ptr Function): string =
|
||||
result = "function"
|
|
@ -1,22 +0,0 @@
|
|||
# Copyright 2020 Mattia Giambirtone
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
#TODO: Implement
|
||||
|
||||
proc hashFloat(f: float): uint32 =
|
||||
# TODO: Any improvement?
|
||||
result = 2166136261u32
|
||||
result = result xor uint32 f
|
||||
result *= 16777619
|
|
@ -1,16 +0,0 @@
|
|||
# Copyright 2020 Mattia Giambirtone
|
||||
#
|
||||
# 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 jobject
|
|
@ -16,6 +16,8 @@
|
|||
## types inherit from this simple structure
|
||||
|
||||
import ../memory
|
||||
import ../meta/opcode
|
||||
import strformat
|
||||
|
||||
|
||||
type
|
||||
|
@ -63,34 +65,6 @@ type
|
|||
message*: ptr String
|
||||
|
||||
|
||||
proc `convert`*(a: ptr Obj): ptr Obj =
|
||||
## Performs conversions from a JAPL
|
||||
## supertype to a subtype
|
||||
|
||||
case a.kind:
|
||||
of ObjectType.String:
|
||||
result = cast[ptr String](a)
|
||||
of ObjectType.Function:
|
||||
result = cast[ptr Function](a)
|
||||
of ObjectType.Integer:
|
||||
result = cast[ptr Integer](a)
|
||||
of ObjectType.Float:
|
||||
result = cast[ptr Float](a)
|
||||
of ObjectType.Bool:
|
||||
result = cast[ptr Bool](a)
|
||||
of ObjectType.Nan:
|
||||
result = cast[ptr NotANumber](a)
|
||||
of ObjectType.Infinity:
|
||||
result = cast[ptr Infinity](a)
|
||||
of ObjectType.BaseObject:
|
||||
result = cast[ptr Obj](a)
|
||||
of ObjectType.Class, ObjectType.Module:
|
||||
discard # TODO: Implement
|
||||
else:
|
||||
raise newException(Exception, "Attempted JAPL type conversion with unknown source object")
|
||||
|
||||
|
||||
|
||||
proc allocateObject*(size: int, kind: ObjectType): ptr Obj =
|
||||
## Wrapper around reallocate to create a new generic JAPL object
|
||||
result = cast[ptr Obj](reallocate(nil, 0, size))
|
||||
|
@ -108,35 +82,146 @@ proc objType*(obj: ptr Obj): ObjectType =
|
|||
return obj.kind
|
||||
|
||||
|
||||
# Methods for string objects
|
||||
|
||||
proc stringify*(s: ptr String): string =
|
||||
result = ""
|
||||
for i in 0..<s.len:
|
||||
result = result & (&"{s.str[i]}")
|
||||
|
||||
proc isFalsey*(s: ptr String): bool =
|
||||
result = s.len == 0
|
||||
|
||||
|
||||
proc hash*(self: ptr String): uint32 =
|
||||
result = 2166136261u32
|
||||
var i = 0
|
||||
while i < self.len:
|
||||
result = result xor uint32 self.str[i]
|
||||
result *= 16777619
|
||||
i += 1
|
||||
return result
|
||||
|
||||
|
||||
proc eq*(a: ptr String, b: ptr String): bool =
|
||||
if a.len != b.len:
|
||||
return false
|
||||
elif a.hash != b.hash:
|
||||
return false
|
||||
for i in 0..a.len - 1:
|
||||
if a.str[i] != b.str[i]:
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
proc newString*(str: string): ptr String =
|
||||
# TODO -> Unicode
|
||||
result = allocateObj(String, ObjectType.String)
|
||||
result.str = allocate(UncheckedArray[char], char, len(str))
|
||||
for i in 0..len(str) - 1:
|
||||
result.str[i] = str[i]
|
||||
result.len = len(str)
|
||||
result.hashValue = result.hash()
|
||||
|
||||
|
||||
proc typeName*(s: ptr String): string =
|
||||
return "string"
|
||||
|
||||
|
||||
proc asStr*(s: string): ptr Obj =
|
||||
## Creates a string object
|
||||
result = newString(s)
|
||||
|
||||
# End of string object methods
|
||||
|
||||
|
||||
# Integer object methods
|
||||
|
||||
proc stringify(self: ptr Integer): string =
|
||||
result = $self.intValue
|
||||
|
||||
|
||||
proc stringify(self: ptr Float): string =
|
||||
result = $self.floatValue
|
||||
|
||||
|
||||
# End of integer object methods
|
||||
|
||||
|
||||
|
||||
# Function object methods
|
||||
|
||||
type
|
||||
FunctionType* = enum
|
||||
FUNC, SCRIPT
|
||||
|
||||
|
||||
proc newFunction*(name: string = "", chunk: Chunk = newChunk(), arity: int = 0): ptr Function =
|
||||
result = allocateObj(Function, ObjectType.Function)
|
||||
if name.len > 1:
|
||||
result.name = newString(name)
|
||||
else:
|
||||
result.name = nil
|
||||
result.arity = arity
|
||||
result.chunk = chunk
|
||||
|
||||
|
||||
proc isFalsey*(fn: ptr Function): bool =
|
||||
return false
|
||||
|
||||
|
||||
proc valuesEqual*(a, b: ptr Function): bool =
|
||||
result = a.name.stringify == b.name.stringify
|
||||
|
||||
|
||||
proc typeName*(self: ptr Function): string =
|
||||
result = "function"
|
||||
|
||||
|
||||
proc stringify*(fn: ptr Function): string =
|
||||
if fn.name != nil:
|
||||
result = "<function " & stringify(fn.name) & ">" # idk why this doesn't work with &"{...}", too tired to investigate
|
||||
else:
|
||||
result = "<code object>"
|
||||
|
||||
|
||||
## Generic base methods
|
||||
|
||||
|
||||
proc stringify*(obj: ptr Obj): string =
|
||||
## Returns a string representation
|
||||
## of the object
|
||||
if obj.kind != ObjectType.BaseObject: # NOTE: Consider how to reduce the boilerplate
|
||||
var newObj = convert obj
|
||||
result = newObj.stringify()
|
||||
else:
|
||||
if obj.kind == ObjectType.String:
|
||||
result = cast[ptr String](obj).stringify()
|
||||
elif obj.kind == ObjectType.Function:
|
||||
result = cast[ptr Function](obj).stringify()
|
||||
elif obj.kind == ObjectType.Integer:
|
||||
result = cast[ptr Integer](obj).stringify()
|
||||
elif obj.kind == ObjectType.Float:
|
||||
result = cast[ptr Float](obj).stringify()
|
||||
elif obj.kind == ObjectType.Bool:
|
||||
result = cast[ptr Bool](obj).stringify()
|
||||
elif obj.kind == ObjectType.Nan:
|
||||
result = cast[ptr NotANumber](obj).stringify()
|
||||
elif obj.kind == ObjectType.Infinity:
|
||||
result = cast[ptr Infinity](obj).stringify()
|
||||
elif obj.kind == ObjectType.BaseObject:
|
||||
result = "<object (built-in type)>"
|
||||
else:
|
||||
discard # Unreachable
|
||||
|
||||
|
||||
proc isFalsey*(obj: ptr Obj): bool =
|
||||
## Returns true if the given
|
||||
## object is falsey
|
||||
if obj.kind != ObjectType.BaseObject: # NOTE: Consider how to reduce the boilerplate
|
||||
var newObj = convert obj
|
||||
result = newObj.isFalsey()
|
||||
else:
|
||||
result = false
|
||||
|
||||
result = false
|
||||
|
||||
|
||||
proc typeName*(obj: ptr Obj): string =
|
||||
## This method should return the
|
||||
## name of the object type
|
||||
if obj.kind != ObjectType.BaseObject:
|
||||
var newObj = convert obj
|
||||
result = newObj.typeName()
|
||||
else:
|
||||
result = "object"
|
||||
|
||||
result = "object"
|
||||
|
||||
|
||||
proc bool*(obj: ptr Obj): bool =
|
||||
|
@ -145,28 +230,17 @@ proc bool*(obj: ptr Obj): bool =
|
|||
## or not. Returns true if the
|
||||
## object is truthy, or false
|
||||
## if it is falsey
|
||||
if obj.kind != ObjectType.BaseObject:
|
||||
var newObj = convert obj
|
||||
result = newObj.bool()
|
||||
else:
|
||||
result = false
|
||||
result = false
|
||||
|
||||
|
||||
proc eq*(a: ptr Obj, b: ptr Obj): bool =
|
||||
## Compares two objects for equality
|
||||
if a.kind != ObjectType.BaseObject:
|
||||
var newObj = convert(a)
|
||||
result = newObj.eq(b)
|
||||
else:
|
||||
result = a.kind == b.kind
|
||||
result = a.kind == b.kind
|
||||
|
||||
|
||||
proc hash*(self: ptr Obj): uint32 =
|
||||
# TODO: Make this actually useful
|
||||
if self.kind == ObjectType.BaseObject:
|
||||
result = 2166136261u32
|
||||
else:
|
||||
result = convert(self).hash()
|
||||
result = 2166136261u32
|
||||
|
||||
|
||||
proc add(self, other: ptr Obj): ptr Obj =
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
# Copyright 2020 Mattia Giambirtone
|
||||
#
|
||||
# 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.
|
||||
|
||||
# This module implements the interface for strings in JAPL.
|
||||
# Strings are manually-allocated arrays of characters, and are
|
||||
# therefore immutable from the user's perspective. They are
|
||||
# natively ASCII encoded, but soon they will support for unicode.
|
||||
|
||||
import jobject
|
||||
import strformat
|
||||
import ../memory
|
||||
|
||||
|
||||
proc stringify*(s: ptr String): string =
|
||||
result = ""
|
||||
for i in 0..<s.len:
|
||||
result = result & (&"{s.str[i]}")
|
||||
|
||||
|
||||
proc isFalsey*(s: ptr String): bool =
|
||||
result = s.len == 0
|
||||
|
||||
|
||||
proc hash*(self: ptr String): uint32 =
|
||||
result = 2166136261u32
|
||||
var i = 0
|
||||
while i < self.len:
|
||||
result = result xor uint32 self.str[i]
|
||||
result *= 16777619
|
||||
i += 1
|
||||
return result
|
||||
|
||||
|
||||
proc eq*(a: ptr String, b: ptr String): bool =
|
||||
if a.len != b.len:
|
||||
return false
|
||||
elif a.hash != b.hash:
|
||||
return false
|
||||
for i in 0..a.len - 1:
|
||||
if a.str[i] != b.str[i]:
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
proc newString*(str: string): ptr String =
|
||||
# TODO -> Unicode
|
||||
result = allocateObj(String, ObjectType.String)
|
||||
result.str = allocate(UncheckedArray[char], char, len(str))
|
||||
for i in 0..len(str) - 1:
|
||||
result.str[i] = str[i]
|
||||
result.len = len(str)
|
||||
result.hashValue = result.hash()
|
||||
|
||||
|
||||
proc typeName*(s: ptr String): string =
|
||||
return "string"
|
||||
|
||||
|
||||
proc asStr*(s: string): ptr Obj =
|
||||
## Creates a string object
|
||||
result = newString(s)
|
|
@ -154,7 +154,7 @@ proc slice(self: var VM): bool =
|
|||
self.error(newIndexError("string index out of bounds"))
|
||||
return false
|
||||
else:
|
||||
self.push(addObject(addr self, jstring.newString(&"{str[index]}")))
|
||||
self.push(addObject(addr self, jobject.newString(&"{str[index]}")))
|
||||
return true
|
||||
else:
|
||||
self.error(newTypeError(&"unsupported slicing for object of type '{peeked.typeName()}'"))
|
||||
|
@ -185,14 +185,14 @@ proc sliceRange(self: var VM): bool =
|
|||
if startIndex < 0:
|
||||
sliceStart = (len(str) + sliceEnd.toInt()).asInt()
|
||||
elif startIndex - 1 > len(str) - 1:
|
||||
self.push(addObject(addr self, jstring.newString("")))
|
||||
self.push(addObject(addr self, jobject.newString("")))
|
||||
return true
|
||||
if endIndex - 1 > len(str) - 1:
|
||||
sliceEnd = len(str).asInt()
|
||||
if startIndex > endIndex:
|
||||
self.push(addObject(addr self, jstring.newString("")))
|
||||
self.push(addObject(addr self, jobject.newString("")))
|
||||
return true
|
||||
self.push(addObject(addr self, jstring.newString(str[sliceStart.toInt()..<sliceEnd.toInt()])))
|
||||
self.push(addObject(addr self, jobject.newString(str[sliceStart.toInt()..<sliceEnd.toInt()])))
|
||||
return true
|
||||
else:
|
||||
self.error(newTypeError(&"unsupported slicing for object of type '{popped.typeName()}'"))
|
||||
|
|
Loading…
Reference in New Issue