diff --git a/src/compiler.nim b/src/compiler.nim index 069b767..b8c2037 100644 --- a/src/compiler.nim +++ b/src/compiler.nim @@ -318,7 +318,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) = @@ -385,15 +385,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) = @@ -443,13 +441,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) = @@ -1114,7 +1112,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 diff --git a/src/config.nim b/src/config.nim index 2569761..e9fb284 100644 --- a/src/config.nim +++ b/src/config.nim @@ -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) diff --git a/src/lexer.nim b/src/lexer.nim index 133b524..4e1da37 100644 --- a/src/lexer.nim +++ b/src/lexer.nim @@ -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)) diff --git a/src/main.nim b/src/main.nim index e8f106f..a595720 100644 --- a/src/main.nim +++ b/src/main.nim @@ -18,7 +18,7 @@ import strformat import parseopt import os -import common +import config import vm proc repl() = diff --git a/src/types/exceptions.nim b/src/types/exceptions.nim deleted file mode 100644 index c7ceacd..0000000 --- a/src/types/exceptions.nim +++ /dev/null @@ -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) - diff --git a/src/types/function.nim b/src/types/function.nim deleted file mode 100644 index 89f6210..0000000 --- a/src/types/function.nim +++ /dev/null @@ -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 = &"" - else: - result = &"" - - -proc valuesEqual*(a, b: ptr Function): bool = - result = a.name.stringify == b.name.stringify - - -proc typeName*(self: ptr Function): string = - result = "function" diff --git a/src/types/jfloat.nim b/src/types/jfloat.nim deleted file mode 100644 index 1dcb4f0..0000000 --- a/src/types/jfloat.nim +++ /dev/null @@ -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 diff --git a/src/types/jint.nim b/src/types/jint.nim deleted file mode 100644 index d28fb6d..0000000 --- a/src/types/jint.nim +++ /dev/null @@ -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 diff --git a/src/types/jobject.nim b/src/types/jobject.nim index a9f230d..00378d4 100644 --- a/src/types/jobject.nim +++ b/src/types/jobject.nim @@ -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.. 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 = "" # idk why this doesn't work with &"{...}", too tired to investigate + else: + result = "" + + +## 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 = "" + 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 = diff --git a/src/types/jstring.nim b/src/types/jstring.nim deleted file mode 100644 index 3eba28d..0000000 --- a/src/types/jstring.nim +++ /dev/null @@ -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.. 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) diff --git a/src/vm.nim b/src/vm.nim index 735e61c..6be0edd 100644 --- a/src/vm.nim +++ b/src/vm.nim @@ -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()..