diff --git a/nois.bnf b/nois.bnf index 34e8e326..eceb17b8 100644 --- a/nois.bnf +++ b/nois.bnf @@ -44,9 +44,7 @@ statement ::= var-def | fn-def | trait-def | impl-def | type-def | r ; sub-expr ::= operand postfix-op* ; - operand ::= if-expr - | if-let-expr - | while-expr + operand ::= while-expr | for-expr | match-expr | closure-expr @@ -114,10 +112,6 @@ list-expr ::= O-BRACKET (expr (COMMA expr)*)? COMMA? C-BRACKET ; type-annot ::= COLON type ; -if-expr ::= IF-KEYWORD expr block (ELSE-KEYWORD block)? - ; -if-let-expr ::= IF-KEYWORD LET-KEYWORD pattern EQUALS expr block (ELSE-KEYWORD block)? - ; while-expr ::= WHILE-KEYWORD expr block ; for-expr ::= FOR-KEYWORD pattern IN-KEYWORD expr block diff --git a/src/ast/index.ts b/src/ast/index.ts index fcdf7f47..ae086958 100644 --- a/src/ast/index.ts +++ b/src/ast/index.ts @@ -133,7 +133,6 @@ export const astExprKinds = [ 'binary-expr', 'closure-expr', 'list-expr', - 'if-let-expr', 'while-expr', 'for-expr', 'match-expr' @@ -182,7 +181,6 @@ export const astKinds = [ 'type-bounds', 'fn-type', 'generic', - 'if-expr', 'match-clause', 'pattern', 'con-pattern', diff --git a/src/ast/operand.ts b/src/ast/operand.ts index f064dc22..b437e453 100644 --- a/src/ast/operand.ts +++ b/src/ast/operand.ts @@ -2,7 +2,6 @@ import { LexerToken } from '../lexer/lexer' import { ParseNode, ParseTree, filterNonAstNodes } from '../parser' import { nameLikeTokens } from '../parser/fns' import { Context, Definition } from '../scope' -import { VirtualIdentifierMatch } from '../scope/vid' import { Virtual } from '../semantic' import { assert } from '../util/todo' import { Expr, buildExpr } from './expr' @@ -12,8 +11,6 @@ import { Block, buildBlock, buildStatement } from './statement' import { Type, buildType } from './type' export type Operand = ( - | IfExpr - | IfLetExpr | WhileExpr | ForExpr | MatchExpr @@ -33,10 +30,6 @@ export type Operand = ( export const buildOperand = (node: ParseNode, ctx: Context): Operand => { const n = filterNonAstNodes(node)[0] switch (n.kind) { - case 'if-expr': - return buildIfExpr(n, ctx) - case 'if-let-expr': - return buildIfLetExpr(n, ctx) case 'while-expr': return buildWhileExpr(n, ctx) case 'for-expr': @@ -71,49 +64,6 @@ export const identifierFromOperand = (operand: Operand): Identifier | undefined return undefined } -export type IfExpr = BaseAstNode & { - kind: 'if-expr' - condition: Expr - thenBlock: Block - elseBlock?: Block -} - -export const buildIfExpr = (node: ParseNode, ctx: Context): IfExpr => { - const nodes = filterNonAstNodes(node) - let idx = 0 - // skip if-keyword - idx++ - const condition = buildExpr(nodes[idx++], ctx) - const thenBlock = buildBlock(nodes[idx++], ctx) - // skip else keyword - idx++ - const elseBlock = nodes.at(idx) ? buildBlock(nodes[idx++], ctx) : undefined - return { kind: 'if-expr', parseNode: node, condition, thenBlock, elseBlock } -} - -export type IfLetExpr = BaseAstNode & { - kind: 'if-let-expr' - pattern: Pattern - expr: Expr - thenBlock: Block - elseBlock?: Block -} - -export const buildIfLetExpr = (node: ParseNode, ctx: Context): IfLetExpr => { - const nodes = filterNonAstNodes(node) - let idx = 0 - // skip if and let keywords - idx++ - idx++ - const pattern = buildPattern(nodes[idx++], ctx) - const expr = buildExpr(nodes[idx++], ctx) - const thenBlock = buildBlock(nodes[idx++], ctx) - // skip else keyword - idx++ - const elseBlock = nodes.at(idx) ? buildBlock(nodes[idx++], ctx) : undefined - return { kind: 'if-let-expr', parseNode: node, pattern, expr, thenBlock, elseBlock } -} - export type WhileExpr = BaseAstNode & { kind: 'while-expr' condition: Expr diff --git a/src/codegen/js/expr.ts b/src/codegen/js/expr.ts index bc160b09..f99a9b6a 100644 --- a/src/codegen/js/expr.ts +++ b/src/codegen/js/expr.ts @@ -8,7 +8,7 @@ import { operatorImplMap } from '../../semantic/op' import { ConcreteGeneric } from '../../typecheck' import { unreachable } from '../../util/todo' import { EmitNode, EmitToken, emitToken, emitTree, jsError, jsVariable } from './node' -import { emitBlock, emitBlockStatements } from './statement' +import { emitBlockStatements } from './statement' export type EmitExpr = { emit: EmitNode @@ -182,26 +182,6 @@ export const emitBinaryExpr = (binaryExpr: BinaryExpr, ctx: Context): EmitExpr = export const emitOperand = (operand: Operand, ctx: Context): EmitExpr => { const resultVar = nextVariable(ctx) switch (operand.kind) { - case 'if-expr': { - const { emit: cEmit, resultVar: cVar } = emitExpr(operand.condition, ctx) - const thenBlock = emitBlock(operand.thenBlock, ctx, resultVar) - const elseBlock = operand.elseBlock - ? emitTree([emitToken('else'), emitBlock(operand.elseBlock, ctx, resultVar)]) - : emitToken('') - return { - emit: emitTree([ - jsVariable(resultVar), - cEmit, - emitToken(`if(${extractValue(cVar)})`), - thenBlock, - elseBlock - ]), - resultVar - } - } - case 'if-let-expr': - // TODO - return { emit: jsError('if-let'), resultVar } case 'while-expr': { const { emit: cEmit, resultVar: cVar } = emitExpr(operand.condition, ctx) const { emit: cEndEmit, resultVar: cEndVar } = emitExpr(operand.condition, ctx) diff --git a/src/parser/fns/expr.ts b/src/parser/fns/expr.ts index 2ffb3479..7544bf7c 100644 --- a/src/parser/fns/expr.ts +++ b/src/parser/fns/expr.ts @@ -38,18 +38,14 @@ export const parseSubExpr = (parser: Parser): void => { } /** - * operand ::= if-expr | match-expr | closure-expr | O-PAREN expr C-PAREN | list-expr | STRING | CHAR | number | TRUE + * operand ::= match-expr | closure-expr | O-PAREN expr C-PAREN | list-expr | STRING | CHAR | number | TRUE * | FALSE | identifier | type */ export const parseOperand = (parser: Parser): void => { const dynamicTokens: TokenKind[] = ['char', 'int', 'float', 'bool'] const mark = parser.open() - if (parser.at('if-keyword') && parser.nth(1) === 'let-keyword') { - parseIfLetExpr(parser) - } else if (parser.at('if-keyword')) { - parseIfExpr(parser) - } else if (parser.at('while-keyword')) { + if (parser.at('while-keyword')) { parseWhileExpr(parser) } else if (parser.at('for-keyword')) { parseForExpr(parser) @@ -94,37 +90,6 @@ export const parseListExpr = (parser: Parser): void => { parser.close(mark, 'list-expr') } -/** - * if-expr ::= IF-KEYWORD expr block (ELSE-KEYWORD block)? - */ -export const parseIfExpr = (parser: Parser): void => { - const mark = parser.open() - parser.expect('if-keyword') - parseExpr(parser) - parseBlock(parser) - if (parser.consume('else-keyword')) { - parseBlock(parser) - } - parser.close(mark, 'if-expr') -} - -/** - * if-let-expr ::= IF-KEYWORD LET-KEYWORD pattern EQUALS expr block (ELSE-KEYWORD block)? - */ -export const parseIfLetExpr = (parser: Parser): void => { - const mark = parser.open() - parser.expect('if-keyword') - parser.expect('let-keyword') - parsePattern(parser) - parser.expect('equals') - parseExpr(parser) - parseBlock(parser) - if (parser.consume('else-keyword')) { - parseBlock(parser) - } - parser.close(mark, 'if-let-expr') -} - /** * while-expr ::= WHILE-KEYWORD expr block */ diff --git a/src/parser/index.ts b/src/parser/index.ts index 77c322bb..ee685d44 100644 --- a/src/parser/index.ts +++ b/src/parser/index.ts @@ -66,8 +66,6 @@ export const treeKinds = [ 'type-bounds', 'fn-type', 'fn-type-params', - 'if-expr', - 'if-let-expr', 'while-expr', 'for-expr', 'match-expr', diff --git a/src/parser/parser.spec.ts b/src/parser/parser.spec.ts index 1f9dc2d6..ac3c668f 100644 --- a/src/parser/parser.spec.ts +++ b/src/parser/parser.spec.ts @@ -161,53 +161,6 @@ describe('parser', () => { }) }) - describe('if-expr', () => { - it('general', () => { - const { tree, errors } = parse('if a { b } else { c }') - expect(errors).toEqual([]) - // biome-ignore format: compact - expect(tree).toEqual( -{ module: - [ { statement: - [ { expr: - [ { 'sub-expr': - [ { operand: - [ { 'if-expr': - [ { 'if-keyword': 'if' }, - { expr: [ { 'sub-expr': [ { operand: [ { identifier: [ { name: 'a' } ] } ] } ] } ] }, - { block: - [ { 'o-brace': '{' }, - { statement: [ { expr: [ { 'sub-expr': [ { operand: [ { identifier: [ { name: 'b' } ] } ] } ] } ] } ] }, - { 'c-brace': '}' } ] }, - { 'else-keyword': 'else' }, - { block: - [ { 'o-brace': '{' }, - { statement: [ { expr: [ { 'sub-expr': [ { operand: [ { identifier: [ { name: 'c' } ] } ] } ] } ] } ] }, - { 'c-brace': '}' } ] } ] } ] } ] } ] } ] } ] } - ) - }) - - it('mismatch paren', () => { - const { errors } = parse('if a { b) }') - expect(errors.length).toEqual(1) - expect(errors[0]).toEqual({ - expected: [], - got: { kind: 'c-paren', span: { end: 9, start: 8 }, value: ')' }, - message: 'expected statement' - }) - }) - - it('duplicate else clause', () => { - const { errors } = parse('if a { b } else { c } else { d }') - expect(errors.length).toEqual(2) - expect(errors[0]).toEqual({ - expected: [], - got: { kind: 'o-brace', span: { end: 28, start: 27 }, value: '{' }, - message: 'expected statement' - }) - }) - }) - describe('match', () => { it('list-pattern', () => { const { tree, errors } = parse('match a { [] {} [b, tail] {} }') diff --git a/src/phase/name-resolve.ts b/src/phase/name-resolve.ts index 655d0740..36769df2 100644 --- a/src/phase/name-resolve.ts +++ b/src/phase/name-resolve.ts @@ -44,14 +44,6 @@ export const resolveName = (node: AstNode, ctx: Context): void => { node.bounds.forEach(b => resolveName(b, ctx)) break } - case 'if-expr': { - resolveName(node.condition, ctx) - resolveName(node.thenBlock, ctx) - if (node.elseBlock) { - resolveName(node.elseBlock, ctx) - } - break - } case 'match-clause': { withScope(ctx, () => { node.patterns.forEach(p => resolveName(p, ctx)) diff --git a/src/semantic/expr.ts b/src/semantic/expr.ts index 911aeecf..485f38a3 100644 --- a/src/semantic/expr.ts +++ b/src/semantic/expr.ts @@ -3,7 +3,7 @@ import { Arg, AstNode } from '../ast' import { BinaryExpr, Expr, UnaryExpr } from '../ast/expr' import { MatchExpr } from '../ast/match' import { CallOp } from '../ast/op' -import { ClosureExpr, ForExpr, Identifier, IfExpr, IfLetExpr, ListExpr, Name, Operand, WhileExpr } from '../ast/operand' +import { ClosureExpr, ForExpr, Identifier, ListExpr, Name, Operand, WhileExpr } from '../ast/operand' import { FieldDef } from '../ast/type-def' import { Context, Scope, addError, enterScope, fnDefScope, instanceScope, leaveScope } from '../scope' import { bool, future, iter, iterable, show, string, unwrap } from '../scope/std' @@ -75,12 +75,6 @@ export const checkOperand = (operand: Operand, ctx: Context): void => { checkOperand(operand.operand, ctx) operand.type = operand.operand.type break - case 'if-expr': - checkIfExpr(operand, ctx) - break - case 'if-let-expr': - checkIfLetExpr(operand, ctx) - break case 'while-expr': checkWhileExpr(operand, ctx) break @@ -231,63 +225,6 @@ export const checkBinaryExpr = (binaryExpr: BinaryExpr, ctx: Context): void => { } } -export const checkIfExpr = (ifExpr: IfExpr, ctx: Context): void => { - const module = ctx.moduleStack.at(-1)! - const scope = module.scopeStack.at(-1)! - - checkExpr(ifExpr.condition, ctx) - const condType = ifExpr.condition.type ?? unknownType - if (!isAssignable(condType, bool, ctx)) { - addError(ctx, typeError(ctx, ifExpr.condition, condType, bool)) - } - - checkIfExprCommon(ifExpr, scope, ctx) -} - -export const checkIfLetExpr = (ifLetExpr: IfLetExpr, ctx: Context): void => { - const module = ctx.moduleStack.at(-1)! - const scopeOuter = module.scopeStack.at(-1)! - const scope: Scope = { - kind: 'block', - definitions: new Map(), - closures: [], - isLoop: false, - allBranchesReturned: false - } - enterScope(module, scope, ctx) - - checkExpr(ifLetExpr.expr, ctx) - assert(!!ifLetExpr.expr.type) - // pattern definitions should only be available in `then` block - checkPattern(ifLetExpr.pattern, ifLetExpr.expr.type!, ctx) - - checkIfExprCommon(ifLetExpr, scopeOuter, ctx) - - leaveScope(module, ctx) -} - -export const checkIfExprCommon = (ifExpr: IfExpr | IfLetExpr, scope: Scope, ctx: Context): void => { - const thenAbr = checkBlock(ifExpr.thenBlock, ctx) - if (ifExpr.elseBlock) { - const elseAbr = checkBlock(ifExpr.elseBlock, ctx) - - if (scope.kind === 'block' && thenAbr && elseAbr) { - scope.allBranchesReturned = true - } - - const thenType = ifExpr.thenBlock.type! - const elseType = ifExpr.elseBlock.type! - const combined = combine(thenType, elseType, ctx) - if (combined) { - ifExpr.type = combined - } else { - ifExpr.type = { kind: 'unknown-type', mismatchedBranches: { then: thenType, else: elseType } } - } - } else { - ifExpr.type = { kind: 'unknown-type', mismatchedBranches: { then: ifExpr.thenBlock.type! } } - } -} - export const checkWhileExpr = (whileExpr: WhileExpr, ctx: Context): void => { const module = ctx.moduleStack.at(-1)! const outerScope = module.scopeStack.at(-1)!