Added built-in operators as a single instruction (src/peon/stdlib/arithmetics.pn is now fully functional)

This commit is contained in:
Mattia Giambirtone 2022-06-14 18:10:13 +02:00
parent 73381513f9
commit 4591e5ca0e
5 changed files with 402 additions and 147 deletions

View File

@ -500,6 +500,107 @@ proc dispatch*(self: PeonVM) =
self.ip += self.readLong()
else:
discard self.pop()
# Built-in operations on primitive types
of AddInt64:
self.push(PeonObject(kind: Int64, long: self.pop().long + self.pop().long))
of SubInt64:
let second = self.pop()
self.push(PeonObject(kind: Int64, long: self.pop().long - second.long))
of MulInt64:
self.push(PeonObject(kind: Int64, long: self.pop().long * self.pop().long))
of DivInt64:
let second = self.pop()
self.push(PeonObject(kind: Int64, long: self.pop().long div second.long))
of AddUInt64:
self.push(PeonObject(kind: UInt64, uLong: self.pop().uLong + self.pop().uLong))
of SubUInt64:
let second = self.pop()
self.push(PeonObject(kind: UInt64, uLong: self.pop().uLong - second.uLong))
of MulUInt64:
self.push(PeonObject(kind: UInt64, uLong: self.pop().uLong * self.pop().uLong))
of DivUInt64:
let second = self.pop()
self.push(PeonObject(kind: UInt64, uLong: self.pop().uLong div second.uLong))
of AddInt32:
self.push(PeonObject(kind: Int32, `int`: self.pop().`int` + self.pop().`int`))
of SubInt32:
let second = self.pop()
self.push(PeonObject(kind: Int32, `int`: self.pop().`int` - second.`int`))
of MulInt32:
self.push(PeonObject(kind: Int32, `int`: self.pop().`int` * self.pop().`int`))
of DivInt32:
let second = self.pop()
self.push(PeonObject(kind: Int32, `int`: self.pop().`int` div second.`int`))
of AddUInt32:
self.push(PeonObject(kind: UInt32, uInt: self.pop().uInt + self.pop().uInt))
of SubUInt32:
let second = self.pop()
self.push(PeonObject(kind: UInt32, uInt: self.pop().uInt - second.uInt))
of MulUInt32:
self.push(PeonObject(kind: UInt32, uInt: self.pop().uInt * self.pop().uInt))
of DivUInt32:
let second = self.pop()
self.push(PeonObject(kind: UInt32, uInt: self.pop().uInt div second.uInt))
of AddInt16:
self.push(PeonObject(kind: Int16, short: self.pop().short + self.pop().short))
of SubInt16:
let second = self.pop()
self.push(PeonObject(kind: Int16, short: self.pop().short - second.short))
of MulInt16:
self.push(PeonObject(kind: Int16, short: self.pop().short * self.pop().short))
of DivInt16:
let second = self.pop()
self.push(PeonObject(kind: Int16, short: self.pop().short div second.short))
of AddUInt16:
self.push(PeonObject(kind: UInt16, uShort: self.pop().uShort + self.pop().uShort))
of SubUInt16:
let second = self.pop()
self.push(PeonObject(kind: UInt16, uShort: self.pop().uShort - second.uShort))
of MulUInt16:
self.push(PeonObject(kind: UInt16, uShort: self.pop().uShort * self.pop().uShort))
of DivUInt16:
let second = self.pop()
self.push(PeonObject(kind: UInt16, uShort: self.pop().uShort div second.uShort))
of AddInt8:
self.push(PeonObject(kind: Int8, tiny: self.pop().tiny + self.pop().tiny))
of SubInt8:
let second = self.pop()
self.push(PeonObject(kind: Int8, tiny: self.pop().tiny - second.tiny))
of MulInt8:
self.push(PeonObject(kind: Int8, tiny: self.pop().tiny * self.pop().tiny))
of DivInt8:
let second = self.pop()
self.push(PeonObject(kind: Int8, tiny: self.pop().tiny div second.tiny))
of AddUInt8:
self.push(PeonObject(kind: UInt8, uTiny: self.pop().uTiny + self.pop().uTiny))
of SubUInt8:
let second = self.pop()
self.push(PeonObject(kind: UInt8, uTiny: self.pop().uTiny - second.uTiny))
of MulUInt8:
self.push(PeonObject(kind: UInt8, uTiny: self.pop().uTiny * self.pop().uTiny))
of DivUInt8:
let second = self.pop()
self.push(PeonObject(kind: UInt8, uTiny: self.pop().uTiny div second.uTiny))
of AddFloat64:
self.push(PeonObject(kind: Float64, `float`: self.pop().`float` + self.pop().`float`))
of SubFloat64:
let second = self.pop()
self.push(PeonObject(kind: Float64, `float`: self.pop().`float` - second.`float`))
of MulFloat64:
self.push(PeonObject(kind: Float64, `float`: self.pop().`float` * self.pop().`float`))
of DivFloat64:
let second = self.pop()
self.push(PeonObject(kind: Float64, `float`: self.pop().`float` / second.`float`))
of AddFloat32:
self.push(PeonObject(kind: Float32, halfFloat: self.pop().halfFloat + self.pop().halfFloat))
of SubFloat32:
let second = self.pop()
self.push(PeonObject(kind: Float32, halfFloat: self.pop().halfFloat - second.halfFloat))
of MulFloat32:
self.push(PeonObject(kind: Float32, halfFloat: self.pop().halfFloat * self.pop().halfFloat))
of DivFloat32:
let second = self.pop()
self.push(PeonObject(kind: Float32, halfFloat: self.pop().halfFloat / second.halfFloat))
else:
discard

