Skip to content

Commit

Permalink
Scope resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Jun 16, 2023
1 parent 0e040e6 commit 14f584b
Show file tree
Hide file tree
Showing 11 changed files with 78 additions and 54 deletions.
9 changes: 7 additions & 2 deletions data/features.no
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// imports
// use std::Display
// use std::io::{println}
// use std::Option::*

// type
type Unit

Expand Down Expand Up @@ -55,10 +60,10 @@ println("hello")
vec.fmt()

// alternative syntax
Vec2.fmt(vec)
Vec2::fmt(vec)

// method call syntax for ambiguous methods
Vec2.Display.fmt(vec)
Vec2::Display::fmt(vec)

// if
let d = if True { 4 } else { 2 + 6 }
Expand Down
20 changes: 11 additions & 9 deletions nois.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ statement ::= var-def | fn-def | kind-def | impl-def | type-def | retu
;
type-con-params ::= O-PAREN (field-def (COMMA field-def)*)? COMMA? C-PAREN
;
field-def ::= IDENTIFIER type-annot
field-def ::= identifier type-annot
;
type-con-list ::= O-BRACE (type-con (COMMA type-con)* COMMA?)? C-BRACE
;
type-con ::= IDENTIFIER type-con-params?
type-con ::= identifier type-con-params?
;
return-stmt ::= RETURN-KEYWORD expr?
;
Expand All @@ -39,7 +39,7 @@ statement ::= var-def | fn-def | kind-def | impl-def | type-def | retu
| CHAR
| INT
| FLOAT
| IDENTIFIER
| identifier
;
infix-op ::= add-op | sub-op | mult-op | div-op | exp-op | mod-op | access-op | eq-op | ne-op
| ge-op | le-op | gt-op | lt-op | and-op | or-op | assign-op;
Expand Down Expand Up @@ -74,7 +74,9 @@ statement ::= var-def | fn-def | kind-def | impl-def | type-def | retu
;
con-op ::= O-PAREN (field-init (COMMA field-init)*)? COMMA? C-PAREN
;
field-init ::= IDENTIFIER COLON expr
field-init ::= NAME COLON expr
;
identifier ::= (NAME COLON COLON)* NAME
;
block ::= O-BRACE statement* C-BRACE
;
Expand All @@ -90,11 +92,11 @@ params ::= O-PAREN (param (COMMA param)*)? COMMA? C-PAREN
;
type-annot ::= COLON type-expr
;
type-expr ::= IDENTIFIER type-params?
type-expr ::= identifier type-params?
;
type-params ::= O-ANGLE (type-param (COMMA type-param)* COMMA?)? C-ANGLE
;
type-param ::= type-expr | IDENTIFIER COLON type-bounds
type-param ::= type-expr | NAME COLON type-bounds
;
type-bounds ::= type-expr (PLUS type-expr)*
;
Expand All @@ -112,13 +114,13 @@ match-expr ::= MATCH-KEYWORD expr match-clauses
;
guard ::= IF-KEYWORD expr
;
pattern ::= con-pattern | STRING | CHAR | prefix-op? (INT | FLOAT) | IDENTIFIER | hole
pattern ::= con-pattern | STRING | CHAR | prefix-op? (INT | FLOAT) | identifier | hole
;
con-pattern ::= IDENTIFIER con-pattern-params
con-pattern ::= identifier con-pattern-params
;
con-pattern-params::= O-PAREN (field-pattern (COMMA field-pattern)*)? COMMA? C-PAREN
;
field-pattern ::= IDENTIFIER (COLON pattern)? | spread-op
field-pattern ::= NAME (COLON pattern)? | spread-op
;
hole ::= UNDERSCORE
;
14 changes: 7 additions & 7 deletions src/lexer/lexer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ let main = (): Unit {
const tokens = tokenize(code)
expect(tokens.map(t => [t.kind, t.value])).toEqual([
['let-keyword', 'let'],
['identifier', 'main'],
['name', 'main'],
['equals', '='],
['o-paren', '('],
['c-paren', ')'],
['colon', ':'],
['identifier', 'Unit'],
['name', 'Unit'],
['o-brace', '{'],
['newline', '\n'],
['identifier', 'print'],
['name', 'print'],
['o-paren', '('],
['int', '4'],
['c-paren', ')'],
Expand Down Expand Up @@ -163,19 +163,19 @@ let main = (): Unit {
expect(tokens.map(t => [t.kind, t.value])).toEqual([
['int', '1'],
['plus', '+'],
['identifier', 'call'],
['name', 'call'],
['o-paren', '('],
['string', '"str"'],
['c-paren', ')'],
['period', '.'],
['identifier', 'ok'],
['name', 'ok'],
['o-paren', '('],
['c-paren', ')'],
['slash', '/'],
['o-paren', '('],
['int', '12'],
['minus', '-'],
['identifier', 'a'],
['name', 'a'],
['o-paren', '('],
['c-paren', ')'],
['c-paren', ')'],
Expand All @@ -185,7 +185,7 @@ let main = (): Unit {

it('tokenize unknown literal', () => {
expect(tokenize(`hello ~~~ 123`)).toEqual([
{ kind: 'identifier', value: 'hello', location: { start: 0, end: 4 } },
{ kind: 'name', value: 'hello', location: { start: 0, end: 4 } },
{ kind: 'unknown', value: '~~~', location: { start: 6, end: 8 } },
{ kind: 'int', value: '123', location: { start: 10, end: 12 } },
{ kind: 'eof', value: '', location: { start: 13, end: 13 } }
Expand Down
12 changes: 6 additions & 6 deletions src/lexer/lexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const lexerKeywordKinds = [
'match-keyword'
]

export const lexerDynamicKinds = ['identifier', 'string', 'char', 'int', 'float']
export const lexerDynamicKinds = ['name', 'string', 'char', 'int', 'float']

const lexerParseIndependentKinds = ['newline', 'comment']

Expand Down Expand Up @@ -137,7 +137,7 @@ export const tokenize = (code: String): ParseToken[] => {
continue
}

const fns = [parseFloat, parseInt, parseComment, parseNewline, parseConstToken, parseIdentifier,
const fns = [parseFloat, parseInt, parseComment, parseNewline, parseConstToken, parseName,
parseCharLiteral, parseStringLiteral]

let parsed = false
Expand Down Expand Up @@ -218,15 +218,15 @@ const parseConstToken = (chars: string[], tokens: ParseToken[], pos: { pos: numb
return false
}

const parseIdentifier = (chars: string[], tokens: ParseToken[], pos: { pos: number }): boolean => {
const parseName = (chars: string[], tokens: ParseToken[], pos: { pos: number }): boolean => {
if (isAlpha(chars[pos.pos])) {
const start = pos.pos
const identifier: string[] = []
const name: string[] = []
while (isAlpha(chars[pos.pos]) || isNumeric(chars[pos.pos])) {
identifier.push(chars[pos.pos])
name.push(chars[pos.pos])
pos.pos++
}
tokens.push(createToken('identifier', identifier.join(''), pos, start))
tokens.push(createToken('name', name.join(''), pos, start))
return true
}
return false
Expand Down
23 changes: 19 additions & 4 deletions src/parser/fns/expr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ export const parseSubExpr = (parser: Parser): void => {

/**
* operand ::= if-expr | match-expr | closure-expr | O-PAREN expr C-PAREN | list-expr | STRING | CHAR | INT | FLOAT
* | IDENTIFIER | type-expr
* | identifier | type-expr
*/
export const parseOperand = (parser: Parser): void => {
const dynamicTokens: TokenKind[] = ['string', 'char', 'int', 'float', 'identifier']
const dynamicTokens: TokenKind[] = ['string', 'char', 'int', 'float']

const mark = parser.open()
if (parser.at('if-keyword')) {
Expand All @@ -66,6 +66,8 @@ export const parseOperand = (parser: Parser): void => {
parser.expect('c-paren')
} else if (parser.at('o-bracket')) {
parseListExpr(parser)
} else if (parser.at('name')) {
parseIdentifier(parser)
} else if (parser.atAny(dynamicTokens)) {
parser.expectAny(dynamicTokens)
} else {
Expand All @@ -92,11 +94,11 @@ export const parseListExpr = (parser: Parser): void => {
}

/**
* type-expr ::= IDENTIFIER type-params?
* type-expr ::= identifier type-params?
*/
export const parseTypeExpr = (parser: Parser): void => {
const mark = parser.open()
parser.expect('identifier')
parseIdentifier(parser)
if (parser.at('o-angle')) {
parseTypeParams(parser)
}
Expand Down Expand Up @@ -141,3 +143,16 @@ export const parseForExpr = (parser: Parser): void => {
parser.close(mark, 'for-expr')
}

/**
* identifier ::= (NAME COLON COLON)* NAME
*/
export const parseIdentifier = (parser: Parser): void => {
const mark = parser.open()
while (parser.nth(1) === 'colon' && parser.nth(2) === 'colon' && !parser.eof()) {
parser.expect('name')
parser.expect('colon')
parser.expect('colon')
}
parser.expect('name')
parser.close(mark, 'identifier')
}
14 changes: 7 additions & 7 deletions src/parser/fns/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { TokenKind } from '../../lexer/lexer'
import { Parser } from '../parser'
import { parseExpr, parseTypeExpr } from './expr'
import { parseExpr, parseIdentifier, parseTypeExpr } from './expr'
import { parseBlock, parseStatement } from './statement'
import { parsePattern } from './match'

export const prefixOpFirstTokens: TokenKind[] = ['excl', 'minus', 'period', 'plus']
export const postfixOpFirstTokens: TokenKind[] = ['o-paren']
export const infixOpFirstTokens: TokenKind[] = ['ampersand', 'asterisk', 'c-angle', 'caret', 'equals', 'excl', 'minus',
'o-angle', 'percent', 'period', 'pipe', 'plus', 'slash']
export const exprFirstTokens: TokenKind[] = ['char', 'identifier', 'if-keyword', 'while-keyword', 'for-keyword',
export const exprFirstTokens: TokenKind[] = ['char', 'name', 'if-keyword', 'while-keyword', 'for-keyword',
'match-keyword', 'int', 'float', 'o-bracket', 'o-paren', 'string', ...prefixOpFirstTokens]
export const paramFirstTokens: TokenKind[] = ['identifier']
export const paramFirstTokens: TokenKind[] = ['name']

/**
* module ::= statement*
Expand Down Expand Up @@ -91,11 +91,11 @@ export const parseConOp = (parser: Parser): void => {
}

/**
* field-init ::= IDENTIFIER COLON expr
* field-init ::= NAME COLON expr
*/
export const parseFieldInit = (parser: Parser): void => {
const mark = parser.open()
parser.expect('identifier')
parser.expect('name')
parser.expect('colon')
parseExpr(parser)
parser.close(mark, 'field-init')
Expand Down Expand Up @@ -156,12 +156,12 @@ export const parseTypeParams = (parser: Parser): void => {
}

/**
* type-param ::= type-expr | IDENTIFIER COLON type-bounds
* type-param ::= type-expr | identifier COLON type-bounds
*/
export const parseTypeParam = (parser: Parser): void => {
const mark = parser.open()
if (parser.nth(1) === 'colon') {
parser.expect('identifier')
parseIdentifier(parser)
parser.expect('colon')
parseTypeBounds(parser)
} else {
Expand Down
19 changes: 10 additions & 9 deletions src/parser/fns/match.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Parser } from '../parser'
import { parseExpr } from './expr'
import { parseExpr, parseIdentifier } from './expr'
import { parseBlock } from './statement'
import { prefixOpFirstTokens } from './index'
import { parsePrefixOp, parseSpreadOp } from './op'
Expand Down Expand Up @@ -60,11 +60,11 @@ export const parseGuard = (parser: Parser): void => {
}

/**
* pattern ::= con-pattern | STRING | CHAR | prefix-op? (INT | FLOAT) | IDENTIFIER | hole
* pattern ::= con-pattern | STRING | CHAR | prefix-op? (INT | FLOAT) | identifier | hole
*/
export const parsePattern = (parser: Parser): void => {
const mark = parser.open()
if (parser.at('identifier') && parser.nth(1) === 'o-paren') {
if (parser.at('name') && parser.nth(1) === 'o-paren') {
parseConPattern(parser)
} else if (parser.consume('string')) {
} else if (parser.consume('char')) {
Expand All @@ -75,7 +75,8 @@ export const parsePattern = (parser: Parser): void => {
if (parser.consume('int')) {
} else if (parser.consume('float')) {
}
} else if (parser.consume('identifier')) {
} else if (parser.at('name')) {
parseIdentifier(parser)
} else if (parser.at('underscore')) {
parseHole(parser)
} else {
Expand All @@ -85,11 +86,11 @@ export const parsePattern = (parser: Parser): void => {
}

/**
* con-pattern ::= IDENTIFIER con-pattern-params
* con-pattern ::= identifier con-pattern-params
*/
export const parseConPattern = (parser: Parser): void => {
const mark = parser.open()
parser.expect('identifier')
parseIdentifier(parser)
parseConPatternParams(parser)
parser.close(mark, 'con-pattern')
}
Expand All @@ -111,12 +112,12 @@ export const parseConPatternParams = (parser: Parser): void => {
}

/**
* field-pattern ::= IDENTIFIER (COLON pattern) | spread-op
* field-pattern ::= NAME (COLON pattern) | spread-op
*/
export const parseFieldPattern = (parser: Parser): void => {
const mark = parser.open()
if (parser.at('identifier')) {
parser.expect('identifier')
if (parser.at('name')) {
parser.expect('name')
if (parser.consume('colon')) {
parsePattern(parser)
}
Expand Down
2 changes: 1 addition & 1 deletion src/parser/fns/op.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export const parseSpreadOp = (parser: Parser): void => {
*/
export const parsePostfixOp = (parser: Parser): void => {
const mark = parser.open()
if (parser.at('o-paren') && parser.nth(1) === 'identifier' && parser.nth(2) === 'colon') {
if (parser.at('o-paren') && parser.nth(1) === 'name' && parser.nth(2) === 'colon') {
parseConOp(parser)
} else if (parser.at('o-paren')) {
parseCallOp(parser)
Expand Down
10 changes: 5 additions & 5 deletions src/parser/fns/type-def.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Parser } from '../parser'
import { parseTypeExpr } from './expr'
import { parseIdentifier, parseTypeExpr } from './expr'
import { paramFirstTokens, parseTypeAnnot } from './index'

/**
Expand Down Expand Up @@ -34,11 +34,11 @@ export const parseTypeConParams = (parser: Parser): void => {
}

/**
* field-def ::= IDENTIFIER type-annot
* field-def ::= identifier type-annot
*/
export const parseFieldDef = (parser: Parser): void => {
const mark = parser.open()
parser.expect('identifier')
parseIdentifier(parser)
parseTypeAnnot(parser)
parser.close(mark, 'field-def')
}
Expand All @@ -60,11 +60,11 @@ export const parseTypeConList = (parser: Parser): void => {
}

/**
* type-con ::= IDENTIFIER con-params?
* type-con ::= identifier con-params?
*/
export const parseTypeCon = (parser: Parser): void => {
const mark = parser.open()
parser.expect('identifier')
parseIdentifier(parser)
if (parser.at('o-paren')) {
parseTypeConParams(parser)
}
Expand Down
Loading

0 comments on commit 14f584b

Please sign in to comment.