Updated .gitignore, slightly edited README and added some more tests

This commit is contained in:
Mattia Giambirtone 2022-06-19 14:44:14 +02:00
parent 998ba94902
commit 94ce81b6f1
12 changed files with 253 additions and 200 deletions

3
.gitignore vendored
View File

@ -2,7 +2,6 @@
nimcache/ nimcache/
nimblecache/ nimblecache/
htmldocs/ htmldocs/
tests/*.pbc # Peon bytecode files *.pbc # Peon bytecode files
peon
bin/ bin/
.vscode/ .vscode/

View File

@ -52,25 +52,26 @@ In no particular order, here's a list of stuff that's done/to do (might be incom
Toolchain: Toolchain:
- Tokenizer (with dynamic symbol table) -> Done - Tokenizer (with dynamic symbol table) [X]
- Parser (with support for custom operators, even builtins) -> Done - Parser (with support for custom operators, even builtins) [X]
- Compiler [ ] -> Being written - Compiler [ ] -> Being written
- VM [ ] -> Being written - VM [ ] -> Being written
- Bytecode (de-)serializer -> Done - Bytecode (de-)serializer [X]
- Static code debugger [x] -> Done - Static code debugger [X]
- Runtime debugger/inspection tool -> TODO - Runtime debugger/inspection tool [ ]
Type system: Type system:
- Custom types -> TODO - Custom types [ ]
- Intrinsics -> Done - Intrinsics [X]
- Generics -> TODO - Generics [ ] -> WIP
- Function calls -> WIP - Functions [X]
Misc: Misc:
- Pragmas -> TODO - Pragmas [ ] -> WIP (Some pragmas implemented)
- Attribute resolution -> TODO - Attribute resolution [ ]
- method-like call syntax without actual methods (dispatched at compile-time) [ ]
- ... More? - ... More?
## The name ## The name

View File

@ -33,7 +33,7 @@ type
of Byte: of Byte:
`byte`*: byte `byte`*: byte
of Int8: of Int8:
tiny*: uint8 tiny*: int8
of UInt8: of UInt8:
uTiny*: uint8 uTiny*: uint8
of Int16: of Int16:

View File

@ -99,20 +99,6 @@ proc peek(self: PeonVM, distance: int = 0): PeonObject =
return self.operands[self.operands.high() + distance] 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) = proc pushc(self: PeonVM, val: PeonObject) =
## Pushes a new object to the ## Pushes a new object to the
## call stack ## call stack
@ -257,7 +243,7 @@ proc constReadInt8(self: PeonVM, idx: int): PeonObject =
## chunk's constant table and ## chunk's constant table and
## returns a Peon object. Assumes ## returns a Peon object. Assumes
## the constant is an Int8 ## 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 = proc constReadUInt8(self: PeonVM, idx: int): PeonObject =
@ -601,6 +587,14 @@ proc dispatch*(self: PeonVM) =
of DivFloat32: of DivFloat32:
let second = self.pop() let second = self.pop()
self.push(PeonObject(kind: Float32, halfFloat: self.pop().halfFloat / second.halfFloat)) 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: else:
discard discard

View File

@ -41,7 +41,7 @@ type
UInt32, Int64, UInt64, Float32, Float64, UInt32, Int64, UInt64, Float32, Float64,
Char, Byte, String, Function, CustomType, Char, Byte, String, Function, CustomType,
Nil, Nan, Bool, Inf, Typevar, Generic, Nil, Nan, Bool, Inf, Typevar, Generic,
Mutable, Reference, Pointer Reference, Pointer
Any # Any is used internally in a few cases, Any # Any is used internally in a few cases,
# for example when looking for operators # for example when looking for operators
# when only the type of the arguments is of # when only the type of the arguments is of
@ -49,6 +49,7 @@ type
Type = ref object Type = ref object
## A wrapper around ## A wrapper around
## compile-time types ## compile-time types
mutable: bool
case kind: TypeKind: case kind: TypeKind:
of Function: of Function:
name: string name: string
@ -59,8 +60,10 @@ type
returnType: Type returnType: Type
isBuiltinFunction: bool isBuiltinFunction: bool
builtinOp: string builtinOp: string
of Mutable, Reference, Pointer: of Reference, Pointer:
value: Type value: Type
of Generic:
node: IdentExpr
else: else:
discard discard
@ -177,11 +180,11 @@ proc declaration(self: Compiler, node: Declaration)
proc peek(self: Compiler, distance: int = 0): ASTNode proc peek(self: Compiler, distance: int = 0): ASTNode
proc identifier(self: Compiler, node: IdentExpr) proc identifier(self: Compiler, node: IdentExpr)
proc varDecl(self: Compiler, node: VarDecl) proc varDecl(self: Compiler, node: VarDecl)
proc inferType(self: Compiler, node: LiteralExpr): Type proc inferType(self: Compiler, node: LiteralExpr, strictMutable: bool = true): Type
proc inferType(self: Compiler, node: Expression): Type proc inferType(self: Compiler, node: Expression, strictMutable: bool = true): Type
proc findByName(self: Compiler, name: string): seq[Name] proc findByName(self: Compiler, name: string): seq[Name]
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]
proc compareTypes(self: Compiler, a, b: Type, strictRef: bool = true): bool proc compareTypes(self: Compiler, a, b: Type, strictMutable: bool = true): bool
proc patchReturnAddress(self: Compiler, pos: int) proc patchReturnAddress(self: Compiler, pos: int)
proc handleMagicPragma(self: Compiler, pragma: Pragma, node: ASTnode) proc handleMagicPragma(self: Compiler, pragma: Pragma, node: ASTnode)
proc handlePurePragma(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 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 ## Compares two type objects
## for equality (works with nil!) ## 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: # Next, we see the type discriminant:
# If they're different, then they can't # If they're different, then they can't
# be the same type! # be the same type!
if strictRef: return false
return false elif a.mutable != b.mutable and strictMutable:
else: # Are they both (im)mutable? If not,
# Well... unless we don't care about # they're different
# whether it's a ref/value/mutable type return false
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
case a.kind: case a.kind:
# If all previous checks pass, it's time # If all previous checks pass, it's time
# to go through each possible type peon # 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 # A value type's type is always equal to
# another one's # another one's
return true return true
of Reference, Pointer, Mutable: of Reference, Pointer:
# Here we already know that both # 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 # types in this branch, so we just need
# to compare their values # to compare their values
return self.compareTypes(a.value, b.value) 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): elif not self.compareTypes(a.returnType, b.returnType):
return false return false
for (argA, argB) in zip(a.args, b.args): 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 false
return true return true
else: else:
@ -537,7 +534,7 @@ proc toIntrinsic(name: string): Type =
return nil 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 ## Infers the type of a given literal expression
if node.isNil(): if node.isNil():
return nil return nil
@ -549,7 +546,7 @@ proc inferType(self: Compiler, node: LiteralExpr): Type =
if size.len() == 1: if size.len() == 1:
return Type(kind: Int64) return Type(kind: Int64)
let typ = size[1].toIntrinsic() let typ = size[1].toIntrinsic()
if not self.compareTypes(typ, nil): if not self.compareTypes(typ, nil, strictMutable):
return typ return typ
else: else:
self.error(&"invalid type specifier '{size[1]}' for int") 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": if size.len() == 1 or size[1] == "f64":
return Type(kind: Float64) return Type(kind: Float64)
let typ = size[1].toIntrinsic() let typ = size[1].toIntrinsic()
if not self.compareTypes(typ, nil): if not self.compareTypes(typ, nil, strictMutable):
return typ return typ
else: else:
self.error(&"invalid type specifier '{size[1]}' for float") self.error(&"invalid type specifier '{size[1]}' for float")
@ -578,7 +575,7 @@ proc inferType(self: Compiler, node: LiteralExpr): Type =
discard # TODO 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 ## Infers the type of a given expression and
## returns it ## returns it
if node.isNil(): if node.isNil():
@ -588,16 +585,16 @@ proc inferType(self: Compiler, node: Expression): Type =
let node = IdentExpr(node) let node = IdentExpr(node)
let name = self.resolve(node) let name = self.resolve(node)
if not name.isNil(): if not name.isNil():
return name.valueType result = name.valueType
else: else:
result = node.name.lexeme.toIntrinsic() result = node.name.lexeme.toIntrinsic()
of unaryExpr: of unaryExpr:
return self.inferType(UnaryExpr(node).a) return self.inferType(UnaryExpr(node).a)
of binaryExpr: of binaryExpr:
let node = BinaryExpr(node) let node = BinaryExpr(node)
var a = self.inferType(node.a) var a = self.inferType(node.a, strictMutable)
var b = self.inferType(node.b) var b = self.inferType(node.b, strictMutable)
if not self.compareTypes(a, b): if not self.compareTypes(a, b, strictMutable):
return nil return nil
return a return a
of {intExpr, hexExpr, binExpr, octExpr, of {intExpr, hexExpr, binExpr, octExpr,
@ -611,7 +608,7 @@ proc inferType(self: Compiler, node: Expression): Type =
if not node.returnType.isNil(): if not node.returnType.isNil():
result.returnType = self.inferType(node.returnType) result.returnType = self.inferType(node.returnType)
for argument in node.arguments: 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: of callExpr:
var node = CallExpr(node) var node = CallExpr(node)
case node.callee.kind: case node.callee.kind:
@ -624,14 +621,21 @@ proc inferType(self: Compiler, node: Expression): Type =
else: else:
result = nil result = nil
of lambdaExpr: of lambdaExpr:
result = self.inferType(LambdaExpr(node.callee).returnType) result = self.inferType(LambdaExpr(node.callee).returnType, strictMutable)
else: else:
discard # Unreachable 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: else:
discard # Unreachable 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 ## Infers the type of a given declaration
## and returns it ## and returns it
if node.isNil(): if node.isNil():
@ -648,7 +652,7 @@ proc inferType(self: Compiler, node: Declaration): Type =
if not resolved.isNil(): if not resolved.isNil():
return resolved.valueType return resolved.valueType
else: else:
return self.inferType(node.value) return self.inferType(node.value, strictMutable)
else: else:
return # Unreachable return # Unreachable
@ -661,22 +665,26 @@ proc typeToStr(self: Compiler, typ: Type): string =
UInt32, Int64, UInt64, Float32, Float64, UInt32, Int64, UInt64, Float32, Float64,
Char, Byte, String, Nil, TypeKind.Nan, Bool, Char, Byte, String, Nil, TypeKind.Nan, Bool,
TypeKind.Inf: TypeKind.Inf:
return ($typ.kind).toLowerAscii() result &= ($typ.kind).toLowerAscii()
of Pointer: of Pointer:
return &"ptr {self.typeToStr(typ.value)}" result &= &"ptr {self.typeToStr(typ.value)}"
of Reference: of Reference:
return &"ref {self.typeToStr(typ.value)}" result &= &"ref {self.typeToStr(typ.value)}"
of Mutable:
return &"var {self.typeToStr(typ.value)}"
of Function: of Function:
result = "fn (" result &= "fn ("
for i, (argName, argType) in typ.args: 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: if i < typ.args.len() - 1:
result &= ", " result &= ", "
result &= ")" result &= ")"
if not typ.returnType.isNil(): if not typ.returnType.isNil():
result &= &": {self.typeToStr(typ.returnType)}" result &= &": {self.typeToStr(typ.returnType)}"
of Generic:
result = typ.node.name.lexeme
else: else:
discard discard
@ -774,19 +782,19 @@ proc findByName(self: Compiler, name: string): seq[Name] =
result.add(obj) 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 ## Looks for objects that have already been declared
## with the given name and type ## with the given name and type
for obj in self.findByName(name): for obj in self.findByName(name):
if self.compareTypes(obj.valueType, kind, strictRef): if self.compareTypes(obj.valueType, kind, strictMutable):
result.add(obj) 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 ## Tries to find a matching function implementation
## compatible with the given type and returns its ## compatible with the given type and returns its
## name object ## name object
let impl = self.findByType(name, kind, strictRef=false) let impl = self.findByType(name, kind, strictMutable)
if impl.len() == 0: if impl.len() == 0:
var msg = &"cannot find a suitable implementation for '{name}'" var msg = &"cannot find a suitable implementation for '{name}'"
let names = self.findByName(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()})" msg &= &", wrong number of arguments ({name.valueType.args.len()} expected, got {kind.args.len()})"
else: else:
for i, arg in kind.args: 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'" msg &= &", first mismatch at position {i + 1}: {name.valueType.args[i].name} is immutable, not 'var'"
break break
elif not self.compareTypes(arg.kind, name.valueType.args[i].kind): 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) = proc unary(self: Compiler, node: UnaryExpr) =
## Compiles unary expressions such as decimal ## Compiles unary expressions such as decimal
## and bitwise negation ## and bitwise negation
let valueType = self.inferType(node.a) let valueType = self.inferType(node.a, strictMutable=false)
let funct = self.matchImpl(node.token.lexeme, Type(kind: Function, returnType: Type(kind: Any), args: @[("", valueType)])) let funct = self.matchImpl(node.token.lexeme, Type(kind: Function, returnType: Type(kind: Any), args: @[("", valueType)]), strictMutable=false)
self.callUnaryOp(funct, node) self.callUnaryOp(funct, node)
proc binary(self: Compiler, node: BinaryExpr) = proc binary(self: Compiler, node: BinaryExpr) =
## Compiles all binary expressions ## Compiles all binary expressions
let typeOfA = self.inferType(node.a) let typeOfA = self.inferType(node.a, strictMutable=false)
let typeOfB = self.inferType(node.b) 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)])) let funct = self.matchImpl(node.token.lexeme, Type(kind: Function, returnType: Type(kind: Any), args: @[("", typeOfA), ("", typeOfB)]), strictMutable=false)
self.callBinaryOp(funct, node) self.callBinaryOp(funct, node)
@ -1035,9 +1045,8 @@ proc declareName(self: Compiler, node: Declaration, mutable: bool = false) =
isLet: node.isLet, isLet: node.isLet,
isClosedOver: false, isClosedOver: false,
line: node.token.line)) line: node.token.line))
# TODO: if mutable:
# if mutable: self.names[^1].valueType.mutable = true
# self.names[^1].valueType = Type(kind: Mutable, value: self.names[^1].valueType)
# We emit a jump of 0 because this may become a # We emit a jump of 0 because this may become a
# StoreHeap instruction. If they variable is # StoreHeap instruction. If they variable is
# not closed over, we'll sadly be wasting a # 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()) self.emitBytes(0.toTriple())
of NodeKind.funDecl: of NodeKind.funDecl:
var node = FunDecl(node) 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, self.names.add(Name(depth: self.scopeDepth,
isPrivate: node.isPrivate, isPrivate: node.isPrivate,
isConst: false, isConst: false,
@ -1070,10 +1089,6 @@ proc declareName(self: Compiler, node: Declaration, mutable: bool = false) =
isClosedOver: false, isClosedOver: false,
line: node.token.line)) line: node.token.line))
let fn = self.names[^1] 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 var name: Name
for argument in node.arguments: for argument in node.arguments:
if self.names.high() > 16777215: if self.names.high() > 16777215:
@ -1094,18 +1109,6 @@ proc declareName(self: Compiler, node: Declaration, mutable: bool = false) =
isFunctionArgument: true) isFunctionArgument: true)
self.names.add(name) self.names.add(name)
name.valueType = self.inferType(argument.valueType) 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 it's still nil, it's an error!
if name.valueType.isNil(): if name.valueType.isNil():
self.error(&"cannot determine the type of argument '{argument.name.token.lexeme}'") 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 args: seq[tuple[name: string, kind: Type]] = @[]
var argExpr: seq[Expression] = @[] var argExpr: seq[Expression] = @[]
var kind: Type var kind: Type
var strictMutable = true
# TODO: Keyword arguments # TODO: Keyword arguments
for i, argument in node.arguments.positionals: for i, argument in node.arguments.positionals:
kind = self.inferType(argument) kind = self.inferType(argument)
@ -1347,6 +1351,8 @@ proc callExpr(self: Compiler, node: CallExpr) =
if argument.kind == identExpr: if argument.kind == identExpr:
self.error(&"reference to undeclared identifier '{IdentExpr(argument).name.lexeme}'") self.error(&"reference to undeclared identifier '{IdentExpr(argument).name.lexeme}'")
self.error(&"cannot infer the type of argument {i + 1} in function call") self.error(&"cannot infer the type of argument {i + 1} in function call")
if kind.mutable:
strictMutable = false
args.add(("", kind)) args.add(("", kind))
argExpr.add(argument) argExpr.add(argument)
for argument in node.arguments.keyword: for argument in node.arguments.keyword:
@ -1356,7 +1362,7 @@ proc callExpr(self: Compiler, node: CallExpr) =
var funct: Name var funct: Name
case node.callee.kind: case node.callee.kind:
of identExpr: 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: of NodeKind.callExpr:
var node = node.callee var node = node.callee
while node.kind == callExpr: while node.kind == callExpr:
@ -1464,8 +1470,6 @@ proc returnStmt(self: Compiler, node: ReturnStmt) =
let actual = self.inferType(node.value) let actual = self.inferType(node.value)
let expected = self.inferType(self.currentFunction) let expected = self.inferType(self.currentFunction)
var comp: Type = actual 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 ## Having the return type
if actual.isNil() and not expected.returnType.isNil(): if actual.isNil() and not expected.returnType.isNil():
if not node.value.isNil(): if not node.value.isNil():
@ -1621,13 +1625,13 @@ proc varDecl(self: Compiler, node: VarDecl) =
name = CallExpr(node.value).callee.token.lexeme name = CallExpr(node.value).callee.token.lexeme
self.error(&"reference to undeclared identifier '{name}'") self.error(&"reference to undeclared identifier '{name}'")
self.error(&"'{node.name.token.lexeme}' has no type") 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") self.error(&"invalid type '{self.typeToStr(expected)}' for var")
elif not self.compareTypes(expected, actual): elif not self.compareTypes(expected, actual):
if not expected.isNil(): 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.error(&"expected value of type '{self.typeToStr(expected)}', but '{node.name.token.lexeme}' is of type '{self.typeToStr(actual)}'")
self.expression(node.value) 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.emitByte(StoreVar)
self.emitBytes(self.names.len().toTriple()) self.emitBytes(self.names.len().toTriple())
@ -1683,6 +1687,16 @@ proc funDecl(self: Compiler, node: FunDecl) =
## Compiles function declarations ## Compiles function declarations
var function = self.currentFunction var function = self.currentFunction
self.declareName(node) 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) self.dispatchPragmas(node)
let fn = self.names[^(node.arguments.len() + 1)] let fn = self.names[^(node.arguments.len() + 1)]
var jmp: int var jmp: int

View File

@ -77,7 +77,10 @@ type
nanExpr, nanExpr,
infExpr, infExpr,
identExpr, # Identifier identExpr, # Identifier
pragmaExpr pragmaExpr,
varExpr,
refExpr,
ptrExpr
# Here I would've rather used object variants, and in fact that's what was in # 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 # 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 callee*: Expression # The object being called
arguments*: tuple[positionals: seq[Expression], keyword: seq[tuple[ arguments*: tuple[positionals: seq[Expression], keyword: seq[tuple[
name: IdentExpr, value: Expression]]] name: IdentExpr, value: Expression]]]
genericArgs*: seq[Expression]
UnaryExpr* = ref object of Expression UnaryExpr* = ref object of Expression
operator*: Token operator*: Token
@ -171,8 +175,7 @@ type
LambdaExpr* = ref object of Expression LambdaExpr* = ref object of Expression
body*: Statement body*: Statement
arguments*: seq[tuple[name: IdentExpr, valueType: Expression, arguments*: seq[tuple[name: IdentExpr, valueType: Expression]]
mutable: bool, isRef: bool, isPtr: bool]]
defaults*: seq[Expression] defaults*: seq[Expression]
isGenerator*: bool isGenerator*: bool
isAsync*: bool isAsync*: bool
@ -253,8 +256,7 @@ type
FunDecl* = ref object of Declaration FunDecl* = ref object of Declaration
name*: IdentExpr name*: IdentExpr
body*: Statement body*: Statement
arguments*: seq[tuple[name: IdentExpr, valueType: Expression, arguments*: seq[tuple[name: IdentExpr, valueType: Expression]]
mutable: bool, isRef: bool, isPtr: bool]]
defaults*: seq[Expression] defaults*: seq[Expression]
isAsync*: bool isAsync*: bool
isGenerator*: bool isGenerator*: bool
@ -264,14 +266,22 @@ type
TypeDecl* = ref object of Declaration TypeDecl* = ref object of Declaration
name*: IdentExpr name*: IdentExpr
fields*: seq[tuple[name: IdentExpr, valueType: Expression, fields*: seq[tuple[name: IdentExpr, valueType: Expression, isPrivate: bool]]
mutable: bool, isRef: bool, isPtr: bool, isPrivate: bool]]
defaults*: seq[Expression] defaults*: seq[Expression]
isRef*: bool valueType*: Expression
Pragma* = ref object of Expression Pragma* = ref object of Expression
name*: IdentExpr name*: IdentExpr
args*: seq[LiteralExpr] 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 = proc isConst*(self: ASTNode): bool =
@ -311,6 +321,27 @@ proc newPragma*(name: IdentExpr, args: seq[LiteralExpr]): Pragma =
result.token = name.token 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 = proc newIntExpr*(literal: Token): IntExpr =
result = IntExpr(kind: intExpr) result = IntExpr(kind: intExpr)
result.literal = literal result.literal = literal
@ -377,11 +408,10 @@ proc newGroupingExpr*(expression: Expression, token: Token): GroupingExpr =
result.token = token result.token = token
proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: Expression, proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: Expression]],
mutable: bool, isRef: bool, isPtr: bool]], defaults: seq[Expression], defaults: seq[Expression], body: Statement, isGenerator: bool,
body: Statement, isGenerator: bool, isAsync: bool, token: Token, isAsync: bool, token: Token, returnType: Expression, pragmas: seq[Pragma],
returnType: Expression, pragmas: seq[Pragma], generics: seq[tuple[name: IdentExpr, cond: Expression]]): LambdaExpr =
generics: seq[tuple[name: IdentExpr, cond: Expression]]): LambdaExpr =
result = LambdaExpr(kind: lambdaExpr) result = LambdaExpr(kind: lambdaExpr)
result.body = body result.body = body
result.arguments = arguments result.arguments = arguments
@ -414,15 +444,15 @@ proc newSetItemExpr*(obj: Expression, name: IdentExpr, value: Expression,
proc newCallExpr*(callee: Expression, arguments: tuple[positionals: seq[ proc newCallExpr*(callee: Expression, arguments: tuple[positionals: seq[
Expression], keyword: seq[tuple[name: IdentExpr, value: Expression]]], Expression], keyword: seq[tuple[name: IdentExpr, value: Expression]]],
token: Token): CallExpr = token: Token, genericArgs: seq[Expression] = @[]): CallExpr =
result = CallExpr(kind: callExpr) result = CallExpr(kind: callExpr)
result.callee = callee result.callee = callee
result.arguments = arguments result.arguments = arguments
result.token = token result.token = token
result.genericArgs = @[]
proc newSliceExpr*(expression: Expression, ends: seq[Expression], proc newSliceExpr*(expression: Expression, ends: seq[Expression], token: Token): SliceExpr =
token: Token): SliceExpr =
result = SliceExpr(kind: sliceExpr) result = SliceExpr(kind: sliceExpr)
result.expression = expression result.expression = expression
result.ends = ends result.ends = ends
@ -579,7 +609,7 @@ proc newVarDecl*(name: IdentExpr, value: Expression, isConst: bool = false,
result.pragmas = pragmas 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, body: Statement, isAsync, isGenerator: bool,
isPrivate: bool, token: Token, pragmas: seq[Pragma], isPrivate: bool, token: Token, pragmas: seq[Pragma],
returnType: Expression, generics: seq[tuple[name: IdentExpr, cond: Expression]]): FunDecl = 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 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], 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 = TypeDecl(kind: typeDecl)
result.name = name result.name = name
result.fields = fields result.fields = fields
@ -609,8 +639,7 @@ proc newTypeDecl*(name: IdentExpr, fields: seq[tuple[name: IdentExpr, valueType:
result.token = token result.token = token
result.pragmas = pragmas result.pragmas = pragmas
result.generics = generics result.generics = generics
result.isRef = isRef result.valueType = valueType
proc `$`*(self: ASTNode): string = 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})""" 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: of typeDecl:
var self = TypeDecl(self) 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: of lambdaExpr:
var self = LambdaExpr(self) 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})""" 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: of pragmaExpr:
var self = Pragma(self) var self = Pragma(self)
result &= &"Pragma(name={self.name}, args={self.args})" 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: else:
discard discard

View File

@ -91,6 +91,10 @@ type
LoadNan, LoadNan,
LoadInf, LoadInf,
## Operations on primitive types ## Operations on primitive types
NegInt64, # No unsigned variants (how would you negate something that has no sign?)
NegInt32,
NegInt16,
NegInt8,
AddInt64, AddInt64,
AddUInt64, AddUInt64,
AddInt32, AddInt32,

View File

@ -349,19 +349,27 @@ proc primary(self: Parser): Expression =
result = newInfExpr(self.step()) result = newInfExpr(self.step())
of Function: of Function:
discard self.step() discard self.step()
result = Expression(self.funDecl(isLambda = true)) result = Expression(self.funDecl(isLambda=true))
of Coroutine: of Coroutine:
discard self.step() discard self.step()
result = Expression(self.funDecl(isAsync = true, isLambda = true)) result = Expression(self.funDecl(isAsync=true, isLambda=true))
of Generator: of Generator:
discard self.step() discard self.step()
result = Expression(self.funDecl(isGenerator = true, result = Expression(self.funDecl(isGenerator=true, isLambda=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: else:
self.error("invalid syntax") 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() ## Utility function called iteratively by self.call()
## to parse a function call ## to parse a function call
let tok = self.peek(-1) let tok = self.peek(-1)
@ -394,9 +402,15 @@ proc makeCall(self: Parser, callee: Expression): Expression =
result = newCallExpr(callee, arguments, tok) result = newCallExpr(callee, arguments, tok)
proc parseGenericArgs(self: Parser) =
## Parses function generic arguments
## like function[type](arg)
discard
proc call(self: Parser): Expression = proc call(self: Parser): Expression =
## Parses function calls, object field ## Parses function calls and object field
## accessing and slicing expressions ## accessing
result = self.primary() result = self.primary()
while true: while true:
if self.match(LeftParen): if self.match(LeftParen):
@ -404,6 +418,9 @@ proc call(self: Parser): Expression =
elif self.match(Dot): elif self.match(Dot):
self.expect(Identifier, "expecting attribute name after '.'") self.expect(Identifier, "expecting attribute name after '.'")
result = newGetItemExpr(result, newIdentExpr(self.peek(-1)), self.peek(-1)) result = newGetItemExpr(result, newIdentExpr(self.peek(-1)), self.peek(-1))
elif self.match(LeftBracket):
self.parseGenericArgs() # TODO
result = self.makeCall(result)
else: else:
break break
@ -707,7 +724,7 @@ proc forStmt(self: Parser): Statement =
var increment: Expression = nil var increment: Expression = nil
if self.match(Semicolon): if self.match(Semicolon):
discard discard
elif self.match(Var): elif self.match(TokenType.Var):
initializer = self.varDecl() initializer = self.varDecl()
if not VarDecl(initializer).isPrivate: if not VarDecl(initializer).isPrivate:
self.error("cannot declare public for loop initializer") 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(): if isConst and not value.isConst():
self.error("constant initializer is not a constant") self.error("constant initializer is not a constant")
else: else:
if tok.kind != Var: if tok.kind != TokenType.Var:
self.error(&"{tok.lexeme} declaration requires an initializer") self.error(&"{tok.lexeme} declaration requires an initializer")
value = newNilExpr(Token(lexeme: "nil")) value = newNilExpr(Token(lexeme: "nil"))
self.expect(Semicolon, "expecting semicolon after declaration") self.expect(Semicolon, "expecting semicolon after declaration")
@ -843,7 +860,7 @@ proc varDecl(self: Parser, isLet: bool = false,
for pragma in self.parsePragmas(): for pragma in self.parsePragmas():
pragmas.add(pragma) pragmas.add(pragma)
case tok.kind: case tok.kind:
of Var: of TokenType.Var:
result = newVarDecl(name, value, isPrivate = isPrivate, token = tok, result = newVarDecl(name, value, isPrivate = isPrivate, token = tok,
valueType = valueType, pragmas = (@[])) valueType = valueType, pragmas = (@[]))
of Const: of Const:
@ -859,10 +876,8 @@ proc varDecl(self: Parser, isLet: bool = false,
result.pragmas = pragmas result.pragmas = pragmas
proc parseDeclArguments(self: Parser, arguments: var seq[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, parameter: var tuple[name: IdentExpr, valueType: Expression],
valueType: Expression, mutable: bool,
isRef: bool, isPtr: bool],
defaults: var seq[Expression]) = defaults: var seq[Expression]) =
## Helper to parse declaration arguments and avoid code duplication ## Helper to parse declaration arguments and avoid code duplication
while not self.check(RightParen): 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") self.expect(Identifier, "expecting parameter name")
parameter.name = newIdentExpr(self.peek(-1)) parameter.name = newIdentExpr(self.peek(-1))
if self.match(":"): 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() parameter.valueType = self.expression()
for i in countdown(arguments.high(), 0): for i in countdown(arguments.high(), 0):
if arguments[i].valueType != nil: if arguments[i].valueType != nil:
break break
arguments[i].valueType = parameter.valueType arguments[i].valueType = parameter.valueType
arguments[i].mutable = parameter.mutable
else: else:
parameter.valueType = nil parameter.valueType = nil
if parameter in arguments: if parameter in arguments:
@ -907,14 +912,12 @@ proc parseFunExpr(self: Parser): LambdaExpr =
## Parses the return value of a function ## Parses the return value of a function
## when it is another function. Works ## when it is another function. Works
## recursively ## recursively
var arguments: seq[tuple[name: IdentExpr, valueType: Expression, var arguments: seq[tuple[name: IdentExpr, valueType: Expression]] = @[]
mutable: bool, isRef: bool, isPtr: bool]] = @[]
var defaults: seq[Expression] = @[] var defaults: seq[Expression] = @[]
result = newLambdaExpr(arguments, defaults, nil, isGenerator = self.peek(-1).kind == Generator, result = newLambdaExpr(arguments, defaults, nil, isGenerator=self.peek(-1).kind == Generator,
isAsync = self.peek(-1).kind == Coroutine, token = self.peek(-1), isAsync=self.peek(-1).kind == Coroutine, token=self.peek(-1),
returnType = nil, pragmas = (@[]), generics=(@[])) returnType=nil, pragmas=(@[]), generics=(@[]))
var parameter: tuple[name: IdentExpr, valueType: Expression, var parameter: tuple[name: IdentExpr, valueType: Expression]
mutable: bool, isRef: bool, isPtr: bool]
if self.match(LeftParen): if self.match(LeftParen):
self.parseDeclArguments(arguments, parameter, defaults) self.parseDeclArguments(arguments, parameter, defaults)
if self.match(":"): if self.match(":"):
@ -944,8 +947,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
## (with or without a name, where applicable) ## (with or without a name, where applicable)
let tok = self.peek(-1) let tok = self.peek(-1)
var enclosingFunction = self.currentFunction var enclosingFunction = self.currentFunction
var arguments: seq[tuple[name: IdentExpr, valueType: Expression, var arguments: seq[tuple[name: IdentExpr, valueType: Expression]] = @[]
mutable: bool, isRef: bool, isPtr: bool]] = @[]
var defaults: seq[Expression] = @[] var defaults: seq[Expression] = @[]
var returnType: Expression var returnType: Expression
var pragmas: seq[Pragma] = @[] 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 # are nameless, so we can sort the ambiguity by checking
# if there's an identifier after the keyword # if there's an identifier after the keyword
self.currentFunction = newFunDecl(newIdentExpr(self.peek(-1)), arguments, defaults, newBlockStmt(@[], Token()), self.currentFunction = newFunDecl(newIdentExpr(self.peek(-1)), arguments, defaults, newBlockStmt(@[], Token()),
isAsync = isAsync, isAsync=isAsync,
isGenerator = isGenerator, isGenerator=isGenerator,
isPrivate = true, isPrivate=true,
token = tok, pragmas = (@[]), token=tok, pragmas=(@[]),
returnType = nil, returnType=nil,
generics=(@[])) generics=(@[]))
if self.match("*"): if self.match("*"):
FunDecl(self.currentFunction).isPrivate = false FunDecl(self.currentFunction).isPrivate = false
@ -996,8 +998,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
else: else:
returnType = self.expression() returnType = self.expression()
if self.match(LeftParen): if self.match(LeftParen):
var parameter: tuple[name: IdentExpr, valueType: Expression, var parameter: tuple[name: IdentExpr, valueType: Expression]
mutable: bool, isRef: bool, isPtr: bool]
self.parseDeclArguments(arguments, parameter, defaults) self.parseDeclArguments(arguments, parameter, defaults)
if self.match(":"): if self.match(":"):
# Function's return type # Function's return type
@ -1122,55 +1123,30 @@ proc typeDecl(self: Parser): TypeDecl =
let isPrivate = not self.match("*") let isPrivate = not self.match("*")
self.checkDecl(isPrivate) self.checkDecl(isPrivate)
var name = newIdentExpr(self.peek(-1)) var name = newIdentExpr(self.peek(-1))
var isRef = false var fields: seq[tuple[name: IdentExpr, valueType: Expression, isPrivate: bool]] = @[]
var fields: seq[tuple[name: IdentExpr, valueType: Expression,
mutable: bool, isRef: bool, isPtr: bool, isPrivate: bool]] = @[]
var defaults: seq[Expression] = @[] var defaults: seq[Expression] = @[]
var generics: seq[tuple[name: IdentExpr, cond: Expression]] = @[] var generics: seq[tuple[name: IdentExpr, cond: Expression]] = @[]
var pragmas: seq[Pragma] = @[] 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): if self.match(LeftBracket):
self.parseGenerics(result) self.parseGenerics(result)
self.expect("=", "expecting '=' after type name") self.expect("=", "expecting '=' after type name")
case self.step().kind: result.valueType = self.expression()
of Ref:
isRef = true
echo self.peek()
self.expect(Object, "invalid syntax")
of Object:
discard
else:
self.error("invalid syntax")
self.expect(LeftBrace, "expecting '{' after type declaration") self.expect(LeftBrace, "expecting '{' after type declaration")
if self.match(TokenType.Pragma): if self.match(TokenType.Pragma):
for pragma in self.parsePragmas(): for pragma in self.parsePragmas():
pragmas.add(pragma) pragmas.add(pragma)
var var
argName: IdentExpr argName: IdentExpr
argMutable: bool
argRef: bool
argPtr: bool
argPrivate: bool argPrivate: bool
argType: Expression argType: Expression
while not self.match(RightBrace) and not self.done(): while not self.match(RightBrace) and not self.done():
argRef = false
argPtr = false
argMutable = false
self.expect(Identifier, "expecting field name") self.expect(Identifier, "expecting field name")
argName = newIdentExpr(self.peek(-1)) argName = newIdentExpr(self.peek(-1))
argPrivate = not self.match("*") argPrivate = not self.match("*")
self.expect(":", "expecting ':' after field name") 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() argType = self.expression()
result.fields.add((argName, argType, argMutable, argRef, argPtr, argPrivate)) result.fields.add((argName, argType, argPrivate))
if self.match("="): if self.match("="):
result.defaults.add(self.expression()) result.defaults.add(self.expression())
self.expect(";", "expecting semicolon after field declaration") self.expect(";", "expecting semicolon after field declaration")
@ -1180,7 +1156,7 @@ proc typeDecl(self: Parser): TypeDecl =
proc declaration(self: Parser): Declaration = proc declaration(self: Parser): Declaration =
## Parses declarations ## Parses declarations
case self.peek().kind: case self.peek().kind:
of Var, Const, Let: of TokenType.Var, Const, Let:
let keyword = self.step() let keyword = self.step()
result = self.varDecl(isLet = keyword.kind == Let, result = self.varDecl(isLet = keyword.kind == Let,
isConst = keyword.kind == Const) isConst = keyword.kind == Const)

View File

@ -400,7 +400,7 @@ proc fillSymbolTable(tokenizer: Lexer) =
tokenizer.symbols.addKeyword("assert", TokenType.Assert) tokenizer.symbols.addKeyword("assert", TokenType.Assert)
tokenizer.symbols.addKeyword("const", Const) tokenizer.symbols.addKeyword("const", Const)
tokenizer.symbols.addKeyword("let", Let) tokenizer.symbols.addKeyword("let", Let)
tokenizer.symbols.addKeyword("var", Var) tokenizer.symbols.addKeyword("var", TokenType.Var)
tokenizer.symbols.addKeyword("import", Import) tokenizer.symbols.addKeyword("import", Import)
tokenizer.symbols.addKeyword("yield", TokenType.Yield) tokenizer.symbols.addKeyword("yield", TokenType.Yield)
tokenizer.symbols.addKeyword("return", TokenType.Return) tokenizer.symbols.addKeyword("return", TokenType.Return)
@ -415,8 +415,8 @@ proc fillSymbolTable(tokenizer: Lexer) =
tokenizer.symbols.addKeyword("nil", TokenType.Nil) tokenizer.symbols.addKeyword("nil", TokenType.Nil)
tokenizer.symbols.addKeyword("true", True) tokenizer.symbols.addKeyword("true", True)
tokenizer.symbols.addKeyword("false", False) tokenizer.symbols.addKeyword("false", False)
tokenizer.symbols.addKeyword("ref", Ref) tokenizer.symbols.addKeyword("ref", TokenType.Ref)
tokenizer.symbols.addKeyword("ptr", Ptr) tokenizer.symbols.addKeyword("ptr", TokenType.Ptr)
for sym in [">", "<", "=", "~", "/", "+", "-", "_", "*", "?", "@", ":"]: for sym in [">", "<", "=", "~", "/", "+", "-", "_", "*", "?", "@", ":"]:
tokenizer.symbols.addSymbol(sym, Symbol) tokenizer.symbols.addSymbol(sym, Symbol)

View File

@ -1,5 +1,11 @@
## Builtin arithmetic operators for Peon ## 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 { operator `+`(a, b: int): int {
#pragma[magic: "AddInt64", pure] #pragma[magic: "AddInt64", pure]

View File

@ -12,5 +12,5 @@ operator `+`(a, b: int): int {
getFunction()(5); getFunction()(5);
var x = `+`; var x = getFunction;
x(1, 2); x()(3);

24
tests/mutable.pn Normal file
View File

@ -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!