Skip to content

Commit

Permalink
Parser: rules
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Jun 8, 2023
1 parent ddea266 commit ffb37fc
Show file tree
Hide file tree
Showing 7 changed files with 435 additions and 76 deletions.
2 changes: 1 addition & 1 deletion data/hello.no
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
let main = (): Unit {
fn main(): Unit {
print("Hello, World!")
}
40 changes: 25 additions & 15 deletions nois.bnf
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
module ::= statement*
;
statement ::= variable-def | type-def | return-stmt | expr
statement ::= var-def | fn-def | type-def | return-stmt | expr
;
variable-def ::= LET-KEYWORD IDENTIFIER EQUALS expr
var-def ::= LET-KEYWORD IDENTIFIER type-annot? EQUALS expr
;
type-def ::= TYPE-KEYWORD IDENTIFIER TODO
fn-def ::= FN-KEYWORD IDENTIFIER O-PAREN params? C-PAREN type-annot? block?
;
type-def ::= TYPE-KEYWORD type-expr (constr-params | constr-list)
;
constr-params ::= O-PAREN params? C-PAREN
;
constr-list ::= O-BRACE (constructor (COMMA constructor)* COMMA?)? C-BRACE
;
constructor ::= IDENTIFIER constr-params?
;
return-stmt ::= RETURN-KEYWORD expr?
;
expr ::= sub-expr (infix-op sub-expr)*
;
sub-expr ::= prefix-op operand
| operand postfix-op?
sub-expr ::= prefix-op operand | operand postfix-op?
;
operand ::= if-expr
| fn-expr
| lambda-expr
| O-PAREN expr C-PAREN
| STRING
| CHAR
Expand Down Expand Up @@ -49,22 +56,25 @@ statement ::= variable-def | type-def | return-stmt | expr

postfix-op ::= call-op
;
call-op ::= O-PAREN args C-PAREN
call-op ::= args
;
args ::= O-PAREN (expr (COMMA expr)*)? COMMA? C-PAREN
;
lambda-expr ::= lambda-params type-annot? block
;
args ::= expr (COMMA expr)* COMMA?
lambda-params ::= PIPE (param (COMMA param)*)? COMMA? PIPE
;
fn-expr ::= PIPE params PIPE COLON type-expr block
params ::= O-PAREN (param (COMMA param)*)? COMMA? C-PAREN
;
params ::= param (COMMA param)* COMMA?
param ::= IDENTIFIER type-annot?
;
param ::= IDENTIFIER COLON type-expr
block ::= O-BRACE statement* C-BRACE | O-BRACE C-BRACE
;
block ::= O-BRACE statement* C-BRACE
| O-BRACE C-BRACE
type-annot ::= COLON type-expr
;
type-expr ::= IDENTIFIER (O-ANGLE type-params C-ANGLE)?
type-expr ::= IDENTIFIER type-params?
;
type-params ::= type-expr (COMMA type-expr)* COMMA?
type-params ::= O-ANGLE (type-expr (COMMA type-expr)* COMMA?)? C-ANGLE
;
if-expr ::= IF-KEYWORD expr block (ELSE-KEYWORD block)?
;
15 changes: 9 additions & 6 deletions src/error.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import { indexToLocation, LocationRange, prettyIndex, prettyLocation } from './location'
import { indexToLocation, prettyIndex, prettyLocation } from './location'
import { Source } from './source'
import { ParseToken, TokenKind } from './lexer/lexer'

export interface SyntaxErrorInfo {
export interface SyntaxError {
expected: TokenKind[],
got: TokenKind,
location: LocationRange
got: ParseToken,
message?: string
}

export const prettyLexerError = (token: ParseToken): string => {
return `lexer error: unknown token \`${token.value}\``
}

export const prettySyntaxError = (error: SyntaxErrorInfo): string => {
return `syntax error: expected \`${error.expected}\`, got \`${error.got}\``
export const prettySyntaxError = (error: SyntaxError): string => {
if (error.message) {
return `syntax error: ${error.message}`
}
return `syntax error: expected \`${error.expected}\`, got \`${error.got.kind}\``
}

export const prettySourceMessage = (message: string, index: number, source: Source): string => {
Expand Down
19 changes: 12 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { tokenize } from './lexer/lexer'
import { readFileSync } from 'fs'
import { join, resolve } from 'path'
import { compactNode, parseModule, Parser } from './parser/parser'
import { prettySourceMessage, prettySyntaxError } from './error'


const version = JSON.parse(readFileSync(join(__dirname, '..', 'package.json')).toString()).version
Expand All @@ -18,10 +20,13 @@ if (!path) {
const source = { str: readFileSync(resolve(path)).toString(), filename: path }

const tokens = tokenize(source.str)
// const token = parse(tokens)
// if ('expected' in token) {
// console.error(prettySourceMessage(prettySyntaxError(token), token.location.start, source))
// process.exit(1)
// }
//
// console.dir(compactToken(flattenToken(token)), { depth: null, colors: true, compact: true })
const parser = new Parser(tokens)
parseModule(parser)
const root = parser.buildTree()

for (const error of parser.errors) {
console.error(prettySourceMessage(prettySyntaxError(error), error.got.location.start, source))
process.exit(1)
}

console.dir(compactNode(root), { depth: null, colors: true, compact: true })
2 changes: 2 additions & 0 deletions src/lexer/lexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const lexerTokenKinds = <const>[
'kind-keyword',
'impl-keyword',
'let-keyword',
'fn-keyword',
'if-keyword',
'else-keyword',
'return-keyword',
Expand Down Expand Up @@ -61,6 +62,7 @@ export const constTokenKindMap: Map<TokenKind, string> = new Map([
['return-keyword', 'return'],
['impl-keyword', 'impl'],
['let-keyword', 'let'],
['fn-keyword', 'fn'],

['o-paren', '('],
['c-paren', ')'],
Expand Down
28 changes: 27 additions & 1 deletion src/parser/parser.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,30 @@
import { compactNode, parseModule, Parser } from './parser'
import { tokenize } from '../lexer/lexer'
import { expect } from '@jest/globals'

describe('parser', () => {
it('buildTree', () => {

const parse = (code) => {
const p = new Parser(tokenize(code))
parseModule(p)
const tree = p.buildTree()
return { tree: compactNode(tree), errors: p.errors }
}

it('parse fn-def empty', () => {
const { tree, errors } = parse('fn main() {}')
expect(errors.length).toEqual(0)
expect(tree).toEqual({
'module': [{
'statement': [{
'fn-def': [
{ 'fn-keyword': 'fn' },
{ 'identifier': 'main' },
{ 'o-paren': '(' }, { 'c-paren': ')' },
{ 'block': [{ 'o-brace': '{' }, { 'c-brace': '}' }] }
]
}]
}]
})
})
})
Loading

0 comments on commit ffb37fc

Please sign in to comment.