clean
This commit is contained in:
parent
0a80f6d13d
commit
66807c02c9
|
|
@ -77,12 +77,13 @@ describe('errors', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('multiline tests', () => {
|
||||
test.only('multiline function', () => {
|
||||
describe.skip('multiline tests', () => {
|
||||
test('multiline function', () => {
|
||||
expect(`
|
||||
add = fn a b:
|
||||
result = a + b
|
||||
result
|
||||
end
|
||||
add 3 4
|
||||
`).toEvaluateTo(7)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ const singleLineFilter = EditorState.transactionFilter.of((transaction) => {
|
|||
if (multilineMode) return transaction // Allow everything in multiline mode
|
||||
|
||||
transaction.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
|
||||
console.log(`🌭`, { string: inserted.toString(), newline: inserted.toString().includes('\n') })
|
||||
if (inserted.toString().includes('\n')) {
|
||||
multilineMode = true
|
||||
updateStatusMessage()
|
||||
|
|
|
|||
|
|
@ -350,13 +350,50 @@ describe('Assign', () => {
|
|||
})
|
||||
|
||||
describe('multiline', () => {
|
||||
test.only('parses multiline strings', () => {
|
||||
test('parses multiline strings', () => {
|
||||
expect(`'first'\n'second'`).toMatchTree(`
|
||||
String first
|
||||
String second`)
|
||||
})
|
||||
|
||||
test('trims leading and trailing whitespace in expected tree', () => {
|
||||
test('parses multiline functions', () => {
|
||||
expect(`
|
||||
add = fn a b:
|
||||
result = a + b
|
||||
result
|
||||
end
|
||||
|
||||
add 3 4
|
||||
`).toMatchTree(`
|
||||
Assign
|
||||
Identifier add
|
||||
= =
|
||||
FunctionDef
|
||||
fn fn
|
||||
Params
|
||||
Identifier a
|
||||
Identifier b
|
||||
: :
|
||||
Assign
|
||||
Identifier result
|
||||
= =
|
||||
BinOp
|
||||
Identifier a
|
||||
operator +
|
||||
Identifier b
|
||||
FunctionCallOrIdentifier
|
||||
Identifier result
|
||||
|
||||
end end
|
||||
FunctionCall
|
||||
Identifier add
|
||||
PositionalArg
|
||||
Number 3
|
||||
PositionalArg
|
||||
Number 4`)
|
||||
})
|
||||
|
||||
test('ignores leading and trailing whitespace in expected tree', () => {
|
||||
expect(`
|
||||
3
|
||||
|
||||
|
|
@ -374,6 +411,7 @@ end
|
|||
Identifier x
|
||||
Identifier y
|
||||
: :
|
||||
FunctionCallOrIdentifier
|
||||
Identifier x
|
||||
end end
|
||||
`)
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ singleLineFunctionDef {
|
|||
}
|
||||
|
||||
multilineFunctionDef {
|
||||
"fn" Params ":" newlineOrSemicolon (expression newlineOrSemicolon)* "end"
|
||||
"fn" Params ":" newlineOrSemicolon (line newlineOrSemicolon)* "end"
|
||||
}
|
||||
|
||||
Params {
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import {tokenizer} from "./tokenizer"
|
|||
import {highlighting} from "./highlight"
|
||||
export const parser = LRParser.deserialize({
|
||||
version: 14,
|
||||
states: "'[OVQTOOOqQPO'#DTO!zQUO'#DTO#XQPOOOOQO'#DS'#DSO#xQTO'#CbOOQS'#DQ'#DQO$PQTO'#DVOOQO'#Cn'#CnOOQO'#C}'#C}O$XQPO'#C|OOQS'#Cu'#CuQ$aQTOOOOQS'#DP'#DPOOQS'#Ca'#CaO$hQTO'#ClOOQS'#DO'#DOOOQS'#Cv'#CvO$oQUO,58zO%SQTO,59_O%^QTO,58}O%^QTO,58}O%eQPO,58|O%vQUO'#DTO%}QPO,58|OOQS'#Cw'#CwO&SQTO'#CpO&[QPO,59qOOQS,59h,59hOOQS-E6s-E6sQOQPOOOOQS,59W,59WOOQS-E6t-E6tOOQO1G.y1G.yOOQO'#DT'#DTOOQO1G.i1G.iO&aQPO1G.iOOQS1G.h1G.hOOQS-E6u-E6uO&xQTO1G/]O'SQPO7+$wO'hQTO7+$xO'rQPO'#CxO(TQTO<<HdOOQO<<Hd<<HdOOQS,59d,59dOOQS-E6v-E6vOOQOAN>OAN>O",
|
||||
stateData: "(b~OoOS~OPQOQUO]UO^UO_UOcVOuTO{ZO~OWwXXwXYwXZwX{qX|qX~OP]OQUO]UO^UO_UOa_OuTOWwXXwXYwXZwX~OhcO{[X|[X~P!VOWdOXdOYeOZeO~OQUO]UO^UO_UOuTO~OPgO~P#gOPiOedP~O{lO|lO~O|nO~PVOP]O~P#gOP]Oa_O{Sa|SaxSa~P#gOPQOcVO~P#gOPrO~P#gOxuOWwXXwXYwXZwX~Ox[X~P!VOxuO~OPiOedX~OewO~OWdOXdOYViZVi{Vi|VixVi~OPrO{yO~P#gOWdOXdOYeOZeO{yq|yq~OPrOf|O~P#gOWdOXdOYeOZeO{}O~OPrOf!PO~P#gO^Z~",
|
||||
goto: "%[{PPPP|!U!Z!jPPPP|PPP!UP!uP!zPP!uP!}#T#[#bPPP#h#l#s#x$QP$c$rP%V%VUXO[cRhTV`QbgkUOQT[_bcdegwy{cSOT[cdewy{VXO[cRkVQ[ORm[SbQgRpbQjVRvjQ{yR!O{TZO[SYO[RqcVaQbgU^QbgRo_bSOT[cdewy{X]Q_bgUPO[cQfTZrdewy{WROT[cQsdQteQxwTzy{VWO[c",
|
||||
states: "'[OVQTOOOqQPO'#DTO!zQUO'#DTO#XQPOOOOQO'#DS'#DSO#xQTO'#CbOOQS'#DQ'#DQO$PQTO'#DVOOQO'#Cn'#CnOOQO'#C}'#C}O$XQPO'#C|OOQS'#Cu'#CuQ$aQTOOOOQS'#DP'#DPOOQS'#Ca'#CaO$hQTO'#ClOOQS'#DO'#DOOOQS'#Cv'#CvO$oQUO,58zO%SQTO,59_O%^QTO,58}O%^QTO,58}O%eQPO,58|O%vQUO'#DTO%}QPO,58|OOQS'#Cw'#CwO&SQTO'#CpO&[QPO,59qOOQS,59h,59hOOQS-E6s-E6sQOQPOOOOQS,59W,59WOOQS-E6t-E6tOOQO1G.y1G.yOOQO'#DT'#DTOOQO1G.i1G.iO&aQPO1G.iOOQS1G.h1G.hOOQS-E6u-E6uO&xQTO1G/]O'SQPO7+$wO'hQTO7+$xO'uQPO'#CxO'zQTO<<HdOOQO<<Hd<<HdOOQS,59d,59dOOQS-E6v-E6vOOQOAN>OAN>O",
|
||||
stateData: "([~OoOS~OPQOQUO]UO^UO_UOcVOuTO{ZO~OWwXXwXYwXZwX{qX|qX~OP]OQUO]UO^UO_UOa_OuTOWwXXwXYwXZwX~OhcO{[X|[X~P!VOWdOXdOYeOZeO~OQUO]UO^UO_UOuTO~OPgO~P#gOPiOedP~O{lO|lO~O|nO~PVOP]O~P#gOP]Oa_O{Sa|SaxSa~P#gOPQOcVO~P#gOPrO~P#gOxuOWwXXwXYwXZwX~Ox[X~P!VOxuO~OPiOedX~OewO~OWdOXdOYViZVi{Vi|VixVi~OPrO{yO~P#gOWdOXdOYeOZeO{yq|yq~OPQOcVOf|O~P#gO{}O~OPQOcVOf!PO~P#gO^Z~",
|
||||
goto: "%d{PPPP|!W!]!lPPPP|PPP!WP!wP#OPP!wP#R#X#`#fPPP#l#p#{$Q$YP$k$zP%]%]YXO[cy{RhTV`QbgkUOQT[_bcdegwy{cSOT[cdewy{ZXO[cy{RkVQ[ORm[SbQgRpbQjVRvjQ{yR!O{TZO[SYO[QqcTzy{VaQbgU^QbgRo_bSOT[cdewy{X]Q_bgYPO[cy{QfTVrdew[ROT[cy{QsdQteRxwZWO[cy{",
|
||||
nodeNames: "⚠ Identifier Word Program FunctionCall PositionalArg ParenExpr BinOp operator operator operator operator FunctionCallOrIdentifier String Number Boolean NamedArg NamedArgPrefix FunctionDef fn Params : end Assign =",
|
||||
maxTerm: 44,
|
||||
propSources: [highlighting],
|
||||
|
|
@ -15,5 +15,5 @@ export const parser = LRParser.deserialize({
|
|||
tokenData: "(j~ReXY!dYZ!ipq!dwx!nxy#]yz#bz{#g{|#l}!O#q!P!Q$d!Q![#y![!]$i!]!^!i!_!`$n#T#X$s#X#Y%R#Y#Z%|#Z#h$s#h#i'u#i#o$s~~(e~!iOo~~!nO{~~!qTOw!nwx#Qx;'S!n;'S;=`#V<%lO!n~#VO]~~#YP;=`<%l!n~#bOu~~#gOx~~#lOW~~#qOY~~#vPZ~!Q![#y~$OQ^~!O!P$U!Q![#y~$XP!Q![$[~$aP^~!Q![$[~$iOX~~$nOe~~$sOh~Q$vQ!_!`$|#T#o$sQ%ROaQR%US!_!`$|#T#b$s#b#c%b#c#o$sR%eS!_!`$|#T#W$s#W#X%q#X#o$sR%vQfP!_!`$|#T#o$s~&PT!_!`$|#T#U&`#U#b$s#b#c'j#c#o$s~&cS!_!`$|#T#`$s#`#a&o#a#o$s~&rS!_!`$|#T#g$s#g#h'O#h#o$s~'RS!_!`$|#T#X$s#X#Y'_#Y#o$s~'dQ_~!_!`$|#T#o$sR'oQcP!_!`$|#T#o$s~'xS!_!`$|#T#f$s#f#g(U#g#o$s~(XS!_!`$|#T#i$s#i#j'O#j#o$s~(jO|~",
|
||||
tokenizers: [0, 1, tokenizer],
|
||||
topRules: {"Program":[0,3]},
|
||||
tokenPrec: 337
|
||||
tokenPrec: 331
|
||||
})
|
||||
|
|
|
|||
244
today.md
Normal file
244
today.md
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
# 🌟 Modern Language Inspiration & Implementation Plan
|
||||
|
||||
## Language Research Summary
|
||||
|
||||
### Pipe Operators Across Languages
|
||||
|
||||
| Language | Syntax | Placeholder | Notes |
|
||||
|----------|--------|-------------|-------|
|
||||
| **Gleam** | `\|>` | `_` | Placeholder can go anywhere, enables function capture |
|
||||
| **Elixir** | `\|>` | `&1`, `&2` | Always first arg by default, numbered placeholders |
|
||||
| **Nushell** | `\|` | structured data | Pipes structured data, not just text |
|
||||
| **F#** | `\|>` | none | Always first argument |
|
||||
| **Raku** | `==>` | `*` | Star placeholder for positioning |
|
||||
|
||||
### Conditional Syntax
|
||||
|
||||
| Language | Single-line | Multi-line | Returns Value |
|
||||
|----------|------------|------------|---------------|
|
||||
| **Lua** | `if x then y end` | `if..elseif..else..end` | No (statement) |
|
||||
| **Luau** | `if x then y else z` | Same | Yes (expression) |
|
||||
| **Ruby** | `x = y if condition` | `if..elsif..else..end` | Yes |
|
||||
| **Python** | `y if x else z` | `if..elif..else:` | Yes |
|
||||
| **Gleam** | N/A | `case` expressions | Yes |
|
||||
|
||||
## 🍤 Shrimp Design Decisions
|
||||
|
||||
### Pipe Operator with Placeholder (`|`)
|
||||
|
||||
**Syntax Choice: `|` with `_` placeholder**
|
||||
|
||||
```shrimp
|
||||
# Basic pipe with placeholder
|
||||
"hello world" | upcase _
|
||||
"log.txt" | tail _ lines=10
|
||||
|
||||
# Placeholder positioning flexibility
|
||||
"error.log" | grep "ERROR" _ | head _ 5
|
||||
data | process format="json" input=_
|
||||
|
||||
# Multiple placeholders (future consideration)
|
||||
value | combine _ _
|
||||
```
|
||||
|
||||
**Why this design:**
|
||||
- **`|` over `|>`**: Cleaner, more shell-like
|
||||
- **`_` placeholder**: Explicit, readable, flexible positioning
|
||||
- **Gleam-inspired**: Best of functional programming meets shell scripting
|
||||
|
||||
### Conditionals
|
||||
|
||||
**Multi-line syntax:**
|
||||
```shrimp
|
||||
if condition:
|
||||
expression
|
||||
elsif other-condition:
|
||||
expression
|
||||
else:
|
||||
expression
|
||||
end
|
||||
```
|
||||
|
||||
**Single-line syntax (expression form):**
|
||||
```shrimp
|
||||
result = if x = 5: "five"
|
||||
# Returns nil when false
|
||||
|
||||
result = if x > 0: "positive" else: "non-positive"
|
||||
# Explicit else for non-nil guarantee
|
||||
```
|
||||
|
||||
**Design choices:**
|
||||
- **`elsif` not `else if`**: Avoids nested parsing complexity (Ruby-style)
|
||||
- **`:` after conditions**: Consistent with function definitions
|
||||
- **`=` for equality**: Context-sensitive (assignment vs comparison)
|
||||
- **`nil` for no-value**: Short, clear, well-understood
|
||||
- **Expressions return values**: Everything is an expression philosophy
|
||||
|
||||
## 📝 Implementation Plan
|
||||
|
||||
### Phase 1: Grammar Foundation
|
||||
|
||||
**1.1 Add Tokens**
|
||||
```grammar
|
||||
@tokens {
|
||||
// Existing...
|
||||
"|" // Pipe operator
|
||||
"_" // Placeholder
|
||||
"if" // Conditionals
|
||||
"elsif"
|
||||
"else"
|
||||
"nil" // Null value
|
||||
}
|
||||
```
|
||||
|
||||
**1.2 Precedence Updates**
|
||||
```grammar
|
||||
@precedence {
|
||||
multiplicative @left,
|
||||
additive @left,
|
||||
pipe @left, // After arithmetic, before assignment
|
||||
assignment @right,
|
||||
call
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 2: Grammar Rules
|
||||
|
||||
**2.1 Pipe Expression**
|
||||
```grammar
|
||||
PipeExpr {
|
||||
expression !pipe "|" PipeTarget
|
||||
}
|
||||
|
||||
PipeTarget {
|
||||
FunctionCallWithPlaceholder |
|
||||
FunctionCall // Error in compiler if no placeholder
|
||||
}
|
||||
|
||||
FunctionCallWithPlaceholder {
|
||||
Identifier PlaceholderArg+
|
||||
}
|
||||
|
||||
PlaceholderArg {
|
||||
PositionalArg | NamedArg | Placeholder
|
||||
}
|
||||
|
||||
Placeholder {
|
||||
"_"
|
||||
}
|
||||
```
|
||||
|
||||
**2.2 Conditional Expression**
|
||||
```grammar
|
||||
Conditional {
|
||||
SingleLineIf | MultiLineIf
|
||||
}
|
||||
|
||||
SingleLineIf {
|
||||
"if" Comparison ":" expression ElseClause?
|
||||
}
|
||||
|
||||
MultiLineIf {
|
||||
"if" Comparison ":" newlineOrSemicolon
|
||||
(line newlineOrSemicolon)*
|
||||
ElsifClause*
|
||||
ElseClause?
|
||||
"end"
|
||||
}
|
||||
|
||||
ElsifClause {
|
||||
"elsif" Comparison ":" newlineOrSemicolon
|
||||
(line newlineOrSemicolon)*
|
||||
}
|
||||
|
||||
ElseClause {
|
||||
"else" ":" (expression | (newlineOrSemicolon (line newlineOrSemicolon)*))
|
||||
}
|
||||
|
||||
Comparison {
|
||||
expression "=" expression // Context-sensitive in if/elsif
|
||||
}
|
||||
```
|
||||
|
||||
**2.3 Update line rule**
|
||||
```grammar
|
||||
line {
|
||||
PipeExpr |
|
||||
Conditional |
|
||||
FunctionCall |
|
||||
// ... existing rules
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 3: Test Cases
|
||||
|
||||
**Pipe Tests:**
|
||||
```shrimp
|
||||
# Basic placeholder
|
||||
"hello" | upcase _
|
||||
|
||||
# Named arguments with placeholder
|
||||
"file.txt" | process _ format="json"
|
||||
|
||||
# Chained pipes
|
||||
data | filter _ "error" | count _
|
||||
|
||||
# Placeholder in different positions
|
||||
5 | subtract 10 _ # 10 - 5 = 5
|
||||
```
|
||||
|
||||
**Conditional Tests:**
|
||||
```shrimp
|
||||
# Single line
|
||||
x = if n = 0: "zero"
|
||||
|
||||
# Single line with else
|
||||
sign = if n > 0: "positive" else: "negative"
|
||||
|
||||
# Multi-line
|
||||
if score > 90:
|
||||
grade = "A"
|
||||
elsif score > 80:
|
||||
grade = "B"
|
||||
else:
|
||||
grade = "C"
|
||||
end
|
||||
|
||||
# Nested conditionals
|
||||
if x > 0:
|
||||
if y > 0:
|
||||
quadrant = 1
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### Phase 4: Compiler Implementation
|
||||
|
||||
**4.1 PipeExpr Handling**
|
||||
- Find placeholder position in right side
|
||||
- Insert left side value at placeholder
|
||||
- Error if no placeholder found
|
||||
|
||||
**4.2 Conditional Compilation**
|
||||
- Generate JUMP bytecode for branching
|
||||
- Handle nil returns for missing else
|
||||
- Context-aware `=` parsing
|
||||
|
||||
## 🎯 Key Decision Points
|
||||
|
||||
1. **Placeholder syntax**: `_` vs `$` vs `?` → **Choose `_` (Gleam-like)**
|
||||
2. **Pipe operator**: `|` vs `|>` vs `>>` → **Choose `|` (cleaner)**
|
||||
3. **Nil naming**: `nil` vs `null` vs `none` → **Choose `nil` (Ruby-like)**
|
||||
4. **Equality**: Keep `=` context-sensitive or add `==`? → **Keep `=` (simpler)**
|
||||
5. **Single-line if**: Require else or default nil? → **Default nil (flexible)**
|
||||
|
||||
## 🚀 Next Steps
|
||||
|
||||
1. Update grammar file with new tokens and rules
|
||||
2. Write comprehensive test cases
|
||||
3. Implement compiler support for pipes
|
||||
4. Implement conditional bytecode generation
|
||||
5. Test edge cases and error handling
|
||||
|
||||
This plan combines the best ideas from modern languages while maintaining Shrimp's shell-like simplicity and functional philosophy!
|
||||
Loading…
Reference in New Issue
Block a user