Skip to content

feat: parse and preserve parameter decorators in AST#3023

Open
PaperPrototype wants to merge 2 commits intoAssemblyScript:mainfrom
PaperPrototype:parse-parameter-decorators-v2
Open

feat: parse and preserve parameter decorators in AST#3023
PaperPrototype wants to merge 2 commits intoAssemblyScript:mainfrom
PaperPrototype:parse-parameter-decorators-v2

Conversation

@PaperPrototype
Copy link
Copy Markdown

@PaperPrototype PaperPrototype commented May 4, 2026

Made a new PR instead of force pushing (see original #3015)

Claude (with my own modifications):

Adds support for decorator syntax on function parameters (e.g. @decorator value: i32). Decorators are parsed and stored in ParameterNode.decorators and FunctionTypeNode.explicitThisDecorators, making them available to transformer plugins without affecting compilation.

  • Parser: parses leading parameter decorators into AST; emits TS1206 only for misplaced decorators (e.g. @decorator variableName: i32)
  • AST: new decorators field on ParameterNode, explicitThisDecorators on FunctionTypeNode, decoratedFunctionTypes index on Source
  • Serialization: inline parameter decorator serialization in extra/ast.ts
  • Transform API: new beforeCompile(program) hook in cli/index.d.ts for transformer-owned validation after program init, before WASM compilation
  • Tests: parser round-trip, compiler clean-compile, parser error cases, and transform example that reads parameter decorators via afterInitialize

Me:

The following syntax is now parsed and included in the AST:

function regular(@first value: i32): void {}
function multiParam(@first huh: string, @second(123, 456) works: i32, @third("can pass a string?") hello: string): void {}
function rest(@rest ...values: i32[]): void {}
function withthis(@self this: i32, value: i32): i32 { return this; }

class Box {
  constructor(@field public value: i32) {}
  method(@arg value: i32): void {}
}

type Callback = (@arg value: i32) => void;
const expression = function(@arg value: i32): void {};
const arrow = (@arg value: i32): void => {};
namespace ns {
  export function nested(@arg value: i32): void {}
}

I also added a beforeCompile hook to the transformer API so transformers can validate decorators if they want to (as per @MaxGraey suggestion)

Adds support for decorator syntax on function parameters (e.g.
`@decorator value: i32`). Decorators are parsed and stored in
`ParameterNode.decorators` and `FunctionTypeNode.explicitThisDecorators`,
making them available to transformer plugins without affecting compilation.

- Parser: parses leading parameter decorators into AST; emits TS1206
  only for misplaced decorators (e.g. `value @Deco: i32`)
- AST: new `decorators` field on ParameterNode, `explicitThisDecorators`
  on FunctionTypeNode, `decoratedFunctionTypes` index on Source
- Serialization: inline parameter decorator serialization in extra/ast.ts
- Transform API: new `beforeCompile(program)` hook in cli/index.d.ts for
  transformer-owned validation after program init, before WASM compilation
- Tests: parser round-trip, compiler clean-compile, parser error cases,
  and transform example that reads parameter decorators via afterInitialize

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@PaperPrototype
Copy link
Copy Markdown
Author

I regenerated fixtures as well, there was no difference.

@PaperPrototype
Copy link
Copy Markdown
Author

PaperPrototype commented May 4, 2026

Oh, do I need to add my name to the contributors list? Er, idk how that all works. EDIT: I already did, i just forgot

@MaxGraey
Copy link
Copy Markdown
Member

MaxGraey commented May 4, 2026

You could have just forced a merge of the latest main branch into your branch. But if you've already started a new one you should close the previous one

@PaperPrototype
Copy link
Copy Markdown
Author

PaperPrototype commented May 5, 2026

I am scared of force pushing, too many nightmares (I almost deleted an entire open source project's main branch once... scary moment) ignore this cough

@PaperPrototype
Copy link
Copy Markdown
Author

PaperPrototype commented May 5, 2026

You could have just forced a merge of the latest main branch into your branch. But if you've already started a new one you should close the previous one

Oh, I see what you meant... yeah that would have worked 🤣

Comment thread cli/index.d.ts
Comment on lines +276 to +280
/** Called after program initialization, before WASM compilation. Transformers should use
* this hook to perform custom validation of AST constructs such as parameter decorators
* and emit their own diagnostics. Decorators remain in the AST unchanged. */
beforeCompile?(program: Program): void | Promise<void>;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Transformers should use

This can be cited as an example, not as a requirement. Also, the comment is rather wordy.

Comment thread cli/index.d.ts
/** Called after program initialization, before WASM compilation. Transformers should use
* this hook to perform custom validation of AST constructs such as parameter decorators
* and emit their own diagnostics. Decorators remain in the AST unchanged. */
beforeCompile?(program: Program): void | Promise<void>;
Copy link
Copy Markdown
Member

@MaxGraey MaxGraey May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I haven't seen any indication that it's been integrated into the pipeline and well tested. For instance afterParse:
https://github.com/search?q=repo%3AAssemblyScript%2Fassemblyscript+afterParse&type=code
for beforeCompile need to do the same

Comment thread cli/index.d.ts
/** Called after program initialization, before WASM compilation. Transformers should use
* this hook to perform custom validation of AST constructs such as parameter decorators
* and emit their own diagnostics. Decorators remain in the AST unchanged. */
beforeCompile?(program: Program): void | Promise<void>;
Copy link
Copy Markdown
Member

@MaxGraey MaxGraey May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's even less clear why we need afterInitialize when we have afterInitialize?

validate?(program: Program): Promise<bool> | bool;

Specialized validate would be appropriate more, as I suggested. Because it could optionally interrupt the compilation flow without trigger exception (soft bail)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants