Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalid generated JS #645

Closed
osa1 opened this issue Dec 26, 2024 · 2 comments
Closed

Invalid generated JS #645

osa1 opened this issue Dec 26, 2024 · 2 comments

Comments

@osa1
Copy link
Contributor

osa1 commented Dec 26, 2024

Sorry for the large repro..

Repro
import std/os/env
import std/os/file
import std/os/path

fun main()
  val args = get-args()
  val file-path = match args
    Cons(file-path, Nil) -> file-path
    _ -> throw("Usage: program <file path>", ExnError)
  val file-contents = read-text-file(file-path.path)
  val file-contents-chars = file-contents.vector
  val token-iter = token-iter(file-contents-chars)
  val token-list = collect({ token-iter.next })
  println("tokens parsed: " ++ token-list.length.show)
  ()

value type token
  Int(value: int)
  Str(length: int) // length of the string in number of chracters, including double quotes
  TTrue // T prefix to as Koka doesn't prefix variants with types, confuses bool.True with this
  TFalse
  Null
  LBracket
  RBracket
  LBrace
  RBrace
  Colon
  Comma

fun token/show(token: token): string
  match token
    Int(value) -> "Int(" ++ value.show ++ ")"
    Str(length) -> "Str(length = " ++ length.show ++ ")"
    TTrue -> "true"
    TFalse -> "false"
    Null -> "null"
    LBracket -> "["
    RBracket -> "]"
    LBrace -> "{"
    RBrace -> "}"
    Colon -> ":"
    Comma -> ","

value struct token-iter<h>
  chars: vector<char>
  idx: ref<h, int>

value struct parse-result
  // Character index (not byte index!) of the parse event.
  char-offset: int

  // Nothing means parse error.
  token: maybe<token>

fun token-iter(chars: vector<char>): <alloc<h>> token-iter<h>
  Token-iter(chars = chars, idx = ref(0))

fun token-iter/next-char-idx(token-iter: token-iter<h>): <read<h>, write<h>> maybe<(int, char)>
  val char-idx = !token-iter.idx
  val char = token-iter.chars.at(char-idx)
  token-iter.idx.set(char-idx + 1)
  char.map(fn(c) (char-idx, c))

fun token-iter/next-char(token-iter: token-iter<h>): <read<h>, write<h>> maybe<char>
  val char-idx = !token-iter.idx
  val char = token-iter.chars.at(char-idx)
  token-iter.idx.set(char-idx + 1)
  char

fun token-iter/peek-char(token-iter: token-iter<h>): <read<h>> maybe<char>
  token-iter.chars.at(!token-iter.idx)

fun token-iter/next(token-iter: token-iter<h>): <div, read<h>, write<h>> maybe<parse-result>
  // Strange code below to work around issues with early returns in Koka 3.1.2.
  // (https://github.com/koka-lang/koka/discussions/643)

  // Skip whitespace
  val next-char0 =
    match token-iter.next-char-idx
      Just((idx, c)) ->
        if c.is-white then
          return token-iter.next
        else
          Just((idx, c))
      Nothing -> Nothing

  val (char-idx, char) =
    match next-char0
      Nothing -> return Nothing
      Just(c) -> c

  match char
    '"' ->
      match token-iter.consume-string
        Nothing -> parse-error(char-idx)
        Just(string-end) -> parse-ok(char-idx, Str(length = string-end - char-idx + 1))

    't' ->
      if token-iter.next-char == Just('r') &&
           token-iter.next-char == Just('u') &&
           token-iter.next-char == Just('e') then
        parse-ok(char-idx, TTrue)
      else
        parse-error(char-idx)

    'f' ->
      if token-iter.next-char == Just('a') &&
           token-iter.next-char == Just('l') &&
           token-iter.next-char == Just('s') &&
           token-iter.next-char == Just('e') then
        parse-ok(char-idx, TFalse)
      else
        parse-error(char-idx)

    'n' ->
      if token-iter.next-char == Just('u') &&
           token-iter.next-char == Just('l') &&
           token-iter.next-char == Just('l') then
        parse-ok(char-idx, Null)
      else
        parse-error(char-idx)

    c | c.is-digit ->
      val int = token-iter.parse-int(c)
      parse-ok(char-idx, Int(value = int))

    ',' -> parse-ok(char-idx, Comma)

    ':' -> parse-ok(char-idx, Colon)

    '[' -> parse-ok(char-idx, LBracket)

    ']' -> parse-ok(char-idx, RBracket)

    '{' -> parse-ok(char-idx, LBrace)

    '}' -> parse-ok(char-idx, RBrace)

    _ -> parse-error(char-idx)

// Consume characters until '"'. Returns index of '"'. Consumes the '"'.
fun token-iter/consume-string(token-iter: token-iter<h>): <div, read<h>, write<h>> maybe<int>
  match token-iter.next-char-idx
    Just((idx, c)) ->
      match c
        '"' -> Just(idx)
        _ -> token-iter.consume-string

    Nothing ->
      Nothing

fun token-iter/parse-int(token-iter: token-iter<h>, c0: char): <div, read<h>, write<h>> int
  token-iter.parse-int-loop(c0.int - '0'.int)

fun token-iter/parse-int-loop(token-iter: token-iter<h>, value: int): <div, read<h>, write<h>> int
  match token-iter.peek-char
    Just(c) | c.is-digit ->
      token-iter.next-char // increment character index
      token-iter.parse-int-loop((value * 10) + (c.int - '0'.int))
    _ ->
      value

fun parse-error(idx: int): maybe<parse-result>
  Just(Parse-result(char-offset = idx, token = Nothing))

fun parse-ok(idx: int, token: token): maybe<parse-result>
  Just(Parse-result(char-offset = idx, token = Just(token)))

fun collect(iter: () -> <div, exn|e> maybe<parse-result>): <div, exn|e> list<token>
  match iter()
    Just(Parse-result(_, token = Just(token))) -> Cons(token, iter.collect)
    Just(Parse-result(idx, _)) -> throw("Lexical error at " ++ idx.show, ExnError)
    Nothing -> Nil

node output:

$ node .koka/v3.1.3/js-debug-2828eb/parse_dash_iter__main.mjs
file:///home/omer/koka/why-effects/.koka/v3.1.3/js-debug-2828eb/parse_dash_iter.mjs:336
    var _x12 = (_x_x2328 === null) ? return ($std_core_types.Nothing) : _x_x2328.value;
                                     ^^^^^^

SyntaxError: Unexpected token 'return'
    at compileSourceTextModule (node:internal/modules/esm/utils:340:16)
    at ModuleLoader.moduleStrategy (node:internal/modules/esm/translators:102:18)
    at #translate (node:internal/modules/esm/loader:433:12)
    at ModuleLoader.loadAndTranslate (node:internal/modules/esm/loader:480:27)

Node.js v22.11.0

Tried with dev branch.

@TimWhiting
Copy link
Collaborator

Fixed with #646, root cause same as #644

@TimWhiting
Copy link
Collaborator

Closing as #644 is tracking this same issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants