Skip to content

Commit

Permalink
crypto.hash224 (#176)
Browse files Browse the repository at this point in the history
  • Loading branch information
imaman authored Nov 13, 2024
1 parent b8ca41b commit 8808459
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 0 deletions.
21 changes: 21 additions & 0 deletions modules/septima-lang/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,27 @@ Array.isArray('not array') // Returns false
Array.isArray({ key: 'val' }) // Returns false
```
#### Cryptographic Hashing
Septima provides a secure hashing function through the `crypto.hash224()` method, which computes SHA-224 hashes of any value. The method takes a single argument of any type and returns a hexadecimal string representing the hash.
```javascript
// Hash a simple string
crypto.hash224('hello') // Returns a 56-character hex string

// Hash numbers
crypto.hash224(42) // Hashes the number 42

// Hash complex objects
crypto.hash224({ name: 'Alice', roles: ['admin', 'user'], settings: { theme: 'dark' } }) // Hashes the entire object structure

// Hashes are deterministic but unique per input
crypto.hash224('A') === crypto.hash224('A') // true (same input = same hash)
crypto.hash224('A') !== crypto.hash224('B') // true (different input = different hash)
```
Note: Septima's `crypto` object is not intended to be compatible with Node.js's `crypto` module. It provides its own simplified cryptographic utilities specifically designed for Septima's use cases.
### Console Output for Debugging
Like JavaScript, Septima provides `console.log()` for debugging and monitoring your code. However, unlike JavaScript's `console.log()` which can take multiple arguments, Septima's version accepts only a single argument. In keeping with Septima's functional nature, `console.log()` returns its argument, making it useful within expressions.
Expand Down
4 changes: 4 additions & 0 deletions modules/septima-lang/change-log.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,7 @@
### PR/174

- allow a dangling comma after last formal arg of an arrow function `(a,b,) => a + b`

### PR/176

- allow computing hash values: `crypto.hash224({a: 1, b: 2})`
4 changes: 4 additions & 0 deletions modules/septima-lang/src/runtime.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import crypto from 'crypto'

import { AstNode, show, Unit, UnitId } from './ast-node'
import { extractMessage } from './extract-message'
import { failMe } from './fail-me'
Expand Down Expand Up @@ -97,6 +99,7 @@ export class Runtime {
})

const parse = Value.foreign(o => JSON.parse(o.toString()))
const hash224 = Value.foreign(o => crypto.createHash('sha224').update(JSON.stringify(o.unwrap())).digest('hex'))

let lib = new SymbolFrame('Object', { destination: Value.obj({ keys, entries, fromEntries }) }, empty, 'INTERNAL')
lib = new SymbolFrame('String', { destination: Value.foreign(o => Value.str(o.toString())) }, lib, 'INTERNAL')
Expand All @@ -105,6 +108,7 @@ export class Runtime {
lib = new SymbolFrame('Array', { destination: Value.obj({ isArray }) }, lib, 'INTERNAL')
lib = new SymbolFrame('console', { destination: Value.obj({ log }) }, lib, 'INTERNAL')
lib = new SymbolFrame('JSON', { destination: Value.obj({ parse }) }, lib, 'INTERNAL')
lib = new SymbolFrame('crypto', { destination: Value.obj({ hash224 }) }, lib, 'INTERNAL')

if (generateTheArgsObject) {
lib = new SymbolFrame('args', { destination: Value.from(this.args) }, lib, 'INTERNAL')
Expand Down
18 changes: 18 additions & 0 deletions modules/septima-lang/tests/septima.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import crypto from 'crypto'

import { Septima } from '../src/septima'

/**
Expand Down Expand Up @@ -1013,6 +1015,22 @@ describe('septima', () => {
expect(run(`JSON.parse(5000)`)).toEqual(5000)
})
})
describe('hash224', () => {
const hashOf = (u: unknown) => crypto.createHash('sha224').update(JSON.stringify(u)).digest('hex')
test('can compute hash values of strings', () => {
expect(run(`crypto.hash224('A')`)).toEqual(hashOf('A'))
})
test('can compute hash values of complex objects', () => {
expect(run(`crypto.hash224({a: 1, b: [{x: 'X'}, ["Y"]]})`)).toEqual(hashOf({ a: 1, b: [{ x: 'X' }, ['Y']] }))
})
test('the hash changes when the input changes', () => {
expect(run(`crypto.hash224(110002)`)).toEqual(hashOf(110002))
expect(run(`crypto.hash224(110003)`)).toEqual(hashOf(110003))
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const [h1, h2] = run(`[crypto.hash224(110002), crypto.hash224(110003)]`) as string[]
expect(h1).not.toEqual(h2)
})
})
test.todo('sort') // expect(run(`[4, 10, 9, 256, 5, 300, 2, 70].sort()`)).toEqual('--')
test.todo('support file names in locations')
test.todo('string interpolation via `foo` strings')
Expand Down

0 comments on commit 8808459

Please sign in to comment.