diff --git a/.gitignore b/.gitignore index 2afa862..6eb59e6 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ nimcache/ nimblecache/ htmldocs/ -tests/*.pbc # Peon bytecode files -peon +*.pbc # Peon bytecode files bin/ .vscode/ diff --git a/README.md b/README.md index f521db5..99385a6 100644 --- a/README.md +++ b/README.md @@ -52,25 +52,26 @@ In no particular order, here's a list of stuff that's done/to do (might be incom Toolchain: - - Tokenizer (with dynamic symbol table) -> Done - - Parser (with support for custom operators, even builtins) -> Done + - Tokenizer (with dynamic symbol table) [X] + - Parser (with support for custom operators, even builtins) [X] - Compiler [ ] -> Being written - VM [ ] -> Being written - - Bytecode (de-)serializer -> Done - - Static code debugger [x] -> Done - - Runtime debugger/inspection tool -> TODO + - Bytecode (de-)serializer [X] + - Static code debugger [X] + - Runtime debugger/inspection tool [ ] Type system: - - Custom types -> TODO - - Intrinsics -> Done - - Generics -> TODO - - Function calls -> WIP + - Custom types [ ] + - Intrinsics [X] + - Generics [ ] -> WIP + - Functions [X] Misc: - - Pragmas -> TODO - - Attribute resolution -> TODO + - Pragmas [ ] -> WIP (Some pragmas implemented) + - Attribute resolution [ ] + - method-like call syntax without actual methods (dispatched at compile-time) [ ] - ... More? ## The name diff --git a/src/backend/types.nim b/src/backend/types.nim index e15c93b..0688218 100644 --- a/src/backend/types.nim +++ b/src/backend/types.nim @@ -33,7 +33,7 @@ type of Byte: `byte`*: byte of Int8: - tiny*: uint8 + tiny*: int8 of UInt8: uTiny*: uint8 of Int16: diff --git a/src/backend/vm.nim b/src/backend/vm.nim index 6c2e590..bde82cc 100644 --- a/src/backend/vm.nim +++ b/src/backend/vm.nim @@ -99,20 +99,6 @@ proc peek(self: PeonVM, distance: int = 0): PeonObject = return self.operands[self.operands.high() + distance] -proc get(self: PeonVM, idx: int): PeonObject = - ## Accessor method that abstracts - ## indexing the through stack - ## frames - return self.operands[idx + self.frames[^1]] - - -proc set(self: PeonVM, idx: int, val: PeonObject) = - ## Setter method that abstracts - ## indexing through stack - ## frames - self.operands[idx + self.frames[^1]] = val - - proc pushc(self: PeonVM, val: PeonObject) = ## Pushes a new object to the ## call stack @@ -257,7 +243,7 @@ proc constReadInt8(self: PeonVM, idx: int): PeonObject = ## chunk's constant table and ## returns a Peon object. Assumes ## the constant is an Int8 - result = PeonObject(kind: Int8, tiny: self.chunk.consts[idx]) + result = PeonObject(kind: Int8, tiny: int8(self.chunk.consts[idx])) proc constReadUInt8(self: PeonVM, idx: int): PeonObject = @@ -601,6 +587,14 @@ proc dispatch*(self: PeonVM) = of DivFloat32: let second = self.pop() self.push(PeonObject(kind: Float32, halfFloat: self.pop().halfFloat / second.halfFloat)) + of NegInt64: + self.push(PeonObject(kind: Int64, long: -self.pop().long)) + of NegInt32: + self.push(PeonObject(kind: Int32, `int`: -self.pop().`int`)) + of NegInt16: + self.push(PeonObject(kind: Int16, short: -self.pop().short)) + of NegInt8: + self.push(PeonObject(kind: Int8, tiny: -self.pop().tiny)) else: discard diff --git a/src/frontend/compiler.nim b/src/frontend/compiler.nim index a9dccb3..8464f63 100644 --- a/src/frontend/compiler.nim +++ b/src/frontend/compiler.nim @@ -41,7 +41,7 @@ type UInt32, Int64, UInt64, Float32, Float64, Char, Byte, String, Function, CustomType, Nil, Nan, Bool, Inf, Typevar, Generic, - Mutable, Reference, Pointer + Reference, Pointer Any # Any is used internally in a few cases, # for example when looking for operators # when only the type of the arguments is of @@ -49,6 +49,7 @@ type Type = ref object ## A wrapper around ## compile-time types + mutable: bool case kind: TypeKind: of Function: name: string @@ -59,8 +60,10 @@ type returnType: Type isBuiltinFunction: bool builtinOp: string - of Mutable, Reference, Pointer: + of Reference, Pointer: value: Type + of Generic: + node: IdentExpr else: discard @@ -177,11 +180,11 @@ proc declaration(self: Compiler, node: Declaration) proc peek(self: Compiler, distance: int = 0): ASTNode proc identifier(self: Compiler, node: IdentExpr) proc varDecl(self: Compiler, node: VarDecl) -proc inferType(self: Compiler, node: LiteralExpr): Type -proc inferType(self: Compiler, node: Expression): Type +proc inferType(self: Compiler, node: LiteralExpr, strictMutable: bool = true): Type +proc inferType(self: Compiler, node: Expression, strictMutable: bool = true): Type proc findByName(self: Compiler, name: string): seq[Name] -proc findByType(self: Compiler, name: string, kind: Type, strictRef: bool = true): seq[Name] -proc compareTypes(self: Compiler, a, b: Type, strictRef: bool = true): bool +proc findByType(self: Compiler, name: string, kind: Type, strictMutable: bool = true): seq[Name] +proc compareTypes(self: Compiler, a, b: Type, strictMutable: bool = true): bool proc patchReturnAddress(self: Compiler, pos: int) proc handleMagicPragma(self: Compiler, pragma: Pragma, node: ASTnode) proc handlePurePragma(self: Compiler, pragma: Pragma, node: ASTnode) @@ -429,7 +432,7 @@ proc detectClosureVariable(self: Compiler, name: Name, depth: int = self.scopeDe name.isClosedOver = true -proc compareTypes(self: Compiler, a, b: Type, strictRef: bool = true): bool = +proc compareTypes(self: Compiler, a, b: Type, strictMutable: bool = true): bool = ## Compares two type objects ## for equality (works with nil!) @@ -454,17 +457,11 @@ proc compareTypes(self: Compiler, a, b: Type, strictRef: bool = true): bool = # Next, we see the type discriminant: # If they're different, then they can't # be the same type! - if strictRef: - return false - else: - # Well... unless we don't care about - # whether it's a ref/value/mutable type - if a.kind in {Reference, Pointer, Mutable}: - return self.compareTypes(a.value, b, strictRef) - elif b.kind in {Reference, Pointer, Mutable}: - return self.compareTypes(a, b.value, strictRef) - else: - return false + return false + elif a.mutable != b.mutable and strictMutable: + # Are they both (im)mutable? If not, + # they're different + return false case a.kind: # If all previous checks pass, it's time # to go through each possible type peon @@ -475,9 +472,9 @@ proc compareTypes(self: Compiler, a, b: Type, strictRef: bool = true): bool = # A value type's type is always equal to # another one's return true - of Reference, Pointer, Mutable: + of Reference, Pointer: # Here we already know that both - # a and b are of either of the three + # a and b are of either of the two # types in this branch, so we just need # to compare their values return self.compareTypes(a.value, b.value) @@ -488,7 +485,7 @@ proc compareTypes(self: Compiler, a, b: Type, strictRef: bool = true): bool = elif not self.compareTypes(a.returnType, b.returnType): return false for (argA, argB) in zip(a.args, b.args): - if not self.compareTypes(argA.kind, argB.kind): + if not self.compareTypes(argA.kind, argB.kind, strictMutable): return false return true else: @@ -537,7 +534,7 @@ proc toIntrinsic(name: string): Type = return nil -proc inferType(self: Compiler, node: LiteralExpr): Type = +proc inferType(self: Compiler, node: LiteralExpr, strictMutable: bool = true): Type = ## Infers the type of a given literal expression if node.isNil(): return nil @@ -549,7 +546,7 @@ proc inferType(self: Compiler, node: LiteralExpr): Type = if size.len() == 1: return Type(kind: Int64) let typ = size[1].toIntrinsic() - if not self.compareTypes(typ, nil): + if not self.compareTypes(typ, nil, strictMutable): return typ else: self.error(&"invalid type specifier '{size[1]}' for int") @@ -560,7 +557,7 @@ proc inferType(self: Compiler, node: LiteralExpr): Type = if size.len() == 1 or size[1] == "f64": return Type(kind: Float64) let typ = size[1].toIntrinsic() - if not self.compareTypes(typ, nil): + if not self.compareTypes(typ, nil, strictMutable): return typ else: self.error(&"invalid type specifier '{size[1]}' for float") @@ -578,7 +575,7 @@ proc inferType(self: Compiler, node: LiteralExpr): Type = discard # TODO -proc inferType(self: Compiler, node: Expression): Type = +proc inferType(self: Compiler, node: Expression, strictMutable: bool = true): Type = ## Infers the type of a given expression and ## returns it if node.isNil(): @@ -588,16 +585,16 @@ proc inferType(self: Compiler, node: Expression): Type = let node = IdentExpr(node) let name = self.resolve(node) if not name.isNil(): - return name.valueType + result = name.valueType else: result = node.name.lexeme.toIntrinsic() of unaryExpr: return self.inferType(UnaryExpr(node).a) of binaryExpr: let node = BinaryExpr(node) - var a = self.inferType(node.a) - var b = self.inferType(node.b) - if not self.compareTypes(a, b): + var a = self.inferType(node.a, strictMutable) + var b = self.inferType(node.b, strictMutable) + if not self.compareTypes(a, b, strictMutable): return nil return a of {intExpr, hexExpr, binExpr, octExpr, @@ -611,7 +608,7 @@ proc inferType(self: Compiler, node: Expression): Type = if not node.returnType.isNil(): result.returnType = self.inferType(node.returnType) for argument in node.arguments: - result.args.add((argument.name.token.lexeme, self.inferType(argument.valueType))) + result.args.add((argument.name.token.lexeme, self.inferType(argument.valueType, strictMutable))) of callExpr: var node = CallExpr(node) case node.callee.kind: @@ -624,14 +621,21 @@ proc inferType(self: Compiler, node: Expression): Type = else: result = nil of lambdaExpr: - result = self.inferType(LambdaExpr(node.callee).returnType) + result = self.inferType(LambdaExpr(node.callee).returnType, strictMutable) else: discard # Unreachable + of varExpr: + result = self.inferType(Var(node).value) + result.mutable = true + of refExpr: + result = Type(kind: Reference, value: self.inferType(Ref(node).value, strictMutable)) + of ptrExpr: + result = Type(kind: Pointer, value: self.inferType(Ptr(node).value, strictMutable)) else: discard # Unreachable -proc inferType(self: Compiler, node: Declaration): Type = +proc inferType(self: Compiler, node: Declaration, strictMutable: bool = true): Type = ## Infers the type of a given declaration ## and returns it if node.isNil(): @@ -648,7 +652,7 @@ proc inferType(self: Compiler, node: Declaration): Type = if not resolved.isNil(): return resolved.valueType else: - return self.inferType(node.value) + return self.inferType(node.value, strictMutable) else: return # Unreachable @@ -661,22 +665,26 @@ proc typeToStr(self: Compiler, typ: Type): string = UInt32, Int64, UInt64, Float32, Float64, Char, Byte, String, Nil, TypeKind.Nan, Bool, TypeKind.Inf: - return ($typ.kind).toLowerAscii() + result &= ($typ.kind).toLowerAscii() of Pointer: - return &"ptr {self.typeToStr(typ.value)}" + result &= &"ptr {self.typeToStr(typ.value)}" of Reference: - return &"ref {self.typeToStr(typ.value)}" - of Mutable: - return &"var {self.typeToStr(typ.value)}" + result &= &"ref {self.typeToStr(typ.value)}" of Function: - result = "fn (" + result &= "fn (" for i, (argName, argType) in typ.args: - result &= &"{argName}: {self.typeToStr(argType)}" + result &= &"{argName}: " + echo argType[] + if argType.mutable: + result &= "var " + result &= self.typeToStr(argType) if i < typ.args.len() - 1: result &= ", " result &= ")" if not typ.returnType.isNil(): result &= &": {self.typeToStr(typ.returnType)}" + of Generic: + result = typ.node.name.lexeme else: discard @@ -774,19 +782,19 @@ proc findByName(self: Compiler, name: string): seq[Name] = result.add(obj) -proc findByType(self: Compiler, name: string, kind: Type, strictRef: bool = true): seq[Name] = +proc findByType(self: Compiler, name: string, kind: Type, strictMutable: bool = true): seq[Name] = ## Looks for objects that have already been declared ## with the given name and type for obj in self.findByName(name): - if self.compareTypes(obj.valueType, kind, strictRef): + if self.compareTypes(obj.valueType, kind, strictMutable): result.add(obj) -proc matchImpl(self: Compiler, name: string, kind: Type): Name = +proc matchImpl(self: Compiler, name: string, kind: Type, strictMutable: bool = true): Name = ## Tries to find a matching function implementation ## compatible with the given type and returns its ## name object - let impl = self.findByType(name, kind, strictRef=false) + let impl = self.findByType(name, kind, strictMutable) if impl.len() == 0: var msg = &"cannot find a suitable implementation for '{name}'" let names = self.findByName(name) @@ -803,7 +811,9 @@ proc matchImpl(self: Compiler, name: string, kind: Type): Name = msg &= &", wrong number of arguments ({name.valueType.args.len()} expected, got {kind.args.len()})" else: for i, arg in kind.args: - if name.valueType.args[i].kind.kind == Mutable and arg.kind.kind != Mutable: + echo name.valueType.args[i].kind.mutable + echo arg.kind.mutable + if name.valueType.args[i].kind.mutable and not arg.kind.mutable: msg &= &", first mismatch at position {i + 1}: {name.valueType.args[i].name} is immutable, not 'var'" break elif not self.compareTypes(arg.kind, name.valueType.args[i].kind): @@ -991,16 +1001,16 @@ proc callBinaryOp(self: Compiler, fn: Name, op: BinaryExpr) = proc unary(self: Compiler, node: UnaryExpr) = ## Compiles unary expressions such as decimal ## and bitwise negation - let valueType = self.inferType(node.a) - let funct = self.matchImpl(node.token.lexeme, Type(kind: Function, returnType: Type(kind: Any), args: @[("", valueType)])) + let valueType = self.inferType(node.a, strictMutable=false) + let funct = self.matchImpl(node.token.lexeme, Type(kind: Function, returnType: Type(kind: Any), args: @[("", valueType)]), strictMutable=false) self.callUnaryOp(funct, node) proc binary(self: Compiler, node: BinaryExpr) = ## Compiles all binary expressions - let typeOfA = self.inferType(node.a) - let typeOfB = self.inferType(node.b) - let funct = self.matchImpl(node.token.lexeme, Type(kind: Function, returnType: Type(kind: Any), args: @[("", typeOfA), ("", typeOfB)])) + let typeOfA = self.inferType(node.a, strictMutable=false) + let typeOfB = self.inferType(node.b, strictMutable=false) + let funct = self.matchImpl(node.token.lexeme, Type(kind: Function, returnType: Type(kind: Any), args: @[("", typeOfA), ("", typeOfB)]), strictMutable=false) self.callBinaryOp(funct, node) @@ -1035,9 +1045,8 @@ proc declareName(self: Compiler, node: Declaration, mutable: bool = false) = isLet: node.isLet, isClosedOver: false, line: node.token.line)) - # TODO: - # if mutable: - # self.names[^1].valueType = Type(kind: Mutable, value: self.names[^1].valueType) + if mutable: + self.names[^1].valueType.mutable = true # We emit a jump of 0 because this may become a # StoreHeap instruction. If they variable is # not closed over, we'll sadly be wasting a @@ -1055,6 +1064,16 @@ proc declareName(self: Compiler, node: Declaration, mutable: bool = false) = self.emitBytes(0.toTriple()) of NodeKind.funDecl: var node = FunDecl(node) + # We declare the generics before the function so we + # can refer to them + for gen in node.generics: + self.names.add(Name(depth: self.scopeDepth + 1, + isPrivate: true, + isConst: false, + owner: self.currentModule, + line: node.token.line, + valueType: Type(kind: Generic, mutable: false, node: gen.name), + name: gen.name)) self.names.add(Name(depth: self.scopeDepth, isPrivate: node.isPrivate, isConst: false, @@ -1070,10 +1089,6 @@ proc declareName(self: Compiler, node: Declaration, mutable: bool = false) = isClosedOver: false, line: node.token.line)) let fn = self.names[^1] - if fn.valueType.returnType.isNil() and not node.returnType.isNil() and node.returnType.kind == identExpr: - for g in node.generics: - if g.name == IdentExpr(node.returnType): - fn.valueType.returnType = Type(kind: Generic) var name: Name for argument in node.arguments: if self.names.high() > 16777215: @@ -1094,18 +1109,6 @@ proc declareName(self: Compiler, node: Declaration, mutable: bool = false) = isFunctionArgument: true) self.names.add(name) name.valueType = self.inferType(argument.valueType) - if argument.mutable: - name.valueType = Type(kind: Mutable, value: name.valueType) - elif argument.isRef: - name.valueType = Type(kind: Reference, value: name.valueType) - elif argument.isPtr: - name.valueType = Type(kind: Pointer, value: name.valueType) - # We check if the argument's type is a generic - if name.valueType.isNil() and argument.valueType.kind == identExpr: - for gen in node.generics: - if gen.name == IdentExpr(argument.valueType): - name.valueType = Type(kind: Generic) - break # If it's still nil, it's an error! if name.valueType.isNil(): self.error(&"cannot determine the type of argument '{argument.name.token.lexeme}'") @@ -1340,6 +1343,7 @@ proc callExpr(self: Compiler, node: CallExpr) = var args: seq[tuple[name: string, kind: Type]] = @[] var argExpr: seq[Expression] = @[] var kind: Type + var strictMutable = true # TODO: Keyword arguments for i, argument in node.arguments.positionals: kind = self.inferType(argument) @@ -1347,6 +1351,8 @@ proc callExpr(self: Compiler, node: CallExpr) = if argument.kind == identExpr: self.error(&"reference to undeclared identifier '{IdentExpr(argument).name.lexeme}'") self.error(&"cannot infer the type of argument {i + 1} in function call") + if kind.mutable: + strictMutable = false args.add(("", kind)) argExpr.add(argument) for argument in node.arguments.keyword: @@ -1356,7 +1362,7 @@ proc callExpr(self: Compiler, node: CallExpr) = var funct: Name case node.callee.kind: of identExpr: - funct = self.matchImpl(IdentExpr(node.callee).name.lexeme, Type(kind: Function, returnType: Type(kind: Any), args: args)) + funct = self.matchImpl(IdentExpr(node.callee).name.lexeme, Type(kind: Function, returnType: Type(kind: Any), args: args), strictMutable) of NodeKind.callExpr: var node = node.callee while node.kind == callExpr: @@ -1464,8 +1470,6 @@ proc returnStmt(self: Compiler, node: ReturnStmt) = let actual = self.inferType(node.value) let expected = self.inferType(self.currentFunction) var comp: Type = actual - if not expected.isNil() and not expected.returnType.isNil() and expected.returnType.kind in {Reference, Pointer, Mutable}: - comp = expected.returnType.value ## Having the return type if actual.isNil() and not expected.returnType.isNil(): if not node.value.isNil(): @@ -1621,13 +1625,13 @@ proc varDecl(self: Compiler, node: VarDecl) = name = CallExpr(node.value).callee.token.lexeme self.error(&"reference to undeclared identifier '{name}'") self.error(&"'{node.name.token.lexeme}' has no type") - elif not expected.isNil() and expected.kind == Mutable: # I mean, variables *are* already mutable (some of them anyway) + elif not expected.isNil() and expected.mutable: # I mean, variables *are* already mutable (some of them anyway) self.error(&"invalid type '{self.typeToStr(expected)}' for var") elif not self.compareTypes(expected, actual): if not expected.isNil(): self.error(&"expected value of type '{self.typeToStr(expected)}', but '{node.name.token.lexeme}' is of type '{self.typeToStr(actual)}'") self.expression(node.value) - self.declareName(node, mutable=node.token.kind == Var) + self.declareName(node, mutable=node.token.kind == TokenType.Var) self.emitByte(StoreVar) self.emitBytes(self.names.len().toTriple()) @@ -1683,6 +1687,16 @@ proc funDecl(self: Compiler, node: FunDecl) = ## Compiles function declarations var function = self.currentFunction self.declareName(node) + if node.generics.len() > 0: + # We can't know the type of + # generic arguments yet, so + # we wait for the function to + # be called to compile its code + # or dispatch any pragmas. We + # still declare its name so that + # it can be assigned to variables + # and passed to functions + return self.dispatchPragmas(node) let fn = self.names[^(node.arguments.len() + 1)] var jmp: int diff --git a/src/frontend/meta/ast.nim b/src/frontend/meta/ast.nim index 21213b6..2e22ba8 100644 --- a/src/frontend/meta/ast.nim +++ b/src/frontend/meta/ast.nim @@ -77,7 +77,10 @@ type nanExpr, infExpr, identExpr, # Identifier - pragmaExpr + pragmaExpr, + varExpr, + refExpr, + ptrExpr # Here I would've rather used object variants, and in fact that's what was in # place before, but not being able to re-declare a field of the same type in @@ -152,6 +155,7 @@ type callee*: Expression # The object being called arguments*: tuple[positionals: seq[Expression], keyword: seq[tuple[ name: IdentExpr, value: Expression]]] + genericArgs*: seq[Expression] UnaryExpr* = ref object of Expression operator*: Token @@ -171,8 +175,7 @@ type LambdaExpr* = ref object of Expression body*: Statement - arguments*: seq[tuple[name: IdentExpr, valueType: Expression, - mutable: bool, isRef: bool, isPtr: bool]] + arguments*: seq[tuple[name: IdentExpr, valueType: Expression]] defaults*: seq[Expression] isGenerator*: bool isAsync*: bool @@ -253,8 +256,7 @@ type FunDecl* = ref object of Declaration name*: IdentExpr body*: Statement - arguments*: seq[tuple[name: IdentExpr, valueType: Expression, - mutable: bool, isRef: bool, isPtr: bool]] + arguments*: seq[tuple[name: IdentExpr, valueType: Expression]] defaults*: seq[Expression] isAsync*: bool isGenerator*: bool @@ -264,14 +266,22 @@ type TypeDecl* = ref object of Declaration name*: IdentExpr - fields*: seq[tuple[name: IdentExpr, valueType: Expression, - mutable: bool, isRef: bool, isPtr: bool, isPrivate: bool]] + fields*: seq[tuple[name: IdentExpr, valueType: Expression, isPrivate: bool]] defaults*: seq[Expression] - isRef*: bool + valueType*: Expression Pragma* = ref object of Expression name*: IdentExpr args*: seq[LiteralExpr] + + Var* = ref object of Expression + value*: Expression + + Ref* = ref object of Expression + value*: Expression + + Ptr* = ref object of Expression + value*: Expression proc isConst*(self: ASTNode): bool = @@ -311,6 +321,27 @@ proc newPragma*(name: IdentExpr, args: seq[LiteralExpr]): Pragma = result.token = name.token +proc newVarExpr*(expression: Expression, token: Token): Var = + new(result) + result.kind = varExpr + result.value = expression + result.token = token + + +proc newRefExpr*(expression: Expression, token: Token): Ref = + new(result) + result.kind = refExpr + result.value = expression + result.token = token + + +proc newPtrExpr*(expression: Expression, token: Token): Ptr = + new(result) + result.kind = ptrExpr + result.value = expression + result.token = token + + proc newIntExpr*(literal: Token): IntExpr = result = IntExpr(kind: intExpr) result.literal = literal @@ -377,11 +408,10 @@ proc newGroupingExpr*(expression: Expression, token: Token): GroupingExpr = result.token = token -proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: Expression, - mutable: bool, isRef: bool, isPtr: bool]], defaults: seq[Expression], - body: Statement, isGenerator: bool, isAsync: bool, token: Token, - returnType: Expression, pragmas: seq[Pragma], - generics: seq[tuple[name: IdentExpr, cond: Expression]]): LambdaExpr = +proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: Expression]], + defaults: seq[Expression], body: Statement, isGenerator: bool, + isAsync: bool, token: Token, returnType: Expression, pragmas: seq[Pragma], + generics: seq[tuple[name: IdentExpr, cond: Expression]]): LambdaExpr = result = LambdaExpr(kind: lambdaExpr) result.body = body result.arguments = arguments @@ -414,15 +444,15 @@ proc newSetItemExpr*(obj: Expression, name: IdentExpr, value: Expression, proc newCallExpr*(callee: Expression, arguments: tuple[positionals: seq[ Expression], keyword: seq[tuple[name: IdentExpr, value: Expression]]], - token: Token): CallExpr = + token: Token, genericArgs: seq[Expression] = @[]): CallExpr = result = CallExpr(kind: callExpr) result.callee = callee result.arguments = arguments result.token = token + result.genericArgs = @[] -proc newSliceExpr*(expression: Expression, ends: seq[Expression], - token: Token): SliceExpr = +proc newSliceExpr*(expression: Expression, ends: seq[Expression], token: Token): SliceExpr = result = SliceExpr(kind: sliceExpr) result.expression = expression result.ends = ends @@ -579,7 +609,7 @@ proc newVarDecl*(name: IdentExpr, value: Expression, isConst: bool = false, result.pragmas = pragmas -proc newFunDecl*(name: IdentExpr, arguments: seq[tuple[name: IdentExpr, valueType: Expression, mutable: bool, isRef: bool, isPtr: bool]], defaults: seq[Expression], +proc newFunDecl*(name: IdentExpr, arguments: seq[tuple[name: IdentExpr, valueType: Expression]], defaults: seq[Expression], body: Statement, isAsync, isGenerator: bool, isPrivate: bool, token: Token, pragmas: seq[Pragma], returnType: Expression, generics: seq[tuple[name: IdentExpr, cond: Expression]]): FunDecl = @@ -598,9 +628,9 @@ proc newFunDecl*(name: IdentExpr, arguments: seq[tuple[name: IdentExpr, valueTyp result.generics = generics -proc newTypeDecl*(name: IdentExpr, fields: seq[tuple[name: IdentExpr, valueType: Expression, mutable: bool, isRef: bool, isPtr: bool, isPrivate: bool]], +proc newTypeDecl*(name: IdentExpr, fields: seq[tuple[name: IdentExpr, valueType: Expression, isPrivate: bool]], defaults: seq[Expression], isPrivate: bool, token: Token, pragmas: seq[Pragma], - generics: seq[tuple[name: IdentExpr, cond: Expression]], isRef: bool): TypeDecl = + generics: seq[tuple[name: IdentExpr, cond: Expression]], valueType: Expression): TypeDecl = result = TypeDecl(kind: typeDecl) result.name = name result.fields = fields @@ -609,8 +639,7 @@ proc newTypeDecl*(name: IdentExpr, fields: seq[tuple[name: IdentExpr, valueType: result.token = token result.pragmas = pragmas result.generics = generics - result.isRef = isRef - + result.valueType = valueType proc `$`*(self: ASTNode): string = @@ -699,7 +728,7 @@ proc `$`*(self: ASTNode): string = result &= &"""FunDecl(name={self.name}, body={self.body}, type={self.returnType}, arguments=[{self.arguments.join(", ")}], defaults=[{self.defaults.join(", ")}], generics=[{self.generics.join(", ")}], async={self.isAsync}, generator={self.isGenerator}, private={self.isPrivate}, pragmas={self.pragmas})""" of typeDecl: var self = TypeDecl(self) - result &= &"""TypeDecl(name={self.name}, fields={self.fields}, defaults={self.defaults}, private={self.isPrivate}, pragmas={self.pragmas}, generics={self.generics}, ref={self.isRef}, pragmas={self.pragmas})""" + result &= &"""TypeDecl(name={self.name}, fields={self.fields}, defaults={self.defaults}, private={self.isPrivate}, pragmas={self.pragmas}, generics={self.generics}, pragmas={self.pragmas}, type={self.valueType})""" of lambdaExpr: var self = LambdaExpr(self) result &= &"""Lambda(body={self.body}, type={self.returnType}, arguments=[{self.arguments.join(", ")}], defaults=[{self.defaults.join(", ")}], generator={self.isGenerator}, async={self.isAsync}, pragmas={self.pragmas})""" @@ -724,6 +753,12 @@ proc `$`*(self: ASTNode): string = of pragmaExpr: var self = Pragma(self) result &= &"Pragma(name={self.name}, args={self.args})" + of varExpr: + result &= &"Var({Var(self).value})" + of refExpr: + result &= &"Ptr({Ref(self).value})" + of ptrExpr: + result &= &"Ptr({Ptr(self).value})" else: discard diff --git a/src/frontend/meta/bytecode.nim b/src/frontend/meta/bytecode.nim index 35917e2..0a605e8 100644 --- a/src/frontend/meta/bytecode.nim +++ b/src/frontend/meta/bytecode.nim @@ -91,6 +91,10 @@ type LoadNan, LoadInf, ## Operations on primitive types + NegInt64, # No unsigned variants (how would you negate something that has no sign?) + NegInt32, + NegInt16, + NegInt8, AddInt64, AddUInt64, AddInt32, diff --git a/src/frontend/parser.nim b/src/frontend/parser.nim index 87c4689..087a013 100644 --- a/src/frontend/parser.nim +++ b/src/frontend/parser.nim @@ -349,19 +349,27 @@ proc primary(self: Parser): Expression = result = newInfExpr(self.step()) of Function: discard self.step() - result = Expression(self.funDecl(isLambda = true)) + result = Expression(self.funDecl(isLambda=true)) of Coroutine: discard self.step() - result = Expression(self.funDecl(isAsync = true, isLambda = true)) + result = Expression(self.funDecl(isAsync=true, isLambda=true)) of Generator: discard self.step() - result = Expression(self.funDecl(isGenerator = true, - isLambda = true)) + result = Expression(self.funDecl(isGenerator=true, isLambda=true)) + of TokenType.Var: + discard self.step() + result = newVarExpr(self.expression(), self.peek(-1)) + of TokenType.Ref: + discard self.step() + result = newRefExpr(self.expression(), self.peek(-1)) + of TokenType.Ptr: + discard self.step() + result = newPtrExpr(self.expression(), self.peek(-1)) else: self.error("invalid syntax") -proc makeCall(self: Parser, callee: Expression): Expression = +proc makeCall(self: Parser, callee: Expression): CallExpr = ## Utility function called iteratively by self.call() ## to parse a function call let tok = self.peek(-1) @@ -394,9 +402,15 @@ proc makeCall(self: Parser, callee: Expression): Expression = result = newCallExpr(callee, arguments, tok) +proc parseGenericArgs(self: Parser) = + ## Parses function generic arguments + ## like function[type](arg) + discard + + proc call(self: Parser): Expression = - ## Parses function calls, object field - ## accessing and slicing expressions + ## Parses function calls and object field + ## accessing result = self.primary() while true: if self.match(LeftParen): @@ -404,6 +418,9 @@ proc call(self: Parser): Expression = elif self.match(Dot): self.expect(Identifier, "expecting attribute name after '.'") result = newGetItemExpr(result, newIdentExpr(self.peek(-1)), self.peek(-1)) + elif self.match(LeftBracket): + self.parseGenericArgs() # TODO + result = self.makeCall(result) else: break @@ -707,7 +724,7 @@ proc forStmt(self: Parser): Statement = var increment: Expression = nil if self.match(Semicolon): discard - elif self.match(Var): + elif self.match(TokenType.Var): initializer = self.varDecl() if not VarDecl(initializer).isPrivate: self.error("cannot declare public for loop initializer") @@ -835,7 +852,7 @@ proc varDecl(self: Parser, isLet: bool = false, if isConst and not value.isConst(): self.error("constant initializer is not a constant") else: - if tok.kind != Var: + if tok.kind != TokenType.Var: self.error(&"{tok.lexeme} declaration requires an initializer") value = newNilExpr(Token(lexeme: "nil")) self.expect(Semicolon, "expecting semicolon after declaration") @@ -843,7 +860,7 @@ proc varDecl(self: Parser, isLet: bool = false, for pragma in self.parsePragmas(): pragmas.add(pragma) case tok.kind: - of Var: + of TokenType.Var: result = newVarDecl(name, value, isPrivate = isPrivate, token = tok, valueType = valueType, pragmas = (@[])) of Const: @@ -859,10 +876,8 @@ proc varDecl(self: Parser, isLet: bool = false, result.pragmas = pragmas -proc parseDeclArguments(self: Parser, arguments: var seq[tuple[name: IdentExpr, valueType: Expression, mutable: bool, isRef: bool, isPtr: bool]], - parameter: var tuple[name: IdentExpr, - valueType: Expression, mutable: bool, - isRef: bool, isPtr: bool], +proc parseDeclArguments(self: Parser, arguments: var seq[tuple[name: IdentExpr, valueType: Expression]], + parameter: var tuple[name: IdentExpr, valueType: Expression], defaults: var seq[Expression]) = ## Helper to parse declaration arguments and avoid code duplication while not self.check(RightParen): @@ -871,21 +886,11 @@ proc parseDeclArguments(self: Parser, arguments: var seq[tuple[name: IdentExpr, self.expect(Identifier, "expecting parameter name") parameter.name = newIdentExpr(self.peek(-1)) if self.match(":"): - parameter.mutable = false - parameter.isPtr = false - parameter.isRef = false - if self.match(Var): - parameter.mutable = true - elif self.match(Ptr): - parameter.isPtr = true - elif self.match(Ref): - parameter.isRef = true parameter.valueType = self.expression() for i in countdown(arguments.high(), 0): if arguments[i].valueType != nil: break arguments[i].valueType = parameter.valueType - arguments[i].mutable = parameter.mutable else: parameter.valueType = nil if parameter in arguments: @@ -907,14 +912,12 @@ proc parseFunExpr(self: Parser): LambdaExpr = ## Parses the return value of a function ## when it is another function. Works ## recursively - var arguments: seq[tuple[name: IdentExpr, valueType: Expression, - mutable: bool, isRef: bool, isPtr: bool]] = @[] + var arguments: seq[tuple[name: IdentExpr, valueType: Expression]] = @[] var defaults: seq[Expression] = @[] - result = newLambdaExpr(arguments, defaults, nil, isGenerator = self.peek(-1).kind == Generator, - isAsync = self.peek(-1).kind == Coroutine, token = self.peek(-1), - returnType = nil, pragmas = (@[]), generics=(@[])) - var parameter: tuple[name: IdentExpr, valueType: Expression, - mutable: bool, isRef: bool, isPtr: bool] + result = newLambdaExpr(arguments, defaults, nil, isGenerator=self.peek(-1).kind == Generator, + isAsync=self.peek(-1).kind == Coroutine, token=self.peek(-1), + returnType=nil, pragmas=(@[]), generics=(@[])) + var parameter: tuple[name: IdentExpr, valueType: Expression] if self.match(LeftParen): self.parseDeclArguments(arguments, parameter, defaults) if self.match(":"): @@ -944,8 +947,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, ## (with or without a name, where applicable) let tok = self.peek(-1) var enclosingFunction = self.currentFunction - var arguments: seq[tuple[name: IdentExpr, valueType: Expression, - mutable: bool, isRef: bool, isPtr: bool]] = @[] + var arguments: seq[tuple[name: IdentExpr, valueType: Expression]] = @[] var defaults: seq[Expression] = @[] var returnType: Expression var pragmas: seq[Pragma] = @[] @@ -957,11 +959,11 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, # are nameless, so we can sort the ambiguity by checking # if there's an identifier after the keyword self.currentFunction = newFunDecl(newIdentExpr(self.peek(-1)), arguments, defaults, newBlockStmt(@[], Token()), - isAsync = isAsync, - isGenerator = isGenerator, - isPrivate = true, - token = tok, pragmas = (@[]), - returnType = nil, + isAsync=isAsync, + isGenerator=isGenerator, + isPrivate=true, + token=tok, pragmas=(@[]), + returnType=nil, generics=(@[])) if self.match("*"): FunDecl(self.currentFunction).isPrivate = false @@ -996,8 +998,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, else: returnType = self.expression() if self.match(LeftParen): - var parameter: tuple[name: IdentExpr, valueType: Expression, - mutable: bool, isRef: bool, isPtr: bool] + var parameter: tuple[name: IdentExpr, valueType: Expression] self.parseDeclArguments(arguments, parameter, defaults) if self.match(":"): # Function's return type @@ -1122,55 +1123,30 @@ proc typeDecl(self: Parser): TypeDecl = let isPrivate = not self.match("*") self.checkDecl(isPrivate) var name = newIdentExpr(self.peek(-1)) - var isRef = false - var fields: seq[tuple[name: IdentExpr, valueType: Expression, - mutable: bool, isRef: bool, isPtr: bool, isPrivate: bool]] = @[] + var fields: seq[tuple[name: IdentExpr, valueType: Expression, isPrivate: bool]] = @[] var defaults: seq[Expression] = @[] var generics: seq[tuple[name: IdentExpr, cond: Expression]] = @[] var pragmas: seq[Pragma] = @[] - result = newTypeDecl(name, fields, defaults, isPrivate, token, pragmas, generics, isRef) + result = newTypeDecl(name, fields, defaults, isPrivate, token, pragmas, generics, nil) if self.match(LeftBracket): self.parseGenerics(result) self.expect("=", "expecting '=' after type name") - case self.step().kind: - of Ref: - isRef = true - echo self.peek() - self.expect(Object, "invalid syntax") - of Object: - discard - else: - self.error("invalid syntax") + result.valueType = self.expression() self.expect(LeftBrace, "expecting '{' after type declaration") if self.match(TokenType.Pragma): for pragma in self.parsePragmas(): pragmas.add(pragma) var argName: IdentExpr - argMutable: bool - argRef: bool - argPtr: bool argPrivate: bool argType: Expression while not self.match(RightBrace) and not self.done(): - argRef = false - argPtr = false - argMutable = false self.expect(Identifier, "expecting field name") argName = newIdentExpr(self.peek(-1)) argPrivate = not self.match("*") self.expect(":", "expecting ':' after field name") - case self.step().kind: - of Ref: - argRef = true - of Ptr: - argPtr = true - of Var: - argMutable = true - else: - self.current -= 1 argType = self.expression() - result.fields.add((argName, argType, argMutable, argRef, argPtr, argPrivate)) + result.fields.add((argName, argType, argPrivate)) if self.match("="): result.defaults.add(self.expression()) self.expect(";", "expecting semicolon after field declaration") @@ -1180,7 +1156,7 @@ proc typeDecl(self: Parser): TypeDecl = proc declaration(self: Parser): Declaration = ## Parses declarations case self.peek().kind: - of Var, Const, Let: + of TokenType.Var, Const, Let: let keyword = self.step() result = self.varDecl(isLet = keyword.kind == Let, isConst = keyword.kind == Const) diff --git a/src/main.nim b/src/main.nim index b5acc89..b81f235 100644 --- a/src/main.nim +++ b/src/main.nim @@ -400,7 +400,7 @@ proc fillSymbolTable(tokenizer: Lexer) = tokenizer.symbols.addKeyword("assert", TokenType.Assert) tokenizer.symbols.addKeyword("const", Const) tokenizer.symbols.addKeyword("let", Let) - tokenizer.symbols.addKeyword("var", Var) + tokenizer.symbols.addKeyword("var", TokenType.Var) tokenizer.symbols.addKeyword("import", Import) tokenizer.symbols.addKeyword("yield", TokenType.Yield) tokenizer.symbols.addKeyword("return", TokenType.Return) @@ -415,8 +415,8 @@ proc fillSymbolTable(tokenizer: Lexer) = tokenizer.symbols.addKeyword("nil", TokenType.Nil) tokenizer.symbols.addKeyword("true", True) tokenizer.symbols.addKeyword("false", False) - tokenizer.symbols.addKeyword("ref", Ref) - tokenizer.symbols.addKeyword("ptr", Ptr) + tokenizer.symbols.addKeyword("ref", TokenType.Ref) + tokenizer.symbols.addKeyword("ptr", TokenType.Ptr) for sym in [">", "<", "=", "~", "/", "+", "-", "_", "*", "?", "@", ":"]: tokenizer.symbols.addSymbol(sym, Symbol) diff --git a/src/peon/stdlib/arithmetics.pn b/src/peon/stdlib/arithmetics.pn index baf4850..739c02a 100644 --- a/src/peon/stdlib/arithmetics.pn +++ b/src/peon/stdlib/arithmetics.pn @@ -1,5 +1,11 @@ ## Builtin arithmetic operators for Peon +# Note: These do nothing on their own. All they do +# is serve as placeholders for emitting specific VM +# instructions. They're implemented this way because: +# - They tie into the existing type system nicely +# - It makes the implementation easier and more flexible + operator `+`(a, b: int): int { #pragma[magic: "AddInt64", pure] diff --git a/tests/functionObj.pn b/tests/functionObj.pn index 7e1732f..055aff8 100644 --- a/tests/functionObj.pn +++ b/tests/functionObj.pn @@ -12,5 +12,5 @@ operator `+`(a, b: int): int { getFunction()(5); -var x = `+`; -x(1, 2); \ No newline at end of file +var x = getFunction; +x()(3); \ No newline at end of file diff --git a/tests/mutable.pn b/tests/mutable.pn new file mode 100644 index 0000000..86a6bee --- /dev/null +++ b/tests/mutable.pn @@ -0,0 +1,24 @@ +operator `=`[T](a: var T, b: T) { + #pragma[magic: "GenericAssign"] +} + + +operator `+`(a, b: int): int { + #pragma[magic: "AddInt64", pure] +} + + +operator `+=`(a: var int, b: int) { + a = a + b; +} + + +fn identity(x: var int): int { + x += 1; + return x; +} + + +var x = 5; +identity(x); +# identity(38); # If you uncomment this, the compiler errors out!