tree-sitter-supercollider
tree-sitter-supercollider copied to clipboard
Fix: method-call and flatten binary operators precedence, Add: List Comprehension
Fix method-call precedence, unify binary operators, and implement list comprehensions
Summary
- Operators: All binary operators now use consistent left-to-right evaluation
- Method chains bind tightly: New
_primary/_postfixrules ensure method calls attach to the right expression - List comprehensions: Full support for
{:syntax with generators, guards, bindings, side effects, and termination - Fixed named arguments:
parameter: valuesyntax now correctly stores values - Control flow:
if,while,switchhandle expressions consistently - Block syntax: Code blocks use
{ … }, parentheses use( … ) - Added dedicated rules for nil-check operators
Up to discussion:
- ~~Keyword messages: Separated from binary operators into dedicated rule for
[1,2,3] collect: {...}syntax~~
Motivation
Problems fixed:
- No support for list comprehensions (
{: x*2, x <- (1..10)}) identifier:patterns conflated with binary operators{ … }and(…)confusion.midiratiobinding to entire expressions instead of grouped RHScurve: -1stored incorrectly undername:instead ofvalue:- Operator precedence tiers that don't exist in sclang
This branch is organized into small commits:
Some changes span over different (non-)consecutive commits. But it is easy to distinguish logical changes:
- Introduce block/group distinction (1)
- Add _primary/_postfix hierarchy (2-3)
- Flatten binary precedence (4-6)
- Fix argument handling (7)
- Update control structures (10)
- Clean up unused code (8-9, 11-13)
- Add keyword messages (14-15)
- Add list comprehensions (15-18)
- reorganized
_expression_statementand_object(21) - Dedicated rules for nil-check operators (22)
List comprehensions
List comprehension syntax:
{: body, qualifier, qualifier, ... }
Supported qualifiers:
- Generators:
x <- (1..10) - Guards:
x.odd - Variable bindings:
var z = x*x - Side effects:
:: x.postln - Termination:
:while x < 100
Keyword messages
Separated identifier: patterns from binary operators:
keyword_message: $ => prec.left(PRECEDENCE.keyword_message, seq(
field('receiver', $._postfix),
field('selector', alias(/[a-zA-Z_]\w*:/, $.keyword_selector)),
field('argument', $._postfix)
))
This handles [1,2,3] select: {|x| x > 1} as a distinct syntactic form.
Blocks & grouping
code_block: '{' parameter_list? expression_sequence? '}'group: '(' expression ')'function_blocknow usescode_blockdirectly
Chains > binops
- Introduce
_primary(atoms) and_postfix(primary + chain) - Method chains parse left-associatively with high
CALLprecedence function_callreceivers restricted to_primary
Flat binary operators (left-to-right)
All symbolic binary operators now share one BIN tier:
binary_expression: $ => prec.left(PRECEDENCE.BIN, seq(
field('left', $._postfix),
field('operator', choice(
'||','&&','|','^','&','==','!=','<','<=','>','>=',
'<<','>>','+','-','++','+/+','*','/','%','**'
)),
field('right', $._postfix)
))
Arguments
- Drop
argument_calls/unnamed_argument named_argument: name ':' valuewith RHS undervalue:
Control structures
if,while,switchexpressions take_postfix(chains bind)
Before / After
List comprehensions
- Before: Not supported
- After: Supported
{: [x,y], x <- (1..5), y <- (1..x), (x+y).isPrime }
Keyword messages
- Dedicated AST node with receiver/selector/argument fields
.midiratio binding
- Before: Could attach to
\freq.kr(440) * (…)as a whole - After: Attaches to grouped RHS term:
\freq.kr(440) * (Env.perc(...).ar * 48).midiratio
└────── group ────────┘└postfix┘
Named argument
- Before:
curve: -1created twoname:fields - After:
name: curve,value: -1
Testing
Needs tests for:
- List comprehension qualifiers
- Keyword message vs binary operator disambiguation
- Postfix vs group binding
- Named args with negative values
- Left-to-right operator evaluation
Amazing @smoge !! I am completly swamped in my life atm but just wanna say this looks so good. Will give it a bit more comment later. Maybe @bsssssss could look at this overhaul ?
Just had a quick look at this, that is awesome work ! Feels somewhat more natural to follow to me :)
I had trouble with neovim's tree-sitter complaining about queries in highlights.scm due to some typos and non-existing nodes. I also added some keywords. Here is the patch. Curious to know what editor are you using @smoge ?
From limited testing i noticed some things,
groups are not parsed correctly at the top level it seems like
(
v = 2;
)
gives us this AST
(source_file ; [0, 0] - [3, 0]
(ERROR ; [0, 0] - [3, 0]
(group ; [0, 0] - [2, 1]
(variable_definition ; [1, 0] - [1, 5]
name: (variable ; [1, 0] - [1, 1]
(instance_var ; [1, 0] - [1, 1]
name: (identifier))) ; [1, 0] - [1, 1]
value: (literal ; [1, 4] - [1, 5]
(number ; [1, 4] - [1, 5]
(integer))))))) ; [1, 4] - [1, 5]
Maybe because group it is not part of expression ?
With ~clock = TempoClock(180/60); , we get
(source_file ; [0, 0] - [1, 0]
(variable_definition ; [0, 0] - [0, 27]
name: (variable ; [0, 0] - [0, 6]
(environment_var ; [0, 0] - [0, 6]
name: (identifier) ; [0, 0] - [0, 1]
name: (identifier))) ; [0, 1] - [0, 6]
(ERROR ; [0, 9] - [0, 19]
(identifier)) ; [0, 9] - [0, 19]
value: (group ; [0, 19] - [0, 27]
(binary_expression ; [0, 20] - [0, 26]
left: (literal ; [0, 20] - [0, 23]
(number ; [0, 20] - [0, 23]
(integer))) ; [0, 20] - [0, 23]
right: (literal ; [0, 24] - [0, 26]
(number ; [0, 24] - [0, 26]
(integer))))))) ; [0, 24] - [0, 26]
Not sure why ?
#63