Skip to content

Commit

Permalink
Int & float literals
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Jun 11, 2023
1 parent 8cd00c8 commit 8f4db61
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 43 deletions.
3 changes: 1 addition & 2 deletions data/features.no
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ fn pushIf<T>(flag: Bool, l: List<T>, item: T): Unit {

fn foo(): Unit {
// constructor
// TODO: floats
// let vec = Vec2(x: 2., y: 4.)
let vec = Vec2(x: 2., y: 4.)

// field accessor
vec.x
Expand Down
35 changes: 25 additions & 10 deletions src/lexer/lexer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ let main = (): Unit {
['newline', '\n'],
['identifier', 'print'],
['o-paren', '('],
['number', '4'],
['int', '4'],
['c-paren', ')'],
['newline', '\n'],
['c-brace', '}'],
Expand All @@ -31,11 +31,26 @@ let main = (): Unit {
expect(tokens.at(-1)!.location).toEqual({ start: 33, end: 33 })
})

it('tokenize number literal simple', () => {
expect(tokenize('14')).toEqual([
{ kind: 'number', value: '14', location: { start: 0, end: 1 } },
{ kind: 'eof', value: '', location: { start: 2, end: 2 } }
])
describe('tokenize int', () => {
it('tokenize int simple', () => {
expect(tokenize('14 2 0')).toEqual([
{ kind: 'int', value: '14', location: { start: 0, end: 1 } },
{ kind: 'int', value: '2', location: { start: 3, end: 3 } },
{ kind: 'int', value: '0', location: { start: 5, end: 5 } },
{ kind: 'eof', value: '', location: { start: 6, end: 6 } }
])
})
})

describe('tokenize float', () => {
it('tokenize float simple', () => {
expect(tokenize('14.0 2.0 0.0')).toEqual([
{ kind: 'float', value: '14.0', location: { start: 0, end: 3 } },
{ kind: 'float', value: '2.0', location: { start: 5, end: 7 } },
{ kind: 'float', value: '0.0', location: { start: 9, end: 11 } },
{ kind: 'eof', value: '', location: { start: 12, end: 12 } }
])
})
})

it('tokenize string literal', () => {
Expand All @@ -55,7 +70,7 @@ let main = (): Unit {
it('tokenize expression', () => {
const tokens = tokenize(`1+call("str").ok() / (12 - a())`)
expect(tokens.map(t => [t.kind, t.value])).toEqual([
['number', '1'],
['int', '1'],
['plus', '+'],
['identifier', 'call'],
['o-paren', '('],
Expand All @@ -67,7 +82,7 @@ let main = (): Unit {
['c-paren', ')'],
['slash', '/'],
['o-paren', '('],
['number', '12'],
['int', '12'],
['minus', '-'],
['identifier', 'a'],
['o-paren', '('],
Expand All @@ -81,7 +96,7 @@ let main = (): Unit {
expect(tokenize(`hello ~~~ 123`)).toEqual([
{ kind: 'identifier', value: 'hello', location: { start: 0, end: 4 } },
{ kind: 'unknown', value: '~~~', location: { start: 6, end: 8 } },
{ kind: 'number', value: '123', location: { start: 10, end: 12 } },
{ kind: 'int', value: '123', location: { start: 10, end: 12 } },
{ kind: 'eof', value: '', location: { start: 13, end: 13 } }
])
})
Expand All @@ -90,7 +105,7 @@ let main = (): Unit {
expect(tokenize(`//this is 4\n4`)).toEqual([
{ 'kind': 'comment', 'location': { 'end': 10, 'start': 0 }, 'value': '//this is 4' },
{ 'kind': 'newline', 'location': { 'end': 11, 'start': 11 }, 'value': '\n' },
{ 'kind': 'number', 'location': { 'end': 12, 'start': 12 }, 'value': '4' },
{ 'kind': 'int', 'location': { 'end': 12, 'start': 12 }, 'value': '4' },
{ 'kind': 'eof', 'location': { 'end': 13, 'start': 13 }, 'value': '' }
])
})
Expand Down
57 changes: 31 additions & 26 deletions src/lexer/lexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ export const lexerTokenKinds = <const>[
'identifier',
'string',
'char',
'number',
'int',
'float',

// parse independent
'newline',
Expand Down Expand Up @@ -97,6 +98,9 @@ export const constTokenKindMap: Map<TokenKind, string> = new Map([
['underscore', '_'],
])

const intRegex = /^\d+/
const floatRegex = /^((\d+\.\d*)|(\d*\.\d+)|(\d+e[+-]?\d+))/

/**
* Independent tokens are automatically advanced by parser by default
*/
Expand Down Expand Up @@ -128,8 +132,8 @@ export const tokenize = (code: String): ParseToken[] => {
continue
}

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

let parsed = false
for (const f of fns) {
Expand Down Expand Up @@ -223,33 +227,33 @@ const parseIdentifier = (chars: string[], tokens: ParseToken[], pos: { pos: numb
return false
}

/**
*
* TODO: floats
* TODO: sign
* TODO: scientific notation
*
* @param chars
* @param tokens
* @param pos
*/
const parseNumberLiteral = (chars: string[], tokens: ParseToken[], pos: { pos: number }): boolean => {
if (isNumeric(chars[pos.pos])) {
const start = pos.pos
const number: string[] = []
while (isNumeric(chars[pos.pos])) {
number.push(chars[pos.pos])
pos.pos++
}
// TODO: verify literal
tokens.push(createToken('number', number.join(''), pos, start))
return true
}
return false
const parseFloat = (chars: string[], tokens: ParseToken[], pos: { pos: number }): boolean => {
const leftCode = chars.slice(pos.pos).join('')
const match = leftCode.match(floatRegex)
if (!match) return false

const float = match[0]
const start = pos.pos
pos.pos += float.length
tokens.push(createToken('float', float, pos, start))
return true
}

const parseInt = (chars: string[], tokens: ParseToken[], pos: { pos: number }): boolean => {
const leftCode = chars.slice(pos.pos).join('')
const match = leftCode.match(intRegex)
if (!match) return false

const int = match[0]
const start = pos.pos
pos.pos += int.length
tokens.push(createToken('int', int, pos, start))
return true
}

/**
* TODO: escape characters
* TODO: UTF characters
*
* @param chars
* @param tokens
Expand All @@ -275,6 +279,7 @@ const parseCharLiteral = (chars: string[], tokens: ParseToken[], pos: { pos: num

/**
* TODO: escape characters
* TODO: UTF characters
*
* @param chars
* @param tokens
Expand Down
8 changes: 3 additions & 5 deletions src/parser/parser-fns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ const prefixOpFirstTokens: TokenKind[] = ['excl', 'minus', 'period', 'plus']
const postfixOpFirstTokens: TokenKind[] = ['o-paren']
const infixOpFirstTokens: TokenKind[] = ['ampersand', 'asterisk', 'c-angle', 'caret', 'equals', 'excl', 'minus',
'o-angle', 'percent', 'period', 'pipe', 'plus', 'slash']
const exprFirstTokens: TokenKind[] = ['char', 'identifier', 'if-keyword', 'number', 'o-paren', 'string', ...prefixOpFirstTokens]
const exprFirstTokens: TokenKind[] = ['char', 'identifier', 'if-keyword', 'int', 'float', 'o-paren',
'string', ...prefixOpFirstTokens]
const statementFirstTokens: TokenKind[] = ['let-keyword', 'fn-keyword', 'kind-keyword', 'impl-keyword', 'type-keyword',
'return-keyword', 'type-keyword', ...exprFirstTokens]
const paramFirstTokens: TokenKind[] = ['identifier']

const exprFollowTokens: TokenKind[] = ['c-brace', 'c-paren', 'char', 'comma', 'excl', 'identifier', 'if-keyword',
'let-keyword', 'minus', 'number', 'o-brace', 'o-paren', 'period', 'plus', 'return-keyword', 'string', 'type-keyword']

/**
* module ::= statement*
*/
Expand Down Expand Up @@ -241,7 +239,7 @@ const parseSubExpr = (parser: Parser): void => {
* | IDENTIFIER | type-expr
*/
const parseOperand = (parser: Parser): void => {
const dynamicTokens: TokenKind[] = ['string', 'char', 'number', 'identifier']
const dynamicTokens: TokenKind[] = ['string', 'char', 'int', 'float', 'identifier']

const mark = parser.open()
if (parser.at('if-keyword')) {
Expand Down

0 comments on commit 8f4db61

Please sign in to comment.