Date: 2026-01-20 Perry Version: Current main branch (with fixes applied) VSCode Version: Latest (shallow clone)
Result: After implementing fixes for TsTypeAssertion, ??=/||=/&&= operators, comma operator, and 'in' operator, Perry can now compile simple VSCode files to object code. More complex files are blocked by generators, dynamic function calls, and function overloading.
| File | Result | Error Type | Progress |
|---|---|---|---|
charCode.ts |
PASS* | Object file generated | Full lowering + codegen |
uint.ts |
PASS* | Object file generated | Full lowering + codegen |
types.ts |
FAIL | Function overloading | Past lowering, fails in codegen |
arrays.ts |
FAIL | Yield expression (generators) | Lowering stage |
strings.ts |
FAIL | Yield expression (generators) | Lowering stage |
assert.ts |
FAIL | Dynamic function call | Past TsTypeAssertion, new error |
lazy.ts |
FAIL | Dynamic method call | Same as before |
cache.ts |
FAIL | Dynamic new expression | Past ??=, new error |
errors.ts |
FAIL | Dynamic function call | Past TsTypeAssertion, new error |
event.ts |
FAIL | Dynamic new expression | Past ??=, new error |
lifecycle.ts |
FAIL | Unsupported pattern | Same as before |
async.ts |
FAIL | Dynamic new expression | Past TsTypeAssertion, new error |
* These files successfully generate object files but fail at link time because they're library modules without a main entry point.
The following fixes were implemented during this study:
// crates/perry-hir/src/lower.rs
ast::Expr::TsTypeAssertion(ts_assertion) => {
lower_expr(ctx, &ts_assertion.expr)
}// crates/perry-hir/src/lower.rs
ast::Expr::Seq(seq) => {
let mut last_expr = Expr::Undefined;
for expr in &seq.exprs {
last_expr = lower_expr(ctx, expr)?;
}
Ok(last_expr)
}// crates/perry-hir/src/lower.rs
ast::AssignOp::NullishAssign => {
let left = Box::new(lower_assign_target_to_expr(ctx, &assign.left)?);
Box::new(Expr::Logical {
op: LogicalOp::Coalesce,
left,
right: Box::new(rhs),
})
}// crates/perry-hir/src/ir.rs - Added new expression type
In {
property: Box<Expr>,
object: Box<Expr>,
},
// crates/perry-hir/src/lower.rs
if matches!(bin.op, ast::BinaryOp::In) {
let property = Box::new(lower_expr(ctx, &bin.left)?);
let object = Box::new(lower_expr(ctx, &bin.right)?);
return Ok(Expr::In { property, object });
}Files: arrays.ts, strings.ts
Generator functions (function*) and yield expressions are not supported. This requires:
- State machine transformation at compile time
- Runtime iterator protocol support
Symbol.iteratorimplementation
Files: assert.ts, errors.ts, lazy.ts
Calling a variable as a function (e.g., callback() where callback is a parameter) isn't fully supported. The error is "Unsupported callee: LocalGet that is not a closure".
Files: cache.ts, event.ts, async.ts
new SomeExpression() where the expression isn't a simple identifier fails with "New expression callee must be an identifier".
Files: types.ts
TypeScript function overloading (multiple declarations with different arities) causes codegen signature conflicts.
Files: lifecycle.ts
Some destructuring patterns are not fully implemented.
Files affected: types.ts, assert.ts, errors.ts, async.ts
Example code:
return validValues.includes(<TSubtype>value); // line 211 of types.tsError:
Error: Unsupported expression type: TsTypeAssertion(...)
Why it fails:
- Perry handles
value as Type(TsAs) but not<Type>value(TsTypeAssertion) - Both are equivalent at runtime - the expression should just pass through
Fix location: crates/perry-hir/src/lower.rs line ~4425
Proposed fix:
ast::Expr::TsTypeAssertion(ts_assertion) => {
// Same as TsAs - just evaluate the expression, type is compile-time only
lower_expr(ctx, &ts_assertion.expr)
}Files affected: arrays.ts
Example code:
function* groupBy<T>(items: Iterable<T>) {
yield currentGroup; // line ~150
}Error:
Error: Unsupported expression type: Yield(YieldExpr { ... })
Why it fails:
- Perry has no support for generator functions (
function*) - Would require significant runtime support for generator state machines
Fix complexity: HIGH - Requires:
- New
Expr::Yieldvariant in HIR - Generator function lowering (state machine transformation)
- Runtime iterator protocol support
Symbol.iteratorimplementation
Files affected: strings.ts
Example code:
for (...; aStart++, bStart++) { ... } // line ~315Error:
Error: Unsupported expression type: Seq(SeqExpr { exprs: [Update(...), Update(...)] })
Why it fails:
- The comma operator evaluates multiple expressions left-to-right, returning the last value
- Perry doesn't handle
SeqExpr
Fix location: crates/perry-hir/src/lower.rs
Proposed fix:
ast::Expr::Seq(seq) => {
// Evaluate all expressions, return the last one's value
let mut stmts = Vec::new();
let exprs: Vec<_> = seq.exprs.iter()
.map(|e| lower_expr(ctx, e))
.collect::<Result<Vec<_>>>()?;
// For HIR, we can wrap all but last in ExprStmt, then return last
// Or add Expr::Seq to HIR
if exprs.is_empty() {
Ok(Expr::Undefined)
} else if exprs.len() == 1 {
Ok(exprs.into_iter().next().unwrap())
} else {
Ok(Expr::Seq(exprs)) // Need to add this variant
}
}Files affected: cache.ts, event.ts
Example code:
this._value ??= this._fn();Error:
Error: Unsupported assignment operator: "??="
Why it fails:
- Modern assignment operators (??=, ||=, &&=) not implemented
a ??= bmeansa = a ?? bbut only evaluatesbifais null/undefined
Fix location: crates/perry-hir/src/lower.rs in assignment handling
Proposed fix:
// In the assignment operator handling:
"??=" => {
// a ??= b → a = (a ?? b)
Ok(Expr::Assign {
target: lower_assign_target(ctx, &assign.left)?,
value: Box::new(Expr::NullishCoalesce {
left: Box::new(lower_assign_target_as_expr(ctx, &assign.left)?),
right: Box::new(lower_expr(ctx, &assign.right)?),
}),
})
}Files affected: lazy.ts
Example code:
const value = executor(); // Where executor is a parameterError:
Error: Unsupported method call: executor
Why it fails:
- Perry's call handling expects known patterns (direct function calls, method calls)
- Calling a variable that holds a function reference isn't fully supported
Fix location: crates/perry-hir/src/lower.rs in call expression handling
Files affected: lifecycle.ts
Error:
Error: Unsupported pattern
Why it fails:
- Some destructuring patterns aren't fully implemented
- Could be rest patterns, nested destructuring, or default values
Two files passed the lowering phase:
- charCode.ts - A
const enumwith numeric values only - uint.ts - Simple constant definitions
Both failed at linking because they have no main function (they're library modules).
This indicates Perry's basic enum/const support works for simple cases.
Based on frequency in VSCode and implementation complexity:
| Priority | Feature | Occurrences | Complexity | Time Estimate |
|---|---|---|---|---|
| 1 | TsTypeAssertion | 4 files | LOW | 1 hour |
| 2 | ??= operator | 2 files | LOW | 2 hours |
| 3 | Comma operator (Seq) | 1 file | LOW | 2 hours |
| 4 | Variable function calls | 1 file | MEDIUM | 4 hours |
| 5 | Destructuring patterns | 1 file | MEDIUM | 1 day |
| 6 | Generators/Yield | 1 file | HIGH | 1-2 weeks |
- Add TsTypeAssertion support - Single line fix in lower.rs
- Add ??= operator - Desugar to nullish coalescing
- Add Seq expression - Evaluate all, return last
- Improve call expression handling - Support calling any expression
- Complete destructuring patterns - Handle rest, defaults
- Generator support - Full state machine transformation
- Library module compilation - Don't require main()
All TypeScript language fixes go in:
crates/perry-hir/src/lower.rs
The main lower_expr function starts around line 2629 and the catch-all error is at line 4430:
_ => Err(anyhow!("Unsupported expression type: {:?}", expr)),New expression types need corresponding variants in:
crates/perry-hir/src/ir.rs
VSCode cannot be compiled with Perry today, but the gaps are well-defined:
- TypeScript Language Features - Missing syntax support (TsTypeAssertion, generators, etc.)
- Advanced Operators - ??=, comma operator
- Call Patterns - Calling variables as functions
The first three quick fixes (TsTypeAssertion, ??=, Seq) would likely unlock several more files. Generator support is the largest missing feature that VSCode uses heavily.
This experiment confirms the feasibility study's assessment: TypeScript language features are the primary blocker, not runtime APIs (which weren't even reached).