Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
nopri.github.io/monkey.singkong
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
2111 lines (1892 sloc)
66.7 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
Simple implementation of The Monkey Programming Language | |
interpreter in Singkong Programming Language | |
Monkey.singkong | |
(c) Noprianto <nopri.anto@icloud.com>, 2020-2021 | |
Website: nopri.github.io | |
License: MIT | |
Version: 0.6 | |
Requires Singkong version 4.8 or later | |
Download Monkey.singkong: https://nopri.github.io/monkey.singkong | |
Download Monkey.singkong runnable jar: https://nopri.github.io/monkeyinterpreter.jar | |
(bundled with Singkong interpreter) | |
-------------------------------------------------------- | |
Singkong is case-insensitive, dynamically typed, procedural, and interpreted | |
programming language that runs on Java Virtual Machine (Java 5.0 or later). | |
It has numbers, booleans, strings, arrays, hash maps, dates, first-class | |
functions, built-in functions, GUI components, database connections, and | |
other features. Singkong program can call Java methods and Singkong interpreter | |
can be embedded and integrated into another applications. | |
Singkong interpreter is distributed as single jar file. It comes with simple GUI | |
editor/interactive evaluator/database tool and can be run on graphical or | |
text user interfaces. | |
More information: https://nopri.github.io/singkong.html | |
Download Singkong: https://nopri.github.io/Singkong.jar | |
(License: Free to use or redistribute, no warranty) | |
-------------------------------------------------------- | |
Singkong is based on Monkey.java: an open source, simple implementation of | |
Monkey programming language interpreter in Java. | |
Monkey.java is based on monkey.py: an open source, simple implementation of | |
Monkey programming language interpreter in Python. | |
Monkey.java and monkey.py, (c) Noprianto <nopri.anto@icloud.com>, 2019. | |
Monkey.singkong is based on monkey.py | |
monkey.py is based on code (in Go) in book: Writing an interpreter in Go | |
; | |
require(4.8) | |
var MONKEY_VERSION = "0.6" | |
var MONKEY_TITLE = "Monkey.singkong " + MONKEY_VERSION | |
var MONKEY_MESSAGE = "Press ENTER to quit" | |
var MONKEY_TOKEN = { | |
"ILLEGAL": "ILLEGAL", | |
"EOF": "EOF", | |
"IDENT": "IDENT", | |
"INT": "INT", | |
"ASSIGN": "=", | |
"PLUS": "+", | |
"MINUS": "-", | |
"BANG": "!", | |
"ASTERISK": "*", | |
"SLASH": "/", | |
"LT": "<", | |
"GT": ">", | |
"COMMA": ",", | |
"SEMICOLON": ";", | |
"LPAREN": "(", | |
"RPAREN": ")", | |
"LBRACE": "{", | |
"RBRACE": "}", | |
"FUNCTION": "FUNCTION", | |
"LET": "LET", | |
"TRUE": "true", | |
"FALSE": "false", | |
"IF": "if", | |
"ELSE": "else", | |
"RETURN": "return", | |
"EQ": "==", | |
"NOT_EQ": "!=", | |
"STRING": "STRING", | |
"LBRACKET": "[", | |
"RBRACKET": "]", | |
"COLON": ":" | |
} | |
var monkey_token_create = fn(type_, literal_) { | |
return {"type": type_, "literal": literal_} | |
} | |
var MONKEY_LEXER = { | |
"KEYWORDS": { | |
"fn": MONKEY_TOKEN["FUNCTION"], | |
"let": MONKEY_TOKEN["LET"], | |
"true": MONKEY_TOKEN["TRUE"], | |
"false": MONKEY_TOKEN["FALSE"], | |
"if": MONKEY_TOKEN["IF"], | |
"else": MONKEY_TOKEN["ELSE"], | |
"return": MONKEY_TOKEN["RETURN"] | |
}, | |
"VALID_IDENTS": array("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"), | |
"VALID_NUMBERS": array("0123456789"), | |
"WHITESPACES": [" ", tab(), cr(), lf()], | |
"read_char": fn(x) { | |
if (x["read"] >= len(x["input"])) { | |
set(x, "ch", "") | |
} else { | |
set(x, "ch", x["input"][x["read"]]) | |
} | |
set(x, "position", x["read"]) | |
set(x, "read", x["read"] + 1) | |
}, | |
"peek_char": fn(x) { | |
if (x["read"] >= len(x["input"])) { | |
return "" | |
} else { | |
return x["input"][x["read"]] | |
} | |
}, | |
"new_token": fn(x, token, t, ch) { | |
set(token, "type", t) | |
set(token, "literal", ch) | |
return token | |
}, | |
"next_token": fn(x) { | |
var t = monkey_token_create("", "") | |
x["skip_whitespace"](x) | |
if (in(["=", "+", "-", "!", "/", "*", "<", ">", ";", "(", ")", ",", "{", "}", "", quote(), "[", "]", ":"], x["ch"])) { | |
if (x["ch"] == "=") { | |
if (x["peek_char"](x) == "=") { | |
var ch = x["ch"] | |
x["read_char"](x) | |
var t = x["new_token"](x, t, MONKEY_TOKEN["EQ"], ch + x["ch"]) | |
} else { | |
var t = x["new_token"](x, t, MONKEY_TOKEN["ASSIGN"], x["ch"]) | |
} | |
} | |
if (x["ch"] == "+") { | |
var t = x["new_token"](x, t, MONKEY_TOKEN["PLUS"], x["ch"]) | |
} | |
if (x["ch"] == "-") { | |
var t = x["new_token"](x, t, MONKEY_TOKEN["MINUS"], x["ch"]) | |
} | |
if (x["ch"] == "!") { | |
if (x["peek_char"](x) == "=") { | |
var ch = x["ch"] | |
x["read_char"](x) | |
var t = x["new_token"](x, t, MONKEY_TOKEN["NOT_EQ"], ch + x["ch"]) | |
} else { | |
var t = x["new_token"](x, t, MONKEY_TOKEN["BANG"], x["ch"]) | |
} | |
} | |
if (x["ch"] == "/") { | |
var t = x["new_token"](x, t, MONKEY_TOKEN["SLASH"], x["ch"]) | |
} | |
if (x["ch"] == "*") { | |
var t = x["new_token"](x, t, MONKEY_TOKEN["ASTERISK"], x["ch"]) | |
} | |
if (x["ch"] == "<") { | |
var t = x["new_token"](x, t, MONKEY_TOKEN["LT"], x["ch"]) | |
} | |
if (x["ch"] == ">") { | |
var t = x["new_token"](x, t, MONKEY_TOKEN["GT"], x["ch"]) | |
} | |
if (x["ch"] == ";") { | |
var t = x["new_token"](x, t, MONKEY_TOKEN["SEMICOLON"], x["ch"]) | |
} | |
if (x["ch"] == "(") { | |
var t = x["new_token"](x, t, MONKEY_TOKEN["LPAREN"], x["ch"]) | |
} | |
if (x["ch"] == ")") { | |
var t = x["new_token"](x, t, MONKEY_TOKEN["RPAREN"], x["ch"]) | |
} | |
if (x["ch"] == ",") { | |
var t = x["new_token"](x, t, MONKEY_TOKEN["COMMA"], x["ch"]) | |
} | |
if (x["ch"] == "{") { | |
var t = x["new_token"](x, t, MONKEY_TOKEN["LBRACE"], x["ch"]) | |
} | |
if (x["ch"] == "}") { | |
var t = x["new_token"](x, t, MONKEY_TOKEN["RBRACE"], x["ch"]) | |
} | |
if (x["ch"] == "") { | |
set(t, "literal", "") | |
set(t, "type", MONKEY_TOKEN["EOF"]) | |
} | |
if (x["ch"] == quote()) { | |
set(t, "literal", x["read_string"](x)) | |
set(t, "type", MONKEY_TOKEN["STRING"]) | |
} | |
if (x["ch"] == "[") { | |
var t = x["new_token"](x, t, MONKEY_TOKEN["LBRACKET"], x["ch"]) | |
} | |
if (x["ch"] == "]") { | |
var t = x["new_token"](x, t, MONKEY_TOKEN["RBRACKET"], x["ch"]) | |
} | |
if (x["ch"] == ":") { | |
var t = x["new_token"](x, t, MONKEY_TOKEN["COLON"], x["ch"]) | |
} | |
} else { | |
if (x["is_letter"](x, x["ch"]) | x["is_digit"](x, x["ch"])) { | |
if (x["is_letter"](x, x["ch"])) { | |
set(t, "literal", x["read_ident"](x)) | |
set(t, "type", x["lookup_ident"](x, t["literal"])) | |
return t | |
} else { | |
set(t, "literal", x["read_number"](x)) | |
set(t, "type", MONKEY_TOKEN["INT"]) | |
return t | |
} | |
} else { | |
var t = x["new_token"](x, t, MONKEY_TOKEN["ILLEGAL"], x["ch"]) | |
} | |
} | |
x["read_char"](x) | |
return t | |
}, | |
"read_ident": fn(x) { | |
var pos = x["position"] | |
repeat { | |
if (empty(x["ch"]) | x["ch"] == null) { | |
return x | |
} | |
var test = x["is_letter"](x, x["ch"]) | |
if (!test) { | |
return x | |
} | |
x["read_char"](x) | |
} | |
var ret = slice(x["input"], pos, x["position"]) | |
return ret | |
}, | |
"read_number": fn(x) { | |
var pos = x["position"] | |
repeat { | |
if (empty(x["ch"]) | x["ch"] == null) { | |
return x | |
} | |
var test = x["is_digit"](x, x["ch"]) | |
if (!test) { | |
return x | |
} | |
x["read_char"](x) | |
} | |
var ret = slice(x["input"], pos, x["position"]) | |
return ret | |
}, | |
"read_string": fn(x) { | |
var pos = x["position"] + 1 | |
repeat { | |
x["read_char"](x) | |
if (empty(x["ch"]) | x["ch"] == quote()) { | |
return x | |
} | |
} | |
var ret = slice(x["input"], pos, x["position"]) | |
return ret | |
}, | |
"lookup_ident": fn(x, s) { | |
var ret = MONKEY_LEXER["KEYWORDS"][s] | |
if (ret != null) { | |
return ret | |
} | |
return MONKEY_TOKEN["IDENT"] | |
}, | |
"is_letter": fn(x, ch) { | |
return in(MONKEY_LEXER["VALID_IDENTS"], ch) | |
}, | |
"is_digit": fn(x, ch) { | |
return in(MONKEY_LEXER["VALID_NUMBERS"], ch) | |
}, | |
"skip_whitespace": fn(x) { | |
repeat { | |
if (in(MONKEY_LEXER["WHITESPACES"], x["ch"])) { | |
x["read_char"](x) | |
} else { | |
return x | |
} | |
} | |
} | |
} | |
var monkey_lexer_create = fn(input_, position, read_, ch) { | |
return {"input": input_, "position": position, "read": read_, "ch": ch} + MONKEY_LEXER | |
} | |
var monkey_lexer_new = fn(s) { | |
var l = monkey_lexer_create("", 0, 0, "") | |
set(l, "input", s) | |
l["read_char"](l) | |
return l | |
} | |
var MONKEY_NODE = { | |
"type": "node", | |
"token_literal": fn(x) { | |
return "" | |
}, | |
"string": fn(x) { | |
return "" | |
} | |
} | |
var MONKEY_STATEMENT = {} + MONKEY_NODE | |
set(MONKEY_STATEMENT, "type", "statement") | |
set(MONKEY_STATEMENT, "statement_node", fn () { return "" }) | |
var MONKEY_EXPRESSION = {} + MONKEY_NODE | |
set(MONKEY_EXPRESSION, "type", "expression") | |
set(MONKEY_EXPRESSION, "expression_node", fn () { return "" }) | |
var monkey_expression_create = fn() { | |
var r = {} + MONKEY_EXPRESSION | |
return r | |
} | |
var MONKEY_IDENTIFIER = {} + MONKEY_EXPRESSION | |
set(MONKEY_IDENTIFIER, "type", "identifier") | |
set(MONKEY_IDENTIFIER, "token_literal", fn(x) { return x["token"]["literal"] }) | |
set(MONKEY_IDENTIFIER, "string", fn(x) { return x["value"] }) | |
var monkey_identifier_create = fn(value) { | |
var r = {} + MONKEY_IDENTIFIER | |
set(r, "token", monkey_token_create("", "")) | |
set(r, "value", value) | |
return r | |
} | |
var MONKEY_LET_STATEMENT = {} + MONKEY_STATEMENT | |
set(MONKEY_LET_STATEMENT, "type", "let_statement") | |
set(MONKEY_LET_STATEMENT, "token_literal", fn(x) { return x["token"]["literal"] }) | |
set(MONKEY_LET_STATEMENT, "string", fn(x) { | |
var r = x["token_literal"](x) + " " | |
var r = r + x["name"]["string"](x["name"]) | |
var r = r + " = " | |
if (x["value"] != null) { | |
var r = r + x["value"]["string"](x["value"]) | |
} | |
var r = r + ";" | |
return r | |
}) | |
var monkey_let_statement_create = fn() { | |
var r = {} + MONKEY_LET_STATEMENT | |
set(r, "token", monkey_token_create("", "")) | |
set(r, "name", monkey_identifier_create("")) | |
set(r, "value", monkey_expression_create()) | |
return r | |
} | |
var MONKEY_RETURN_STATEMENT = {} + MONKEY_STATEMENT | |
set(MONKEY_RETURN_STATEMENT, "type", "return_statement") | |
set(MONKEY_RETURN_STATEMENT, "token_literal", fn(x) { return x["token"]["literal"] }) | |
set(MONKEY_RETURN_STATEMENT, "string", fn(x) { | |
var r = x["token_literal"](x) + " " | |
if (x["return_value"] != null) { | |
var r = r + x["return_value"]["string"](x["return_value"]) | |
} | |
var r = r + ";" | |
return r | |
}) | |
var monkey_return_statement_create = fn() { | |
var r = {} + MONKEY_RETURN_STATEMENT | |
set(r, "token", monkey_token_create("", "")) | |
set(r, "return_value", monkey_expression_create()) | |
return r | |
} | |
var MONKEY_EXPRESSION_STATEMENT = {} + MONKEY_STATEMENT | |
set(MONKEY_EXPRESSION_STATEMENT, "type", "expression_statement") | |
set(MONKEY_EXPRESSION_STATEMENT, "token_literal", fn(x) { return x["token"]["literal"] }) | |
set(MONKEY_EXPRESSION_STATEMENT, "string", fn(x) { | |
if (x["expression"] != null) { | |
return x["expression"]["string"](x["expression"]) | |
} | |
return "" | |
}) | |
var monkey_expression_statement_create = fn() { | |
var r = {} + MONKEY_EXPRESSION_STATEMENT | |
set(r, "token", monkey_token_create("", "")) | |
set(r, "expression", monkey_expression_create()) | |
return r | |
} | |
var MONKEY_BLOCK_STATEMENT = {} + MONKEY_STATEMENT | |
set(MONKEY_BLOCK_STATEMENT, "type", "block_statement") | |
set(MONKEY_BLOCK_STATEMENT, "token_literal", fn(x) { return x["token"]["literal"] }) | |
set(MONKEY_BLOCK_STATEMENT, "is_empty", fn(x) { return len(x["statements"]) == 0 }) | |
set(MONKEY_BLOCK_STATEMENT, "string", fn(x) { | |
var r = ["{"] | |
each(x["statements"], fn(e, i) { | |
var r = r + e["string"](e) | |
var r = r + ";" | |
}) | |
var r = r + "}" | |
return join(newline(), r) | |
}) | |
var monkey_block_statement_create = fn() { | |
var r = {} + MONKEY_BLOCK_STATEMENT | |
set(r, "token", monkey_token_create("", "")) | |
set(r, "statements", []) | |
return r | |
} | |
var MONKEY_INTEGER_LITERAL = {} + MONKEY_EXPRESSION | |
set(MONKEY_INTEGER_LITERAL, "type", "integer_literal") | |
set(MONKEY_INTEGER_LITERAL, "token_literal", fn(x) { return x["token"]["literal"] }) | |
set(MONKEY_INTEGER_LITERAL, "string", fn(x) { return x["token"]["literal"] }) | |
var monkey_integer_literal_create = fn(value) { | |
var r = {} + MONKEY_INTEGER_LITERAL | |
set(r, "token", monkey_token_create("", "")) | |
set(r, "value", value) | |
return r | |
} | |
var MONKEY_STRING_LITERAL = {} + MONKEY_EXPRESSION | |
set(MONKEY_STRING_LITERAL, "type", "string_literal") | |
set(MONKEY_STRING_LITERAL, "token_literal", fn(x) { return x["token"]["literal"] }) | |
set(MONKEY_STRING_LITERAL, "string", fn(x) { return x["token"]["literal"] }) | |
var monkey_string_literal_create = fn(value) { | |
var r = {} + MONKEY_STRING_LITERAL | |
set(r, "token", monkey_token_create("", "")) | |
set(r, "value", value) | |
return r | |
} | |
var MONKEY_FUNCTION_LITERAL = {} + MONKEY_EXPRESSION | |
set(MONKEY_FUNCTION_LITERAL, "type", "function_literal") | |
set(MONKEY_FUNCTION_LITERAL, "token_literal", fn(x) { return x["token"]["literal"] }) | |
set(MONKEY_FUNCTION_LITERAL, "string", fn(x) { | |
var params = [] | |
each(x["parameters"], fn(e, i) { | |
var params = params + e["string"](e) | |
}) | |
var r = x["token_literal"](x) | |
var r = r + "(" | |
var r = r + join(", ", params) | |
var r = r + ")" | |
var r = r + x["body"]["string"](x["body"]) | |
return r | |
}) | |
var monkey_function_literal_create = fn() { | |
var r = {} + MONKEY_FUNCTION_LITERAL | |
set(r, "token", monkey_token_create("", "")) | |
set(r, "parameters", []) | |
set(r, "body", monkey_block_statement_create()) | |
return r | |
} | |
var MONKEY_CALL_EXPRESSION = {} + MONKEY_EXPRESSION | |
set(MONKEY_CALL_EXPRESSION, "type", "call_expression") | |
set(MONKEY_CALL_EXPRESSION, "token_literal", fn(x) { return x["token"]["literal"] }) | |
set(MONKEY_CALL_EXPRESSION, "string", fn(x) { | |
var args = [] | |
each(x["arguments"], fn(e, i) { | |
var args = args + e["string"](e) | |
}) | |
var r = x["function"]["string"](x["function"]) | |
var r = r + "(" | |
var r = r + join(", ", args) | |
var r = r + ")" | |
return r | |
}) | |
var monkey_call_expression_create = fn() { | |
var r = {} + MONKEY_CALL_EXPRESSION | |
set(r, "token", monkey_token_create("", "")) | |
set(r, "function", monkey_expression_create()) | |
set(r, "arguments", []) | |
return r | |
} | |
var MONKEY_BOOLEAN = {} + MONKEY_EXPRESSION | |
set(MONKEY_BOOLEAN, "type", "boolean") | |
set(MONKEY_BOOLEAN, "token_literal", fn(x) { return x["token"]["literal"] }) | |
set(MONKEY_BOOLEAN, "string", fn(x) { return x["token"]["literal"] }) | |
var monkey_boolean_create = fn(value) { | |
var r = {} + MONKEY_BOOLEAN | |
set(r, "token", monkey_token_create("", "")) | |
set(r, "value", value) | |
return r | |
} | |
var MONKEY_PREFIX_EXPRESSION = {} + MONKEY_EXPRESSION | |
set(MONKEY_PREFIX_EXPRESSION, "type", "prefix_expression") | |
set(MONKEY_PREFIX_EXPRESSION, "token_literal", fn(x) { return x["token"]["literal"] }) | |
set(MONKEY_PREFIX_EXPRESSION, "string", fn(x) { | |
var r = "(" | |
var r = r + x["operator"] | |
var r = r + x["right"]["string"](x["right"]) | |
var r = r + ")" | |
return r | |
}) | |
var monkey_prefix_expression_create = fn() { | |
var r = {} + MONKEY_PREFIX_EXPRESSION | |
set(r, "token", monkey_token_create("", "")) | |
set(r, "operator", "") | |
set(r, "right", monkey_expression_create()) | |
return r | |
} | |
var MONKEY_INFIX_EXPRESSION = {} + MONKEY_EXPRESSION | |
set(MONKEY_INFIX_EXPRESSION, "type", "infix_expression") | |
set(MONKEY_INFIX_EXPRESSION, "token_literal", fn(x) { return x["token"]["literal"] }) | |
set(MONKEY_INFIX_EXPRESSION, "string", fn(x) { | |
var r = "(" | |
var r = r + x["left"]["string"](x["left"]) | |
var r = r + " " + x["operator"] + " " | |
var r = r + x["right"]["string"](x["right"]) | |
var r = r + ")" | |
return r | |
}) | |
var monkey_infix_expression_create = fn() { | |
var r = {} + MONKEY_INFIX_EXPRESSION | |
set(r, "token", monkey_token_create("", "")) | |
set(r, "left", monkey_expression_create()) | |
set(r, "operator", "") | |
set(r, "right", monkey_expression_create()) | |
return r | |
} | |
var MONKEY_IF_EXPRESSION = {} + MONKEY_EXPRESSION | |
set(MONKEY_IF_EXPRESSION, "type", "if_expression") | |
set(MONKEY_IF_EXPRESSION, "token_literal", fn(x) { return x["token"]["literal"] }) | |
set(MONKEY_IF_EXPRESSION, "string", fn(x) { | |
var r = "if" | |
var r = r + x["condition"]["string"](x["condition"]) | |
var r = r + " " | |
var r = r + x["consequence"]["string"](x["consequence"]) | |
if (!x["alternative"]["is_empty"](x["alternative"])) { | |
var r = r + " else " | |
var r = r + x["alternative"]["string"](x["alternative"]) | |
} | |
return r | |
}) | |
var monkey_if_expression_create = fn() { | |
var r = {} + MONKEY_IF_EXPRESSION | |
set(r, "token", monkey_token_create("", "")) | |
set(r, "condition", monkey_expression_create()) | |
set(r, "consequence", monkey_block_statement_create()) | |
set(r, "alternative", monkey_block_statement_create()) | |
return r | |
} | |
var MONKEY_ARRAY_LITERAL = {} + MONKEY_EXPRESSION | |
set(MONKEY_ARRAY_LITERAL, "type", "array_literal") | |
set(MONKEY_ARRAY_LITERAL, "token_literal", fn(x) { return x["token"]["literal"] }) | |
set(MONKEY_ARRAY_LITERAL, "string", fn(x) { | |
var elements = [] | |
each(x["elements"], fn(e, i) { | |
var elements = elements + e["string"](e) | |
}) | |
var r = "[" | |
var r = r + join(", ", elements) | |
var r = r + "]" | |
return r | |
}) | |
var monkey_array_literal_create = fn() { | |
var r = {} + MONKEY_ARRAY_LITERAL | |
set(r, "token", monkey_token_create("", "")) | |
set(r, "elements", []) | |
return r | |
} | |
var MONKEY_INDEX_EXPRESSION = {} + MONKEY_EXPRESSION | |
set(MONKEY_INDEX_EXPRESSION, "type", "index_expression") | |
set(MONKEY_INDEX_EXPRESSION, "token_literal", fn(x) { return x["token"]["literal"] }) | |
set(MONKEY_INDEX_EXPRESSION, "string", fn(x) { | |
var r = "(" | |
var r = r + x["left"]["string"](x["left"]) | |
var r = r + "[" | |
var r = r + x["index"]["string"](x["index"]) | |
var r = r + "])" | |
return r | |
}) | |
var monkey_index_expression_create = fn() { | |
var r = {} + MONKEY_INDEX_EXPRESSION | |
set(r, "token", monkey_token_create("", "")) | |
set(r, "left", monkey_expression_create()) | |
set(r, "index", monkey_expression_create()) | |
return r | |
} | |
var MONKEY_HASH_LITERAL = {} + MONKEY_EXPRESSION | |
set(MONKEY_HASH_LITERAL, "type", "hash_literal") | |
set(MONKEY_HASH_LITERAL, "token_literal", fn(x) { return x["token"]["literal"] }) | |
set(MONKEY_HASH_LITERAL, "string", fn(x) { | |
var pairs = [] | |
each(keys(x["pairs"]), fn(e, i) { | |
var v = x["pairs"][e] | |
var pairs = pairs + (e["string"](e) + ":" + v["string"](v)) | |
}) | |
var r = "{" | |
var r = r + join(", ", pairs) | |
var r = r + "}" | |
return r | |
}) | |
var monkey_hash_literal_create = fn() { | |
var r = {} + MONKEY_HASH_LITERAL | |
set(r, "token", monkey_token_create("", "")) | |
set(r, "pairs", {}) | |
return r | |
} | |
var MONKEY_PARSER_PRECEDENCES = { | |
"LOWEST": 1, | |
"EQUALS": 2, | |
"LESSGREATER": 3, | |
"SUM": 4, | |
"PRODUCT": 5, | |
"PREFIX": 6, | |
"CALL": 7, | |
"INDEX": 8 | |
} | |
var MONKEY_PARSER = { | |
"PRECEDENCES": { | |
MONKEY_TOKEN["LPAREN"]: MONKEY_PARSER_PRECEDENCES["CALL"], | |
MONKEY_TOKEN["EQ"]: MONKEY_PARSER_PRECEDENCES["EQUALS"], | |
MONKEY_TOKEN["NOT_EQ"]: MONKEY_PARSER_PRECEDENCES["EQUALS"], | |
MONKEY_TOKEN["LT"]: MONKEY_PARSER_PRECEDENCES["LESSGREATER"], | |
MONKEY_TOKEN["GT"]: MONKEY_PARSER_PRECEDENCES["LESSGREATER"], | |
MONKEY_TOKEN["PLUS"]: MONKEY_PARSER_PRECEDENCES["SUM"], | |
MONKEY_TOKEN["MINUS"]: MONKEY_PARSER_PRECEDENCES["SUM"], | |
MONKEY_TOKEN["SLASH"]: MONKEY_PARSER_PRECEDENCES["PRODUCT"], | |
MONKEY_TOKEN["ASTERISK"]: MONKEY_PARSER_PRECEDENCES["PRODUCT"], | |
MONKEY_TOKEN["LBRACKET"]: MONKEY_PARSER_PRECEDENCES["INDEX"] | |
}, | |
"next_token": fn(x) { | |
set(x, "cur_token", x["peek_token"]) | |
set(x, "peek_token", x["lexer"]["next_token"](x["lexer"])) | |
}, | |
"parse_program": fn(x) { | |
var program = monkey_program_create() | |
var temp = [] | |
repeat { | |
if (x["cur_token"]["type"] == MONKEY_TOKEN["EOF"]) { | |
return null | |
} | |
var s = x["parse_statement"](x) | |
if (s) { | |
var temp = temp + s | |
} | |
x["next_token"](x) | |
} | |
set(program, "statements", temp) | |
return program | |
}, | |
"parse_statement": fn(x) { | |
if (in([MONKEY_TOKEN["LET"], MONKEY_TOKEN["RETURN"]], x["cur_token"]["type"])) { | |
if (x["cur_token"]["type"] == MONKEY_TOKEN["LET"]) { | |
return x["parse_let_statement"](x) | |
} | |
if (x["cur_token"]["type"] == MONKEY_TOKEN["RETURN"]) { | |
return x["parse_return_statement"](x) | |
} | |
} else { | |
return x["parse_expression_statement"](x) | |
} | |
return null | |
}, | |
"parse_let_statement": fn(x) { | |
var s = monkey_let_statement_create() | |
set(s, "token", x["cur_token"]) | |
if (!x["expect_peek"](x, MONKEY_TOKEN["IDENT"])) { | |
return null | |
} | |
set(s, "name", monkey_identifier_create("")) | |
set(s["name"], "token", x["cur_token"]) | |
set(s["name"], "value", x["cur_token"]["literal"]) | |
if (!x["expect_peek"](x, MONKEY_TOKEN["ASSIGN"])) { | |
return null | |
} | |
x["next_token"](x) | |
set(s, "value", x["parse_expression"](x, MONKEY_PARSER_PRECEDENCES["LOWEST"])) | |
if (x["peek_token_is"](x, MONKEY_TOKEN["SEMICOLON"])) { | |
x["next_token"](x) | |
} | |
return s | |
}, | |
"parse_return_statement": fn(x) { | |
var s = monkey_return_statement_create() | |
set(s, "token", x["cur_token"]) | |
x["next_token"](x) | |
set(s, "return_value", x["parse_expression"](x, MONKEY_PARSER_PRECEDENCES["LOWEST"])) | |
if (x["peek_token_is"](x, MONKEY_TOKEN["SEMICOLON"])) { | |
x["next_token"](x) | |
} | |
return s | |
}, | |
"parse_expression_statement": fn(x) { | |
var s = monkey_expression_statement_create() | |
set(s, "token", x["cur_token"]) | |
set(s, "expression", x["parse_expression"](x, MONKEY_PARSER_PRECEDENCES["LOWEST"])) | |
if (x["peek_token_is"](x, MONKEY_TOKEN["SEMICOLON"])) { | |
x["next_token"](x) | |
} | |
return s | |
}, | |
"parse_block_statement": fn(x) { | |
var block = monkey_block_statement_create() | |
set(block, "token", x["cur_token"]) | |
x["next_token"](x) | |
repeat { | |
if (x["cur_token_is"](x, MONKEY_TOKEN["RBRACE"]) | x["cur_token_is"](x, MONKEY_TOKEN["EOF"])) { | |
return null | |
} | |
var s = x["parse_statement"](x) | |
if (s != null) { | |
set(block, "statements", block["statements"] + s) | |
} | |
x["next_token"](x) | |
} | |
return block | |
}, | |
"parse_expression": fn(x, precedence) { | |
var prefix = x["prefix_parse_fns"][x["cur_token"]["type"]] | |
if (prefix == null) { | |
x["no_prefix_parse_fn_error"](x, x["cur_token"]["type"]) | |
return null | |
} | |
var temp = [] | |
var left_exp = prefix(x) | |
repeat { | |
if (x["peek_token_is"](x, MONKEY_TOKEN["SEMICOLON"]) | precedence >= x["peek_precedence"](x)) { | |
return null | |
} | |
var infix = x["infix_parse_fns"][x["peek_token"]["type"]] | |
if (infix == null) { | |
var temp = [left_exp] | |
return left_exp | |
} | |
x["next_token"](x) | |
var left_exp = infix(x, left_exp) | |
} | |
if (len(temp) > 0) { | |
return temp[0] | |
} | |
return left_exp | |
}, | |
"parse_identifier": fn(x) { | |
var ret = monkey_identifier_create("") | |
set(ret, "token", x["cur_token"]) | |
set(ret, "value", x["cur_token"]["literal"]) | |
return ret | |
}, | |
"parse_integer_literal": fn(x) { | |
var lit = monkey_integer_literal_create("") | |
set(lit, "token", x["cur_token"]) | |
var test = integer(number(x["cur_token"]["literal"])) | |
if (string(test) != x["cur_token"]["literal"]) { | |
var msg = "could not parse " + x["cur_token"]["literal"] + " as integer" | |
set(x, "errors", x["errors"] + msg) | |
return null | |
} | |
set(lit, "value", test) | |
return lit | |
}, | |
"parse_string_literal": fn(x) { | |
var lit = monkey_string_literal_create("") | |
set(lit, "token", x["cur_token"]) | |
set(lit, "value", x["cur_token"]["literal"]) | |
return lit | |
}, | |
"parse_array_literal": fn(x) { | |
var a = monkey_array_literal_create() | |
set(a, "token", x["cur_token"]) | |
set(a, "elements", x["parse_expression_list"](x, MONKEY_TOKEN["RBRACKET"])) | |
return a | |
}, | |
"parse_hash_literal": fn(x) { | |
var h = monkey_hash_literal_create() | |
set(h, "token", x["cur_token"]) | |
var temp = [0] | |
repeat { | |
if (x["peek_token_is"](x, MONKEY_TOKEN["RBRACE"])) { | |
return null | |
} | |
x["next_token"](x) | |
var key = x["parse_expression"](x, MONKEY_PARSER_PRECEDENCES["LOWEST"]) | |
if (!x["expect_peek"](x, MONKEY_TOKEN["COLON"])) { | |
set(temp, 0, 1) | |
return null | |
} | |
x["next_token"](x) | |
var value = x["parse_expression"](x, MONKEY_PARSER_PRECEDENCES["LOWEST"]) | |
set(h["pairs"], key, value) | |
if (!x["peek_token_is"](x, MONKEY_TOKEN["RBRACE"])) { | |
if (!x["expect_peek"](x, MONKEY_TOKEN["COMMA"])) { | |
set(temp, 0, 2) | |
return null | |
} | |
} | |
} | |
if (temp[0] != 0) { | |
return null | |
} | |
if (!x["expect_peek"](x, MONKEY_TOKEN["RBRACE"])) { | |
return null | |
} | |
return h | |
}, | |
"parse_boolean": fn(x) { | |
var ret = monkey_boolean_create("") | |
set(ret, "token", x["cur_token"]) | |
set(ret, "value", x["cur_token_is"](x, MONKEY_TOKEN["TRUE"])) | |
return ret | |
}, | |
"parse_prefix_expression": fn(x) { | |
var e = monkey_prefix_expression_create() | |
set(e, "token", x["cur_token"]) | |
set(e, "operator", x["cur_token"]["literal"]) | |
x["next_token"](x) | |
set(e, "right", x["parse_expression"](x, MONKEY_PARSER_PRECEDENCES["PREFIX"])) | |
return e | |
}, | |
"parse_infix_expression": fn(x, left_) { | |
var e = monkey_infix_expression_create() | |
set(e, "token", x["cur_token"]) | |
set(e, "operator", x["cur_token"]["literal"]) | |
set(e, "left", left_) | |
var precedence = x["cur_precedence"](x) | |
x["next_token"](x) | |
set(e, "right", x["parse_expression"](x, precedence)) | |
return e | |
}, | |
"parse_grouped_expression": fn(x) { | |
x["next_token"](x) | |
var e = x["parse_expression"](x, MONKEY_PARSER_PRECEDENCES["LOWEST"]) | |
if (!x["expect_peek"](x, MONKEY_TOKEN["RPAREN"])) { | |
return null | |
} | |
return e | |
}, | |
"parse_if_expression": fn(x) { | |
var e = monkey_if_expression_create() | |
set(e, "token", x["cur_token"]) | |
if (!x["expect_peek"](x, MONKEY_TOKEN["LPAREN"])) { | |
return null | |
} | |
x["next_token"](x) | |
set(e, "condition", x["parse_expression"](x, MONKEY_PARSER_PRECEDENCES["LOWEST"])) | |
if (!x["expect_peek"](x, MONKEY_TOKEN["RPAREN"])) { | |
return null | |
} | |
if (!x["expect_peek"](x, MONKEY_TOKEN["LBRACE"])) { | |
return null | |
} | |
set(e, "consequence", x["parse_block_statement"](x)) | |
if (x["peek_token_is"](x, MONKEY_TOKEN["ELSE"])) { | |
x["next_token"](x) | |
if (!x["expect_peek"](x, MONKEY_TOKEN["LBRACE"])) { | |
return null | |
} | |
set(e, "alternative", x["parse_block_statement"](x)) | |
} | |
return e | |
}, | |
"parse_function_literal": fn(x) { | |
var lit = monkey_function_literal_create() | |
set(lit, "token", x["cur_token"]) | |
if (!x["expect_peek"](x, MONKEY_TOKEN["LPAREN"])) { | |
return null | |
} | |
set(lit, "parameters", x["parse_function_parameters"](x)) | |
if (!x["expect_peek"](x, MONKEY_TOKEN["LBRACE"])) { | |
return null | |
} | |
set(lit, "body", x["parse_block_statement"](x)) | |
return lit | |
}, | |
"parse_function_parameters": fn(x) { | |
var identifiers = [] | |
if (x["peek_token_is"](x, MONKEY_TOKEN["RPAREN"])) { | |
x["next_token"](x) | |
return identifiers | |
} | |
x["next_token"](x) | |
var ident = monkey_identifier_create("") | |
set(ident, "token", x["cur_token"]) | |
set(ident, "value", x["cur_token"]["literal"]) | |
var identifiers = identifiers + ident | |
repeat { | |
if (!x["peek_token_is"](x, MONKEY_TOKEN["COMMA"])) { | |
return null | |
} | |
x["next_token"](x) | |
x["next_token"](x) | |
var ident = monkey_identifier_create("") | |
set(ident, "token", x["cur_token"]) | |
set(ident, "value", x["cur_token"]["literal"]) | |
var identifiers = identifiers + ident | |
} | |
if (!x["expect_peek"](x, MONKEY_TOKEN["RPAREN"])) { | |
return null | |
} | |
return identifiers | |
}, | |
"parse_call_expression": fn(x, function) { | |
var expr = monkey_call_expression_create() | |
set(expr, "token", x["cur_token"]) | |
set(expr, "function", function) | |
set(expr, "arguments", x["parse_expression_list"](x, MONKEY_TOKEN["RPAREN"])) | |
return expr | |
}, | |
"parse_expression_list": fn(x, end) { | |
var ret = [] | |
if (x["peek_token_is"](x, end)) { | |
x["next_token"](x) | |
return ret | |
} | |
x["next_token"](x) | |
var ret = ret + x["parse_expression"](x, MONKEY_PARSER_PRECEDENCES["LOWEST"]) | |
repeat { | |
if (!x["peek_token_is"](x, MONKEY_TOKEN["COMMA"])) { | |
return null | |
} | |
x["next_token"](x) | |
x["next_token"](x) | |
var ret = ret + x["parse_expression"](x, MONKEY_PARSER_PRECEDENCES["LOWEST"]) | |
} | |
if (!x["expect_peek"](x, end)) { | |
return null | |
} | |
return ret | |
}, | |
"parse_index_expression": fn(x, left_) { | |
var expr = monkey_index_expression_create() | |
set(expr, "token", x["cur_token"]) | |
set(expr, "left", left_) | |
x["next_token"](x) | |
set(expr, "index", x["parse_expression"](x, MONKEY_PARSER_PRECEDENCES["LOWEST"])) | |
if (!x["expect_peek"](x, MONKEY_TOKEN["RBRACKET"])) { | |
return null | |
} | |
return expr | |
}, | |
"cur_token_is": fn(x, t) { | |
return x["cur_token"]["type"] == t | |
}, | |
"peek_token_is": fn(x, t) { | |
return x["peek_token"]["type"] == t | |
}, | |
"expect_peek": fn(x, t) { | |
if (x["peek_token_is"](x, t)) { | |
x["next_token"](x) | |
return true | |
} else { | |
x["peek_error"](x, t) | |
return false | |
} | |
}, | |
"peek_error": fn(x, t) { | |
var m = "expected next token to be " + t + ", got " + x["peek_token"]["type"] + " instead" | |
set(x, "errors", x["errors"] + m) | |
}, | |
"register_prefix": fn(x, token_type, f) { | |
set(x["prefix_parse_fns"], token_type, f) | |
}, | |
"register_infix": fn(x, token_type, f) { | |
set(x["infix_parse_fns"], token_type, f) | |
}, | |
"no_prefix_parse_fn_error": fn(x, token_type) { | |
var m = "no prefix parse function for " + token_type + " found" | |
set(x, "errors", x["errors"] + m) | |
}, | |
"peek_precedence": fn(x) { | |
var p = x["PRECEDENCES"][x["peek_token"]["type"]] | |
if (p) { | |
return p | |
} | |
return MONKEY_PARSER_PRECEDENCES["LOWEST"] | |
}, | |
"cur_precedence": fn(x) { | |
var p = x["PRECEDENCES"][x["cur_token"]["type"]] | |
if (p) { | |
return p | |
} | |
return MONKEY_PARSER_PRECEDENCES["LOWEST"] | |
}, | |
} | |
var monkey_parser_create = fn() { | |
var r = {} + MONKEY_PARSER | |
set(r, "lexer", null) | |
set(r, "cur_token", null) | |
set(r, "peek_token", null) | |
set(r, "errors", []) | |
set(r, "prefix_parse_fns", {}) | |
set(r, "infix_parse_fns", {}) | |
r["register_prefix"](r, MONKEY_TOKEN["IDENT"], r["parse_identifier"]) | |
r["register_prefix"](r, MONKEY_TOKEN["INT"], r["parse_integer_literal"]) | |
r["register_prefix"](r, MONKEY_TOKEN["BANG"], r["parse_prefix_expression"]) | |
r["register_prefix"](r, MONKEY_TOKEN["MINUS"], r["parse_prefix_expression"]) | |
r["register_prefix"](r, MONKEY_TOKEN["TRUE"], r["parse_boolean"]) | |
r["register_prefix"](r, MONKEY_TOKEN["FALSE"], r["parse_boolean"]) | |
r["register_prefix"](r, MONKEY_TOKEN["LPAREN"], r["parse_grouped_expression"]) | |
r["register_prefix"](r, MONKEY_TOKEN["IF"], r["parse_if_expression"]) | |
r["register_prefix"](r, MONKEY_TOKEN["FUNCTION"], r["parse_function_literal"]) | |
r["register_prefix"](r, MONKEY_TOKEN["STRING"], r["parse_string_literal"]) | |
r["register_prefix"](r, MONKEY_TOKEN["LBRACKET"], r["parse_array_literal"]) | |
r["register_prefix"](r, MONKEY_TOKEN["LBRACE"], r["parse_hash_literal"]) | |
r["register_infix"](r, MONKEY_TOKEN["PLUS"], r["parse_infix_expression"]) | |
r["register_infix"](r, MONKEY_TOKEN["MINUS"], r["parse_infix_expression"]) | |
r["register_infix"](r, MONKEY_TOKEN["SLASH"], r["parse_infix_expression"]) | |
r["register_infix"](r, MONKEY_TOKEN["ASTERISK"], r["parse_infix_expression"]) | |
r["register_infix"](r, MONKEY_TOKEN["EQ"], r["parse_infix_expression"]) | |
r["register_infix"](r, MONKEY_TOKEN["NOT_EQ"], r["parse_infix_expression"]) | |
r["register_infix"](r, MONKEY_TOKEN["LT"], r["parse_infix_expression"]) | |
r["register_infix"](r, MONKEY_TOKEN["GT"], r["parse_infix_expression"]) | |
r["register_infix"](r, MONKEY_TOKEN["LPAREN"], r["parse_call_expression"]) | |
r["register_infix"](r, MONKEY_TOKEN["LBRACKET"], r["parse_index_expression"]) | |
return r | |
} | |
var monkey_parser_new = fn(l) { | |
var p = monkey_parser_create() | |
set(p, "lexer", l) | |
p["next_token"](p) | |
p["next_token"](p) | |
return p | |
} | |
var MONKEY_PROGRAM = {} + MONKEY_NODE | |
set(MONKEY_PROGRAM, "type", "program") | |
set(MONKEY_PROGRAM, "token_literal", fn(x) { | |
if (len(x["statements"]) > 0) { | |
return x["statements"][0]["token_literal"](x) | |
} else { | |
return "" | |
} | |
}) | |
set(MONKEY_PROGRAM, "string", fn(x) { | |
var ret = [] | |
each(x["statements"], fn(e, i) { | |
var ret = ret + e["string"](e) | |
}) | |
return join("", ret) | |
}) | |
var monkey_program_create = fn() { | |
var r = {} + MONKEY_PROGRAM | |
set(r, "statements", []) | |
return r | |
} | |
var MONKEY_HASHABLE = {} | |
set(MONKEY_HASHABLE, "hashable", true) | |
set(MONKEY_HASHABLE, "hash_key", fn(x) { | |
}) | |
var monkey_hashable_create = fn() { | |
var r = {} + MONKEY_HASHABLE | |
return r | |
} | |
var MONKEY_OBJECT_TYPES = { | |
"INTEGER_OBJ": "INTEGER", | |
"BOOLEAN_OBJ": "BOOLEAN", | |
"NULL_OBJ": "NULL", | |
"RETURN_VALUE_OBJ": "RETURN_VALUE", | |
"ERROR_OBJ": "ERROR", | |
"FUNCTION_OBJ": "FUNCTION", | |
"STRING_OBJ": "STRING", | |
"BUILTIN_OBJ": "BUILTIN", | |
"ARRAY_OBJ": "ARRAY", | |
"HASH_OBJ": "HASH" | |
} | |
var MONKEY_OBJECT = { | |
"type": fn(x) { | |
return x["object_type"] | |
}, | |
"inspect": fn(x) { | |
return "" | |
}, | |
"inspect_value": fn(x) { | |
return x["inspect"](x) | |
} | |
} | |
var monkey_object_create = fn(value, type_) { | |
var r = {} + MONKEY_OBJECT | |
set(r, "value", value) | |
set(r, "object_type", type_) | |
return r | |
} | |
var MONKEY_OBJECT_INTEGER = {} + MONKEY_OBJECT + MONKEY_HASHABLE | |
set(MONKEY_OBJECT_INTEGER, "object_type", MONKEY_OBJECT_TYPES["INTEGER_OBJ"]) | |
set(MONKEY_OBJECT_INTEGER, "inspect", fn(x) { | |
return "" + x["value"] | |
}) | |
set(MONKEY_OBJECT_INTEGER, "hash_key", fn(x) { | |
return hash("" + x["type"](x) + "-" + x["value"]) | |
}) | |
var monkey_object_integer_create = fn() { | |
var r = {} + MONKEY_OBJECT_INTEGER | |
return r | |
} | |
var MONKEY_OBJECT_STRING = {} + MONKEY_OBJECT + MONKEY_HASHABLE | |
set(MONKEY_OBJECT_STRING, "object_type", MONKEY_OBJECT_TYPES["STRING_OBJ"]) | |
set(MONKEY_OBJECT_STRING, "inspect", fn(x) { | |
return "" + quote() + x["value"] + quote() | |
}) | |
set(MONKEY_OBJECT_STRING, "inspect_value", fn(x) { | |
return "" + x["value"] | |
}) | |
set(MONKEY_OBJECT_STRING, "hash_key", fn(x) { | |
return hash("" + x["type"](x) + "-" + hash(x["value"])) | |
}) | |
var monkey_object_string_create = fn() { | |
var r = {} + MONKEY_OBJECT_STRING | |
return r | |
} | |
var MONKEY_OBJECT_BOOLEAN = {} + MONKEY_OBJECT + MONKEY_HASHABLE | |
set(MONKEY_OBJECT_BOOLEAN, "object_type", MONKEY_OBJECT_TYPES["BOOLEAN_OBJ"]) | |
set(MONKEY_OBJECT_BOOLEAN, "inspect", fn(x) { | |
return lower(string(x["value"])) | |
}) | |
set(MONKEY_OBJECT_BOOLEAN, "hash_key", fn(x) { | |
var v = 0 | |
if (x["value"]) { | |
var v = 1 | |
} | |
return hash("" + x["type"](x) + "-" + v) | |
}) | |
var monkey_object_boolean_create = fn(value) { | |
var r = {} + MONKEY_OBJECT_BOOLEAN | |
set(r, "value", value) | |
return r | |
} | |
var MONKEY_OBJECT_NULL = {} + MONKEY_OBJECT | |
set(MONKEY_OBJECT_NULL, "object_type", MONKEY_OBJECT_TYPES["NULL_OBJ"]) | |
set(MONKEY_OBJECT_NULL, "inspect", fn(x) { | |
return "null" | |
}) | |
var monkey_object_null_create = fn() { | |
var r = {} + MONKEY_OBJECT_NULL | |
return r | |
} | |
var MONKEY_OBJECT_RETURN_VALUE = {} + MONKEY_OBJECT | |
set(MONKEY_OBJECT_RETURN_VALUE, "object_type", MONKEY_OBJECT_TYPES["RETURN_VALUE_OBJ"]) | |
set(MONKEY_OBJECT_RETURN_VALUE, "inspect", fn(x) { | |
return x["value"]["inspect"](x["value"]) | |
}) | |
var monkey_object_return_value_create = fn() { | |
var r = {} + MONKEY_OBJECT_RETURN_VALUE | |
return r | |
} | |
var MONKEY_OBJECT_ERROR = {} + MONKEY_OBJECT | |
set(MONKEY_OBJECT_ERROR, "object_type", MONKEY_OBJECT_TYPES["ERROR_OBJ"]) | |
set(MONKEY_OBJECT_ERROR, "inspect", fn(x) { | |
return "ERROR: " + x["message"] | |
}) | |
var monkey_object_error_create = fn(value, message_) { | |
var r = {} + MONKEY_OBJECT_ERROR | |
set(r, "message", message_) | |
set(r, "value", value) | |
return r | |
} | |
var MONKEY_OBJECT_FUNCTION = {} + MONKEY_OBJECT | |
set(MONKEY_OBJECT_FUNCTION, "object_type", MONKEY_OBJECT_TYPES["FUNCTION_OBJ"]) | |
set(MONKEY_OBJECT_FUNCTION, "inspect", fn(x) { | |
var params = [] | |
each(x["parameters"], fn(e, i) { | |
var params = params + e["string"](e) | |
}) | |
var r = "fn" | |
var r = r + "(" | |
var r = r + join(", ", params) | |
var r = r + ")" | |
var r = r + x["body"]["string"](x["body"]) | |
return r | |
}) | |
var monkey_object_function_create = fn() { | |
var r = {} + MONKEY_OBJECT_FUNCTION | |
set(r, "parameters", []) | |
set(r, "body", monkey_block_statement_create()) | |
set(r, "env", monkey_environment_new()) | |
return r | |
} | |
var MONKEY_OBJECT_BUILTIN = {} + MONKEY_OBJECT | |
set(MONKEY_OBJECT_BUILTIN, "object_type", MONKEY_OBJECT_TYPES["BUILTIN_OBJ"]) | |
set(MONKEY_OBJECT_BUILTIN, "inspect", fn(x) { | |
return "builtin function" | |
}) | |
var monkey_object_builtin_create = fn(f, value) { | |
var r = {} + MONKEY_OBJECT_BUILTIN | |
set(r, "fn", f) | |
set(r, "value", value) | |
return r | |
} | |
var MONKEY_OBJECT_ARRAY = {} + MONKEY_OBJECT | |
set(MONKEY_OBJECT_ARRAY, "object_type", MONKEY_OBJECT_TYPES["ARRAY_OBJ"]) | |
set(MONKEY_OBJECT_ARRAY, "inspect", fn(x) { | |
var elements = [] | |
each(x["elements"], fn(e, i) { | |
var elements = elements + e["inspect"](e) | |
}) | |
var r = "[" | |
var r = r + join(", ", elements) | |
var r = r + "]" | |
return r | |
}) | |
var monkey_object_array_create = fn() { | |
var r = {} + MONKEY_OBJECT_ARRAY | |
set(r, "elements", []) | |
return r | |
} | |
var MONKEY_OBJECT_HASH = {} + MONKEY_OBJECT | |
set(MONKEY_OBJECT_HASH, "object_type", MONKEY_OBJECT_TYPES["HASH_OBJ"]) | |
set(MONKEY_OBJECT_HASH, "inspect", fn(x) { | |
var pairs = [] | |
each(keys(x["pairs"]), fn(e, i) { | |
var v = x["pairs"][e] | |
var pair = v["key"]["inspect"](v["key"]) + ": " + v["value"]["inspect"](v["value"]) | |
var pairs = pairs + pair | |
}) | |
var r = "{" | |
var r = r + join(", ", pairs) | |
var r = r + "}" | |
return r | |
}) | |
var monkey_object_hash_create = fn() { | |
var r = {} + MONKEY_OBJECT_HASH | |
set(r, "pairs", {}) | |
return r | |
} | |
var MONKEY_HASH_PAIR = {} | |
set(MONKEY_HASH_PAIR, "type", "hash_pair") | |
var monkey_hash_pair_create = fn() { | |
var r = {} + MONKEY_HASH_PAIR | |
set(r, "key", monkey_object_create("", "")) | |
set(r, "value", monkey_object_create("", "")) | |
return r | |
} | |
var MONKEY_ENVIRONMENT = { | |
"get": fn(x, name) { | |
var obj = x["store"][name] | |
if (obj != null) { | |
return obj | |
} | |
var o = x["outer"]["store"] | |
if (o != null) { | |
var obj = o[name] | |
return obj | |
} | |
return null | |
}, | |
"set": fn(x, name, value) { | |
set(x["store"], name, value) | |
return value | |
}, | |
"debug": fn(x) { | |
each(keys(x["store"]), fn(e, i) { | |
var v = x["store"][e] | |
if (v != null) { | |
println(e, ": ", v["inspect"](v)) | |
} | |
}) | |
}, | |
} | |
var monkey_environment_new = fn() { | |
var o = {} + MONKEY_ENVIRONMENT | |
set(o, "type", "environment") | |
set(o, "store", {}) | |
set(o, "outer", {} + MONKEY_ENVIRONMENT) | |
var r = {} + MONKEY_ENVIRONMENT | |
set(r, "type", "environment") | |
set(r, "store", {}) | |
set(r, "outer", o) | |
return r | |
} | |
var monkey_environment_enclosed = fn(outer) { | |
var r = {} + MONKEY_ENVIRONMENT | |
set(r, "type", "environment") | |
set(r, "store", {}) | |
set(r, "outer", outer) | |
return r | |
} | |
var MONKEY_BUILTIN_FUNCTIONS = { | |
"len": fn(evaluator, args) { | |
if (len(args) != 1) { | |
return evaluator["new_error"](evaluator, "wrong number of arguments, got=" + len(args) + ", want=1") | |
} | |
var a = args[0] | |
if(in([MONKEY_OBJECT_TYPES["STRING_OBJ"], MONKEY_OBJECT_TYPES["ARRAY_OBJ"]], a["object_type"])) { | |
if (a["object_type"] == MONKEY_OBJECT_TYPES["STRING_OBJ"]) { | |
var o = monkey_object_integer_create() | |
set(o, "value", len(a["value"])) | |
return o | |
} | |
if (a["object_type"] == MONKEY_OBJECT_TYPES["ARRAY_OBJ"]) { | |
var o = monkey_object_integer_create() | |
set(o, "value", len(a["elements"])) | |
return o | |
} | |
} else { | |
return evaluator["new_error"](evaluator, "argument to len not supported, got " + a["type"](a)) | |
} | |
}, | |
"first": fn(evaluator, args) { | |
if (len(args) != 1) { | |
return evaluator["new_error"](evaluator, "wrong number of arguments, got=" + len(args) + ", want=1") | |
} | |
var a = args[0] | |
if (a["object_type"] == MONKEY_OBJECT_TYPES["ARRAY_OBJ"]) { | |
if (len(a["elements"]) > 0) { | |
return a["elements"][0] | |
} | |
return evaluator["NULL"] | |
} else { | |
return evaluator["new_error"](evaluator, "argument to first must be ARRAY, got " + a["type"](a)) | |
} | |
}, | |
"last": fn(evaluator, args) { | |
if (len(args) != 1) { | |
return evaluator["new_error"](evaluator, "wrong number of arguments, got=" + len(args) + ", want=1") | |
} | |
var a = args[0] | |
if (a["object_type"] == MONKEY_OBJECT_TYPES["ARRAY_OBJ"]) { | |
var length = len(a["elements"]) | |
if (length > 0) { | |
return a["elements"][length-1] | |
} | |
return evaluator["NULL"] | |
} else { | |
return evaluator["new_error"](evaluator, "argument to last must be ARRAY, got " + a["type"](a)) | |
} | |
}, | |
"rest": fn(evaluator, args) { | |
if (len(args) != 1) { | |
return evaluator["new_error"](evaluator, "wrong number of arguments, got=" + len(args) + ", want=1") | |
} | |
var a = args[0] | |
if (a["object_type"] == MONKEY_OBJECT_TYPES["ARRAY_OBJ"]) { | |
var length = len(a["elements"]) | |
if (length > 0) { | |
var o = monkey_object_array_create() | |
set(o, "elements", slice(a["elements"], 1, length)) | |
return o | |
} | |
return evaluator["NULL"] | |
} else { | |
return evaluator["new_error"](evaluator, "argument to rest must be ARRAY, got " + a["type"](a)) | |
} | |
}, | |
"push": fn(evaluator, args) { | |
if (len(args) != 2) { | |
return evaluator["new_error"](evaluator, "wrong number of arguments, got=" + len(args) + ", want=2") | |
} | |
var a = args[0] | |
if (a["object_type"] == MONKEY_OBJECT_TYPES["ARRAY_OBJ"]) { | |
var temp = [] | |
each(a["elements"], fn(e, i) { | |
var temp = temp + e | |
}) | |
var o = monkey_object_array_create() | |
set(o, "elements", temp + args[1]) | |
return o | |
} else { | |
return evaluator["new_error"](evaluator, "argument to push must be ARRAY, got " + a["type"](a)) | |
} | |
}, | |
"puts": fn(evaluator, args) { | |
var f = println | |
if (gui()) { | |
var f = message | |
} | |
each(args, fn(e, i) { | |
f(e["inspect_value"](e)) | |
}) | |
return evaluator["NULL"] | |
}, | |
} | |
var MONKEY_BUILTINS = { | |
"BUILTINS": { | |
"len": monkey_object_builtin_create(MONKEY_BUILTIN_FUNCTIONS["len"], null), | |
"first": monkey_object_builtin_create(MONKEY_BUILTIN_FUNCTIONS["first"], null), | |
"last": monkey_object_builtin_create(MONKEY_BUILTIN_FUNCTIONS["last"], null), | |
"rest": monkey_object_builtin_create(MONKEY_BUILTIN_FUNCTIONS["rest"], null), | |
"push": monkey_object_builtin_create(MONKEY_BUILTIN_FUNCTIONS["push"], null), | |
"puts": monkey_object_builtin_create(MONKEY_BUILTIN_FUNCTIONS["puts"], null), | |
}, | |
"get": fn(x, f) { | |
return x["BUILTINS"][f] | |
}, | |
} | |
var MONKEY_EVALUATOR = { | |
"NULL": monkey_object_null_create(), | |
"TRUE": monkey_object_boolean_create(true), | |
"FALSE": monkey_object_boolean_create(false), | |
"eval": fn(x, node, env_) { | |
if (node["type"] == "program") { | |
return x["eval_program"](x, node, env_) | |
} | |
if (node["type"] == "expression_statement") { | |
return x["eval"](x, node["expression"], env_) | |
} | |
if (node["type"] == "integer_literal") { | |
var o = monkey_object_integer_create() | |
set(o, "value", node["value"]) | |
return o | |
} | |
if (node["type"] == "boolean") { | |
return x["get_boolean"](x, node["value"]) | |
} | |
if (node["type"] == "prefix_expression") { | |
var right_ = x["eval"](x, node["right"], env_) | |
if (x["is_error"](x, right_)) { | |
return right_ | |
} | |
return x["eval_prefix_expression"](x, node["operator"], right_) | |
} | |
if (node["type"] == "infix_expression") { | |
var left_ = x["eval"](x, node["left"], env_) | |
if (x["is_error"](x, left_)) { | |
return left_ | |
} | |
var right_ = x["eval"](x, node["right"], env_) | |
if (x["is_error"](x, right_)) { | |
return right_ | |
} | |
return x["eval_infix_expression"](x, node["operator"], left_, right_) | |
} | |
if (node["type"] == "block_statement") { | |
return x["eval_block_statement"](x, node, env_) | |
} | |
if (node["type"] == "if_expression") { | |
return x["eval_if_expression"](x, node, env_) | |
} | |
if (node["type"] == "return_statement") { | |
var val = x["eval"](x, node["return_value"], env_) | |
if (x["is_error"](x, val)) { | |
return val | |
} | |
var o = monkey_object_return_value_create() | |
set(o, "value", val) | |
return o | |
} | |
if (node["type"] == "let_statement") { | |
var val = x["eval"](x, node["value"], env_) | |
if (x["is_error"](x, val)) { | |
return val | |
} | |
env_["set"](env_, node["name"]["value"], val) | |
} | |
if (node["type"] == "identifier") { | |
return x["eval_identifier"](x, node, env_) | |
} | |
if (node["type"] == "function_literal") { | |
var params = node["parameters"] | |
var body = node["body"] | |
var en = monkey_environment_new() | |
set(en, "outer", env_["outer"]) | |
var s = {} | |
each(keys(env_["store"]), fn(e, i) { | |
set(s, e, env_["store"][e]) | |
}) | |
set(en, "store", s) | |
var o = monkey_object_function_create() | |
set(o, "parameters", params) | |
set(o, "body", body) | |
set(o, "env", en) | |
return o | |
} | |
if (node["type"] == "call_expression") { | |
var function = x["eval"](x, node["function"], env_) | |
var s = {} | |
each(keys(env_["store"]), fn(e, i) { | |
set(s, e, env_["store"][e]) | |
}) | |
if (x["is_error"](x, function)) { | |
return function | |
} | |
var args = x["eval_expressions"](x, node["arguments"], env_) | |
if (len(args) == 1 & x["is_error"](x, args[0])) { | |
return args[0] | |
} | |
return x["apply_function"](x, function, args, s) | |
} | |
if (node["type"] == "string_literal") { | |
var o = monkey_object_string_create() | |
set(o, "value", node["value"]) | |
return o | |
} | |
if (node["type"] == "array_literal") { | |
var elements = x["eval_expressions"](x, node["elements"], env_) | |
if (len(elements) == 1 & x["is_error"](x, elements[0])) { | |
return elements[0] | |
} | |
var o = monkey_object_array_create() | |
set(o, "elements", elements) | |
return o | |
} | |
if (node["type"] == "index_expression") { | |
var left_ = x["eval"](x, node["left"], env_) | |
if (x["is_error"](x, left_)) { | |
return left_ | |
} | |
var index_ = x["eval"](x, node["index"], env_) | |
if (x["is_error"](x, index_)) { | |
return index_ | |
} | |
return x["eval_index_expression"](x, left_, index_) | |
} | |
if (node["type"] == "hash_literal") { | |
return x["eval_hash_literal"](x, node, env_) | |
} | |
return null | |
}, | |
"eval_program": fn(x, program, env_) { | |
var r = [monkey_object_create("", "")] | |
var MONKEY_OBJECT_TYPES = MONKEY_OBJECT_TYPES | |
each(program["statements"], fn(e, i) { | |
var ret = x["eval"](x, e, env_) | |
set(r, 0, ret) | |
if (ret != null) { | |
if (ret["object_type"] == MONKEY_OBJECT_TYPES["RETURN_VALUE_OBJ"]) { | |
return ret["value"] | |
} | |
} | |
if (ret != null) { | |
if (ret["object_type"] == MONKEY_OBJECT_TYPES["ERROR_OBJ"]) { | |
return ret | |
} | |
} | |
}) | |
return r[0] | |
}, | |
"eval_block_statement": fn(x, block, env_) { | |
var r = [monkey_object_create("", "")] | |
var MONKEY_OBJECT_TYPES = MONKEY_OBJECT_TYPES | |
each(block["statements"], fn(e, i) { | |
var ret = x["eval"](x, e, env_) | |
set(r, 0, ret) | |
if (ret) { | |
var rt = ret["type"](ret) | |
if (in([MONKEY_OBJECT_TYPES["RETURN_VALUE_OBJ"], MONKEY_OBJECT_TYPES["ERROR_OBJ"]], rt)) { | |
set(r, 0, ret) | |
return ret | |
} | |
} | |
}) | |
return r[0] | |
}, | |
"get_boolean": fn(x, val) { | |
if (val) { | |
return x["TRUE"] | |
} | |
return x["FALSE"] | |
}, | |
"eval_prefix_expression": fn(x, operator, right_) { | |
if (operator == "!") { | |
return x["eval_bang_operator_expression"](x, right_) | |
} | |
if (operator == "-") { | |
return x["eval_minus_prefix_operator_expression"](x, right_) | |
} | |
return x["new_error"](x, "unknown operator: " + operator + right_["type"](right_)) | |
}, | |
"eval_infix_expression": fn(x, operator, left_, right_) { | |
if ((left_["type"](left_) == MONKEY_OBJECT_TYPES["INTEGER_OBJ"]) & (right_["type"](right_) == MONKEY_OBJECT_TYPES["INTEGER_OBJ"])) { | |
return x["eval_integer_infix_expression"](x, operator, left_, right_) | |
} | |
if ((left_["type"](left_) == MONKEY_OBJECT_TYPES["STRING_OBJ"]) & (right_["type"](right_) == MONKEY_OBJECT_TYPES["STRING_OBJ"])) { | |
return x["eval_string_infix_expression"](x, operator, left_, right_) | |
} | |
if (operator == "==") { | |
return x["get_boolean"](x, left_["hash_key"](left_) == right_["hash_key"](right_)) | |
} | |
if (operator == "!=") { | |
return x["get_boolean"](x, left_["hash_key"](left_) != right_["hash_key"](right_)) | |
} | |
if (left_["type"](left_) != right_["type"](right_)) { | |
return x["new_error"](x, "type mismatch: " + left_["type"](left_) + " " + operator + " " + right_["type"](right_)) | |
} | |
return x["new_error"](x, "unknown operator: " + left_["type"](left_) + " " + operator + " " + right_["type"](right_)) | |
}, | |
"eval_integer_infix_expression": fn(x, operator, left_, right_) { | |
var left_val = left_["value"] | |
var right_val = right_["value"] | |
var o = monkey_object_integer_create() | |
if (operator == "+") { | |
set(o, "value", left_val + right_val) | |
return o | |
} | |
if (operator == "-") { | |
set(o, "value", left_val - right_val) | |
return o | |
} | |
if (operator == "*") { | |
set(o, "value", left_val * right_val) | |
return o | |
} | |
if (operator == "/") { | |
var t = left_val / right_val | |
if (t != null) { | |
set(o, "value", integer(t)) | |
return o | |
} else { | |
return x["NULL"] | |
} | |
} | |
if (operator == "<") { | |
return x["get_boolean"](x, left_val < right_val) | |
} | |
if (operator == ">") { | |
return x["get_boolean"](x, left_val > right_val) | |
} | |
if (operator == "==") { | |
return x["get_boolean"](x, left_val == right_val) | |
} | |
if (operator == "!=") { | |
return x["get_boolean"](x, left_val != right_val) | |
} | |
return x["new_error"](x, "unknown operator: " + left_["type"](left_) + " " + operator + " " + right_["type"](right_)) | |
}, | |
"eval_string_infix_expression": fn(x, operator, left_, right_) { | |
var left_val = left_["value"] | |
var right_val = right_["value"] | |
var o = monkey_object_string_create() | |
if (operator != "+") { | |
return x["new_error"](x, "unknown operator: " + left_["type"](left_) + " " + operator + " " + right_["type"](right_)) | |
} | |
set(o, "value", left_val + right_val) | |
return o | |
}, | |
"eval_bang_operator_expression": fn(x, right_) { | |
if (right_["hash_key"](right_) == x["TRUE"]["hash_key"](x["TRUE"])) { | |
return x["FALSE"] | |
} | |
if (right_["hash_key"](right_) == x["FALSE"]["hash_key"](x["FALSE"])) { | |
return x["TRUE"] | |
} | |
if (right_["hash_key"](right_) == x["NULL"]["hash_key"](x["NULL"])) { | |
return x["TRUE"] | |
} | |
return x["FALSE"] | |
}, | |
"eval_minus_prefix_operator_expression": fn(x, right_) { | |
if (right_["type"](right_) != MONKEY_OBJECT_TYPES["INTEGER_OBJ"]) { | |
return x["new_error"](x, "unknown operator: -" + right_["type"](right_)) | |
} | |
var val = right_["value"] | |
var ret = monkey_object_integer_create() | |
set(ret, "value", -val) | |
return ret | |
}, | |
"eval_if_expression": fn(x, expression, env_) { | |
var condition = x["eval"](x, expression["condition"], env_) | |
if (x["is_error"](x, condition)) { | |
return condition | |
} | |
if (x["is_truthy"](x, condition)) { | |
return x["eval"](x, expression["consequence"], env_) | |
} | |
if (!expression["alternative"]["is_empty"](expression["alternative"])) { | |
return x["eval"](x, expression["alternative"], env_) | |
} | |
return x["NULL"] | |
}, | |
"eval_identifier": fn(x, node, env_) { | |
var val = env_["get"](env_, node["value"]) | |
if (val) { | |
return val | |
} | |
var builtin = MONKEY_BUILTINS["get"](MONKEY_BUILTINS, node["value"]) | |
if (builtin) { | |
return builtin | |
} | |
return x["new_error"](x, "identifier not found: " + node["value"]) | |
}, | |
"eval_expressions": fn(x, expr, env_) { | |
var result = [] | |
each(expr, fn(e, i) { | |
var evaluated = x["eval"](x, e, env_) | |
if (x["is_error"](x, evaluated)) { | |
var result = result + evaluated | |
return result | |
} | |
var result = result + evaluated | |
}) | |
return result | |
}, | |
"eval_index_expression": fn(x, left_, index_) { | |
if ((left_["type"](left_) == MONKEY_OBJECT_TYPES["ARRAY_OBJ"]) & (index_["type"](index_) == MONKEY_OBJECT_TYPES["INTEGER_OBJ"])) { | |
return x["eval_array_index_expression"](x, left_, index_) | |
} | |
if (left_["type"](left_) == MONKEY_OBJECT_TYPES["HASH_OBJ"]) { | |
return x["eval_hash_index_expression"](x, left_, index_) | |
} | |
return x["new_error"](x, "index operator not supported: " + left_["type"](left_)) | |
}, | |
"eval_array_index_expression": fn(x, array_, index_) { | |
var idx = index_["value"] | |
var max_idx = len(array_["elements"]) - 1 | |
if (idx < 0 | idx > max_idx) { | |
return x["NULL"] | |
} | |
return array_["elements"][idx] | |
}, | |
"eval_hash_literal": fn(x, node, env_) { | |
var pairs = {} | |
var monkey_hash_pair_create = monkey_hash_pair_create | |
var temp = [] | |
each(keys(node["pairs"]), fn(e, i) { | |
var key = x["eval"](x, e, env_) | |
if (x["is_error"](x, key)) { | |
var temp = temp + key | |
return key | |
} | |
if (key["hashable"] != true) { | |
var err = x["new_error"](x, "unusable as hash key: " + key["type"](key)) | |
var temp = temp + err | |
return err | |
} | |
var v = node["pairs"][e] | |
var val = x["eval"](x, v, env_) | |
if (x["is_error"](x, val)) { | |
var temp = temp + val | |
return val | |
} | |
var hashed = key["hash_key"](key) | |
var p = monkey_hash_pair_create() | |
set(p, "key", key) | |
set(p, "value", val) | |
set(pairs, hashed, p) | |
}) | |
if (!empty(temp)) { | |
return temp[0] | |
} | |
var o = monkey_object_hash_create() | |
set(o, "pairs", pairs) | |
return o | |
}, | |
"eval_hash_index_expression": fn(x, hashtable, index_) { | |
if (index_["hashable"] != true) { | |
return x["new_error"](x, "unusable as hash key: " + index_["type"](key)) | |
} | |
var pair = hashtable["pairs"][index_["hash_key"](index_)] | |
if (pair == null) { | |
return x["NULL"] | |
} | |
return pair["value"] | |
}, | |
"apply_function": fn(x, f, args, s) { | |
if (f["object_type"] == MONKEY_OBJECT_TYPES["FUNCTION_OBJ"]) { | |
var extended_env = x["extend_function_env"](x, f, args, s) | |
var evaluated = x["eval"](x, f["body"], extended_env) | |
return x["unwrap_return_value"](x, evaluated) | |
} | |
if (f["object_type"] == MONKEY_OBJECT_TYPES["BUILTIN_OBJ"]) { | |
return f["fn"](x, args) | |
} | |
return x["new_error"](x, "not a function: " + f["type"](f)) | |
}, | |
"extend_function_env": fn(x, f, args, s) { | |
var env_ = monkey_environment_enclosed(f["env"]) | |
set(env_, "store", s) | |
each(f["parameters"], fn(e, i) { | |
var p = e | |
env_["set"](env_, p["value"], args[i]) | |
}) | |
return env_ | |
}, | |
"unwrap_return_value": fn(x, obj) { | |
if (obj["object_type"] == MONKEY_OBJECT_TYPES["RETURN_VALUE_OBJ"]) { | |
return obj["value"] | |
} | |
return obj | |
}, | |
"is_truthy": fn(x, obj) { | |
if (obj["type"](obj) == MONKEY_OBJECT_TYPES["NULL_OBJ"]) { | |
return false | |
} | |
if (obj["hash_key"](obj) == x["TRUE"]["hash_key"](x["TRUE"])) { | |
return true | |
} | |
if (obj["hash_key"](obj) == x["FALSE"]["hash_key"](x["FALSE"])) { | |
return false | |
} | |
return true | |
}, | |
"new_error": fn(x, message_) { | |
var ret = monkey_object_error_create("", "") | |
set(ret, "message", message_) | |
return ret | |
}, | |
"is_error": fn(x, obj) { | |
if (obj) { | |
return obj["type"](obj) == MONKEY_OBJECT_TYPES["ERROR_OBJ"] | |
} | |
return false | |
}, | |
} | |
var monkey_evaluator_new = fn() { | |
var e = {} + MONKEY_EVALUATOR | |
return e | |
} | |
var MONKEY = { | |
"PROMPT": ">> ", | |
"input": fn(s) { | |
return input(s, MONKEY_TITLE) | |
}, | |
"output": fn(s) { | |
each(s, fn(e, i) { | |
print(e) | |
}) | |
println("") | |
}, | |
"lexer": fn(m) { | |
m["output"]([MONKEY_TITLE]) | |
m["output"]([MONKEY_MESSAGE]) | |
repeat { | |
var inp = trim(m["input"](m["PROMPT"])) | |
if (empty(inp)) { | |
return null | |
} | |
var l = monkey_lexer_new(inp) | |
repeat { | |
var t = l["next_token"](l) | |
if (t["type"] == MONKEY_TOKEN["EOF"]) { | |
return null | |
} | |
m["output"](["Type: ", t["type"], ", Literal: ", t["literal"]]) | |
} | |
} | |
}, | |
"parser": fn(m) { | |
m["output"]([MONKEY_TITLE]) | |
m["output"]([MONKEY_MESSAGE]) | |
repeat { | |
var inp = trim(m["input"](m["PROMPT"])) | |
if (empty(inp)) { | |
return null | |
} | |
var l = monkey_lexer_new(inp) | |
var p = monkey_parser_new(l) | |
var program = p["parse_program"](p) | |
if (!empty(p["errors"])) { | |
m["print_parse_errors"](m, p["errors"]) | |
} else { | |
m["output"]([program["string"](program)]) | |
} | |
} | |
}, | |
"print_parse_errors": fn(m, error) { | |
each(error, fn(e, i) { | |
m["output"](["PARSER ERROR: ", e]) | |
}) | |
}, | |
"evaluator_text": fn(m) { | |
m["output"]([MONKEY_TITLE]) | |
m["output"]([MONKEY_MESSAGE]) | |
var env_ = monkey_environment_new() | |
repeat { | |
var inp = trim(m["input"](m["PROMPT"])) | |
if (empty(inp)) { | |
return null | |
} | |
var l = monkey_lexer_new(inp) | |
var p = monkey_parser_new(l) | |
var program = p["parse_program"](p) | |
if (!empty(p["errors"])) { | |
m["print_parse_errors"](m, p["errors"]) | |
} else { | |
var evaluator = monkey_evaluator_new() | |
var evaluated = evaluator["eval"](evaluator, program, env_) | |
if (evaluated != null) { | |
m["output"]([evaluated["inspect"](evaluated)]) | |
} | |
} | |
} | |
}, | |
"evaluator_gui": fn(m) { | |
var env_ = monkey_environment_new() | |
reset() | |
title(MONKEY_TITLE) | |
var ue = component("edit","", true) | |
config(ue, "font", ["monospaced", 1, 14]) | |
config(ue, "border", "result") | |
config(ue, "background", "gray") | |
var ut = component("edit", "") | |
config(ut, "font", ["monospaced", 1, 14]) | |
config(ut, "border", "prompt") | |
var ub = component("button", "eval") | |
config(ub, "active", 0) | |
add([ut, ue]) | |
add_s(ub) | |
var monkey_lexer_new = monkey_lexer_new | |
var monkey_parser_new = monkey_parser_new | |
var monkey_evaluator_new = monkey_evaluator_new | |
event(ub, fn() { | |
var inp = trim(get(ut, "contents")) | |
config(ut, "contents", "") | |
config(ut, "focus", true) | |
if (!empty(inp)) { | |
var ue = ue | |
var c = get(ue, "contents") | |
config(ue, "contents", c + newline() + m["PROMPT"] + inp ) | |
var l = monkey_lexer_new(inp) | |
var p = monkey_parser_new(l) | |
var program = p["parse_program"](p) | |
if (!empty(p["errors"])) { | |
var errors = [] | |
each(p["errors"], fn(e, i) { | |
var errors = errors + ("PARSER ERROR: " + e) | |
}) | |
config(ue, "contents", c + newline() + m["PROMPT"] + inp + newline() + join(newline(), errors)) | |
} else { | |
var evaluator = monkey_evaluator_new() | |
var evaluated = evaluator["eval"](evaluator, program, env_) | |
if (evaluated != null) { | |
config(ue, "contents", c + newline() + m["PROMPT"] + inp + newline() + evaluated["inspect"](evaluated)) | |
} | |
} | |
} | |
}) | |
menubar([ | |
["File", 0, [ ["Quit", 0, true, fn() {frame_close()}] ]], | |
["Help", 0, [ ["About", 0, true, | |
fn() { | |
message( | |
variables()["MONKEY_TITLE"][1] | |
+ lf() + | |
"(c) Noprianto, 2020-2021" | |
) | |
}] ]] | |
]) | |
statusbar(6, "Singkong", true) | |
statusbar(7, singkong()["version_string"], true) | |
closing("Are you sure you want to quit this application?", "Please confirm") | |
show() | |
}, | |
"evaluator": fn(m) { | |
if (gui()) { | |
m["evaluator_gui"](m) | |
} else { | |
m["evaluator_text"](m) | |
} | |
}, | |
} | |
MONKEY["evaluator"](MONKEY) |