try to match lezer API more closely
This commit is contained in:
parent
1b80a159c5
commit
a5b2802c0c
|
|
@ -1,4 +1,5 @@
|
||||||
import { type Token, TokenType } from "./tokenizer2"
|
import { type Token, TokenType } from './tokenizer2'
|
||||||
|
import * as term from './shrimp.terms'
|
||||||
|
|
||||||
export type NodeType =
|
export type NodeType =
|
||||||
| 'Program'
|
| 'Program'
|
||||||
|
|
@ -110,15 +111,191 @@ export const operators: Record<string, any> = {
|
||||||
'|': 'operator',
|
'|': 'operator',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class Tree {
|
||||||
|
constructor(public topNode: SyntaxNode) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: TEMPORARY SHIM
|
||||||
|
class SyntaxNodeType {
|
||||||
|
constructor(public nodeType: NodeType) { }
|
||||||
|
|
||||||
|
is(other: string) {
|
||||||
|
return this.nodeType === other
|
||||||
|
}
|
||||||
|
|
||||||
|
get id(): number {
|
||||||
|
switch (this.nodeType) {
|
||||||
|
case 'Program':
|
||||||
|
return term.Program
|
||||||
|
|
||||||
|
case 'Block':
|
||||||
|
return term.Block
|
||||||
|
|
||||||
|
case 'FunctionCall':
|
||||||
|
return term.FunctionCall
|
||||||
|
|
||||||
|
case 'FunctionCallOrIdentifier':
|
||||||
|
return term.FunctionCallOrIdentifier
|
||||||
|
|
||||||
|
case 'FunctionCallWithBlock':
|
||||||
|
return term.FunctionCallWithBlock
|
||||||
|
|
||||||
|
case 'PositionalArg':
|
||||||
|
return term.PositionalArg
|
||||||
|
|
||||||
|
case 'NamedArg':
|
||||||
|
return term.NamedArg
|
||||||
|
|
||||||
|
case 'FunctionDef':
|
||||||
|
return term.FunctionDef
|
||||||
|
|
||||||
|
case 'Params':
|
||||||
|
return term.Params
|
||||||
|
|
||||||
|
case 'NamedParam':
|
||||||
|
return term.NamedParam
|
||||||
|
|
||||||
|
case 'Null':
|
||||||
|
return term.Null
|
||||||
|
|
||||||
|
case 'Boolean':
|
||||||
|
return term.Boolean
|
||||||
|
|
||||||
|
case 'Number':
|
||||||
|
return term.Number
|
||||||
|
|
||||||
|
case 'String':
|
||||||
|
return term.String
|
||||||
|
|
||||||
|
case 'StringFragment':
|
||||||
|
return term.StringFragment
|
||||||
|
|
||||||
|
case 'CurlyString':
|
||||||
|
return term.CurlyString
|
||||||
|
|
||||||
|
case 'DoubleQuote':
|
||||||
|
return term.DoubleQuote
|
||||||
|
|
||||||
|
case 'EscapeSeq':
|
||||||
|
return term.EscapeSeq
|
||||||
|
|
||||||
|
case 'Interpolation':
|
||||||
|
return term.Interpolation
|
||||||
|
|
||||||
|
case 'Regex':
|
||||||
|
return term.Regex
|
||||||
|
|
||||||
|
case 'Identifier':
|
||||||
|
return term.Identifier
|
||||||
|
|
||||||
|
case 'AssignableIdentifier':
|
||||||
|
return term.AssignableIdentifier
|
||||||
|
|
||||||
|
case 'IdentifierBeforeDot':
|
||||||
|
return term.IdentifierBeforeDot
|
||||||
|
|
||||||
|
case 'Word':
|
||||||
|
return term.Word
|
||||||
|
|
||||||
|
case 'Array':
|
||||||
|
return term.Array
|
||||||
|
|
||||||
|
case 'Dict':
|
||||||
|
return term.Dict
|
||||||
|
|
||||||
|
case 'Comment':
|
||||||
|
return term.Comment
|
||||||
|
|
||||||
|
case 'BinOp':
|
||||||
|
return term.BinOp
|
||||||
|
|
||||||
|
case 'ConditionalOp':
|
||||||
|
return term.ConditionalOp
|
||||||
|
|
||||||
|
case 'ParenExpr':
|
||||||
|
return term.ParenExpr
|
||||||
|
|
||||||
|
case 'Assign':
|
||||||
|
return term.Assign
|
||||||
|
|
||||||
|
case 'CompoundAssign':
|
||||||
|
return term.CompoundAssign
|
||||||
|
|
||||||
|
case 'DotGet':
|
||||||
|
return term.DotGet
|
||||||
|
|
||||||
|
case 'PipeExpr':
|
||||||
|
return term.PipeExpr
|
||||||
|
|
||||||
|
case 'IfExpr':
|
||||||
|
return term.IfExpr
|
||||||
|
|
||||||
|
case 'ElseIfExpr':
|
||||||
|
return term.ElseIfExpr
|
||||||
|
|
||||||
|
case 'ElseExpr':
|
||||||
|
return term.ElseExpr
|
||||||
|
|
||||||
|
case 'WhileExpr':
|
||||||
|
return term.WhileExpr
|
||||||
|
|
||||||
|
case 'TryExpr':
|
||||||
|
return term.TryExpr
|
||||||
|
|
||||||
|
case 'CatchExpr':
|
||||||
|
return term.CatchExpr
|
||||||
|
|
||||||
|
case 'FinallyExpr':
|
||||||
|
return term.FinallyExpr
|
||||||
|
|
||||||
|
case 'Throw':
|
||||||
|
return term.Throw
|
||||||
|
|
||||||
|
case 'Eq':
|
||||||
|
return term.Eq
|
||||||
|
|
||||||
|
case 'Modulo':
|
||||||
|
return term.Modulo
|
||||||
|
|
||||||
|
case 'Plus':
|
||||||
|
return term.Plus
|
||||||
|
|
||||||
|
case 'Star':
|
||||||
|
return term.Star
|
||||||
|
|
||||||
|
case 'Slash':
|
||||||
|
return term.Slash
|
||||||
|
|
||||||
|
case 'Import':
|
||||||
|
return term.Import
|
||||||
|
|
||||||
|
case 'Do':
|
||||||
|
return term.Do
|
||||||
|
|
||||||
|
case 'colon':
|
||||||
|
return term.colon
|
||||||
|
|
||||||
|
case 'keyword':
|
||||||
|
return term.keyword
|
||||||
|
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
get name(): string {
|
||||||
|
return this.nodeType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class SyntaxNode {
|
export class SyntaxNode {
|
||||||
type: NodeType
|
#type: NodeType
|
||||||
from: number
|
from: number
|
||||||
to: number
|
to: number
|
||||||
parent: SyntaxNode | null
|
parent: SyntaxNode | null
|
||||||
children: SyntaxNode[] = []
|
children: SyntaxNode[] = []
|
||||||
|
|
||||||
constructor(type: NodeType, from: number, to: number, parent: SyntaxNode | null = null) {
|
constructor(type: NodeType, from: number, to: number, parent: SyntaxNode | null = null) {
|
||||||
this.type = type
|
this.#type = type
|
||||||
this.from = from
|
this.from = from
|
||||||
this.to = to
|
this.to = to
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
|
|
@ -128,8 +305,16 @@ export class SyntaxNode {
|
||||||
return new SyntaxNode(TokenType[token.type] as NodeType, token.from, token.to, parent ?? null)
|
return new SyntaxNode(TokenType[token.type] as NodeType, token.from, token.to, parent ?? null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get type(): SyntaxNodeType {
|
||||||
|
return new SyntaxNodeType(this.#type)
|
||||||
|
}
|
||||||
|
|
||||||
|
set type(name: NodeType) {
|
||||||
|
this.#type = name
|
||||||
|
}
|
||||||
|
|
||||||
get name(): string {
|
get name(): string {
|
||||||
return this.type
|
return this.type.name
|
||||||
}
|
}
|
||||||
|
|
||||||
get isError(): boolean {
|
get isError(): boolean {
|
||||||
|
|
|
||||||
|
|
@ -118,12 +118,12 @@ export class Parser {
|
||||||
expr = this.exprWithPrecedence()
|
expr = this.exprWithPrecedence()
|
||||||
|
|
||||||
// check for destructuring
|
// check for destructuring
|
||||||
if (expr.type === 'Array' && this.is($T.Operator, '='))
|
if (expr.type.is('Array') && this.is($T.Operator, '='))
|
||||||
return this.destructure(expr)
|
return this.destructure(expr)
|
||||||
|
|
||||||
// check for parens function call
|
// check for parens function call
|
||||||
// ex: (ref my-func) my-arg
|
// ex: (ref my-func) my-arg
|
||||||
if (expr.type === 'ParenExpr' && !this.isExprEnd())
|
if (expr.type.is('ParenExpr') && !this.isExprEnd())
|
||||||
expr = this.functionCall(expr)
|
expr = this.functionCall(expr)
|
||||||
|
|
||||||
// one | echo
|
// one | echo
|
||||||
|
|
@ -321,7 +321,7 @@ export class Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
// atoms are the basic building blocks: literals, identifiers, words
|
// atoms are the basic building blocks: literals, identifiers, words
|
||||||
atom(): SyntaxNode {
|
atom(): SyntaxNode {
|
||||||
if (this.is($T.String))
|
if (this.is($T.String))
|
||||||
return this.string()
|
return this.string()
|
||||||
|
|
||||||
|
|
@ -507,7 +507,7 @@ export class Parser {
|
||||||
if (!this.scope.has(ident))
|
if (!this.scope.has(ident))
|
||||||
return this.word(left)
|
return this.word(left)
|
||||||
|
|
||||||
if (left.type === 'Identifier') left.type = 'IdentifierBeforeDot'
|
if (left.type.is('Identifier')) left.type = 'IdentifierBeforeDot'
|
||||||
|
|
||||||
let parts = []
|
let parts = []
|
||||||
while (this.is($T.Operator, '.')) {
|
while (this.is($T.Operator, '.')) {
|
||||||
|
|
@ -527,7 +527,7 @@ export class Parser {
|
||||||
const dotGet = this.dotGet()
|
const dotGet = this.dotGet()
|
||||||
|
|
||||||
// dotget not in scope, regular Word
|
// dotget not in scope, regular Word
|
||||||
if (dotGet.type === 'Word') return dotGet
|
if (dotGet.type.is('Word')) return dotGet
|
||||||
|
|
||||||
if (this.isExprEnd())
|
if (this.isExprEnd())
|
||||||
return this.functionCallOrIdentifier(dotGet)
|
return this.functionCallOrIdentifier(dotGet)
|
||||||
|
|
@ -580,7 +580,7 @@ export class Parser {
|
||||||
inner = this.dotGet()
|
inner = this.dotGet()
|
||||||
|
|
||||||
// if the dotGet was just a Word, bail
|
// if the dotGet was just a Word, bail
|
||||||
if (inner.type === 'Word') return inner
|
if (inner.type.is('Word')) return inner
|
||||||
}
|
}
|
||||||
|
|
||||||
inner ??= this.identifier()
|
inner ??= this.identifier()
|
||||||
|
|
@ -679,7 +679,7 @@ export class Parser {
|
||||||
const prefix = SyntaxNode.from(this.expect($T.NamedArgPrefix))
|
const prefix = SyntaxNode.from(this.expect($T.NamedArgPrefix))
|
||||||
const val = this.value()
|
const val = this.value()
|
||||||
|
|
||||||
if (!['Null', 'Boolean', 'Number', 'String'].includes(val.type))
|
if (!['Null', 'Boolean', 'Number', 'String'].includes(val.type.name))
|
||||||
throw `[namedParam] default value must be Null|Bool|Num|Str, got ${val.type}\n\n ${this.input}\n`
|
throw `[namedParam] default value must be Null|Bool|Num|Str, got ${val.type}\n\n ${this.input}\n`
|
||||||
|
|
||||||
const node = new SyntaxNode('NamedParam', prefix.from, val.to)
|
const node = new SyntaxNode('NamedParam', prefix.from, val.to)
|
||||||
|
|
@ -887,9 +887,9 @@ function collapseDotGets(origNodes: SyntaxNode[]): SyntaxNode {
|
||||||
while (nodes.length > 0) {
|
while (nodes.length > 0) {
|
||||||
const left = nodes.pop()!
|
const left = nodes.pop()!
|
||||||
|
|
||||||
if (left.type === 'Identifier') left.type = 'IdentifierBeforeDot'
|
if (left.type.is('Identifier')) left.type = 'IdentifierBeforeDot'
|
||||||
|
|
||||||
const dot = new SyntaxNode("DotGet", left.from, right.to);
|
const dot = new SyntaxNode("DotGet", left.from, right.to)
|
||||||
dot.push(left, right)
|
dot.push(left, right)
|
||||||
|
|
||||||
right = dot
|
right = dot
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user