Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
name: Bug report
about: Report a problem with GiavaScript
title: ''
labels: bug
assignees: ''
---

### Description

<!-- A clear description of the bug. -->

### Steps to reproduce

<!--
Provide a minimal JavaScript program that reproduces the issue.

```js
// example
```
-->

### Expected behavior

<!-- What you expected to happen. -->

### Actual behavior

<!-- What actually happened. Include any error messages. -->

### Environment

- GiavaScript version:
- Crystal version:
- OS:
23 changes: 23 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
name: Feature request
about: Suggest a new feature for GiavaScript
title: ''
labels: enhancement
assignees: ''
---

### Feature description

<!-- A clear description of the feature you'd like to see. -->

### Use case

<!-- Why is this feature useful? What problem does it solve? -->

### Examples

<!-- If applicable, show example JavaScript code demonstrating the desired feature. -->

### Additional context

<!-- Any other context, references, or alternatives you've considered. -->
11 changes: 11 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
### Summary

<!-- Describe what this PR changes and why. Keep it focused and scoped. -->

### Checklist

- [ ] Changes are focused and scoped
- [ ] Docs updated for any user-facing behavior changes
- [ ] `crystal spec` passes
- [ ] `reference/REFERENCE.md` regenerated when reference docs changed (`python3 scripts/generate_reference.py`)
- [ ] PR description includes a short summary of behavior changes
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.1.0] - 2025-06-12

