Compare commits

..

No commits in common. "bd1dbe75f34f14270b4feb1e8eb0cad3cba3dea4" and "7645efc4f952997425a7dc096630c139c836b2b4" have entirely different histories.

6 changed files with 7 additions and 44 deletions

View File

@ -78,18 +78,4 @@ describe('pipe expressions', () => {
div = do a b: a / b end div = do a b: a / b end
sub 3 1 | div (sub 110 9 | sub 1) _ | div 5`).toEvaluateTo(10) sub 3 1 | div (sub 110 9 | sub 1) _ | div 5`).toEvaluateTo(10)
}) })
test('pipe with prelude functions (list.reverse and list.map)', () => {
expect(`
double = do x: x * 2 end
range 1 3 | list.reverse | list.map double
`).toEvaluateTo([6, 4, 2])
})
test('pipe with prelude function (echo)', () => {
expect(`
get-msg = do: 'hello' end
get-msg | echo
`).toEvaluateTo(null)
})
}) })

View File

@ -119,13 +119,7 @@ const consumeWordToken = (
} }
// Track identifier validity: must be lowercase, digit, dash, or emoji/unicode // Track identifier validity: must be lowercase, digit, dash, or emoji/unicode
if ( if (!isLowercaseLetter(ch) && !isDigit(ch) && ch !== 45 /* - */ && ch !== 63 /* ? */ && !isEmojiOrUnicode(ch)) {
!isLowercaseLetter(ch) &&
!isDigit(ch) &&
ch !== 45 /* - */ &&
ch !== 63 /* ? */ &&
!isEmojiOrUnicode(ch)
) {
if (!canBeWord) break if (!canBeWord) break
isValidIdentifier = false isValidIdentifier = false
} }
@ -165,9 +159,7 @@ const checkForDotGet = (input: InputStream, stack: Stack, pos: number): number |
// If identifier is in scope, this is property access (e.g., obj.prop) // If identifier is in scope, this is property access (e.g., obj.prop)
// If not in scope, it should be consumed as a Word (e.g., file.txt) // If not in scope, it should be consumed as a Word (e.g., file.txt)
return context?.scope.has(identifierText) || globals.includes(identifierText) return context?.scope.has(identifierText) || globals.includes(identifierText) ? IdentifierBeforeDot : null
? IdentifierBeforeDot
: null
} }
// Decide between AssignableIdentifier and Identifier using grammar state + peek-ahead // Decide between AssignableIdentifier and Identifier using grammar state + peek-ahead
@ -195,10 +187,7 @@ const chooseIdentifierToken = (input: InputStream, stack: Stack): number => {
const nextCh2 = getFullCodePoint(input, peekPos + 1) const nextCh2 = getFullCodePoint(input, peekPos + 1)
// Check for compound assignment operators: +=, -=, *=, /=, %= // Check for compound assignment operators: +=, -=, *=, /=, %=
if ( if ([43/* + */, 45/* - */, 42/* * */, 47/* / */, 37/* % */].includes(nextCh) && nextCh2 === 61/* = */) {
[43 /* + */, 45 /* - */, 42 /* * */, 47 /* / */, 37 /* % */].includes(nextCh) &&
nextCh2 === 61 /* = */
) {
// Found compound operator, check if it's followed by whitespace // Found compound operator, check if it's followed by whitespace
const charAfterOp = getFullCodePoint(input, peekPos + 2) const charAfterOp = getFullCodePoint(input, peekPos + 2)
if (isWhiteSpace(charAfterOp) || charAfterOp === -1 /* EOF */) { if (isWhiteSpace(charAfterOp) || charAfterOp === -1 /* EOF */) {

View File

@ -1,7 +1,6 @@
import { expect } from 'bun:test' import { expect } from 'bun:test'
import { parser } from '#parser/shrimp' import { parser } from '#parser/shrimp'
import { setGlobals } from '#parser/tokenizer' import { setGlobals } from '#parser/tokenizer'
import { globals as prelude } from '#prelude'
import { $ } from 'bun' import { $ } from 'bun'
import { assert, errorMessage } from '#utils/utils' import { assert, errorMessage } from '#utils/utils'
import { Compiler } from '#compiler/compiler' import { Compiler } from '#compiler/compiler'
@ -44,8 +43,7 @@ expect.extend({
toMatchTree(received: unknown, expected: string, globals?: Record<string, any>) { toMatchTree(received: unknown, expected: string, globals?: Record<string, any>) {
assert(typeof received === 'string', 'toMatchTree can only be used with string values') assert(typeof received === 'string', 'toMatchTree can only be used with string values')
const allGlobals = { ...prelude, ...(globals || {}) } if (globals) setGlobals(Object.keys(globals))
setGlobals(Object.keys(allGlobals))
const tree = parser.parse(received) const tree = parser.parse(received)
const actual = treeToString(tree, received) const actual = treeToString(tree, received)
const normalizedExpected = trimWhitespace(expected) const normalizedExpected = trimWhitespace(expected)
@ -101,10 +99,9 @@ expect.extend({
assert(typeof received === 'string', 'toEvaluateTo can only be used with string values') assert(typeof received === 'string', 'toEvaluateTo can only be used with string values')
try { try {
const allGlobals = { ...prelude, ...(globals || {}) } if (globals) setGlobals(Object.keys(globals))
setGlobals(Object.keys(allGlobals))
const compiler = new Compiler(received) const compiler = new Compiler(received)
const result = await run(compiler.bytecode, allGlobals) const result = await run(compiler.bytecode, globals)
let value = VMResultToValue(result) let value = VMResultToValue(result)
// Just treat regex as strings for comparison purposes // Just treat regex as strings for comparison purposes

View File

@ -50,4 +50,4 @@ describe('Shrimp', () => {
await shrimp.run('abc = nothing') await shrimp.run('abc = nothing')
expect(shrimp.get('abc')).toEqual('nothing') expect(shrimp.get('abc')).toEqual('nothing')
}) })
}) })

View File

@ -135,12 +135,6 @@ end`
const xInEcho = identifiers[identifiers.length - 1] const xInEcho = identifiers[identifiers.length - 1]
expect(tracker.isInScope('x', xInEcho)).toBe(true) expect(tracker.isInScope('x', xInEcho)).toBe(true)
}) })
test('the prelude functions are always in scope', () => {
const code = `echo "Hello, World!"`
const { tree, tracker } = parseAndGetScope(code)
expect(tracker.isInScope('echo', tree.topNode)).toBe(true)
})
}) })
const parseAndGetScope = (code: string) => { const parseAndGetScope = (code: string) => {

View File

@ -1,7 +1,6 @@
import { SyntaxNode } from '@lezer/common' import { SyntaxNode } from '@lezer/common'
import { TextDocument } from 'vscode-languageserver-textdocument' import { TextDocument } from 'vscode-languageserver-textdocument'
import * as Terms from '../../../src/parser/shrimp.terms' import * as Terms from '../../../src/parser/shrimp.terms'
import { globals } from '../../../src/prelude'
/** /**
* Tracks variables in scope at a given position in the parse tree. * Tracks variables in scope at a given position in the parse tree.
@ -13,8 +12,6 @@ export class ScopeTracker {
constructor(document: TextDocument) { constructor(document: TextDocument) {
this.document = document this.document = document
const preludeKeys = Object.keys(globals)
this.scopeCache.set(0, new Set(preludeKeys))
} }
/** /**