View File

@ -96,6 +96,9 @@ type
isFunctionArgument: bool
# Where is this node declared in the file?
line: int
# is this a builtin function?
isBuiltinFunction: bool
builtinOp: string
Loop = object
## A "loop object" used
## by the compiler to emit
@ -183,7 +186,7 @@ proc compareTypes(self: Compiler, a, b: Type): bool
proc patchReturnAddress(self: Compiler, pos: int)
proc handleMagicPragma(self: Compiler, pragma: Pragma, node: ASTnode)
proc handlePurePragma(self: Compiler, pragma: Pragma, node: ASTnode)
proc dispatchPragmas(self: Compiler, node: ASTnode)
## End of forward declarations
@ -809,9 +812,102 @@ proc emitFunction(self: Compiler, name: Name) =
self.emitBytes(name.codePos.toTriple())
proc handleBuiltinFunction(self: Compiler, fn: Name, args: seq[Expression]) =
## Emits single instructions for builtin functions
## such as addition or subtraction
for argument in args:
self.expression(argument)
case fn.builtinOp:
of "AddInt64":
self.emitByte(AddInt64)
of "SubInt64":
self.emitByte(SubInt64)
of "DivInt64":
self.emitByte(DivInt64)
of "MulInt64":
self.emitByte(MulInt64)
of "AddInt32":
self.emitByte(AddInt32)
of "SubInt32":
self.emitByte(SubInt32)
of "DivInt32":
self.emitByte(DivInt32)
of "MulInt32":
self.emitByte(MulInt32)
of "AddInt16":
self.emitByte(AddInt16)
of "SubInt16":
self.emitByte(SubInt16)
of "DivInt16":
self.emitByte(DivInt16)
of "MulInt16":
self.emitByte(MulInt16)
of "AddInt8":
self.emitByte(AddInt8)
of "SubInt8":
self.emitByte(SubInt8)
of "DivInt8":
self.emitByte(DivInt8)
of "MulInt8":
self.emitByte(MulInt8)
of "AddUInt64":
self.emitByte(AddUInt64)
of "SubUInt64":
self.emitByte(SubUInt64)
of "DivUInt64":
self.emitByte(DivUInt64)
of "MulUInt64":
self.emitByte(MulUInt64)
of "AddUInt32":
self.emitByte(AddUInt32)
of "SubUInt32":
self.emitByte(SubUInt32)
of "DivUInt32":
self.emitByte(DivUInt32)
of "MulUInt32":
self.emitByte(MulUInt32)
of "AddUInt16":
self.emitByte(AddUInt16)
of "SubUInt16":
self.emitByte(SubUInt16)
of "DivUInt16":
self.emitByte(DivUInt16)
of "MulUInt16":
self.emitByte(MulUInt16)
of "AddUInt8":
self.emitByte(AddUInt8)
of "SubUInt8":
self.emitByte(SubUInt8)
of "DivUInt8":
self.emitByte(DivUInt8)
of "MulUInt8":
self.emitByte(MulUInt8)
of "AddFloat64":
self.emitByte(AddInt8)
of "SubFloat64":
self.emitByte(SubInt8)
of "DivFloat64":
self.emitByte(DivInt8)
of "MulFloat64":
self.emitByte(MulInt8)
of "AddFloat32":
self.emitByte(AddFloat32)
of "SubFloat32":
self.emitByte(SubFloat32)
of "DivFloat32":
self.emitByte(DivFloat32)
of "MulFloat32":
self.emitByte(MulFloat32)
else:
discard # Unreachable
proc generateCall(self: Compiler, fn: Name, args: seq[Expression]) =
## Small wrapper that abstracts emitting a call instruction
## for a given function
if fn.isBuiltinFunction:
self.handleBuiltinFunction(fn, args)
return
self.emitFunction(fn)
self.emitByte(LoadReturnAddress)
let pos = self.chunk.code.len()
@ -834,6 +930,7 @@ proc generateCall(self: Compiler, fn: Name, args: seq[Expression]) =
proc generateObjCall(self: Compiler, args: seq[Expression]) =
## Small wrapper that abstracts emitting a call instruction
## for a given function already loaded on the operand stack
self.emitByte(PushC) # Pops the function off the operand stack onto the call stack
self.emitByte(LoadReturnAddress)
let pos = self.chunk.code.len()
@ -1263,7 +1360,10 @@ proc callExpr(self: Compiler, node: CallExpr) =
if not funct.isNil():
self.generateCall(funct, argExpr)
else:
self.generateObjCall(argExpr)
if funct.isBuiltinFunction:
self.handleBuiltinFunction(funct, argExpr)
else:
self.generateObjCall(argExpr)
if self.scopeDepth > 0 and not self.checkCallIsPure(node.callee):
if not self.currentFunction.name.isNil():
self.error(&"cannot make sure that calls to '{self.currentFunction.name.token.lexeme}' are side-effect free")
@ -1529,28 +1629,65 @@ proc typeDecl(self: Compiler, node: TypeDecl) =
proc handleMagicPragma(self: Compiler, pragma: Pragma, node: ASTNode) =
## Handles the "magic" pragma
## Handles the "magic" pragma. Assumes the given name is already
## declared
if pragma.args.len() != 1:
self.error("'magic' pragma: wrong number of arguments")
elif pragma.args[0].kind != strExpr:
self.error("'magic' pragma: wrong type of argument (string expected)")
elif node.kind != NodeKind.funDecl:
self.error("'magic' pragma is not valid in this context")
var node = FunDecl(node)
var fn = self.resolve(node.name)
fn.isBuiltinFunction = true
fn.builtinOp = pragma.args[0].token.lexeme[1..^2]
proc handlePurePragma(self: Compiler, pragma: Pragma, node: ASTNode) =
## Handles the "pure" pragma
case node.kind:
of funDecl:
FunDecl(node).isPure = true
of lambdaExpr:
LambdaExpr(node).isPure = true
else:
self.error("'pure' pragma: invalid usage")
proc dispatchPragmas(self: Compiler, node: ASTnode) =
## Dispatches pragmas bound to objects
var pragmas: seq[Pragma] = @[]
case node.kind:
of funDecl, NodeKind.typeDecl, NodeKind.varDecl:
pragmas = Declaration(node).pragmas
of lambdaExpr:
pragmas = LambdaExpr(node).pragmas
else:
discard # Unreachable
for pragma in pragmas:
if pragma.name.token.lexeme notin self.compilerProcs:
self.error(&"unknown pragma '{pragma.name.token.lexeme}'")
self.compilerProcs[pragma.name.token.lexeme](self, pragma, node)
proc funDecl(self: Compiler, node: FunDecl) =
## Compiles function declarations
var function = self.currentFunction
self.declareName(node)
self.frames.add(self.names.high())
self.dispatchPragmas(node)
let fn = self.names[^(node.arguments.len() + 1)]
# A function's code is just compiled linearly
# and then jumped over
let jmp = self.emitJump(JumpForwards)
# Function's code starts after the jump
fn.codePos = self.chunk.code.len()
for argument in node.arguments:
# Pops off the operand stack onto the
# call stack
self.emitByte(LoadArgument)
var jmp: int
if not fn.isBuiltinFunction:
self.frames.add(self.names.high())
# A function's code is just compiled linearly
# and then jumped over
jmp = self.emitJump(JumpForwards)
# Function's code starts after the jump
fn.codePos = self.chunk.code.len()
for argument in node.arguments:
# Pops off the operand stack onto the
# call stack
self.emitByte(LoadArgument)
if not node.returnType.isNil() and self.inferType(node.returnType).isNil():
# Are we returning a generic type?
var isGeneric = false
@ -1566,11 +1703,8 @@ proc funDecl(self: Compiler, node: FunDecl) =
self.error(&"cannot infer the type of '{node.returnType.token.lexeme}'")
# TODO: Forward declarations
if not node.body.isNil():
if BlockStmt(node.body).code.len() == 0:
if node.pragmas.len() > 0:
discard
else:
self.error("cannot declare function with empty body")
if BlockStmt(node.body).code.len() == 0 and not fn.isBuiltinFunction:
self.error("cannot declare function with empty body")
let fnType = self.inferType(node)
let impl = self.findByType(node.name.token.lexeme, fnType)
if impl.len() > 1:
@ -1583,67 +1717,68 @@ proc funDecl(self: Compiler, node: FunDecl) =
# We store the current function
self.currentFunction = node
# Since the deferred array is a linear
# sequence of instructions and we want
# to keep track to whose function's each
# set of deferred instruction belongs,
# we record the length of the deferred
# array before compiling the function
# and use this info later to compile
# the try/finally block with the deferred
# code
var deferStart = self.deferred.len()
# We let our debugger know a function is starting
let start = self.chunk.code.high()
self.beginScope()
for decl in BlockStmt(node.body).code:
self.declaration(decl)
var typ: Type
var hasVal: bool = false
case self.currentFunction.kind:
of NodeKind.funDecl:
typ = self.inferType(self.currentFunction)
hasVal = self.currentFunction.hasExplicitReturn
of NodeKind.lambdaExpr:
typ = self.inferType(LambdaExpr(Declaration(self.currentFunction)))
hasVal = LambdaExpr(Declaration(self.currentFunction)).hasExplicitReturn
if not fn.isBuiltinFunction:
# Since the deferred array is a linear
# sequence of instructions and we want
# to keep track to whose function's each
# set of deferred instruction belongs,
# we record the length of the deferred
# array before compiling the function
# and use this info later to compile
# the try/finally block with the deferred
# code
var deferStart = self.deferred.len()
# We let our debugger know a function is starting
let start = self.chunk.code.high()
self.beginScope()
for decl in BlockStmt(node.body).code:
self.declaration(decl)
var typ: Type
var hasVal: bool = false
case self.currentFunction.kind:
of NodeKind.funDecl:
typ = self.inferType(self.currentFunction)
hasVal = self.currentFunction.hasExplicitReturn
of NodeKind.lambdaExpr:
typ = self.inferType(LambdaExpr(Declaration(self.currentFunction)))
hasVal = LambdaExpr(Declaration(self.currentFunction)).hasExplicitReturn
else:
discard # Unreachable
if hasVal and self.currentFunction.returnType.isNil() and not typ.returnType.isNil():
self.error("non-empty return statement is not allowed in void functions")
elif not hasVal and not self.currentFunction.returnType.isNil():
self.error("function has an explicit return type, but no return statement was found")
self.endFunctionBeforeReturn()
hasVal = hasVal and not typ.returnType.isNil()
self.endScope(deleteNames=true, fromFunc=true)
# Terminates the function's context
self.emitByte(OpCode.Return)
if hasVal:
self.emitByte(1)
else:
discard # Unreachable
if hasVal and self.currentFunction.returnType.isNil() and not typ.returnType.isNil():
self.error("non-empty return statement is not allowed in void functions")
elif not hasVal and not self.currentFunction.returnType.isNil():
self.error("function has an explicit return type, but no return statement was found")
self.endFunctionBeforeReturn()
hasVal = hasVal and not typ.returnType.isNil()
self.endScope(deleteNames=true, fromFunc=true)
# Terminates the function's context
self.emitByte(OpCode.Return)
if hasVal:
self.emitByte(1)
else:
self.emitByte(0)
# Function is ending!
self.chunk.cfi.add(start.toTriple())
self.chunk.cfi.add(self.chunk.code.high().toTriple())
self.chunk.cfi.add(self.frames[^1].toTriple())
self.chunk.cfi.add(uint8(node.arguments.len()))
if not node.name.isNil():
self.chunk.cfi.add(node.name.token.lexeme.len().toDouble())
var s = node.name.token.lexeme
if node.name.token.lexeme.len() >= uint16.high().int:
s = node.name.token.lexeme[0..uint16.high()]
self.chunk.cfi.add(s.toBytes())
else:
self.chunk.cfi.add(0.toDouble())
# Currently defer is not functional so we
# just pop the instructions
for i in countup(deferStart, self.deferred.len() - 1, 1):
self.deferred.delete(i)
self.emitByte(0)
# Function is ending!
self.chunk.cfi.add(start.toTriple())
self.chunk.cfi.add(self.chunk.code.high().toTriple())
self.chunk.cfi.add(self.frames[^1].toTriple())
self.chunk.cfi.add(uint8(node.arguments.len()))
if not node.name.isNil():
self.chunk.cfi.add(node.name.token.lexeme.len().toDouble())
var s = node.name.token.lexeme
if node.name.token.lexeme.len() >= uint16.high().int:
s = node.name.token.lexeme[0..uint16.high()]
self.chunk.cfi.add(s.toBytes())
else:
self.chunk.cfi.add(0.toDouble())
# Currently defer is not functional so we
# just pop the instructions
for i in countup(deferStart, self.deferred.len() - 1, 1):
self.deferred.delete(i)
self.patchJump(jmp)
# This makes us compile nested functions correctly
self.patchJump(jmp)
# This makes us compile nested functions correctly
discard self.frames.pop()
self.currentFunction = function
discard self.frames.pop()
proc patchReturnAddress(self: Compiler, pos: int) =