### Added
- JavaScript runtime implemented in Crystal
- Tokenizer and expression parser
- Statement parsing for if, for, while, do...while, switch, try/catch/finally
- Function declarations, expressions, and calls
- Arrow function support
- Template literal support
- Built-in types: String, Array, Object, Number, Bool, Date, Math, JSON
- `console.log` built-in global function
- `typeof` and `void` operators
- `parseInt`, `parseFloat`, `isNaN` global functions
- REPL mode with `:quit` command
- File execution mode
4 changes: 1 addition & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,7 @@ If you change any reference source file in `reference/` (`Language.md`, `Types.m
python3 scripts/generate_reference.py
```

Before opening a pull request, make sure this file is clean in `git diff` unless your change intentionally updates it:

- `reference/REFERENCE.md`
Before opening a pull request, make sure `reference/REFERENCE.md` shows no unintended changes in `git diff`. If your PR intentionally updates the reference docs, run the generator first. CI checks that this file matches the generator output; a mismatch will fail the build.

## Pull request checklist

Expand Down
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<picture align="center">
<source media="(prefers-color-scheme: dark)" srcset="assets/gs_logo_light.png" width="350">
<source media="(prefers-color-scheme: light)" srcset="assets/gs_logo.png" width="350">
<img alt="GiavaScript Logo" src="gs.png">
<img alt="GiavaScript Logo" src="assets/gs_logo.png">
</picture>
</p>
<h1 align="center">GiavaScript</h1>
Expand Down Expand Up @@ -114,8 +114,13 @@ CI verifies that `reference/REFERENCE.md` matches generated output.
Sample programs are in `examples/`:

- `examples/templateLiterals.js` - string interpolation and expression formatting
- `examples/matrixMultiply.js` - nested loops and array indexing
- `examples/sievePrimes.js` - control flow and simple algorithm implementation
- `examples/arrayFlatFlatMapSplice.js` - array manipulation methods
- `examples/dateBasics.js` - Date object usage
- `examples/functionExpressionsAndOperators.js` - function expressions and operators
- `examples/multilineMethodChaining.js` - method chaining patterns
- `examples/objectKeysValuesEntries.js` - Object.keys, values, entries
- `examples/random.js` - random number generation
- `examples/tryCatchFinally.js` - error handling with try/catch/finally

Run any example with:

Expand Down
22 changes: 20 additions & 2 deletions reference/Language.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,26 @@ Status of core JavaScript language features in GiavaScript.
| Logical operators (`&&`, `\|\|`, `!`) | Available |
| `typeof` operator | Available |
| `void` operator | Available |
| Unary plus (`+`) | Available |
| Comments (`//`, `/* */`) | Available |
| Template literals | Available |

### Equality operator semantics

- `==` and `!=` use coercive (loose) equality behavior.
- `===` and `!==` use strict (non-coercive) equality behavior.

### `typeof` semantics

`typeof` returns string representations of value types:

- `"number"` for `Int32` and `Float64`
- `"string"` for `String`
- `"boolean"` for `Bool`
- `"object"` for `Array`, `Hash`, and `null`
- `"function"` for callable values (user-defined and built-in)
- `"undefined"` for `undefined` and undeclared identifiers (does not throw)

### Logical operator semantics

- `a && b`: evaluates `a` first; if `a` is falsy, returns `a` and does not evaluate `b`; otherwise evaluates and returns `b`.
Expand All @@ -48,11 +62,13 @@ Status of core JavaScript language features in GiavaScript.
| --- | --- |
| Function declarations (`function name(...) { ... }`) | Available |
| Function expressions (`var f = function(...) { ... }`) | Available |
| Named function expressions (`var f = function name(...) { ... }`) | Available |
| Arrow functions (`() => expr`, `x => expr`, `() => { ... }`) | Available |
| Function calls | Available |
| Returning values with `return` | Available |
| First-class function values | Available |
| `if`, `else if`, `else` | Available |
| `for (...)` loops | Available |
| C-style `for` loops (`for (init; condition; update)`) | Available |
| `break` / `continue` inside loops | Available |
| `while` / `do...while` loops | Available |
| `switch` statements | Available |
Expand All @@ -79,9 +95,11 @@ Status of core JavaScript language features in GiavaScript.
| `isNaN()` | Available |
| `Date.now()` | Available |
| `new Date()` | Available |
| `console.log()` | Available |

## Notes

- This reflects current behavior in the interpreter and specs.
- This reflects the current behavior in the interpreter and specs.
- `let` and `const` declarations return explicit errors: `Error: unsupported declaration 'let'` and `Error: unsupported declaration 'const'`.
- Use `var` for variable declarations.
- Statements can be separated by newlines without requiring semicolons. A semicolon is not required when two statements are on separate lines.
2 changes: 2 additions & 0 deletions reference/Math.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,5 @@ Status of the JavaScript `Math` global object in GiavaScript.

- `Math.random()` returns a pseudo-random number in the range `[0, 1)`.
- `Math.random()` is not cryptographically secure.
- `Math.max()` with zero arguments returns `-Infinity`. `Math.min()` with zero arguments returns `Infinity`.
- All Math methods that accept numeric arguments coerce non-numeric values to numbers. Invalid coercions produce `NaN` or `Infinity` as appropriate.
26 changes: 23 additions & 3 deletions reference/REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,26 @@ Status of core JavaScript language features in GiavaScript.
| Logical operators (`&&`, `\|\|`, `!`) | Available |
| `typeof` operator | Available |
| `void` operator | Available |
| Unary plus (`+`) | Available |
| Comments (`//`, `/* */`) | Available |
| Template literals | Available |

#### Equality operator semantics

- `==` and `!=` use coercive (loose) equality behavior.
- `===` and `!==` use strict (non-coercive) equality behavior.

#### `typeof` semantics

`typeof` returns string representations of value types:

- `"number"` for `Int32` and `Float64`
- `"string"` for `String`
- `"boolean"` for `Bool`
- `"object"` for `Array`, `Hash`, and `null`
- `"function"` for callable values (user-defined and built-in)
- `"undefined"` for `undefined` and undeclared identifiers (does not throw)

#### Logical operator semantics

- `a && b`: evaluates `a` first; if `a` is falsy, returns `a` and does not evaluate `b`; otherwise evaluates and returns `b`.
Expand All @@ -72,11 +86,13 @@ Status of core JavaScript language features in GiavaScript.
| --- | --- |
| Function declarations (`function name(...) { ... }`) | Available |
| Function expressions (`var f = function(...) { ... }`) | Available |
| Named function expressions (`var f = function name(...) { ... }`) | Available |
| Arrow functions (`() => expr`, `x => expr`, `() => { ... }`) | Available |
| Function calls | Available |
| Returning values with `return` | Available |
| First-class function values | Available |
| `if`, `else if`, `else` | Available |
| `for (...)` loops | Available |
| C-style `for` loops (`for (init; condition; update)`) | Available |
| `break` / `continue` inside loops | Available |
| `while` / `do...while` loops | Available |
| `switch` statements | Available |
Expand All @@ -103,12 +119,14 @@ Status of core JavaScript language features in GiavaScript.
| `isNaN()` | Available |
| `Date.now()` | Available |
| `new Date()` | Available |
| `console.log()` | Available |

### Notes

- This reflects current behavior in the interpreter and specs.
- This reflects the current behavior in the interpreter and specs.
- `let` and `const` declarations return explicit errors: `Error: unsupported declaration 'let'` and `Error: unsupported declaration 'const'`.
- Use `var` for variable declarations.
- Statements can be separated by newlines without requiring semicolons. A semicolon is not required when two statements are on separate lines.

## Type Methods and Properties

Expand Down Expand Up @@ -246,7 +264,7 @@ Status of built-in methods and properties on GiavaScript runtime types.

### Notes

- This reflects current behavior in the interpreter and specs.
- This reflects the current behavior in the interpreter and specs.

## Math

Expand Down Expand Up @@ -304,6 +322,8 @@ Status of the JavaScript `Math` global object in GiavaScript.

- `Math.random()` returns a pseudo-random number in the range `[0, 1)`.
- `Math.random()` is not cryptographically secure.
- `Math.max()` with zero arguments returns `-Infinity`. `Math.min()` with zero arguments returns `Infinity`.
- All Math methods that accept numeric arguments coerce non-numeric values to numbers. Invalid coercions produce `NaN` or `Infinity` as appropriate.

## JSON

Expand Down
2 changes: 1 addition & 1 deletion reference/Types.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,4 @@ Status of built-in methods and properties on GiavaScript runtime types.

## Notes

- This reflects current behavior in the interpreter and specs.
- This reflects the current behavior in the interpreter and specs.
66 changes: 14 additions & 52 deletions src/giavascript/expression_evaluator.cr
Original file line number Diff line number Diff line change
Expand Up @@ -491,44 +491,18 @@ module GiavaScript
private def strict_equality_result(left : Value, right : Value) : Bool
if left.is_a?(Int32) || left.is_a?(Float64)
return false unless right.is_a?(Int32) || right.is_a?(Float64)

left_number = left.to_f64
right_number = right.to_f64
return false if left_number.nan? || right_number.nan?
return left_number == right_number
end

if left.is_a?(String)
return right.is_a?(String) && left == right
end

if left.is_a?(Bool)
return right.is_a?(Bool) && left == right
end

if left.nil?
return right.nil?
return false if left.to_f64.nan? || right.to_f64.nan?
return left.to_f64 == right.to_f64
end

if left.is_a?(UndefinedValue)
return right.is_a?(UndefinedValue)
end

if left.is_a?(Array(Value))
return right.is_a?(Array(Value)) && left.object_id == right.object_id
end

if left.is_a?(Hash(String, Value))
return right.is_a?(Hash(String, Value)) && left.object_id == right.object_id
end

if left.is_a?(BuiltinFunction)
return right.is_a?(BuiltinFunction) && left.object_id == right.object_id
end

if left.is_a?(UserFunction)
return right.is_a?(UserFunction) && left.object_id == right.object_id
end
return right.is_a?(String) && left == right if left.is_a?(String)
return right.is_a?(Bool) && left == right if left.is_a?(Bool)
return right.nil? if left.nil?
return right.is_a?(UndefinedValue) if left.is_a?(UndefinedValue)
return right.is_a?(Array(Value)) && left.object_id == right.object_id if left.is_a?(Array(Value))
return right.is_a?(Hash(String, Value)) && left.object_id == right.object_id if left.is_a?(Hash(String, Value))
return right.is_a?(BuiltinFunction) && left.object_id == right.object_id if left.is_a?(BuiltinFunction)
return right.is_a?(UserFunction) && left.object_id == right.object_id if left.is_a?(UserFunction)

false
end
Expand Down Expand Up @@ -618,22 +592,10 @@ module GiavaScript
private def truthy?(value : Value) : Bool
return false if value.nil?
return false if value.is_a?(UndefinedValue)

if value.is_a?(Bool)
return value
end

if value.is_a?(String)
return !value.empty?
end

if value.is_a?(Int32)
return value != 0
end

if value.is_a?(Float64)
return value != 0.0
end
return value if value.is_a?(Bool)
return !value.empty? if value.is_a?(String)
return value != 0 if value.is_a?(Int32)
return value != 0.0 if value.is_a?(Float64)

true
end
Expand Down
6 changes: 4 additions & 2 deletions src/giavascript/expression_parser.cr
Original file line number Diff line number Diff line change
Expand Up @@ -322,11 +322,12 @@ module GiavaScript
advance_token

parameters = [] of String
param_set = Set(String).new
unless @current.kind == Tokenizer::TokenKind::RParen
loop do
raise invalid_rhs_error unless @current.kind == Tokenizer::TokenKind::Identifier
parameter = @current.lexeme
raise invalid_rhs_error if parameters.includes?(parameter)
raise invalid_rhs_error unless param_set.add?(parameter)
parameters << parameter
advance_token

Expand Down Expand Up @@ -362,6 +363,7 @@ module GiavaScript
advance_token

parameters = [] of String
param_set = Set(String).new

if @current.kind == Tokenizer::TokenKind::RParen
advance_token
Expand All @@ -372,7 +374,7 @@ module GiavaScript
elsif @current.kind == Tokenizer::TokenKind::Identifier
loop do
param = @current.lexeme
return restore_and_nil(saved_cursor, saved_token) if parameters.includes?(param)
return restore_and_nil(saved_cursor, saved_token) unless param_set.add?(param)
parameters << param
advance_token

Expand Down
Loading
Loading