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/
nimblecache/
htmldocs/
tests/*.pbc # Peon bytecode files
peon
*.pbc # Peon bytecode files
bin/
.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:
- 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,5 +12,5 @@ operator `+`(a, b: int): int {
getFunction()(5);
var x = `+`;
x(1, 2);
var x = getFunction;
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!