View File

@ -123,6 +123,14 @@ type
DivUInt16,
DivInt8,
DivUInt8,
AddFloat64,
SubFloat64,
DivFloat64,
MulFloat64,
AddFloat32,
SubFloat32,
DivFloat32,
MulFloat32,
## Basic stack operations
Pop, # Pops an element off the stack and discards it
PopRepl, # Same as Pop, but also prints the value of what's popped (used in REPL mode)
@ -184,7 +192,10 @@ const simpleInstructions* = {Return, LoadNil,
MulUInt16, MulInt8, MulUInt8,
DivInt64, DivUInt64, DivInt32,
DivUInt32, DivInt16, DivUInt16,
DivInt8, DivUInt8
DivInt8, DivUInt8, AddFloat64,
SubFloat64, DivFloat64, MulFloat64,
AddFloat32, SubFloat32, DivFloat32,
MulFloat32
}
# Constant instructions are instructions that operate on the bytecode constant table

View File

@ -1226,7 +1226,7 @@ proc declaration(self: Parser): Declaration =
proc parse*(self: Parser, tokens: seq[Token], file: string): seq[Declaration] =
## Parses a sequence of tokens into a sequence of AST nodes
self.tokens = @[]
self.tokens = tokens
self.file = file
self.current = 0
self.currentLoop = LoopContext.None

