Skip to content

Latest commit

 

History

History
375 lines (274 loc) · 10.3 KB

File metadata and controls

375 lines (274 loc) · 10.3 KB

VSCode Compilation Feasibility Study - Experimental Results

Summary

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.


Files Tested (After Fixes)

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.


Fixes Applied

The following fixes were implemented during this study:

1. TsTypeAssertion (Angle Bracket Syntax) ✅ FIXED

// crates/perry-hir/src/lower.rs
ast::Expr::TsTypeAssertion(ts_assertion) => {
    lower_expr(ctx, &ts_assertion.expr)
}

2. Comma Operator (Seq Expression) ✅ FIXED

// 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)
}

3. Logical Assignment Operators (&&=, ||=, ??=) ✅ FIXED

// 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),
    })
}

4. 'in' Operator ✅ FIXED

// 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 });
}

Remaining Blockers (New Errors After Fixes)

1. Generator Functions / Yield (HIGH priority)

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.iterator implementation

2. Dynamic Function Calls (MEDIUM priority)

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".

3. Dynamic New Expressions (MEDIUM priority)

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".

4. Function Overloading (LOW priority for now)

Files: types.ts

TypeScript function overloading (multiple declarations with different arities) causes codegen signature conflicts.

5. Destructuring Patterns (MEDIUM priority)

Files: lifecycle.ts

Some destructuring patterns are not fully implemented.


Specific Failures (Original Analysis)

1. TsTypeAssertion (Angle Bracket Syntax)

Files affected: types.ts, assert.ts, errors.ts, async.ts

Example code:

return validValues.includes(<TSubtype>value);  // line 211 of types.ts

Error:

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)
}

2. Yield Expression (Generators)

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:

  1. New Expr::Yield variant in HIR
  2. Generator function lowering (state machine transformation)
  3. Runtime iterator protocol support
  4. Symbol.iterator implementation

3. Comma Operator (Seq Expression)

Files affected: strings.ts

Example code:

for (...; aStart++, bStart++) { ... }  // line ~315

Error:

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
    }
}

4. Nullish Coalescing Assignment (??=)

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 ??= b means a = a ?? b but only evaluates b if a is 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)?),
        }),
    })
}

5. Unsupported Method Call Pattern

Files affected: lazy.ts

Example code:

const value = executor();  // Where executor is a parameter

Error:

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


6. Unsupported Pattern (Destructuring)

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

Files That Successfully Lower

Two files passed the lowering phase:

  1. charCode.ts - A const enum with numeric values only
  2. 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.


Priority Fix Order

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

Recommended Next Steps

Quick Wins (Today)

  1. Add TsTypeAssertion support - Single line fix in lower.rs
  2. Add ??= operator - Desugar to nullish coalescing
  3. Add Seq expression - Evaluate all, return last

Medium Term (This Week)

  1. Improve call expression handling - Support calling any expression
  2. Complete destructuring patterns - Handle rest, defaults

Longer Term

  1. Generator support - Full state machine transformation
  2. Library module compilation - Don't require main()

Code Locations for Fixes

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

Conclusion

VSCode cannot be compiled with Perry today, but the gaps are well-defined:

  1. TypeScript Language Features - Missing syntax support (TsTypeAssertion, generators, etc.)
  2. Advanced Operators - ??=, comma operator
  3. 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).