Thanks to visit codestin.com
Credit goes to github.com

Skip to content
/ ScriptML Public

ScriptML (𝝈MΞ“) is a small experimental ML-like functional language with dependent types, compiled to JavaScript

Notifications You must be signed in to change notification settings

srad/ScriptML

Repository files navigation

ScriptML (𝝈MΞ“)

logo1

A small experimental ML-like functional language with dependent types, compiled to JavaScript.

ScriptML is a research project and toy compiler, written in TypeScript, that explores:

  • ML syntax (inspired by OCaml/F#)
  • Inductive types (Nat, List, user ADTs)
  • Pattern matching
  • Recursion and totality checks
  • Dependent function types (Ξ -types)
  • IO via embedded thunks that lower to JS functions
  • Modules and imports with qualified names and open

The compiler emits either standalone JavaScript or directly evaluates programs.


Quick start

git clone https://github.com/yourname/ScriptML
cd ScriptML
npm install

Run the CLI:

# Compile and run a program
npx ts-node src/index.ts run demo/arith.sml

# Just compile to JS
npx ts-node src/index.ts js demo/arith.sml > out.js
node out.js

Run the test suite:

npm test

Language overview

ScriptML has a typed Ξ»-calculus core with Nats, Lists, user ADTs, IO, and now modules.

Basic expressions

zero             // the natural number 0
succ zero        // 1
succ (succ zero) // 2

"hello"          // string literal
["a", "b", "c"]  // list literal

Functions

let id =
  fun (x: _) -> x
in
id (succ zero)

Functions are curried. Type annotations are optional (: followed by type).

Let bindings

let two = succ (succ zero) in
let three = succ two in
three

let is recursive by default: the name is in scope in its own definition.

If / then / else

if n = zero then "empty"
else "not empty"

Infix operators

Common operators are supported: + - * / % = <> < > <= >= ^ (where ^ is string concatenation).

Pattern matching

On Nats:

match n with
| zero -> zero
| succ k, acc -> succ (double k)

On Lists:

type List a = Nil | Cons of a, List a

let length =
  fun (xs: _) ->
    match xs with
    | Nil -> zero
    | Cons of h, t -> succ (length t)
in
length [zero, succ zero, succ (succ zero)]

Modules

ScriptML supports a lightweight module system.

  • A file can begin with module <Name> (optional; defaults to the filename).
  • Import another file with import "path/to/file.sml" as Alias.
  • Use qualified names like Math.add.
  • Use open <Module> to bring a module’s values into scope without qualification.

Example module:

module Math

val add = fun (a: _) -> fun (b: _) -> a + b;
val sub = fun (a: _) -> fun (b: _) -> a - b;

Use it:

import "std/math.sml" as Math

println ("3 + 4 = " ^ Math.add 3 4)

Or with open:

import "std/math.sml" as Math
open Math

println ("3 - 4 = " ^ sub 3 4)

All val and type decls in a module are exported by default. open is just syntactic sugar for creating local aliases.


IO

IO is modeled with thunks (() => value) and bind / pure:

println "Hello, world!"

bind (promptYesNo "Continue? (y/n)")
     (fun (ans: _) ->
        if ans = 1 then println "OK" else println "Bye")

Builtins provided via __io:

  • println : String -> IO Unit
  • print : String -> IO Unit
  • readLine : IO String
  • promptYesNo : String -> IO Nat (1 for yes, 0 for no`)
  • readFileUtf8, writeFileUtf8, readStdinUtf8
  • args : List String

Grammar

A simplified EBNF for ScriptML:

program   ::= [ "module" Ident ] { import | decl } [ term ]

import    ::= "import" Str "as" Ident
            | "open" Ident

decl      ::= "type" Ident { Ident } "=" ctor { "|" ctor }
            | "val" Ident "=" term ";"

ctor      ::= Ident [ "of" type { "," type } ]

term      ::= let | fun | if | match | expr
let       ::= "let" Ident "=" term "in" term
fun       ::= "fun" "(" Ident [ ":" type ] ")" "->" term
if        ::= "if" term "then" term "else" term

match     ::= "match" term "with" 
              { "|" pattern "->" term }
pattern   ::= "zero"
            | "succ" Ident "," Ident
            | Ident
            | Ident "of" Ident { "," Ident }

expr      ::= expr atom | atom
atom      ::= Ident
            | Ident "." Ident     // qualified variable
            | Int
            | Str
            | "zero"
            | "succ" atom
            | "[" [ term { "," term } ] "]"
            | "(" term ")"

type      ::= type "->" type
            | "Nat"
            | "Unit"
            | Ident
            | Ident type
            | "(" type ")"

Examples

Arithmetic

let add =
  fun (a: _) ->
  fun (b: _) ->
    match a with
    | zero -> b
    | succ k, acc -> succ (add k b)
in

add (succ (succ zero)) (succ zero)  // 2 + 1 = 3

Guess the number (binary search)

let rec loop =
  fun (lo: _) ->
  fun (hi: _) ->
    if lo = hi then
      println ("Your number is " ^ lo)
    else
      let mid = lo + ((hi - lo) / 2) in
      bind (promptYesNo ("Is your number greater than " ^ mid ^ "? (y/n)"))
      (fun (ans: _) ->
        if ans = 1
          then loop (mid + 1) hi
          else loop lo mid)
in

loop 1 100

Nim-21

// 21 sticks, each turn take 1–3, computer replies.

let rec play =
  fun (n: _) ->
    if n = 0 then println "Game over!"
    else
      bind (println ("Sticks remaining: " ^ n))
      (fun (_: _) ->
        bind (promptYesNo "Take 1? (y/n)")
        (fun (ans: _) ->
          let n1 = n - (if ans = 1 then 1 else 2) in
          play n1))
in

play 21

Development

  • Compiler written in TypeScript, split across:

    • lexer.ts
    • parser.ts (Pratt parser)
    • elab_emit.ts (elaboration + JS codegen)
    • compiler.ts (top-level driver, import/module loader)
  • Tests in __tests__, goldens in tests/fixtures


License

MIT

About

ScriptML (𝝈MΞ“) is a small experimental ML-like functional language with dependent types, compiled to JavaScript

Resources

Stars

Watchers

Forks