Skip to content

Commit 968f4ff

Browse files
0.0.9
1 parent 8fc0aa5 commit 968f4ff

File tree

7 files changed

+266
-2
lines changed

7 files changed

+266
-2
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## [0.0.9] - 2024-01-27
2+
3+
- Add JSON Scanner
4+
15
## [0.0.8] - 2024-01-20
26

37
- Add Built in Orderby inside js prototype

__tests__/json/simple.test.ts

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Scanner } from '../../src/json-parser/scanner'
2+
3+
describe('JSON Scanner Tests', () => {
4+
it('should scan a simple Empty JSON', () => {
5+
const source = `[{}]`
6+
7+
const scanner = new Scanner(source)
8+
scanner.scan()
9+
10+
expect(scanner.tokens).toHaveLength(5)
11+
})
12+
13+
it('should scan a simple JSON with strings ', () => {
14+
const source = `[{ "is_user": true , "age": 20 }]`
15+
16+
const scanner = new Scanner(source)
17+
scanner.scan()
18+
19+
expect(scanner.tokens).toHaveLength(12)
20+
})
21+
22+
it('should scan a JSON with inner schemas', () => {
23+
const source = `[{
24+
"user": {
25+
"name": "John",
26+
"age": 20
27+
}
28+
}]`
29+
30+
const scanner = new Scanner(source)
31+
scanner.scan()
32+
33+
expect(scanner.tokens).toHaveLength(16)
34+
})
35+
})

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "sql-js-data-mapper",
3-
"version": "0.0.8",
3+
"version": "0.0.9",
44
"description": "A tiny compiler that takes SQL and JS object and runs the sql on the object as if the object became a table in the database, using only js",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",
@@ -34,4 +34,4 @@
3434
"@rollup/plugin-babel": "^6.0.2",
3535
"tape": "^5.6.3"
3636
}
37-
}
37+
}

src/json-parser/parser.ts

Whitespace-only changes.

src/json-parser/scanner.ts

