From 623964374a265f145fd40fec4e3640a90094dd53 Mon Sep 17 00:00:00 2001 From: sfj Date: Wed, 10 Sep 2025 15:43:53 +0200 Subject: [PATCH] js gen + syntax --- .gitignore | 1 + compile.phi | 326 +++++++++++++++- phi.js | 474 +++++++++++++++++------- program.phi | 1 - vim/syntax/phi.vim | 2 +- vscode/install.sh | 7 + vscode/phi/syntaxes/phi.tmLanguage.json | 36 +- 7 files changed, 674 insertions(+), 173 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..641a92d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +out.js diff --git a/compile.phi b/compile.phi index 1a18172..6a576bb 100644 --- a/compile.phi +++ b/compile.phi @@ -1,6 +1,198 @@ -(let identChars "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890+-*/%&|=?!<>'_") +(fn Emitter (ast) (do + (let output ()) + (fn generate () (do + (call emit "#!/usr/bin/env node\n") + (call emit_exprs ast) + (return (call strings_join output)) + )) + + (fn emit_exprs (exprs) (do + (for expr exprs (do + (call emit_expr expr) + (call emit "\n") + )) + )) + + (fn emit_expr (expr) (do + (let (ty line) expr) + (if (== ty "list") (do + (call emit_list expr) + ) (if (== ty "int") ( + (let (_ _ value) expr) + (call emit (call format "%" value)) + ) (if (== ty "string") ( + (let (_ _ value) expr) + (call emit (call format "\"%\"" (call string_escape value))) + ) (if (== ty "ident") ( + (let (_ _ value) expr) + (call emit (call format "_%" value)) + ) (do + (call panic "unknown expr type '%' on line %" ty line) + ))))) + )) + + (fn emit_list (expr) (do + (let (ty line s) expr) + (if (== (call len s) 0) (do + (call emit "[]") + (return) + )) + (let ((_ _ id)) s) + (if (== id "fn") (do + (let (_ (_ _ name) (_ _ params) body) s) + (call emit (call format "function _%(" name)) + + (let first true) + (for (_ _ name) params (do + (if (not first) (do + (call emit ", ") + )) + (= first false) + + (call emit (call format "_%" name)) + )) + (call emit (call format ") {\n" name)) + (call emit_expr body) + (call emit "}") + ) (if (== id "do") (do + (call emit_exprs (call slice s 1)) + ) (if (== id "return") (do + (let (_ value) s) + (call emit "return ") + (if (!= value null) (do + (call emit_expr value) + ) (do + (call emit "null") + )) + (call emit ";") + ) (if (== id "call") (do + (let (_ callee) s) + (let args (call slice s 2)) + (call emit_expr callee) + (call emit "(") + + (let first true) + (for arg args (do + (if (not first) (do + (call emit ", ") + )) + (= first false) + + (call emit_expr arg) + )) + + (call emit ")") + ) (do + (call emit "[") + (let first true) + (for e s (do + (if (not first) (do + (call emit ", ") + )) + (= first false) + + (call emit_expr e) + )) + (call emit "]") + ))))) + )) + + (fn emit (str) (do + (call push output str) + )) + + (return (generate)) +)) + +(fn string_escape (str) (do + (let str_len (call len str)) + (let i 0) + (let result "") + (loop (do + (if (>= i str_len) (break)) + (let ch (call at str i)) + (if (== ch "\"") (do + (+= result "\\\"") + ) (if (== ch "\t") (do + (+= result "\\t") + ) (if (== ch "\r") (do + (+= result "\\r") + ) (if (== ch "\n") (do + (+= result "\\n") + ) (if (== ch "\0") (do + (+= result "\\0") + ) (do + (+= result ch) + )))))) + (+= i 1) + )) + (return result) +)) + +(fn Parser (tokens) (do + (let i 0) + (let tok (call at tokens i)) + + (fn parse () (do + (let exprs ()) + (loop (do + (if (call done) (break)) + (call push exprs (call parse_expr)) + )) + (return exprs) + )) + + (fn parse_expr () (do + (let (ty line value) tok) + (if (call eat "(") (do + (let values ()) + (loop (do + (if (call test ")") (break)) + (call push values (call parse_expr)) + )) + (if (not (call eat ")")) (do + (call panic "expected ')' on line %" (call at tok 1)) + )) + (return ("list" line values)) + ) (if (call eat "string") (do + (return ("string" line value)) + ) (if (call eat "int") (do + (return ("int" line (call string_to_int value))) + ) (if (call eat "ident") (do + (return ("ident" line value)) + ) (do + (call panic "expected expression, got '%' on line %" ty line) + ))))) + )) + + (fn eat (pat) (do + (if (not (call test pat)) (return false)) + (call step) + (return true) + )) + + (fn step () (do + (+= i 1) + (if (not (call done)) (do + (let new_tok (call at tokens i)) + (= tok new_tok) + )) + )) + + (fn test (pat) (do + (if (call done) (return false)) + (let (ty) tok) + (return (== pat ty)) + )) + + (fn done () (do + (return (>= i (call len tokens))) + )) + + (return (parse)) +)) (fn tokenize (text) (do (let text_len (call len text)) @@ -15,14 +207,13 @@ (let ch (call at text i)) (if (call contains " \t\r\n" ch) (do - (call println "line = %, ch = '%'" line ch) (if (== ch "\n") (do (+= line 1) )) (+= i 1) ) (if (call slice_eq text i "//") (do (loop (do - if (or (>= i text_len) (== (call at text i) "\n") (do + (if (or (>= i text_len) (== (call at text i) "\n")) (do (break) )) (+= i 1) @@ -31,8 +222,52 @@ (call push tokens (ch line)) (+= i 1) ) (if (== ch "\"") (do - + (let value "") (+= i 1) + (= ch (call at text i)) + (loop (do + (if (or (>= i text_len) (== ch "\"")) (do + (break) + )) + (if (== ch "\\") (do + (+= i 1) + (if (>= i text_len) (do + (break) + )) + (= ch (call at text i)) + (if (== ch "t") (do + (+= value "\t") + ) (if (== ch "r") (do + (+= value "\r") + ) (if (== ch "n") (do + (+= value "\n") + ) (if (== ch "0") (do + (+= value "\n") + ) (do + (+= value ch) + ))))) + ) (do + (+= value ch) + )) + (+= i 1) + (= ch (call at text i)) + )) + (if (or (>= i text_len) (!= ch "\"") (do + (call panic "expected '\"' on line %" line) + ))) + (+= i 1) + (call push tokens ("string" line value)) + ) (if (call contains "0123456789" ch) (do + (let value "") + (loop (do + (= ch (call at text i)) + (if (or (>= i text_len) (not (call contains "0123456789" ch))) (do + (break) + )) + (+= value ch) + (+= i 1) + )) + (call push tokens ("int" line value)) ) (if (call contains identChars ch) (do (let value "") (loop (do @@ -40,19 +275,22 @@ (if (or (>= i text_len) (not (call contains identChars ch))) (do (break) )) - (call push value ch) + (+= value ch) (+= i 1) )) (call push tokens ("ident" line value)) ) (do (call println "illegal char '%'" ch) (+= i 1) - )))))) + ))))))) )) (return tokens) )) +(let identChars (+ "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890+-*/%&|=?!<>'_")) + (fn contains (text ch) (do (let text_len (call len text)) (let i 0) @@ -69,9 +307,11 @@ (fn slice_eq (str slice_idx substr) (do (let str_len (call len str)) (let substr_len (call len substr)) - (let i slice_idx) + (let i 0) (loop (do - (if (or (>= (+ slice_idx i) str_len) (>= i substr_len)) + (if (>= i substr_len) + (return true)) + (if (>= (+ slice_idx i) str_len) (return false)) (if (!= (call at str (+ slice_idx i)) (call at substr i)) (return false)) @@ -80,14 +320,76 @@ (return true) )) +(fn print_expr (expr depth) (do + (let (ty line value) expr) + (if (== ty "list") (do + (call println "%(% %" (call indent depth) ty line) + (for e value (do + (call print_expr e (+ depth 1)) + )) + (call println "%)" (call indent depth)) + ) (do + (call println "%%" (call indent depth) expr) + )) +)) + +(fn indent (depth) (do + (let space "") + (let i 0) + (loop (do + (if (>= i depth) (break)) + (+= space " ") + (+= i 1) + )) + (return space) +)) + +(fn slice (list idx) (do + (let list_len (call len list)) + (let elems ()) + (let i idx) + (loop (do + (if (>= i list_len) (break)) + (call push elems (call at list i)) + (+= i 1) + )) + (return elems) +)) + (let text (call read_text_file "program.phi")) -(let tokens (call tokenize text)) - +(call println "reading file...") (call println "=== text ===") (call println text) + +(call println "tokenizing...") +(let tokens (call tokenize text)) + (call println "=== tokens ===") -(call println tokens) -(call println (+ 1 2)) +(for (tok line value) tokens (do + (call println "%\t%\t%" line tok (if (!= value null) value "")) +)) + +(call println "parsing...") +(let parser (call Parser tokens)) +(let (parse) parser) +(let ast (call parse)) + +(call println "=== ast ===") +(for expr ast (do + (call print_expr expr 0) +)) + +(call println "emitting...") +(let emitter (call Emitter ast)) +(let (emit) emitter) +(let js_code (call emit)) + +(call println "=== js ===") +(call println js_code) + +(call println "writing file...") +(call write_text_file "out.js" js_code) + diff --git a/phi.js b/phi.js index 6798668..e8f0f6f 100644 --- a/phi.js +++ b/phi.js @@ -4,13 +4,14 @@ import * as fs from "node:fs"; import process from "node:process"; function main() { - const text = fs.readFileSync(process.argv[2]).toString(); + const filename = process.argv[2]; + const text = fs.readFileSync(filename).toString(); const ast = new Parser(text).parse(); let lastValue = null; - const evaluator = new Evaluator(); + const evaluator = new Evaluator(filename); for (const expr of ast) { const result = evaluator.eval(expr); if (result.type !== "value") { @@ -24,14 +25,22 @@ function main() { } class Evaluator { - constructor() { - this.syms = { parent: undefined, map: new Map(builtins) }; + constructor(filename) { + this.syms = { parent: undefined, map: new Map(this.builtins) }; + this.currentLine = 0; + this.callStack = [{ name: "", line: 0 }]; + this.filename = filename; } /** * @param {Expr} expr */ eval(expr) { + if (!expr) { + console.log(this.callStack) + throw new Error() + } + this.currentLine = expr.line; if (expr.type === "list") { return this.evalList(expr, expr.line); } else if (expr.type === "int") { @@ -48,7 +57,11 @@ class Evaluator { `could not find symbol '${expr.value}' on line ${expr.line}`, ); } - return { type: "value", value: sym }; + if (sym.type === "local") { + return { type: "value", value: { ...sym.value } }; + } else { + return { type: "value", value: { ...sym } }; + } } else { throw new Error( `unknown expr type '${expr.type}' on line ${expr.line}`, @@ -68,97 +81,29 @@ class Evaluator { const s = expr.values; const id = s[0]?.type === "ident" ? s[0].value : undefined; if (id === "fn") { - const name = s[1].value; - this.syms.map.set(name, { - type: "fn", - name, - params: s[2].values.map((ident) => ident.value), - body: s[3], - syms: this.syms, - }); - return { type: "value", value: { type: "null" } }; + return this.evalFn(expr); } else if (id === "return") { return { type: "return", value: s[1] ? this.evalToValue(s[1]) : { type: "null" }, }; } else if (id === "let") { - const value = this.evalToValue(s[2]); - this.syms.map.set(s[1].value, value); - return { type: "value", value: { type: "null" } }; + return this.evalLet(expr); } else if (id === "if") { - const cond = this.evalToValue(s[1]); - if (cond.type !== "bool") { - throw new Error( - `expected bool on line ${expr.line}`, - ); - } - if (cond.value) { - return this.eval(s[2]); - } else if (s[3]) { - return this.eval(s[3]); - } else { - return { type: "value", value: "null" }; - } + return this.evalIf(expr); } else if (id === "loop") { - while (true) { - const result = this.eval(s[1]); - if (result.type === "break") { - return { type: "value", value: result.value }; - } else if (result.type !== "value") { - return result; - } - } + return this.evalLoop(expr); + } else if (id === "for") { + return this.evalFor(expr); } else if (id === "break") { return { type: "break", value: s[1] ? this.evalToValue(s[1]) : { type: "null" }, }; } else if (id === "do") { - let lastValue = { type: "null" }; - - for (const expr of s.slice(1)) { - const result = this.eval(expr); - if (result.type !== "value") { - return result; - } - lastValue = result.value; - } - return { type: "value", value: lastValue }; + return this.evalDo(expr); } else if (id === "call") { - const args = s.slice(2).map((arg) => this.evalToValue(arg)); - const fnValue = this.evalToValue(s[1]); - - if (fnValue.type === "builtin") { - return { type: "value", value: fnValue.fn(...args) }; - } else if (fnValue.type !== "fn") { - throw new Error("cannot call non-function"); - } - const callerSyms = this.syms; - this.syms = { - parent: fnValue.syms, - map: new Map(), - }; - if (fnValue.params.length !== args.length) { - throw new Error( - `incorrect amount of arguments on line ${line}`, - ); - } - for (let i = 0; i < fnValue.params.length; ++i) { - this.syms.map.set(fnValue.params[i], args[i]); - } - - let returnValue = { type: "null" }; - const result = this.eval(fnValue.body); - - if (result.type === "value" || result.type === "return") { - returnValue = result.value; - } else { - throw new Error(`illegal ${result.type} across boundry`); - } - - this.syms = callerSyms; - return { type: "value", value: returnValue }; + return this.evalCall(expr); } else if (id === "not") { const value = this.evalToValue(s[1]); return { @@ -181,44 +126,31 @@ class Evaluator { } else { return { type: "value", value: left }; } - } else if (id in artithmeticOps) { + } else if (id in this.artithmeticOps) { const left = this.evalToValue(s[1]); const right = this.evalToValue(s[2]); + if (id === "+" && left.type === "string" && right.type === "string") { + return { type: "value", value: { type: "string", value: left.value + right.value } }; + } return { type: "value", value: { type: "int", - value: artithmeticOps[id](left.value, right.value), + value: this.artithmeticOps[id](left.value, right.value), }, }; - } else if (id in comparisonOps) { + } else if (id in this.comparisonOps) { const left = this.evalToValue(s[1]); const right = this.evalToValue(s[2]); return { type: "value", value: { type: "bool", - value: comparisonOps[id](left.value, right.value), + value: this.comparisonOps[id](left.value, right.value), }, }; - } else if (id in assignOps) { - if (s[1].type === "ident") { - const sym = this.findSym(s[1].value); - if (!sym) { - throw new Error( - `could not find symbol '${expr.value}' on line ${expr.line}`, - ); - } - const right = this.evalToValue(s[2]); - const newValue = assignOps[id](sym, right); - sym.type = newValue.type; - sym.value = newValue.value; - } else { - throw new Error( - `cannot assign to expression on line ${expr.line}`, - ); - } - return { type: "value", value: { type: "null" } }; + } else if (id in this.assignOps) { + return this.evalAssign(expr); } else { return { type: "value", @@ -230,6 +162,196 @@ class Evaluator { } } + evalFn(expr) { + const s = expr.values; + const name = s[1].value; + this.syms.map.set(name, { + type: "fn", + line: expr.line, + name, + params: s[2].values.map((ident) => ident.value), + body: s[3], + syms: this.syms, + }); + return { type: "value", value: { type: "null" } }; + } + + evalLet(expr) { + const s = expr.values; + const value = this.evalToValue(s[2]); + this.assignPattern(s[1], value); + return { type: "value", value: { type: "null" } }; + } + + evalIf(expr) { + const s = expr.values; + const cond = this.evalToValue(s[1]); + if (cond.type !== "bool") { + throw new Error( + `expected bool on line ${expr.line}`, + ); + } + if (cond.value) { + return this.eval(s[2]); + } else if (s[3]) { + return this.eval(s[3]); + } else { + return { type: "value", value: "null" }; + } + } + + evalLoop(expr) { + const s = expr.values; + while (true) { + const result = this.eval(s[1]); + if (result.type === "break") { + return { type: "value", value: result.value }; + } else if (result.type !== "value") { + return result; + } + } + } + + evalFor(expr) { + const s = expr.values; + + const value = this.evalToValue(s[2]); + if (value.type !== "list") { + throw new Error( + `expected list on line ${expr.line}`, + ); + } + + const outerSyms = this.syms; + this.syms = { parent: outerSyms, map: new Map() }; + + for (let i = 0; i < value.values.length; ++i) { + this.assignPattern(s[1], value.values[i]); + const result = this.eval(s[3]); + if (result.type === "break") { + return { type: "value", value: result.value }; + } else if (result.type !== "value") { + return result; + } + } + this.syms = outerSyms; + return { type: "value", value: { type: "null" } } + } + + evalDo(expr) { + const s = expr.values; + const outerSyms = this.syms; + this.syms = { parent: outerSyms, map: new Map() }; + + let lastValue = { type: "null" }; + + for (const expr of s.slice(1)) { + const result = this.eval(expr); + if (result.type !== "value") { + return result; + } + lastValue = result.value; + } + + this.syms = outerSyms; + return { type: "value", value: lastValue }; + } + + evalCall(expr) { + const s = expr.values; + const args = s.slice(2).map((arg) => this.evalToValue(arg)); + const fnValue = this.evalToValue(s[1]); + + if (fnValue.type === "builtin") { + return { type: "value", value: fnValue.fn(...args) }; + } else if (fnValue.type !== "fn") { + throw new Error("cannot call non-function"); + } + + if (this.callStack.length > 100) { + this.panic("stack overflow") + this.printCallStack() + process.exit(1); + } + + this.callStack.push({ name: fnValue.name, line: expr.line }); + const callerSyms = this.syms; + this.syms = { + parent: fnValue.syms, + map: new Map(), + }; + if (fnValue.params.length !== args.length) { + throw new Error( + `incorrect amount of arguments for function '${fnValue.name}' on line ${expr.line}`, + ); + } + for (let i = 0; i < fnValue.params.length; ++i) { + this.syms.map.set(fnValue.params[i], args[i]); + } + + let returnValue = { type: "null" }; + const result = this.eval(fnValue.body); + + if (result.type === "value" || result.type === "return") { + returnValue = result.value; + } else { + throw new Error(`illegal ${result.type} across boundry`); + } + + this.syms = callerSyms; + this.callStack.pop() + return { type: "value", value: returnValue }; + } + + evalAssign(expr) { + const s = expr.values; + const id = s[0].value; + if (s[1].type === "ident") { + const sym = this.findSym(s[1].value); + if (!sym) { + throw new Error( + `could not find symbol '${expr.value}' on line ${expr.line}`, + ); + } + const value = this.evalToValue(s[2]); + if (sym.type === "local") { + sym.value = this.assignOps[id](sym.value, value); + } else { + throw new Error( + `cannot assign to symbol on line ${expr.line}`, + ); + } + } else { + throw new Error( + `cannot assign to expression on line ${expr.line}`, + ); + } + return { type: "value", value: { type: "null" } }; + } + + /** @param {Expr} pattern */ + assignPattern(pattern, value) { + if (pattern.type === "ident") { + if (pattern.value === "_") { + return; + } + this.syms.map.set(pattern.value, { + type: "local", + line: pattern.line, + value, + }); + } else if (pattern.type === "list") { + if (value.type !== "list") { + throw new Error(`expected list on line ${pattern.line}`); + } + for (const [i, p] of pattern.values.entries()) { + this.assignPattern(p, value.values[i] ?? { type: "null" }) + } + } else { + throw new Error(`cannot assign to pattern on line ${pattern.line}`); + } + } + findSym(ident, syms = this.syms) { if (syms.map.has(ident)) { return syms.map.get(ident); @@ -239,28 +361,30 @@ class Evaluator { return undefined; } } -} -const artithmeticOps = { - "+": (left, right) => right + left, - "-": (left, right) => right - left, -}; -const comparisonOps = { - "==": (left, right) => left === right, - "!=": (left, right) => left !== right, - "<": (left, right) => left < right, - ">": (left, right) => left > right, - "<=": (left, right) => left <= right, - ">=": (left, right) => left >= right, -}; -const assignOps = { - "=": (_, right) => right, - "+=": (left, right) => ({ type: "int", value: left.value + right.value }), - "-=": (left, right) => ({ type: "int", value: left.value - right.value }), -}; + panic(msg) { + console.error(`\x1b[1;91mpanic\x1b[1;97m: ${msg}\x1b[0m"`); + this.printCallStack(); + } -const builtinFns = { - println(msg, ...args) { + printCallStack() { + const last = this.callStack[this.callStack.length - 1]; + console.error(` \x1b[90mat \x1b[37m${last.name} \x1b[90m(${this.filename}:${this.currentLine})\x1b[0m`); + for (let i = this.callStack.length - 2; i >= 0; --i) { + const name = this.callStack[i].name; + const line = this.callStack[i + 1].line; + console.error(` \x1b[90mat \x1b[37m${name} \x1b[90m(${this.filename}:${line})\x1b[0m`); + } + } + + builtinFormat(msg, ...args) { + let value = valueToPrint(msg); + for (const arg of args) { + value = value.replace("%", valueToPrint(arg)); + } + return { type: "string", value }; + } + builtinPrintln(msg, ...args) { let text = valueToPrint(msg); for (const arg of args) { @@ -269,44 +393,108 @@ const builtinFns = { console.log(text); return { type: "null" }; - }, - read_text_file(path) { + } + builtinPanic(msg, ...args) { + let text = valueToPrint(msg); + + for (const arg of args) { + text = text.replace("%", valueToPrint(arg)); + } + + this.panic(text); + process.exit(1); + return { type: "null" }; + } + builtinReadTextFile(path) { const text = fs.readFileSync(path.value).toString(); return { type: "string", value: text }; - }, - push(list, value) { + } + builtinWriteTextFile(path, text) { + fs.writeFileSync(path.value, text.value); + return { type: "null" }; + } + builtinPush(list, value) { if (list.type === "string") { list.value += value.value; return list; } list.values.push(value); return list; - }, - at(value, index) { + } + builtinAt(value, index) { if (value.type === "string") { return { type: "string", value: value.value[index.value] }; } return value.values[index.value]; - }, - len(value) { + } + builtinLen(value) { if (value.type === "string") { return { type: "int", value: value.value.length }; } return { type: "int", value: value.values.length }; - }, -}; + } + builtinStringToInt(value) { + return { type: "int", value: Number(value.value) }; + } + builtinCharCode(value) { + return { type: "int", value: value.value.charCodeAt(0) }; + } + builtinStringsJoin(value) { + return { + type: "string", + value: value.values + .map(value => value.value) + .join("") + }; + } -const consts = { - "null": { type: "null" }, - "false": { type: "bool", value: false }, - "true": { type: "bool", value: true }, -}; + artithmeticOps = { + "+": (left, right) => right + left, + "-": (left, right) => right - left, + }; + comparisonOps = { + "==": (left, right) => left === right, + "!=": (left, right) => left !== right, + "<": (left, right) => left < right, + ">": (left, right) => left > right, + "<=": (left, right) => left <= right, + ">=": (left, right) => left >= right, + }; + assignOps = { + "=": (_, right) => right, + "+=": (left, right) => ({ + type: left.type === "string" && left.type === right.type ? "string" : "int", + value: left.value + right.value + }), + "-=": (left, right) => ({ type: "int", value: left.value - right.value }), + }; -const builtins = [ - ...Object.entries(builtinFns) - .map(([key, fn]) => [key, { type: "builtin", fn }]), - ...Object.entries(consts), -]; + builtinFns = { + "format": (...args) => this.builtinFormat(...args), + "println": (...args) => this.builtinPrintln(...args), + "panic": (...args) => this.builtinPanic(...args), + "read_text_file": (...args) => this.builtinReadTextFile(...args), + "write_text_file": (...args) => this.builtinWriteTextFile(...args), + "push": (...args) => this.builtinPush(...args), + "at": (...args) => this.builtinAt(...args), + "len": (...args) => this.builtinLen(...args), + "string_to_int": (...args) => this.builtinStringToInt(...args), + "char_code": (...args) => this.builtinCharCode(...args), + "strings_join": (...args) => this.builtinStringsJoin(...args), + }; + + consts = { + "null": { type: "null" }, + "false": { type: "bool", value: false }, + "true": { type: "bool", value: true }, + }; + + builtins = [ + ...Object.entries(this.builtinFns) + .map(([key, fn]) => [key, { type: "builtin", fn }]), + ...Object.entries(this.consts), + ]; +} function valueToPrint(value) { if (value.type === "null") { diff --git a/program.phi b/program.phi index 10ded09..2f47e27 100644 --- a/program.phi +++ b/program.phi @@ -1,4 +1,3 @@ - (fn hello () (do (call println "hello world") (call println "hello world") diff --git a/vim/syntax/phi.vim b/vim/syntax/phi.vim index 3fb443e..0d348e0 100644 --- a/vim/syntax/phi.vim +++ b/vim/syntax/phi.vim @@ -8,7 +8,7 @@ if exists("b:current_syntax") endif -syn keyword Keyword fn call return loop break if let do +syn keyword Keyword fn call return loop for break if let do syn keyword Operator and or not syn keyword Special null syn keyword Boolean true false diff --git a/vscode/install.sh b/vscode/install.sh index c485fa5..e385860 100644 --- a/vscode/install.sh +++ b/vscode/install.sh @@ -2,6 +2,13 @@ set -xe +if [ $# -eq 0 ] +then + echo "No VS Code location specified." + echo "USAGE: ./install.sh " + echo "EXAMPLE: ./install.sh ~/.vscode" +fi + SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}") CODE_PATH=$1 diff --git a/vscode/phi/syntaxes/phi.tmLanguage.json b/vscode/phi/syntaxes/phi.tmLanguage.json index d7d7ff5..de67758 100644 --- a/vscode/phi/syntaxes/phi.tmLanguage.json +++ b/vscode/phi/syntaxes/phi.tmLanguage.json @@ -2,28 +2,32 @@ "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", "name": "phi", "patterns": [ - { - "include": "#keywords" - }, - { - "include": "#operators" - }, - { - "include": "#numbers" - }, - { - "include": "#strings" - }, - { - "include": "#idents" - } + { "include": "#comments" }, + { "include": "#keywords" }, + { "include": "#operators" }, + { "include": "#numbers" }, + { "include": "#strings" }, + { "include": "#idents" } ], "repository": { + "comments": { + "patterns": [ + { + "name": "comment.line.phi", + "begin": "//", + "end": "\\n" + } + ] + }, "keywords": { "patterns": [ { "name": "keyword.control.phi", - "match": "\\b(fn|call|return|loop|break|if|let|do)\\b" + "match": "\\b(fn|return|loop|for|break|if|do|call)\\b" + }, + { + "name": "storage.type.phi", + "match": "\\b(let)\\b" }, { "name": "keyword.operator.phi",