Skip to content

Commit

Permalink
Ast: invalid chaining error; error handling refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Apr 26, 2024
1 parent 477ca67 commit 7f7b461
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 23 deletions.
6 changes: 4 additions & 2 deletions src/ast/expr.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ParseNode, filterNonAstNodes } from '../parser'
import { Context } from '../scope'
import { Context, addError } from '../scope'
import { Typed, Virtual } from '../semantic'
import { invalidOperatorChainError } from '../semantic/error'
import { assert } from '../util/todo'
import { AstNode } from './index'
import { BinaryOp, PostfixOp, associativityMap, buildBinaryOp, buildPostfixOp, precedenceMap } from './op'
Expand Down Expand Up @@ -49,7 +50,8 @@ export const buildExpr = (node: ParseNode, ctx: Context): Expr => {
const o1Assoc = associativityMap.get(o1.kind)!
const o2Assoc = associativityMap.get(o2.kind)!
if (o1Prec === o2Prec && o1Assoc === 'none' && o2Assoc === 'none') {
throw Error(`cannot chain operators \`${o1.kind}\` and \`${o2.kind}\``)
addError(ctx, invalidOperatorChainError(ctx, o1, o2))
break
}
if ((o1Assoc !== 'right' && o1Prec === o2Prec) || o1Prec < o2Prec) {
operatorStack.pop()
Expand Down
15 changes: 15 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
import { colorError, prettySourceMessage } from './error'
import { getSpan } from './parser'
import { Context } from './scope'

export const parseOption = (longName: string): string | undefined => {
const arg = process.argv.find(a => a.startsWith(`--${longName}`))
if (arg?.includes('=')) {
return arg.split('=')[1]
}
return arg !== undefined ? '' : undefined
}

export const reportErrors = (ctx: Context): void | never => {
if (ctx.errors.length > 0) {
for (const error of ctx.errors) {
console.error(
prettySourceMessage(colorError(error.message), getSpan(error.node.parseNode), error.source, error.notes)
)
}
process.exit(1)
}
}
22 changes: 6 additions & 16 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { existsSync, readFileSync, statSync } from 'fs'
import { basename, dirname, join } from 'path'
import { fileURLToPath } from 'url'
import { parseOption } from './cli'
import { parseOption, reportErrors } from './cli'
import { fromCmd } from './config'
import { colorError, colorWarning, prettySourceMessage } from './error'
import { colorWarning, prettySourceMessage } from './error'
import { Package } from './package'
import { buildModule } from './package/build'
import { emitPackage } from './package/emit'
Expand Down Expand Up @@ -111,6 +111,8 @@ if (isDir) {
lib = [std]
}

reportErrors(ctx)

const packages = [...lib, pkg]

const std = packages.find(pkg => pkg.name === 'std')
Expand All @@ -137,26 +139,14 @@ if (ctx.config.libCheck) {
}
assert(ctx.moduleStack.length === 0, ctx.moduleStack.length.toString())

if (ctx.errors.length > 0) {
for (const error of ctx.errors) {
console.error(
prettySourceMessage(
colorError(error.message),
getSpan(error.node.parseNode),
error.module.source,
error.notes
)
)
}
process.exit(1)
}
reportErrors(ctx)

for (const warning of ctx.warnings) {
console.error(
prettySourceMessage(
colorWarning(warning.message),
getSpan(warning.node.parseNode),
warning.module.source,
warning.source,
warning.notes
)
)
Expand Down
8 changes: 7 additions & 1 deletion src/package/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ import { Source } from '../source'
export const buildModule = (
source: Source,
vid: VirtualIdentifier,
// TODO: might be better off being separate `AstContext` with only errors and source
ctx: Context,
compiled = false
): Module | undefined => {
const dummyModule: Module = <any>{ source: source }
ctx.moduleStack.push(dummyModule)
const tokens = tokenize(source.code)
const errorTokens = tokens.filter(t => erroneousTokenKinds.includes(t.kind))
if (errorTokens.length > 0) {
Expand All @@ -34,5 +37,8 @@ export const buildModule = (
}

const mod = /mod\.no$/.test(source.filepath)
return buildModuleAst(root, vid, source, mod, ctx, compiled)
const ast = buildModuleAst(root, vid, source, mod, ctx, compiled)

ctx.moduleStack.pop()
return ast
}
18 changes: 14 additions & 4 deletions src/semantic/error.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import { Arg, AstNode, Module, Param } from '../ast'
import { Expr } from '../ast/expr'
import { MatchClause, MatchExpr, PatternExpr } from '../ast/match'
import { CallOp } from '../ast/op'
import { BinaryOp, CallOp } from '../ast/op'
import { Identifier, Name, Operand } from '../ast/operand'
import { FnDef, ImplDef, Statement, VarDef } from '../ast/statement'
import { Type } from '../ast/type'
import { FieldDef } from '../ast/type-def'
import { Context } from '../scope'
import { vidToString } from '../scope/util'
import { MethodDef } from '../scope/vid'
import { Source } from '../source'
import { VirtualFnType, VirtualType, virtualTypeToString } from '../typecheck'
import { unreachable } from '../util/todo'
import { assert, unreachable } from '../util/todo'
import { MatchTree, unmatchedPaths } from './exhaust'

export interface SemanticError {
code: number
module: Module
source: Source
node: AstNode<any>
message: string
notes: string[]
Expand All @@ -27,7 +28,10 @@ export const semanticError = (
node: AstNode<any>,
message: string,
notes: string[] = []
): SemanticError => ({ code, module: ctx.moduleStack.at(-1)!, node, message, notes })
): SemanticError => {
assert(ctx.moduleStack.length > 0)
return { code, source: ctx.moduleStack.at(-1)!.source, node, message, notes }
}

export const notFoundError = (
ctx: Context,
Expand Down Expand Up @@ -250,6 +254,12 @@ export const nonDestructurableTypeError = (
return semanticError(32, ctx, patternExpr, msg)
}

export const invalidOperatorChainError = (ctx: Context, o1: BinaryOp, o2: BinaryOp): SemanticError => {
const msg = `invalid operator chaining: \`${o1.kind}\` and \`${o2.kind}\``
// TODO: composite location spans
return semanticError(33, ctx, o1, msg)
}

export const unexpectedTypeError = (
ctx: Context,
node: AstNode<any>,
Expand Down

0 comments on commit 7f7b461

Please sign in to comment.