View File

@ -2,192 +2,200 @@
operator `+`(a, b: int): int {
#pragma[magic: AddInt64, pure]
return;
#pragma[magic: "AddInt64", pure]
}
operator `+`(a, b: uint): uint {
#pragma[magic: AddUInt64, pure]
return;
operator `+`(a, b: uint64): uint64 {
#pragma[magic: "AddUInt64", pure]
}
operator `+`(a, b: int32): int32 {
#pragma[magic: AddInt32, pure]
return;
#pragma[magic: "AddInt32", pure]
}
operator `+`(a, b: uint32): uint32 {
#pragma[magic: AddUInt32, pure]
return;
#pragma[magic: "AddUInt32", pure]
}
operator `+`(a, b: int16): int16 {
#pragma[magic: AddInt16, pure]
return;
#pragma[magic: "AddInt16", pure]
}
operator `+`(a, b: uint16): uint16 {
#pragma[magic: AddUInt16, pure]
return;
#pragma[magic: "AddUInt16", pure]
}
operator `+`(a, b: int8): int8 {
#pragma[magic: AddInt8, pure]
return;
#pragma[magic: "AddInt8", pure]
}
operator `+`(a, b: uint8): uint8 {
#pragma[magic: AddUInt8, pure]
return;
#pragma[magic: "AddUInt8", pure]
}
operator `+`(a, b: float64): float64 {
#pragma[magic: "AddFloat64", pure]
}
operator `+`(a, b: float32): float32 {
#pragma[magic: "AddFloat32", pure]
}
operator `-`(a, b: int): int {
#pragma[magic: SubInt64, pure]
return;
#pragma[magic: "SubInt64", pure]
}
operator `-`(a, b: uint): uint {
#pragma[magic: SubUInt64, pure]
return;
operator `-`(a, b: uint64): uint64 {
#pragma[magic: "SubUInt64", pure]
}
operator `-`(a, b: int32): int32 {
#pragma[magic: SubInt32, pure]
return;
#pragma[magic: "SubInt32", pure]
}
operator `-`(a, b: uint32): uint32 {
#pragma[magic: SubUInt32, pure]
return;
#pragma[magic: "SubUInt32", pure]
}
operator `-`(a, b: int16): int16 {
#pragma[magic: SubInt16, pure]
return;
#pragma[magic: "SubInt16", pure]
}
operator `-`(a, b: uint16): uint16 {
#pragma[magic: SubUInt16, pure]
return;
#pragma[magic: "SubUInt16", pure]
}
operator `-`(a, b: int8): int8 {
#pragma[magic: SubInt8, pure]
return;
#pragma[magic: "SubInt8", pure]
}
operator `-`(a, b: uint8): uint8 {
#pragma[magic: SubUInt8, pure]
return;
#pragma[magic: "SubUInt8", pure]
}
operator `-`(a, b: float64): float64 {
#pragma[magic: "SubFloat64", pure]
}
operator `-`(a, b: float32): float32 {
#pragma[magic: "SubFloat32", pure]
}
operator `*`(a, b: int): int {
#pragma[magic: MulInt64, pure]
return;
#pragma[magic: "MulInt64", pure]
}
operator `*`(a, b: uint): uint {
#pragma[magic: MulUInt64, pure]
return;
operator `*`(a, b: uint64): uint64 {
#pragma[magic: "MulUInt64", pure]
}
operator `*`(a, b: int32): int32 {
#pragma[magic: MulInt32, pure]
return;
#pragma[magic: "MulInt32", pure]
}
operator `*`(a, b: uint32): uint32 {
#pragma[magic: MulUInt32, pure]
return;
#pragma[magic: "MulUInt32", pure]
}
operator `*`(a, b: int16): int16 {
#pragma[magic: MulInt16, pure]
return;
#pragma[magic: "MulInt16", pure]
}
operator `*`(a, b: uint16): uint16 {
#pragma[magic: MulUInt16, pure]
return;
#pragma[magic: "MulUInt16", pure]
}
operator `*`(a, b: int8): int8 {
#pragma[magic: MulInt8, pure]
return;
#pragma[magic: "MulInt8", pure]
}
operator `*`(a, b: uint8): uint8 {
#pragma[magic: MulUInt8, pure]
return;
#pragma[magic: "MulUInt8", pure]
}
operator `*`(a, b: float64): float64 {
#pragma[magic: "MulFloat64", pure]
}
operator `*`(a, b: float32): float32 {
#pragma[magic: "MulFloat32", pure]
}
operator `/`(a, b: int): int {
#pragma[magic: DivInt64, pure]
return;
#pragma[magic: "DivInt64", pure]
}
operator `/`(a, b: uint): uint {
#pragma[magic: DivUInt64, pure]
return;
operator `/`(a, b: uint64): uint64 {
#pragma[magic: "DivUInt64", pure]
}
operator `/`(a, b: int32): int32 {
#pragma[magic: DivInt32, pure]
return;
#pragma[magic: "DivInt32", pure]
}
operator `/`(a, b: uint32): uint32 {
#pragma[magic: DivUInt32, pure]
return;
#pragma[magic: "DivUInt32", pure]
}
operator `/`(a, b: int16): int16 {
#pragma[magic: DivInt16, pure]
return;
#pragma[magic: "DivInt16", pure]
}
operator `/`(a, b: uint16): uint16 {
#pragma[magic: DivUInt16, pure]
return;
#pragma[magic: "DivUInt16", pure]
}
operator `/`(a, b: int8): int8 {
#pragma[magic: DivInt8, pure]
return;
#pragma[magic: "DivInt8", pure]
}
operator `/`(a, b: uint8): uint8 {
#pragma[magic: DivUInt8, pure]
return;
}
#pragma[magic: "DivUInt8", pure]
}
operator `/`(a, b: float64): float64 {
#pragma[magic: "DivFloat64", pure]
}
operator `/`(a, b: float32): float32 {
#pragma[magic: "DivFloat32", pure]
}