+176
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import { Token, TokenType } from './types'
2+
3+
export class Scanner {
4+
public tokens: Token[] = []
5+
private current: number = 0
6+
private start: number = 0
7+
private line: number = 1
8+
9+
constructor(private readonly source: string) {
10+
this.source = this.source.toLowerCase().trim()
11+
}
12+
13+
scan() {
14+
while (!this.isAtEnd) {
15+
this.start = this.current
16+
this.scanTokens()
17+
this.advance()
18+
}
19+
this.addEOF()
20+
}
21+
22+
scanTokens() {
23+
const char = this.peek
24+
switch (char) {
25+
case ' ':
26+
case '\r':
27+
case '\t':
28+
break
29+
case '\n':
30+
this.line++
31+
break
32+
case 't':
33+
while (this.isCurrentChar) {
34+
this.advance()
35+
}
36+
if (this.lexeme === 'true') {
37+
this.addToken(TokenType.TRUE)
38+
} else {
39+
throw new Error('Invalid token')
40+
}
41+
break
42+
case 'f':
43+
while (this.isCurrentChar) {
44+
this.advance()
45+
}
46+
if (this.lexeme === 'false') {
47+
this.addToken(TokenType.FALSE)
48+
} else {
49+
throw new Error('Invalid token')
50+
}
51+
break
52+
case '[':
53+
this.addToken(TokenType.SQUARE_RIGHT_BRACKET, '[')
54+
break
55+
case ']':
56+
this.addToken(TokenType.SQUARE_LEFT_BRACKET, ']')
57+
break
58+
case '{':
59+
this.addToken(TokenType.RIGHT_BRACKET, '{')
60+
break
61+
case '}':
62+
this.addToken(TokenType.LEFT_BRACKET, '}')
63+
break
64+
case ':':
65+
this.addToken(TokenType.COLON, ':')
66+
break
67+
case ',':
68+
this.addToken(TokenType.COMMA, ',')
69+
break
70+
case '.':
71+
this.addToken(TokenType.DOT, '.')
72+
break
73+
case '"':
74+
this.string()
75+
break
76+
default:
77+
if (this.isCurrentNumber) {
78+
this.number()
79+
break
80+
}
81+
throw new Error('Not implemented yet')
82+
}
83+
}
84+
85+
get peek(): string {
86+
return this.source[this.current]
87+
}
88+
89+
get peekNext(): string {
90+
return this.source[this.current + 1]
91+
}
92+
93+
get isAtEnd(): boolean {
94+
return this.current >= this.source.length
95+
}
96+
97+
get currentToken(): Token {
98+
return this.tokens[this.current - 1]
99+
}
100+
101+
get lexeme() {
102+
return this.source.substring(this.start, this.current)
103+
}
104+
105+
get isCurrentNumber() {
106+
return this.peek >= '0' && this.peek <= '9'
107+
}
108+
109+
get isCurrentChar() {
110+
return this.peek >= 'a' && this.peek <= 'z'
111+
}
112+
113+
match(lexeme: string) {
114+
if (this.peek === lexeme) {
115+
this.advance()
116+
return true
117+
}
118+
return false
119+
}
120+
121+
advance() {
122+
this.current++
123+
}
124+
125+
addToken(type: TokenType, initLexeme?: string) {
126+
this.tokens.push({
127+
lexeme: initLexeme ?? this.lexeme,
128+
line: this.line,
129+
end: this.current,
130+
start: this.start,
131+
type: type,
132+
})
133+
}
134+
135+
addEOF() {
136+
this.tokens.push(
137+
new Token({
138+
lexeme: '\0',
139+
end: this.source.length,
140+
start: this.source.length,
141+
line: this.line,
142+
type: TokenType.EOF,
143+
})
144+
)
145+
}
146+
147+
string() {
148+
this.advance()
149+
while (this.peek !== '"') {
150+
this.advance()
151+
}
152+
if (this.peekNext === ':') {
153+
this.addToken(
154+
TokenType.IDENTIFIER,
155+
this.source.substring(this.start + 1, this.current)
156+
)
157+
} else {
158+
this.addToken(
159+
TokenType.STRING,
160+
this.source.substring(this.start + 1, this.current)
161+
)
162+
}
163+
}
164+
165+
number() {
166+
while (this.isCurrentNumber) {
167+
this.advance()
168+
}
169+
if (this.peek === TokenType.DOT) {
170+
while (this.isCurrentNumber) {
171+
this.advance()
172+
}
173+
}
174+
this.addToken(TokenType.NUMBER)
175+
}
176+
}

src/json-parser/types/index.ts

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
export enum TokenType {
2+
DOT = 'DOT',
3+
COMMA = 'COMMA',
4+
STRING = 'STRING',
5+
NUMBER = 'NUMBER',
6+
EOF = 'EOF',
7+
LEFT_BRACKET = 'LEFT_BRACKET',
8+
RIGHT_BRACKET = 'RIGHT_BRACKET',
9+
COLON = 'COLON',
10+
BOOLEAN = 'BOOLEAN',
11+
SQUARE_LEFT_BRACKET = 'SQUARE_LEFT_BRACKET',
12+
SQUARE_RIGHT_BRACKET = 'SQUARE_RIGHT_BRACKET',
13+
IDENTIFIER = 'IDENTIFIER',
14+
15+
FALSE = 'FALSE',
16+
TRUE = 'TRUE',
17+
NULL = 'NULL',
18+
}
19+
20+
export class Token {
21+
public readonly type: TokenType
22+
public readonly lexeme: string
23+
public readonly line: number
24+
public readonly start: number
25+
public readonly end: number
26+
27+
constructor({
28+
type,
29+
lexeme,
30+
line,
31+
start,
32+
end,
33+
}: {
34+
type: TokenType
35+
lexeme: string
36+
line: number
37+
start: number
38+
end: number
39+
}) {
40+
this.type = type
41+
this.lexeme = lexeme
42+
this.line = line
43+
this.start = start
44+
this.end = end
45+
}
46+
}

src/tokens/tokentype.ts

+3
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,7 @@ export enum TokenType {
107107
SIMILAR = 'SIMILAR',
108108

109109
INTEGER = 'INTEGER',
110+
111+
SQUARE_LEFT_BRACKET = 'SQUARE_LEFT_BRACKET',
112+
SQUARE_RIGHT_BRACKET = 'SQUARE_RIGHT_BRACKET',
110113
}

0 commit comments

Comments
 (0)