Skip to content

Commit

Permalink
Pretty semantic errors
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Jun 18, 2023
1 parent 9d1b7b9 commit 864b35c
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 20 deletions.
23 changes: 18 additions & 5 deletions src/ast/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { buildStatement, buildUseExpr, Statement, UseExpr } from './statement'
import { buildPattern, Pattern } from './match'
import { NodeKind, ParseNode, ParseTree, treeKinds } from '../parser'
import { lexerDynamicKinds } from '../lexer/lexer'
import { lexerDynamicKinds, ParseToken } from '../lexer/lexer'
import { buildIdentifier, buildName, Identifier, Name } from './operand'
import { buildExpr, Expr } from './expr'
import { VirtualIdentifier } from '../scope'
import { Location } from '../location'
import { todo } from '../todo'
import { LocationRange } from '../location'

export interface AstNode<T extends AstNodeKind> {
kind: T
Expand Down Expand Up @@ -95,8 +94,22 @@ export const compactAstNode = (node: AstNode<any>): any => {
)
}

export const getAstLocation = (node: AstNode<any>): Location => {
return todo()
export const getAstLocationRange = (node: AstNode<any>): LocationRange => {
const leftmostNode = (node: ParseNode): ParseToken => {
if ('nodes' in node) {
return leftmostNode(node.nodes[0])
} else {
return node
}
}
const rightmostNode = (node: ParseNode): ParseToken => {
if ('nodes' in node) {
return rightmostNode(node.nodes.at(-1)!)
} else {
return node
}
}
return { start: leftmostNode(node.parseNode).location.start, end: rightmostNode(node.parseNode).location.end }
}

export interface Module extends AstNode<'module'> {
Expand Down
13 changes: 8 additions & 5 deletions src/error.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { indexToLocation, Location, prettyIndex, prettyLocation } from './location'
import { Location, locationToString, prettyLineAt } from './location'
import { Source } from './source'
import { ParseToken, TokenKind } from './lexer/lexer'
import { red } from './output'
Expand All @@ -18,9 +18,12 @@ export const prettySyntaxError = (error: SyntaxError): string => {
return red(`syntax error: ${msg}, got \`${error.got.kind}\``)
}

export const prettySourceMessage = (message: string, index: number, source: Source): string => {
const location = indexToLocation(index, source)
const locationStr = `${location ? `${source.filename}:${prettyLocation(location)}` : '<unknwon location>'}`
export const prettyError = (message: string): string => {
return red(message)
}

export const prettySourceMessage = (message: string, location: Location, source: Source): string => {
const locationStr = `${location ? `${source.filename}:${locationToString(location)}` : '<unknwon location>'}`
const locationMsg = `${' '.repeat(2)}at ${locationStr}`
return [message, locationMsg, '\n' + prettyIndex(index, source, 1) + '\n'].join('\n')
return [message, locationMsg, '\n' + prettyLineAt(location, source, 1) + '\n'].join('\n')
}
21 changes: 15 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { erroneousTokenKinds, tokenize } from './lexer/lexer'
import { existsSync, readFileSync } from 'fs'
import { join, resolve } from 'path'
import { Parser } from './parser/parser'
import { prettyLexerError, prettySourceMessage, prettySyntaxError } from './error'
import { prettyError, prettyLexerError, prettySourceMessage, prettySyntaxError } from './error'
import { parseModule } from './parser/fns'
import { buildModule, compactAstNode, getAstLocation } from './ast'
import { buildModule, compactAstNode, getAstLocationRange, } from './ast'
import { checkModule } from './semantic'
import { Context } from './scope'
import * as console from 'console'
import { indexToLocation } from './location'

const version = JSON.parse(readFileSync(join(__dirname, '..', 'package.json')).toString()).version

Expand All @@ -32,7 +33,9 @@ const tokens = tokenize(source.str)
const errorTokens = tokens.filter(t => erroneousTokenKinds.includes(t.kind))
if (errorTokens.length > 0) {
for (const t of errorTokens) {
console.error(prettySourceMessage(prettyLexerError(t), t.location.start, source))
console.error(
prettySourceMessage(prettyLexerError(t), indexToLocation(t.location.start, source)!, source)
)
}
process.exit(1)
}
Expand All @@ -43,12 +46,14 @@ const root = parser.buildTree()

if (parser.errors.length > 0) {
for (const error of parser.errors) {
console.error(prettySourceMessage(prettySyntaxError(error), error.got.location.start, source))
console.error(
prettySourceMessage(prettySyntaxError(error), indexToLocation(error.got.location.start, source)!, source)
)
}
process.exit(1)
}

const moduleAst = buildModule(root, {scope: [], name: 'test'})
const moduleAst = buildModule(root, { scope: [], name: 'test' })

console.dir(compactAstNode(moduleAst), { depth: null, colors: true, compact: true })

Expand All @@ -57,7 +62,11 @@ checkModule(moduleAst, ctx)

if (ctx.errors.length > 0) {
for (const error of ctx.errors) {
console.log(error)
console.error(prettySourceMessage(
prettyError(error.message),
indexToLocation(getAstLocationRange(error.node).start, source)!,
source
))
}
process.exit(1)
}
5 changes: 2 additions & 3 deletions src/location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ export const indexToLocation = (index: number, source: Source): Location | undef
return undefined
}

export const prettyIndex = (index: number, source: Source, context: number = 0): string => {
const start = indexToLocation(index, source)
export const prettyLineAt = (start: Location, source: Source, context: number = 0): string => {
if (!start) return '<outside of a file>'
const highlight = ' '.repeat(6 + start.column) + '^'
const linesBefore = range(0, Math.min(context, start.line)).map(i => start.line + i - context).map(i => prettyLine(i, source))
Expand All @@ -48,4 +47,4 @@ export const prettyLine = (lineIndex: number, source: Source): string => {
return lineNum + line
}

export const prettyLocation = (location: Location): string => `${location.line + 1}:${location.column + 1}`
export const locationToString = (location: Location): string => `${location.line + 1}:${location.column + 1}`
2 changes: 1 addition & 1 deletion src/semantic/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const checkBinaryExpr = (binaryExpr: BinaryExpr, ctx: Context): void => {
const impls = findImplsById(implId, ctx)
if (impls.length === 0) {
ctx.errors.push({
node: binaryExpr,
node: binaryExpr.binaryOp,
message: `no suitable impl \
${vidToString(implId)}(\
${virtualTypeToString(binaryExpr.lOperand.type!)}, \
Expand Down

0 comments on commit 864b35c

Please sign in to comment.