interface Node {
type: string
loc: SourceLocation | null
}
interface SourceLocation {
source: string | null
start: Position
end: Position
}
interface Position {
line: number // 1-indexed
column: number // 0-indexed
}
interface Identifier : Node {
type: "Identifier"
name: string
}
interface Program : Node {
type: "Program"
body: BlockStatement
}
The whole bash program tree
interface BlockStatement : Node {
type: "BlockStatement"
body: [ Statement ]
}
interface TestOperation {
type: "TestOperation"
operator: FileTestOperator | TestOperator
left: Expression | null
right: Expression
}
enum FileTestOperator {
// TODO
"-e"
}
enum TestOperator {
// TODO
"-eq"
}
interface HereDocument {
type: "HereDocument"
limitString: string
// TODO: maybe substitution should be down at runtime, not at compiling
documents: [ Literal | Substitution ]
suppressLeadingTabs: boolean
enableSubstitution: boolean
}
interface UnaryOperation : ArithmeticOperation {
type: "UnaryOperation"
operator: UnaryOperator
prefix: boolean
argument: Identifier
}
enum UnaryOperator {
"-"
}
interface UpdateOperation : ArithmeticOperation {
type: "UpdateOperation"
operator: UpdateOperator
argument: Identifier
prefix: boolean
}
enum UpdateOperator {
"++" | "--"
}
interface BinaryOperation : ArithmeticOperation {
type: "BinaryOperation"
operator: BinaryOperator
left: Identifier
}
enum BinaryOperator {
// TODO: more
"+" | "-" | "*" | "/" |
"**" | "%" |
"+=" | "-=" | "*=" | "/=" | "%=" |
"<<" | "<<=" | ">>" | ">>=" | "&" | "&=" | "|" | "|=" | "~" | "^" | "^="
">" | "<"
}
// Ternary
interface ConditionalOperation : ArithmeticOperation {
type: "TernaryOperation"
test: ArithmeticOperation
alternate: Literal | ArithmeticOperation
consequent: Literal | ArithmeticOperation
}
interface LogicalOperation : ArithmeticOperation {
type: "LogicalOperation"
operator: LogicalOperator
left: ArithmeticOperation || null
right: ArithmeticOperation
}
enum LogicalOperator {
"||" | "&&" || "!"
}
interface Statement : Node {}
A statement is a thing that can stand alone.
interface SubShell : Statement {
type: "SubShell"
body: BlockStatement
}
// (( ))
interface ArithmeticStatement : Statement {
type: "ArithmeticStatement"
operation: ArithmeticOperation
}
interface IfStatement : Statement {
type: "IfStatement"
test: ArithmeticStatement | Command | TestConstruct
consequent: BlockStatement
alternate: Statement | null
kind: "elif" | "else if" | null
}
interface FunctionDeclaration : Statement {
type: "FunctionDeclaration"
id: Identifier
body: [ Statement ]
}
A function declaration
interface VariableAssignment : Statement {
type: "VariableAssignment"
// a=1 | a[1]=1
left: Identifier | ArrayMemberConstruct
right: Expression
}
interface VariableDeclaration : Statement {
type: "VariableDeclaration"
id: Identifier
// declare -a a=1
kind: "a" | "i" | "r" | "local"
init: Expression | null
}
Actually declare -a a=foo
is a command, but it is better to be treated as a Statement
due to its complication.
interface Command : Statement {
type: "Command"
name: string
argv: Arguments
}
interface Arguments {
type: "Arguments"
args: [ Expression ]
}
The length of the arguments depends on the result of substitution
interface Construct : Node {}
interface MemberConstruct : Construct {
type: "ArrayMemberConstruct"
array: Identifier
property: Literal
}
interface TestConstruct {
type: "TestConstruct"
condition: Expression |
// [] | [[]]
kind: "test" | "extended"
}
interface BraceExpansion : Expansion {
type: "BraceExpansion"
expansions: [ Literal ]
prefix: Literal | Expansion
suffix: Literal
}
// {a..z}
interface ExtendedBraceExpansion : Expansion {
type: "ExtendedBraceExpansion"
start: Literal
end: Literal
// foo{a..z} | {1..9}{a..z}
prefix: Literal | Expansion
suffix: Literal
}
interface Expression : Node {}
An expression is what can be assigned to identifiers or as the item of arguments.
interface Literal : Expression {
type: "Literal"
// 'a'
value: string
// '"a"'
raw: string
}
interface ArrayExpression : Expression {
type: "ArrayExpression"
elements: Arguments
}
interface CommandSubstitution : Expression {
type: "CommandSubstitution"
command: Command
// `command -r` | $(command -r)
kind: "backtick" | "bracket"
}
// "$a"
interface StringSubstitution : Expression {
type: "StringSubstitution"
contents: [ Literal | VariableSubstitution ]
}
// ${a:-default}
interface VariableSubstitution : Expression {
type: "VariableSubstitution"
kind: "brace" | "no-brace"
// null for positional variables
id: Identifier | null
pattern: VariableDirective | null
}
// $(( 2 + 3))
interface ArithmeticSubstitution : Expression {
type: "ArithmeticSubstitution"
content: ArithmeticStatement
}
interface ProcessSubstitution : Expression {
type: "ProcessSubstitution"
command: Command
// >() | <()
kind: "in" | "out"
}
interface MemberAccessDirective : VariableDirective {
type: "MemberAccessDirective"
property: Literal
}
// value[kind]arg
interface GetterDirective : VariableDirective {
type: "GetterDirective"
kind: "-" | ":-" | "=" | ":=" | "+" | ":+" | "?" | ":?"
arg: Literal | Expression
}
// #value[property]
interface LengthDirective : VariableDirective {
type: "LengthDirective"
property: "*" | "@" | null
}
// value[kind]pattern
interface CropDirective : VariableDirective {
type: "CropDirective"
pattern: Literal | Expression
kind: "#" | "##" | "%" | "%%"
}
// value:start[:end]
interface SliceDirective : VariableDirective {
type: "SliceDirective"
start: Literal
end: Literal | null
}
// value/[kind]pattern/replacement
interface ReplaceDirective : VariableDirective {
type: "ReplaceDirective"
pattern: Literal
replacement: Literal
kind: "/" | "#" | "%" | null
}
// !value[kind]
interface SearchDirective : VariableDirective {
type: "SearchDirective"
// the same
kind: "*" | "@"
}