* Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { D, DD, E, S, T } from "./builder"
import { AccessorExpression, Hint, BinaryExpression, CallExpression, ClassDeclaration, ConstructorExpression, DeclarationStatement, ExpressionStatement, FunctionDeclaration, IfStatement, LoopStatement, LWExpression, LWKind, LWStatement, LWType, Modifier, StructureDeclaration, Annotation, SimpleAnnotation, DecoratorKind, MacroInvocation, UnaryExpression, CheckCastExpression, FunctionalType } from "./lws"
import { Hs, Md, std, Ts } from "./stdlib";
const id = <T>(it: T) => it
function check(desc: string, ...data: any[]) {
if (data.includes(undefined))
throw new Error(desc + ' not fully initialized: ' + data.join(", "))
}
class AccessorBuilder<P> {
constructor(
private _cont: (expr: AccessorExpression) => P,
private _object?: LWExpression
) {}
private _accessor?: string | LWExpression
private _hints: Hint[] = []
ptr() { this._object?.hints.push(Hs.ptrVal()); return this }
static() { this._hints.push(Hs.staticMethod()); return this }
excl() { this._hints.push(Hs.excl()); return this }
member(name: string) { this._accessor = name; return this }
indexExpr(expr: LWExpression) { this._accessor = expr; return this }
indexStr(str: string) { this._accessor = E.v(str); return this }
index(): ExpressionBuilder<AccessorBuilder<P>> {
return new ExpressionBuilder(expr => {
this._accessor = expr
return this
})
}
object(): ExpressionBuilder<AccessorBuilder<P>> {
return new ExpressionBuilder(expr => {
this._object = expr
return this
})
}
$(): P {
check("Accessor", this._object, this._accessor)
return this._cont(E.get(this._object!, this._accessor!, this._hints))
}
}
class UnaryBuilder<P> {
constructor(private _cont: (expr: UnaryExpression) => P, private op: string) {}
private _expr?: LWExpression
valueExpr(value: LWExpression) { this._expr = value; return this }
valueStr(str: string) { this._expr = E.v(str); return this }
value(): ExpressionBuilder<UnaryBuilder<P>> {
return new ExpressionBuilder(expr => {
this._expr = expr
return this
})
}
$(): P {
check("Unary", this._expr)
return this._cont(E.unary(this.op, this._expr!))
}
}
class BinaryBuilder<P> {
constructor(private _cont: (expr: BinaryExpression) => P, private op: string) {}
private _lhs?: LWExpression
private _rhs?: LWExpression
leftExpr(value: LWExpression) { this._lhs = value; return this }
leftStr(str: string) { this._lhs = E.v(str); return this }
rightExpr(value: LWExpression) { this._rhs = value; return this }
rightStr(str: string | number) { this._rhs = E.v(str.toString()); return this }
left(): ExpressionBuilder<BinaryBuilder<P>> {
return new ExpressionBuilder(expr => {
this._lhs = expr
return this
})
}
right(): ExpressionBuilder<BinaryBuilder<P>> {
return new ExpressionBuilder(expr => {
this._rhs = expr
return this
})
}
$(): P {
check("Binary", this._lhs, this._rhs)
return this._cont(E.bin(this.op, this._lhs!, this._rhs!))
}
}
class ArgBuilder<P> {
constructor(
private _cont: (arg: LWExpression) => P,
private _arg?: LWExpression
) {}
access(object?: LWExpression): AccessorBuilder<ArgBuilder<P>> {
return new AccessorBuilder(arg => {
this._arg = arg
return this
}, object)
}
call(): CallBuilder<ArgBuilder<P>> {
return new CallBuilder(arg => {
this._arg = arg
return this
})
}
$(): P {
check("Arg", this._arg)
return this._cont(this._arg!)
}
}
class CallBuilder<P> {
constructor(private _cont: (expr: CallExpression) => P) {}
private _callee?: LWExpression
private _receiver?: LWExpression
private _function?: string
private _args: LWExpression[] = []
receiverName(name: string, hints?: Hint[]) { this._receiver = E.v(name, hints); return this }
receiverExpr(object: LWExpression) { this._receiver = object; return this }
functionName(name: string) { this._function = name; return this }
functionExpr(expr: LWExpression) { this._callee = expr; return this }
args(args: LWExpression[]) { this._args.push(...args); return this }
function(): ExpressionBuilder<CallBuilder<P>> {
return new ExpressionBuilder(expr => {
this._callee = expr
return this
})
}
arg(value?: string): ArgBuilder<CallBuilder<P>> {
return new ArgBuilder(arg => {
this._args.push(arg)
return this
}, value ? E.v(value) : undefined)
}
$(): P {
if (!this._callee) {
check("Call", this._function)
this._callee = this._receiver ? E.get(this._receiver, this._function!) : E.v(this._function!)
}
return this._cont(E.call(this._callee, this._args))
}
}
class ConstructorBuilder<P> {
constructor(
private _cont: (expr: ConstructorExpression) => P,
private _name?: string
) {}
private _args: LWExpression[] = []
private _hints: Hint[] = []
asStruct() { this._hints.push(Hs.asStruct()); return this }
stack() { this._hints.push(Hs.stackInstance()); return this }
args(args: LWExpression[]) { this._args.push(...args); return this }
arg(value?: string): ArgBuilder<ConstructorBuilder<P>> {
return new ArgBuilder(arg => {
this._args.push(arg)
return this
}, value ? E.v(value) : undefined)
}
$(): P {
return this._cont(E.instance(this._name ?? 'CTOR_NAME', this._args, [], this._hints))
}
}
class CheckCastBuilder<P> {
constructor(
private _cont: (expr: CheckCastExpression) => P,
private _op: 'cast' | 'instanceof',
private _type: LWType
) {}
private _expr?: LWExpression
private _hints: Hint[] = []
static() { this._hints.push(Hs.staticMethod()); return this }
valueExpr(value: LWExpression) { this._expr = value; return this }
valueStr(value: string) { this._expr = E.v(value); return this }
value(): ExpressionBuilder<CheckCastBuilder<P>> {
return new ExpressionBuilder(expr => {
this._expr = expr
return this
})
}
$(): P {
check("Cast", this._expr)
return this._cont({
kind: LWKind.CheckCastExpression,
op: this._op,
expression: this._expr!,
type: this._type,
hints: this._hints
})
}
}
class ExpressionBuilder<P> {
constructor(private _cont: (expr: LWExpression) => P) {}
private _expr?: LWExpression
access(object?: LWExpression): AccessorBuilder<ExpressionBuilder<P>> {
return new AccessorBuilder(expr => {
this._expr = expr
return this
}, object)
}
binary(op: string): BinaryBuilder<ExpressionBuilder<P>> {
return new BinaryBuilder(expr => {
this._expr = expr
return this
}, op)
}
unary(op: string): UnaryBuilder<ExpressionBuilder<P>> {
return new UnaryBuilder(expr => {
this._expr = expr
return this
}, op)
}
call(): CallBuilder<ExpressionBuilder<P>> {
return new CallBuilder(expr => {
this._expr = expr
return this
})
}
ctor(name?: string): ConstructorBuilder<ExpressionBuilder<P>> {
return new ConstructorBuilder(expr => {
this._expr = expr
return this
}, name)
}
cast(type: LWType): CheckCastBuilder<ExpressionBuilder<P>> {
return new CheckCastBuilder(expr => {
this._expr = expr
return this
}, 'cast', type)
}
instanceof(type: LWType): CheckCastBuilder<ExpressionBuilder<P>> {
return new CheckCastBuilder(expr => {
this._expr = expr
return this
}, 'instanceof', type)
}
$(): P {
check("Expression", this._expr)
return this._cont(this._expr!)
}
$stmt(): ExpressionStatement {
check("Expression", this._expr)
return S.e(this._expr!)
}
}
class DeclarationBuilder<P> {
constructor(private _cont: (stmt: DeclarationStatement) => P, private _name: string, private _type: LWType) {}
private _mutable: boolean = false
private _static: boolean = false
private _expression?: LWExpression
mutable() { this._mutable = true; return this }
static() { this._static = true; return this }
valueExpr(expr: LWExpression) { this._expression = expr; return this }
valueStr(str: string | number) { this._expression = E.v(str.toString()); return this }
value(): ExpressionBuilder<DeclarationBuilder<P>> {
return new ExpressionBuilder(expr => {
this._expression = expr
return this
})
}
$(): P {
return this._cont(S.declaration(this._name, this._type, this._mutable, this._expression, this._static))
}
}
class ReturnBuilder<P> {
constructor(private _cont: (stmt: LWStatement) => P, private _type?: LWType) {}
private _expr?: LWExpression
valueExpr(expr: LWExpression) { this._expr = expr; return this }
valueStr(value: string) { this._expr = E.v(value); return this }
value(): ExpressionBuilder<ReturnBuilder<P>> {
return new ExpressionBuilder(expr => {
this._expr = expr
return this
})
}
access(object?: LWExpression): AccessorBuilder<ReturnBuilder<P>> {
return new AccessorBuilder(expr => {
this._expr = expr
return this
}, object)
}
binary(op: string): BinaryBuilder<ReturnBuilder<P>> {
return new BinaryBuilder(expr => {
this._expr = expr
return this
}, op)
}
unary(op: string): UnaryBuilder<ReturnBuilder<P>> {
return new UnaryBuilder(expr => {
this._expr = expr
return this
}, op)
}
call(): CallBuilder<ReturnBuilder<P>> {
return new CallBuilder(expr => {
this._expr = expr
return this
})
}
cast(type: LWType): CheckCastBuilder<ReturnBuilder<P>> {
return new CheckCastBuilder(expr => {
this._expr = expr
return this
}, 'cast', type)
}
instanceof(type: LWType): CheckCastBuilder<ReturnBuilder<P>> {
return new CheckCastBuilder(expr => {
this._expr = expr
return this
}, 'instanceof', type)
}
ctor(name?: string): ConstructorBuilder<ReturnBuilder<P>> {
return new ConstructorBuilder(expr => {
this._expr = expr
return this
}, name)
}
$(): P {
if (this._expr) {
const wrap = this._type === Ts.prim.void ? S.e : S.return
return this._cont(wrap(this._expr!))
}
return this._cont(S.return())
}
}
class IfBuilder<P> {
constructor(private _cont: (stmt: IfStatement) => P) {}
private _cond?: LWExpression
private _then?: LWStatement
private _else?: LWStatement
condition(cond: LWExpression) { this._cond = cond; return this }
cond(): ExpressionBuilder<IfBuilder<P>> {
return new ExpressionBuilder(expr => {
this._cond = expr
return this
})
}
then(): StatementBuilder<IfBuilder<P>> {
return new StatementBuilder(stmt => {
this._then = stmt
return this
})
}
else(): StatementBuilder<IfBuilder<P>> {
return new StatementBuilder(stmt => {
this._else = stmt
return this
})
}
$(): P {
check("If", this._cond, this._then)
return this._cont(S.if(this._cond!, this._then!, this._else))
}
}
class LoopBuilder<P> {
constructor(private _cont: (stmt: LoopStatement) => P) {}
private _init?: LWStatement
private _cond?: LWExpression
private _step?: LWStatement
private _body?: LWStatement
condition(cond: LWExpression) { this._cond = cond; return this }
bodyStmt(body: LWStatement) { this._body = body; return this }
init(): StatementBuilder<LoopBuilder<P>> {
return new StatementBuilder(stmt => {
this._init = stmt
return this
})
}
cond(): ExpressionBuilder<LoopBuilder<P>> {
return new ExpressionBuilder(expr => {
this._cond = expr
return this
})
}
step(): StatementBuilder<LoopBuilder<P>> {
return new StatementBuilder(stmt => {
this._step = stmt
return this
})
}
body(): StatementBuilder<LoopBuilder<P>> {
return new StatementBuilder(stmt => {
this._body = stmt
return this
})
}
$(): P {
check("Loop", this._cond, this._body)
return this._cont(S.loop(this._cond!, this._body!, this._init, this._step))
}
}
class StatementBuilder<P> {
constructor(private _cont: (stmt: LWStatement) => P) {}
private _stmt?: LWStatement
binary(op: string): BinaryBuilder<StatementBuilder<P>> {
return new BinaryBuilder(stmt => {
this._stmt = S.e(stmt)
return this
}, op)
}
unary(op: string): UnaryBuilder<StatementBuilder<P>> {
return new UnaryBuilder(stmt => {
this._stmt = S.e(stmt)
return this
}, op)
}
block(): BlockBuilder<StatementBuilder<P>> {
return new BlockBuilder(stmts => {
this._stmt = S.block(stmts)
return this
})
}
call(): CallBuilder<StatementBuilder<P>> {
return new CallBuilder(stmt => {
this._stmt = S.e(stmt)
return this
})
}
decl(name: string, type: LWType): DeclarationBuilder<StatementBuilder<P>> {
return new DeclarationBuilder(stmt => {
this._stmt = stmt
return this
}, name, type)
}
if(): IfBuilder<StatementBuilder<P>> {
return new IfBuilder(stmt => {
this._stmt = stmt
return this
})
}
loop(): LoopBuilder<StatementBuilder<P>> {
return new LoopBuilder(stmt => {
this._stmt = stmt
return this
})
}
return(type?: LWType): ReturnBuilder<StatementBuilder<P>> {
return new ReturnBuilder(stmt => {
this._stmt = stmt
return this
}, type)
}
$(): P {
check("Statement", this._stmt)
return this._cont(this._stmt!)
}
}
class BlockBuilder<P> {
constructor(private _cont: (stmts: LWStatement[]) => P) {}
private _stmts: LWStatement[] = []
statements(stmts: LWStatement[]) { this._stmts.push(...stmts); return this }
binary(op: string): BinaryBuilder<BlockBuilder<P>> {
return new BinaryBuilder(stmt => {
this._stmts.push(S.e(stmt))
return this
}, op)
}
unary(op: string): UnaryBuilder<BlockBuilder<P>> {
return new UnaryBuilder(stmt => {
this._stmts.push(S.e(stmt))
return this
}, op)
}
block(): BlockBuilder<BlockBuilder<P>> {
return new BlockBuilder(stmts => {
this._stmts.push(S.block(stmts))
return this
})
}
call(): CallBuilder<BlockBuilder<P>> {
return new CallBuilder(stmt => {
this._stmts.push(S.e(stmt))
return this
})
}
decl(name: string, type: LWType): DeclarationBuilder<BlockBuilder<P>> {
return new DeclarationBuilder(stmt => {
this._stmts.push(stmt)
return this
}, name, type)
}
if(): IfBuilder<BlockBuilder<P>> {
return new IfBuilder(stmt => {
this._stmts.push(stmt)
return this
})
}
loop(): LoopBuilder<BlockBuilder<P>> {
return new LoopBuilder(stmt => {
this._stmts.push(stmt)
return this
})
}
return(type?: LWType): ReturnBuilder<BlockBuilder<P>> {
return new ReturnBuilder(stmt => {
this._stmts.push(stmt)
return this
}, type)
}
$(): P {
return this._cont(this._stmts)
}
}
class ParamBuilder<P> {
constructor(
private _cont: (name: string, type: LWType) => P,
private _name: string
) {}
private _type?: LWType
type(type: LWType) { this._type = type; return this }
typeStr(type: string) { this._type = T.c(type); return this }
$(): P {
check("Parameter", this._type)
return this._cont(this._name, this._type!)
}
}
class FunctionTypeBuilder<P> {
constructor(private _cont: (type: FunctionalType) => P) {}
private _parameters: [name: string, type: LWType][] = []
private _returnType?: LWType
returns(type: LWType) { this._returnType = type; return this }
parameters(params: [name: string, type: LWType][]) { this._parameters.push(...params); return this }
param(name: string): ParamBuilder<FunctionTypeBuilder<P>> {
return new ParamBuilder((name, type) => {
this._parameters.push([name, type])
return this
}, name)
}
$(): P {
check("FunctionType", this._returnType)
return this._cont(T.fn(this._parameters, this._returnType!))
}
}
class FunctionBuilder<P> {
constructor(
private _cont: (decl: FunctionDeclaration) => P,
private _name: string
) {}
private _modifiers: Modifier[] = []
private _parameters: { name: string, type: LWType }[] = []
private _returnType?: LWType
private _body?: LWStatement
private _annotations: Annotation[] = []
native() { this._modifiers.push(Md.native()); return this }
static() { this._modifiers.push(Md.static()); return this }
private() { this._modifiers.push(Md.private()); return this }
returns(type: LWType) { this._returnType = type; return this }
body(body: LWStatement) { this._body = body; return this }
parameters(params: FunctionDeclaration['parameters']) { this._parameters.push(...params); return this }
param(name: string): ParamBuilder<FunctionBuilder<P>> {
return new ParamBuilder((name, type) => {
this._parameters.push({name, type})
return this
}, name)
}
block(): BlockBuilder<FunctionBuilder<P>> {
return new BlockBuilder(stmts => {
this._body = S.block(stmts)
return this
})
}
annotation(name: string) {
const annotation: SimpleAnnotation = {
kind: DecoratorKind.SimpleAnnotation,
name,
}
this._annotations.push(annotation)
return this
}
macro(name: string, ...args: (string | LWType)[]) {
const annotation: MacroInvocation = {
kind: DecoratorKind.MacroCall,
name,
args,
}
this._annotations.push(annotation)
return this
}
$(): P {
return this._cont(
DD({generics: [], modifiers: this._modifiers})
.func(this._name, this._parameters,
this._returnType ?? Ts.prim.void,
this._body, this._annotations))
}
}
class FieldBuilder<P> {
constructor(
private _cont: (name: string, type: LWType, modifiers?: Modifier[]) => P,
private _name: string
) {}
private _type?: LWType
private _modifiers: Modifier[] = []
static() { this._modifiers.push(Md.static()); return this }
optional() { this._modifiers.push(Md.optional()); return this }
readonly() { this._modifiers.push(Md.readonly()); return this }
modifiers(modifiers: Modifier[]) { this._modifiers.push(...modifiers); return this }
type(type: LWType) { this._type = type; return this }
funcType(): FunctionTypeBuilder<FieldBuilder<P>> {
return new FunctionTypeBuilder(type => {
this._type = type
return this
})
}
$(): P {
check("Field", this._name, this._type)
return this._cont(this._name!, this._type!, this._modifiers)
}
}
class StructBuilder {
constructor(private _name: string) {}
private _fields: { name: string, type: LWType, modifiers?: Modifier[] }[] = []
field(name: string): FieldBuilder<StructBuilder> {
return new FieldBuilder((name, type, modifiers) => {
this._fields.push({name, type, modifiers})
return this
}, name)
}
$(): StructureDeclaration {
check("Struct", this._name)
return D.struct(this._name!, this._fields)
}
}
class ClassBuilder {
constructor(private _name: string) {}
private _fields: { name: string, type: LWType, modifiers?: Modifier[] }[] = []
private _methods: FunctionDeclaration[] = []
private _oop: ClassDeclaration['oop'] = {
kind: 'class',
base: undefined,
implementations: []
}
extends(type: LWType) { this._oop!.base = type; return this }
implements(type: LWType) { this._oop!.implementations?.push(type); return this }
interface() { this._oop!.kind = 'interface'; return this }
field(name: string): FieldBuilder<ClassBuilder> {
return new FieldBuilder((name, type, modifiers) => {
this._fields.push({name, type, modifiers})
return this
}, name)
}
method(name: string): FunctionBuilder<ClassBuilder> {
return new FunctionBuilder(func => {
this._methods.push(func)
return this
}, name)
}
ctor(): FunctionBuilder<ClassBuilder> {
return this.method(std.names.members.ctor)
}
$(): ClassDeclaration {
check("Class", this._name)
return D.class(this._name!, this._fields, this._methods, this._oop)
}
}
export class Builders {
static expr(): ExpressionBuilder<LWExpression> { return new ExpressionBuilder(id) }
static stmt(): StatementBuilder<LWStatement> { return new StatementBuilder(id) }
static func(name: string): FunctionBuilder<FunctionDeclaration> { return new FunctionBuilder(id, name) }
static struct(name: string): StructBuilder { return new StructBuilder(name) }
static class(name: string): ClassBuilder { return new ClassBuilder(name) }
static access(object?: LWExpression): AccessorBuilder<AccessorExpression> { return new AccessorBuilder(id, object) }
static binary(op: string): BinaryBuilder<BinaryExpression> { return new BinaryBuilder(id, op) }
static unary(op: string): UnaryBuilder<UnaryExpression> { return new UnaryBuilder(id, op) }
static call(): CallBuilder<CallExpression> { return new CallBuilder(id) }
static cast(type: LWType): CheckCastBuilder<CheckCastExpression> { return new CheckCastBuilder(id, 'cast', type) }
static instanceof(type: LWType): CheckCastBuilder<CheckCastExpression> { return new CheckCastBuilder(id, 'instanceof', type) }
static ctor(name?: string): ConstructorBuilder<ConstructorExpression> { return new ConstructorBuilder(id, name) }
static block(): BlockBuilder<LWStatement> { return new BlockBuilder(S.block) }
static decl(name: string, type: LWType): DeclarationBuilder<DeclarationStatement> { return new DeclarationBuilder(id, name, type) }
static if(): IfBuilder<IfStatement> { return new IfBuilder(id) }
static loop(): LoopBuilder<LoopStatement> { return new LoopBuilder(id) }
static return(type?: LWType): ReturnBuilder<LWStatement> { return new ReturnBuilder(id, type) }
}