Skip to content

Commit

Permalink
Syntax: await-op
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Apr 11, 2024
1 parent 1ef760a commit 6a2554a
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 23 deletions.
10 changes: 8 additions & 2 deletions nois.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ statement ::= var-def | fn-def | trait-def | impl-def | type-def | r
| bool
| identifier
;
infix-op ::= add-op | sub-op | mult-op | div-op | exp-op | mod-op | access-op | eq-op | ne-op
infix-op ::= add-op | sub-op | mult-op | div-op | exp-op | mod-op | eq-op | ne-op
| ge-op | le-op | gt-op | lt-op | and-op | or-op | assign-op;
add-op ::= PLUS;
sub-op ::= MINUS;
Expand All @@ -77,7 +77,11 @@ statement ::= var-def | fn-def | trait-def | impl-def | type-def | r
or-op ::= PIPE PIPE;
assign-op ::= EQUALS;

postfix-op ::= call-op | unwrap-op | bind-op
postfix-op ::= method-call-op | field-access-op | call-op | unwrap-op | bind-op | await-op
;
method-call-op ::= PERIOD NAME type-args? call-op
;
field-access-op ::= PERIOD NAME
;
call-op ::= O-PAREN (arg (COMMA arg)*)? COMMA? C-PAREN
;
Expand All @@ -87,6 +91,8 @@ statement ::= var-def | fn-def | trait-def | impl-def | type-def | r
;
bind-op ::= QMARK
;
await-op ::= PERIOD AWAIT-KEYWORD
;
identifier ::= (NAME COLON COLON)* NAME type-args?
;
type-args ::= O-ANGLE (type (COMMA type)* COMMA?)? C-ANGLE
Expand Down
10 changes: 8 additions & 2 deletions src/ast/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ export const astInfixOpKinds = <const>[
'div-op',
'exp-op',
'mod-op',
'access-op',
'eq-op',
'ne-op',
'ge-op',
Expand All @@ -51,7 +50,14 @@ export const astInfixOpKinds = <const>[
'assign-op'
]

export const astPostfixOpKinds = <const>['method-call-op', 'field-access-op', 'call-op', 'unwrap-op', 'bind-op']
export const astPostfixOpKinds = <const>[
'method-call-op',
'field-access-op',
'call-op',
'unwrap-op',
'bind-op',
'await-op'
]

export const astKinds = <const>[
'module',
Expand Down
11 changes: 6 additions & 5 deletions src/ast/op.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import { Arg, AstNode, AstNodeKind, buildArg } from './index'
import { Name, buildName } from './operand'
import { Type, buildType } from './type'

export type PostfixOp = MethodCallOp | FieldAccessOp | CallOp | UnwrapOp | BindOp
export type PostfixOp = MethodCallOp | FieldAccessOp | CallOp | UnwrapOp | BindOp | AwaitOp

export const isPostfixOp = (op: AstNode<AstNodeKind>): op is PostfixOp => {
return (
op.kind === 'method-call-op' ||
op.kind === 'field-access-op' ||
op.kind === 'call-op' ||
op.kind === 'unwrap-op' ||
op.kind === 'bind-op'
op.kind === 'bind-op' ||
op.kind === 'await-op'
)
}

Expand All @@ -28,6 +29,7 @@ export const buildPostfixOp = (node: ParseNode): PostfixOp => {
return buildCallOp(node)
case 'unwrap-op':
case 'bind-op':
case 'await-op':
return { kind: node.kind, parseNode: node }
default:
throw Error(`expected postfix-op, got ${node.kind}`)
Expand All @@ -41,7 +43,6 @@ export type BinaryOp = (
| DivOp
| ExpOp
| ModOp
| AccessOp
| EqOp
| NeOp
| GeOp
Expand Down Expand Up @@ -162,6 +163,8 @@ export interface UnwrapOp extends AstNode<'unwrap-op'> {}

export interface BindOp extends AstNode<'bind-op'> {}

export interface AwaitOp extends AstNode<'await-op'> {}

export interface AddOp extends AstNode<'add-op'> {}

export interface SubOp extends AstNode<'sub-op'> {}
Expand All @@ -174,8 +177,6 @@ export interface ExpOp extends AstNode<'exp-op'> {}

export interface ModOp extends AstNode<'mod-op'> {}

export interface AccessOp extends AstNode<'access-op'> {}

export interface EqOp extends AstNode<'eq-op'> {}

export interface NeOp extends AstNode<'ne-op'> {}
Expand Down
4 changes: 3 additions & 1 deletion src/lexer/lexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ export const lexerKeywordKinds = <const>[
'for-keyword',
'in-keyword',
'match-keyword',
'pub-keyword'
'pub-keyword',
'await-keyword',
]

export const lexerDynamicKinds = <const>['name', 'string-part', 'char', 'int', 'float', 'bool']
Expand Down Expand Up @@ -92,6 +93,7 @@ export const lexerKeywordMap: [TokenKind, string][] = [
['in-keyword', 'in'],
['match-keyword', 'match'],
['pub-keyword', 'pub'],
['await-keyword', 'await'],

['o-paren', '('],
['c-paren', ')'],
Expand Down
21 changes: 19 additions & 2 deletions src/parser/fns/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
import { Parser } from '..'
import { TokenKind, lexerKeywordKinds } from '../../lexer/lexer'
import { TokenKind } from '../../lexer/lexer'
import { parseBlock, parseParam, parseStatement, parseUseStmt } from './statement'
import { parseTypeAnnot } from './type'

/**
* Tokens that can be used as a name AST node depending on context.
* Includes 'name' itself and all keyword tokens
*/
export const nameLikeTokens: TokenKind[] = ['name', ...lexerKeywordKinds]
export const nameLikeTokens: TokenKind[] = [
'name',
'use-keyword',
'type-keyword',
'trait-keyword',
'impl-keyword',
'let-keyword',
'fn-keyword',
'if-keyword',
'else-keyword',
'return-keyword',
'break-keyword',
'while-keyword',
'for-keyword',
'in-keyword',
'match-keyword',
'pub-keyword'
]

export const infixOpFirstTokens: TokenKind[] = [
'ampersand',
Expand Down
7 changes: 5 additions & 2 deletions src/parser/fns/op.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,18 @@ export const parseInfixOp = (parser: Parser): void => {
}

/**
* postfix-op ::= call-op | unwrap-op | bind-op
* postfix-op ::= method-call-op | field-access-op | call-op | unwrap-op | bind-op | await-op
*/
export const parsePostfixOp = (parser: Parser): void => {
if (parser.at('o-paren')) {
parseCallOp(parser)
return
}
if (parser.at('period')) {
if (
if (parser.nth(1) === 'await-keyword') {
const mark = parser.open()
parser.close(mark, 'await-op')
} else if (
parser.nth(2) === 'o-paren' ||
(parser.nth(2) === 'o-angle' &&
parser.encounter('c-angle', [...nameLikeTokens, 'comma', 'o-angle', 'underscore'], 2))
Expand Down
1 change: 1 addition & 0 deletions src/parser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export const treeKinds = <const>[
'call-op',
'unwrap-op',
'bind-op',
'await-op',
'access-op',
'arg',
'closure-expr',
Expand Down
6 changes: 6 additions & 0 deletions src/scope/std.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { vidFromString } from './util'

export const preludeVid = vidFromString('std::prelude')

// TODO: lack of std types must throw notFoundError
export const bool: VidType = { kind: 'vid-type', identifier: vidFromString('std::bool::Bool'), typeArgs: [] }
export const string: VidType = { kind: 'vid-type', identifier: vidFromString('std::string::String'), typeArgs: [] }

Expand All @@ -21,3 +22,8 @@ export const unwrap: VidType = {
identifier: vidFromString('std::unwrap::Unwrap'),
typeArgs: [holeType]
}
export const future: VidType = {
kind: 'vid-type',
identifier: vidFromString('std::future::Future'),
typeArgs: [holeType]
}
46 changes: 37 additions & 9 deletions src/semantic/expr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { MatchExpr } from '../ast/match'
import { CallOp } from '../ast/op'
import { ClosureExpr, ForExpr, Identifier, IfExpr, IfLetExpr, ListExpr, Name, Operand, WhileExpr } from '../ast/operand'
import { Context, Scope, addError, fnDefScope, instanceScope } from '../scope'
import { bool, iter, iterable, show, string, unwrap } from '../scope/std'
import { bool, future, iter, iterable, show, string, unwrap } from '../scope/std'
import {
InstanceRelation,
getConcreteTrait,
Expand All @@ -15,7 +15,7 @@ import {
typeDefToVirtualType
} from '../scope/trait'
import { idToVid, vidEq, vidFromString, vidToString } from '../scope/util'
import { MethodDef, VariantDef, VirtualIdentifierMatch, resolveVid, typeKinds } from '../scope/vid'
import { MethodDef, VariantDef, VirtualIdentifier, VirtualIdentifierMatch, resolveVid, typeKinds } from '../scope/vid'
import {
VidType,
VirtualFnType,
Expand Down Expand Up @@ -189,6 +189,9 @@ export const checkUnaryExpr = (unaryExpr: UnaryExpr, ctx: Context): void => {
case 'bind-op':
checkBind(unaryExpr, ctx)
return
case 'await-op':
checkAwait(unaryExpr, ctx)
return
}
}

Expand Down Expand Up @@ -674,7 +677,7 @@ export const checkCall_ = (call: CallOp, operand: Operand, args: Expr[], ctx: Co
export const checkUnwrap = (unaryExpr: UnaryExpr, ctx: Context): void => {
const operand = unaryExpr.operand
checkOperand(operand, ctx)
const unwrapType = findUnwrapInnerType(operand.type!, ctx)
const unwrapType = findTraitInnerType(operand.type!, unwrap.identifier, ctx)
if (!unwrapType) {
addError(ctx, typeError(ctx, unaryExpr, operand.type!, unwrap))
unaryExpr.type = unknownType
Expand All @@ -689,7 +692,7 @@ export const checkUnwrap = (unaryExpr: UnaryExpr, ctx: Context): void => {
export const checkBind = (unaryExpr: UnaryExpr, ctx: Context): void => {
const operand = unaryExpr.operand
checkOperand(operand, ctx)
const unwrapType = findUnwrapInnerType(operand.type!, ctx)
const unwrapType = findTraitInnerType(operand.type!, unwrap.identifier, ctx)
if (!unwrapType) {
addError(ctx, typeError(ctx, unaryExpr, operand.type!, unwrap))
unaryExpr.type = unknownType
Expand All @@ -708,17 +711,42 @@ export const checkBind = (unaryExpr: UnaryExpr, ctx: Context): void => {
upcast(operand, operand.type!, unwrap, ctx)
}

export const findUnwrapInnerType = (type: VirtualType, ctx: Context): VirtualType | undefined => {
const unwrapRel = ctx.impls.find(
export const checkAwait = (unaryExpr: UnaryExpr, ctx: Context): void => {
const operand = unaryExpr.operand
checkOperand(operand, ctx)
if (!(operand.type?.kind === 'vid-type' && vidEq(operand.type.identifier, future.identifier))) {
addError(ctx, typeError(ctx, unaryExpr.op, operand.type!, future))
unaryExpr.type = unknownType
return
}
const innerType = operand.type.typeArgs[0]
unaryExpr.type = innerType

const scope = fnDefScope(ctx)
if (!scope) {
addError(ctx, notInFnScopeError(ctx, unaryExpr.op))
return
}
scope.returns.push(operand)

upcast(operand, operand.type!, future, ctx)
}

export const findTraitInnerType = (
type: VirtualType,
trait: VirtualIdentifier,
ctx: Context
): VirtualType | undefined => {
const traitRel = ctx.impls.find(
i =>
vidEq(i.implDef.vid, unwrap.identifier) &&
vidEq(i.implDef.vid, trait) &&
// TODO: properly handle generics
isAssignable(type, replaceGenericsWithHoles(i.forType), ctx)
)
if (!unwrapRel) {
if (!traitRel) {
return undefined
}
return getConcreteTrait(type, unwrapRel, ctx)
return getConcreteTrait(type, traitRel, ctx)
}

export const variantCallRef = (operand: Operand, ctx: Context): VirtualIdentifierMatch<VariantDef> | undefined => {
Expand Down

0 comments on commit 6a2554a

Please sign in to comment.