add object declarations
This commit is contained in:
@ -1068,4 +1068,150 @@ describe('testing pipe operator special', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
test('object expression ast', () => {
|
||||||
|
const code = [
|
||||||
|
'const three = 3',
|
||||||
|
"const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}",
|
||||||
|
].join('\n')
|
||||||
|
const tokens = lexer(code)
|
||||||
|
const { body } = abstractSyntaxTree(tokens)
|
||||||
|
expect(body).toEqual([
|
||||||
|
{
|
||||||
|
type: 'VariableDeclaration',
|
||||||
|
start: 0,
|
||||||
|
end: 15,
|
||||||
|
kind: 'const',
|
||||||
|
declarations: [
|
||||||
|
{
|
||||||
|
type: 'VariableDeclarator',
|
||||||
|
start: 6,
|
||||||
|
end: 15,
|
||||||
|
id: {
|
||||||
|
type: 'Identifier',
|
||||||
|
start: 6,
|
||||||
|
end: 11,
|
||||||
|
name: 'three',
|
||||||
|
},
|
||||||
|
init: {
|
||||||
|
type: 'Literal',
|
||||||
|
start: 14,
|
||||||
|
end: 15,
|
||||||
|
value: 3,
|
||||||
|
raw: '3',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'VariableDeclaration',
|
||||||
|
start: 16,
|
||||||
|
end: 83,
|
||||||
|
kind: 'const',
|
||||||
|
declarations: [
|
||||||
|
{
|
||||||
|
type: 'VariableDeclarator',
|
||||||
|
start: 22,
|
||||||
|
end: 83,
|
||||||
|
id: {
|
||||||
|
type: 'Identifier',
|
||||||
|
start: 22,
|
||||||
|
end: 24,
|
||||||
|
name: 'yo',
|
||||||
|
},
|
||||||
|
init: {
|
||||||
|
type: 'ObjectExpression',
|
||||||
|
start: 27,
|
||||||
|
end: 83,
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
type: 'ObjectProperty',
|
||||||
|
start: 28,
|
||||||
|
end: 39,
|
||||||
|
key: {
|
||||||
|
type: 'Identifier',
|
||||||
|
start: 28,
|
||||||
|
end: 32,
|
||||||
|
name: 'aStr',
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: 'Literal',
|
||||||
|
start: 34,
|
||||||
|
end: 39,
|
||||||
|
value: 'str',
|
||||||
|
raw: "'str'",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'ObjectProperty',
|
||||||
|
start: 41,
|
||||||
|
end: 48,
|
||||||
|
key: {
|
||||||
|
type: 'Identifier',
|
||||||
|
start: 41,
|
||||||
|
end: 45,
|
||||||
|
name: 'anum',
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: 'Literal',
|
||||||
|
start: 47,
|
||||||
|
end: 48,
|
||||||
|
value: 2,
|
||||||
|
raw: '2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'ObjectProperty',
|
||||||
|
start: 50,
|
||||||
|
end: 67,
|
||||||
|
key: {
|
||||||
|
type: 'Identifier',
|
||||||
|
start: 50,
|
||||||
|
end: 60,
|
||||||
|
name: 'identifier',
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: 'Identifier',
|
||||||
|
start: 62,
|
||||||
|
end: 67,
|
||||||
|
name: 'three',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'ObjectProperty',
|
||||||
|
start: 69,
|
||||||
|
end: 82,
|
||||||
|
key: {
|
||||||
|
type: 'Identifier',
|
||||||
|
start: 69,
|
||||||
|
end: 75,
|
||||||
|
name: 'binExp',
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: 'BinaryExpression',
|
||||||
|
start: 77,
|
||||||
|
end: 82,
|
||||||
|
left: {
|
||||||
|
type: 'Literal',
|
||||||
|
start: 77,
|
||||||
|
end: 78,
|
||||||
|
value: 4,
|
||||||
|
raw: '4',
|
||||||
|
},
|
||||||
|
operator: '+',
|
||||||
|
right: {
|
||||||
|
type: 'Literal',
|
||||||
|
start: 81,
|
||||||
|
end: 82,
|
||||||
|
value: 5,
|
||||||
|
raw: '5',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -20,6 +20,7 @@ type syntaxType =
|
|||||||
| 'MemberExpression'
|
| 'MemberExpression'
|
||||||
| 'ArrayExpression'
|
| 'ArrayExpression'
|
||||||
| 'ObjectExpression'
|
| 'ObjectExpression'
|
||||||
|
| 'ObjectProperty'
|
||||||
| 'Property'
|
| 'Property'
|
||||||
| 'LogicalExpression'
|
| 'LogicalExpression'
|
||||||
| 'ConditionalExpression'
|
| 'ConditionalExpression'
|
||||||
@ -288,6 +289,7 @@ export type Value =
|
|||||||
| PipeExpression
|
| PipeExpression
|
||||||
| PipeSubstitution
|
| PipeSubstitution
|
||||||
| ArrayExpression
|
| ArrayExpression
|
||||||
|
| ObjectExpression
|
||||||
|
|
||||||
function makeValue(
|
function makeValue(
|
||||||
tokens: Token[],
|
tokens: Token[],
|
||||||
@ -379,6 +381,16 @@ function makeVariableDeclarators(
|
|||||||
} else {
|
} else {
|
||||||
throw new Error('TODO - handle expression with braces')
|
throw new Error('TODO - handle expression with braces')
|
||||||
}
|
}
|
||||||
|
} else if (
|
||||||
|
contentsStartToken.token.type === 'brace' &&
|
||||||
|
contentsStartToken.token.value === '{'
|
||||||
|
) {
|
||||||
|
const objectExpression = makeObjectExpression(
|
||||||
|
tokens,
|
||||||
|
contentsStartToken.index
|
||||||
|
)
|
||||||
|
init = objectExpression.expression
|
||||||
|
lastIndex = objectExpression.lastIndex
|
||||||
} else if (
|
} else if (
|
||||||
declarationToken.token.type === 'word' &&
|
declarationToken.token.type === 'word' &&
|
||||||
declarationToken.token.value === 'sketch'
|
declarationToken.token.value === 'sketch'
|
||||||
@ -531,6 +543,81 @@ function makeArrayExpression(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ObjectExpression extends GeneralStatement {
|
||||||
|
type: 'ObjectExpression'
|
||||||
|
properties: ObjectProperty[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ObjectProperty extends GeneralStatement {
|
||||||
|
type: 'ObjectProperty'
|
||||||
|
key: Identifier
|
||||||
|
value: Value
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeObjectExpression(
|
||||||
|
tokens: Token[],
|
||||||
|
index: number
|
||||||
|
): {
|
||||||
|
expression: ObjectExpression
|
||||||
|
lastIndex: number
|
||||||
|
} {
|
||||||
|
// should be called with the opening brace '{' index
|
||||||
|
const openingBraceToken = tokens[index]
|
||||||
|
const firstPropertyToken = nextMeaningfulToken(tokens, index)
|
||||||
|
const { properties, lastIndex } = makeObjectProperties(
|
||||||
|
tokens,
|
||||||
|
firstPropertyToken.index
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
expression: {
|
||||||
|
type: 'ObjectExpression',
|
||||||
|
start: openingBraceToken.start,
|
||||||
|
end: tokens[lastIndex].end,
|
||||||
|
properties,
|
||||||
|
},
|
||||||
|
lastIndex,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeObjectProperties(
|
||||||
|
tokens: Token[],
|
||||||
|
index: number,
|
||||||
|
previousProperties: ObjectProperty[] = []
|
||||||
|
): { properties: ObjectProperty[]; lastIndex: number } {
|
||||||
|
// should be called with the key after the opening brace '{'
|
||||||
|
const propertyKeyToken = tokens[index]
|
||||||
|
if (propertyKeyToken.type === 'brace' && propertyKeyToken.value === '}') {
|
||||||
|
return {
|
||||||
|
properties: previousProperties,
|
||||||
|
lastIndex: index,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const colonToken = nextMeaningfulToken(tokens, index)
|
||||||
|
const valueStartToken = nextMeaningfulToken(tokens, colonToken.index)
|
||||||
|
const value = makeValue(tokens, valueStartToken.index)
|
||||||
|
const commaOrClosingBraceToken = nextMeaningfulToken(tokens, value.lastIndex)
|
||||||
|
let objectProperty: ObjectProperty = {
|
||||||
|
type: 'ObjectProperty',
|
||||||
|
start: propertyKeyToken.start,
|
||||||
|
end: value.value.end,
|
||||||
|
key: makeIdentifier(tokens, index),
|
||||||
|
value: value.value,
|
||||||
|
}
|
||||||
|
const nextKeyToken = nextMeaningfulToken(
|
||||||
|
tokens,
|
||||||
|
commaOrClosingBraceToken.index
|
||||||
|
)
|
||||||
|
const nextKeyIndex =
|
||||||
|
commaOrClosingBraceToken.token.type === 'brace' &&
|
||||||
|
commaOrClosingBraceToken.token.value === '}'
|
||||||
|
? commaOrClosingBraceToken.index
|
||||||
|
: nextKeyToken.index
|
||||||
|
return makeObjectProperties(tokens, nextKeyIndex, [
|
||||||
|
...previousProperties,
|
||||||
|
objectProperty,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
export interface BinaryExpression extends GeneralStatement {
|
export interface BinaryExpression extends GeneralStatement {
|
||||||
type: 'BinaryExpression'
|
type: 'BinaryExpression'
|
||||||
operator: string
|
operator: string
|
||||||
|
@ -190,6 +190,22 @@ show(mySketch)
|
|||||||
yo: [1, '2', 3, 9],
|
yo: [1, '2', 3, 9],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
it('execute object expression', () => {
|
||||||
|
const code = [
|
||||||
|
'const three = 3',
|
||||||
|
"const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}",
|
||||||
|
].join('\n')
|
||||||
|
const { root } = exe(code)
|
||||||
|
expect(root).toEqual({
|
||||||
|
three: 3,
|
||||||
|
yo: {
|
||||||
|
aStr: 'str',
|
||||||
|
anum: 2,
|
||||||
|
identifier: 3,
|
||||||
|
binExp: 9,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// helpers
|
// helpers
|
||||||
|
@ -3,6 +3,7 @@ import {
|
|||||||
BinaryPart,
|
BinaryPart,
|
||||||
BinaryExpression,
|
BinaryExpression,
|
||||||
PipeExpression,
|
PipeExpression,
|
||||||
|
ObjectExpression,
|
||||||
} from './abstractSyntaxTree'
|
} from './abstractSyntaxTree'
|
||||||
import { Path, Transform, SketchGeo, sketchFns, ExtrudeGeo } from './sketch'
|
import { Path, Transform, SketchGeo, sketchFns, ExtrudeGeo } from './sketch'
|
||||||
import { BufferGeometry, Quaternion, Vector3 } from 'three'
|
import { BufferGeometry, Quaternion, Vector3 } from 'three'
|
||||||
@ -61,6 +62,9 @@ export const executor = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
} else if (declaration.init.type === 'ObjectExpression') {
|
||||||
|
const obj = executeObjectExpression(_programMemory, declaration.init)
|
||||||
|
_programMemory.root[variableName] = obj
|
||||||
} else if (declaration.init.type === 'SketchExpression') {
|
} else if (declaration.init.type === 'SketchExpression') {
|
||||||
const sketchInit = declaration.init
|
const sketchInit = declaration.init
|
||||||
const fnMemory: ProgramMemory = {
|
const fnMemory: ProgramMemory = {
|
||||||
@ -382,6 +386,41 @@ function executePipeBody(
|
|||||||
throw new Error('Invalid pipe expression')
|
throw new Error('Invalid pipe expression')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function executeObjectExpression(
|
||||||
|
_programMemory: ProgramMemory,
|
||||||
|
objExp: ObjectExpression
|
||||||
|
) {
|
||||||
|
const obj: { [key: string]: any } = {}
|
||||||
|
objExp.properties.forEach((property) => {
|
||||||
|
if (property.type === 'ObjectProperty') {
|
||||||
|
if (property.value.type === 'Literal') {
|
||||||
|
obj[property.key.name] = property.value.value
|
||||||
|
} else if (property.value.type === 'BinaryExpression') {
|
||||||
|
obj[property.key.name] = getBinaryExpressionResult(
|
||||||
|
property.value,
|
||||||
|
_programMemory
|
||||||
|
)
|
||||||
|
} else if (property.value.type === 'PipeExpression') {
|
||||||
|
obj[property.key.name] = getPipeExpressionResult(
|
||||||
|
property.value,
|
||||||
|
_programMemory
|
||||||
|
)
|
||||||
|
} else if (property.value.type === 'Identifier') {
|
||||||
|
obj[property.key.name] = _programMemory.root[property.value.name]
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
`Unexpected property type ${property.value.type} in object expression`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
`Unexpected property type ${property.type} in object expression`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
type SourceRange = [number, number]
|
type SourceRange = [number, number]
|
||||||
|
|
||||||
export type ViewerArtifact =
|
export type ViewerArtifact =
|
||||||
|
@ -122,6 +122,24 @@ show(mySketch)
|
|||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code.trim())
|
expect(recasted).toBe(code.trim())
|
||||||
})
|
})
|
||||||
|
it('recast long object exectution', () => {
|
||||||
|
const code = `const three = 3
|
||||||
|
const yo = {
|
||||||
|
aStr: 'str',
|
||||||
|
anum: 2,
|
||||||
|
identifier: three,
|
||||||
|
binExp: 4 + 5
|
||||||
|
}`
|
||||||
|
const { ast } = code2ast(code)
|
||||||
|
const recasted = recast(ast)
|
||||||
|
expect(recasted).toBe(code.trim())
|
||||||
|
})
|
||||||
|
it('recast short object exectution', () => {
|
||||||
|
const code = `const yo = { key: 'val' }`
|
||||||
|
const { ast } = code2ast(code)
|
||||||
|
const recasted = recast(ast)
|
||||||
|
expect(recasted).toBe(code.trim())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// helpers
|
// helpers
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
FunctionExpression,
|
FunctionExpression,
|
||||||
SketchExpression,
|
SketchExpression,
|
||||||
ArrayExpression,
|
ArrayExpression,
|
||||||
|
ObjectExpression,
|
||||||
} from './abstractSyntaxTree'
|
} from './abstractSyntaxTree'
|
||||||
|
|
||||||
export function recast(
|
export function recast(
|
||||||
@ -22,6 +23,8 @@ export function recast(
|
|||||||
return indentation + recastBinaryExpression(statement.expression)
|
return indentation + recastBinaryExpression(statement.expression)
|
||||||
} else if (statement.expression.type === 'ArrayExpression') {
|
} else if (statement.expression.type === 'ArrayExpression') {
|
||||||
return indentation + recastArrayExpression(statement.expression)
|
return indentation + recastArrayExpression(statement.expression)
|
||||||
|
} else if (statement.expression.type === 'ObjectExpression') {
|
||||||
|
return indentation + recastObjectExpression(statement.expression)
|
||||||
} else if (statement.expression.type === 'CallExpression') {
|
} else if (statement.expression.type === 'CallExpression') {
|
||||||
return indentation + recastCallExpression(statement.expression)
|
return indentation + recastCallExpression(statement.expression)
|
||||||
}
|
}
|
||||||
@ -74,6 +77,25 @@ ${_indentation}${expression.elements
|
|||||||
return flatRecast
|
return flatRecast
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function recastObjectExpression(
|
||||||
|
expression: ObjectExpression,
|
||||||
|
indentation = ''
|
||||||
|
): string {
|
||||||
|
const flatRecast = `{ ${expression.properties
|
||||||
|
.map((prop) => `${prop.key.name}: ${recastValue(prop.value)}`)
|
||||||
|
.join(', ')} }`
|
||||||
|
const maxArrayLength = 40
|
||||||
|
if (flatRecast.length > maxArrayLength) {
|
||||||
|
const _indentation = indentation + ' '
|
||||||
|
return `{
|
||||||
|
${_indentation}${expression.properties
|
||||||
|
.map((prop) => `${prop.key.name}: ${recastValue(prop.value)}`)
|
||||||
|
.join(`,\n${_indentation}`)}
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
return flatRecast
|
||||||
|
}
|
||||||
|
|
||||||
function recastBinaryPart(part: BinaryPart): string {
|
function recastBinaryPart(part: BinaryPart): string {
|
||||||
if (part.type === 'Literal') {
|
if (part.type === 'Literal') {
|
||||||
return recastLiteral(part)
|
return recastLiteral(part)
|
||||||
@ -106,6 +128,8 @@ function recastArgument(argument: Value): string {
|
|||||||
return recastBinaryExpression(argument)
|
return recastBinaryExpression(argument)
|
||||||
} else if (argument.type === 'ArrayExpression') {
|
} else if (argument.type === 'ArrayExpression') {
|
||||||
return recastArrayExpression(argument)
|
return recastArrayExpression(argument)
|
||||||
|
} else if (argument.type === 'ObjectExpression') {
|
||||||
|
return recastObjectExpression(argument)
|
||||||
} else if (argument.type === 'CallExpression') {
|
} else if (argument.type === 'CallExpression') {
|
||||||
return recastCallExpression(argument)
|
return recastCallExpression(argument)
|
||||||
} else if (argument.type === 'FunctionExpression') {
|
} else if (argument.type === 'FunctionExpression') {
|
||||||
@ -136,6 +160,8 @@ function recastValue(node: Value, indentation = ''): string {
|
|||||||
return recastBinaryExpression(node)
|
return recastBinaryExpression(node)
|
||||||
} else if (node.type === 'ArrayExpression') {
|
} else if (node.type === 'ArrayExpression') {
|
||||||
return recastArrayExpression(node, indentation)
|
return recastArrayExpression(node, indentation)
|
||||||
|
} else if (node.type === 'ObjectExpression') {
|
||||||
|
return recastObjectExpression(node, indentation)
|
||||||
} else if (node.type === 'Literal') {
|
} else if (node.type === 'Literal') {
|
||||||
return recastLiteral(node)
|
return recastLiteral(node)
|
||||||
} else if (node.type === 'FunctionExpression') {
|
} else if (node.type === 'FunctionExpression') {
|
||||||
|
@ -349,6 +349,23 @@ describe('testing lexer', () => {
|
|||||||
"brace ']' from 16 to 17",
|
"brace ']' from 16 to 17",
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
it('testing object declaration', () => {
|
||||||
|
const result = stringSummaryLexer(`const yo = {key: 'value'}`)
|
||||||
|
expect(result).toEqual([
|
||||||
|
"word 'const' from 0 to 5",
|
||||||
|
"whitespace ' ' from 5 to 6",
|
||||||
|
"word 'yo' from 6 to 8",
|
||||||
|
"whitespace ' ' from 8 to 9",
|
||||||
|
"operator '=' from 9 to 10",
|
||||||
|
"whitespace ' ' from 10 to 11",
|
||||||
|
"brace '{' from 11 to 12",
|
||||||
|
"word 'key' from 12 to 15",
|
||||||
|
"colon ':' from 15 to 16",
|
||||||
|
"whitespace ' ' from 16 to 17",
|
||||||
|
"string ''value'' from 17 to 24",
|
||||||
|
"brace '}' from 24 to 25",
|
||||||
|
])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// helpers
|
// helpers
|
||||||
|
@ -15,6 +15,7 @@ const PARAN_END = /^\)/
|
|||||||
const ARRAY_START = /^\[/
|
const ARRAY_START = /^\[/
|
||||||
const ARRAY_END = /^\]/
|
const ARRAY_END = /^\]/
|
||||||
const COMMA = /^,/
|
const COMMA = /^,/
|
||||||
|
const COLON = /^:/
|
||||||
|
|
||||||
export const isNumber = (character: string) => NUMBER.test(character)
|
export const isNumber = (character: string) => NUMBER.test(character)
|
||||||
export const isWhitespace = (character: string) => WHITESPACE.test(character)
|
export const isWhitespace = (character: string) => WHITESPACE.test(character)
|
||||||
@ -28,6 +29,7 @@ export const isParanEnd = (character: string) => PARAN_END.test(character)
|
|||||||
export const isArrayStart = (character: string) => ARRAY_START.test(character)
|
export const isArrayStart = (character: string) => ARRAY_START.test(character)
|
||||||
export const isArrayEnd = (character: string) => ARRAY_END.test(character)
|
export const isArrayEnd = (character: string) => ARRAY_END.test(character)
|
||||||
export const isComma = (character: string) => COMMA.test(character)
|
export const isComma = (character: string) => COMMA.test(character)
|
||||||
|
export const isColon = (character: string) => COLON.test(character)
|
||||||
|
|
||||||
function matchFirst(str: string, regex: RegExp) {
|
function matchFirst(str: string, regex: RegExp) {
|
||||||
const theMatch = str.match(regex)
|
const theMatch = str.match(regex)
|
||||||
@ -46,6 +48,7 @@ export interface Token {
|
|||||||
| 'brace'
|
| 'brace'
|
||||||
| 'whitespace'
|
| 'whitespace'
|
||||||
| 'comma'
|
| 'comma'
|
||||||
|
| 'colon'
|
||||||
value: string
|
value: string
|
||||||
start: number
|
start: number
|
||||||
end: number
|
end: number
|
||||||
@ -97,6 +100,9 @@ const returnTokenAtIndex = (str: string, startIndex: number): Token | null => {
|
|||||||
if (isWord(strFromIndex)) {
|
if (isWord(strFromIndex)) {
|
||||||
return makeToken('word', matchFirst(strFromIndex, WORD), startIndex)
|
return makeToken('word', matchFirst(strFromIndex, WORD), startIndex)
|
||||||
}
|
}
|
||||||
|
if (isColon(strFromIndex)) {
|
||||||
|
return makeToken('colon', matchFirst(strFromIndex, COLON), startIndex)
|
||||||
|
}
|
||||||
if (isWhitespace(strFromIndex)) {
|
if (isWhitespace(strFromIndex)) {
|
||||||
return makeToken(
|
return makeToken(
|
||||||
'whitespace',
|
'whitespace',
|
||||||
|
Reference in New Issue
Block a user