├── assets ├── icon.png ├── default_env.tres └── icon.png.import ├── docs ├── gdlisp-repl.png ├── scope.txt └── gdlisp-repl.png.import ├── .gitignore ├── entities ├── OutputContainer.tscn └── OutputContainer.gd ├── screens ├── MainScreen.tscn └── MainScreen.gd ├── utils ├── AppManager.gd ├── SaveDataUtil.gd ├── GDLispSyntaxDoc.gd └── GDLisp.gd ├── tests ├── TestRunner.gd ├── TestBase.gd ├── TestRunner.tscn └── SanityTests.gd ├── project.godot ├── README.md └── LICENSE /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/you-win/gdlisp/HEAD/assets/icon.png -------------------------------------------------------------------------------- /docs/gdlisp-repl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/you-win/gdlisp/HEAD/docs/gdlisp-repl.png -------------------------------------------------------------------------------- /assets/default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=2] 2 | 3 | [sub_resource type="ProceduralSky" id=1] 4 | 5 | [resource] 6 | background_mode = 2 7 | background_sky = SubResource( 1 ) 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # System 2 | .DS_Store 3 | *.swp 4 | *.swo 5 | .vs/ 6 | .vscode/ 7 | 8 | # Rust 9 | target/ 10 | Cargo.lock 11 | **/*.rs.bk 12 | 13 | # Godot 14 | .import/ 15 | export.cfg 16 | export_presets.cfg 17 | .mono/ 18 | data_*/ 19 | export/ 20 | -------------------------------------------------------------------------------- /docs/scope.txt: -------------------------------------------------------------------------------- 1 | ( <- runtime scope 2 | ( <- global scope 3 | (def x 10) 4 | (def y 123) 5 | (def x 12) <- error 6 | 7 | (exp-1 [] 8 | (def a 1) 9 | (def x 2) <- error 10 | (def b 3) 11 | (goto bad-label) <- error 12 | (goto good-label)) 13 | 14 | (exp-2 [] 15 | (def a 2) 16 | (def x 3) <- error 17 | (label bad-label) 18 | (print "hello") 19 | (if (x > 10) (goto end) (+ x 1))) 20 | 21 | (exp-1) 22 | 23 | (label good-label) 24 | 25 | (exp-2) 26 | (goto good-label)) 27 | 28 | (label end)) 29 | 30 | -------------------------------------------------------------------------------- /assets/icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon.png-b6a7fb2db36edd3d95dc42f1dc8c1c5d.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/icon.png" 13 | dest_files=[ "res://.import/icon.png-b6a7fb2db36edd3d95dc42f1dc8c1c5d.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /docs/gdlisp-repl.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/gdlisp-repl.png-fa23472675ea2aad7fd06f0d6e24609f.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://docs/gdlisp-repl.png" 13 | dest_files=[ "res://.import/gdlisp-repl.png-fa23472675ea2aad7fd06f0d6e24609f.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /entities/OutputContainer.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://entities/OutputContainer.gd" type="Script" id=1] 4 | 5 | [node name="OutputContainer" type="MarginContainer"] 6 | anchor_right = 1.0 7 | anchor_bottom = 1.0 8 | margin_left = 10.0 9 | margin_top = 10.0 10 | margin_right = -10.0 11 | margin_bottom = -10.0 12 | script = ExtResource( 1 ) 13 | __meta__ = { 14 | "_edit_use_anchors_": false 15 | } 16 | 17 | [node name="ColorRect" type="ColorRect" parent="."] 18 | margin_right = 1004.0 19 | margin_bottom = 580.0 20 | mouse_filter = 2 21 | color = Color( 1, 1, 1, 0.0862745 ) 22 | 23 | [node name="MarginContainer" type="MarginContainer" parent="."] 24 | margin_right = 1004.0 25 | margin_bottom = 580.0 26 | mouse_filter = 2 27 | custom_constants/margin_right = 10 28 | custom_constants/margin_top = 10 29 | custom_constants/margin_left = 10 30 | custom_constants/margin_bottom = 10 31 | 32 | [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] 33 | margin_left = 10.0 34 | margin_top = 10.0 35 | margin_right = 994.0 36 | margin_bottom = 570.0 37 | alignment = 1 38 | 39 | [node name="UpperLabel" type="Label" parent="MarginContainer/VBoxContainer"] 40 | margin_top = 264.0 41 | margin_right = 984.0 42 | margin_bottom = 278.0 43 | 44 | [node name="LowerLabel" type="Label" parent="MarginContainer/VBoxContainer"] 45 | margin_top = 282.0 46 | margin_right = 984.0 47 | margin_bottom = 296.0 48 | -------------------------------------------------------------------------------- /screens/MainScreen.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://screens/MainScreen.gd" type="Script" id=1] 4 | 5 | [node name="MainScreen" type="Control"] 6 | script = ExtResource( 1 ) 7 | __meta__ = { 8 | "_edit_use_anchors_": false 9 | } 10 | 11 | [node name="CanvasLayer" type="CanvasLayer" parent="."] 12 | 13 | [node name="ColorRect" type="ColorRect" parent="CanvasLayer"] 14 | anchor_right = 1.0 15 | anchor_bottom = 1.0 16 | color = Color( 0.14902, 0.14902, 0.14902, 1 ) 17 | __meta__ = { 18 | "_edit_use_anchors_": false 19 | } 20 | 21 | [node name="OutputContainer" type="MarginContainer" parent="CanvasLayer"] 22 | anchor_right = 1.0 23 | anchor_bottom = 0.8 24 | margin_left = 10.0 25 | margin_top = 10.0 26 | margin_right = -10.0 27 | margin_bottom = -10.0 28 | __meta__ = { 29 | "_edit_use_anchors_": false 30 | } 31 | 32 | [node name="ScrollContainer" type="ScrollContainer" parent="CanvasLayer/OutputContainer"] 33 | margin_right = 1004.0 34 | margin_bottom = 460.0 35 | 36 | [node name="Output" type="VBoxContainer" parent="CanvasLayer/OutputContainer/ScrollContainer"] 37 | margin_right = 1004.0 38 | size_flags_horizontal = 3 39 | 40 | [node name="InputContainer" type="MarginContainer" parent="CanvasLayer"] 41 | anchor_top = 0.8 42 | anchor_right = 1.0 43 | anchor_bottom = 1.0 44 | margin_left = 10.0 45 | margin_right = -10.0 46 | margin_bottom = -10.0 47 | __meta__ = { 48 | "_edit_use_anchors_": false 49 | } 50 | 51 | [node name="Input" type="TextEdit" parent="CanvasLayer/InputContainer"] 52 | margin_right = 1004.0 53 | margin_bottom = 110.0 54 | highlight_current_line = true 55 | draw_tabs = true 56 | draw_spaces = true 57 | -------------------------------------------------------------------------------- /utils/AppManager.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | signal message_logged(message) 4 | 5 | var sdu: SaveDataUtil = SaveDataUtil.new() 6 | 7 | var current_save_data: Dictionary 8 | 9 | ############################################################################### 10 | # Builtin functions # 11 | ############################################################################### 12 | 13 | func _ready() -> void: 14 | randomize() 15 | 16 | ############################################################################### 17 | # Connections # 18 | ############################################################################### 19 | 20 | ############################################################################### 21 | # Private functions # 22 | ############################################################################### 23 | 24 | ############################################################################### 25 | # Public functions # 26 | ############################################################################### 27 | 28 | func log_message(message: String, is_error: bool = false) -> void: 29 | if is_error: 30 | message = "[ERROR] %s" % message 31 | assert(false, message) 32 | print(message) 33 | emit_signal("message_logged", message) 34 | 35 | func does_metadata_exist() -> bool: 36 | return sdu.does_metadata_exist() 37 | 38 | func load_data(path: String = "") -> Dictionary: 39 | """ 40 | Wrapper around SaveDataUtil.load_data(...) 41 | 42 | defaults to metadata value if empty string is passed 43 | """ 44 | return sdu.load_data(path) 45 | 46 | func save_data(file_name: String, data: Dictionary) -> void: 47 | """ 48 | Wrapper around SaveDataUtil.save_data(...) 49 | """ 50 | sdu.save_data(file_name, data) 51 | -------------------------------------------------------------------------------- /tests/TestRunner.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | ############################################################################### 4 | # Builtin functions # 5 | ############################################################################### 6 | 7 | func _ready() -> void: 8 | find_node("Sanity").connect("pressed", self, "_sanity_test") 9 | find_node("Tokenizer").connect("pressed", self, "_tokenizer_test") 10 | find_node("Parser").connect("pressed", self, "_parser_test") 11 | find_node("Evaluator").connect("pressed", self, "_evaluator_test") 12 | find_node("EndToEnd").connect("pressed", self, "_end_to_end_test") 13 | 14 | find_node("RunAll").connect("pressed", self, "_run_all_tests") 15 | 16 | ############################################################################### 17 | # Connections # 18 | ############################################################################### 19 | 20 | func _sanity_test() -> void: 21 | var sanity_test: TestBase = load("res://tests/SanityTests.gd").new() 22 | 23 | sanity_test.run_tests() 24 | 25 | func _tokenizer_test() -> void: 26 | pass 27 | 28 | func _parser_test() -> void: 29 | pass 30 | 31 | func _evaluator_test() -> void: 32 | pass 33 | 34 | func _end_to_end_test() -> void: 35 | pass 36 | 37 | func _run_all_tests() -> void: 38 | _sanity_test() 39 | _tokenizer_test() 40 | _parser_test() 41 | _evaluator_test() 42 | _end_to_end_test() 43 | 44 | ############################################################################### 45 | # Private functions # 46 | ############################################################################### 47 | 48 | ############################################################################### 49 | # Public functions # 50 | ############################################################################### 51 | 52 | 53 | -------------------------------------------------------------------------------- /entities/OutputContainer.gd: -------------------------------------------------------------------------------- 1 | extends MarginContainer 2 | 3 | onready var upper_label: Label = $MarginContainer/VBoxContainer/UpperLabel 4 | onready var lower_label: Label = $MarginContainer/VBoxContainer/LowerLabel 5 | 6 | var _is_mouse_entered: bool = false 7 | 8 | var upper_text: String = "" 9 | var lower_text: String = "" 10 | 11 | ############################################################################### 12 | # Builtin functions # 13 | ############################################################################### 14 | 15 | func _ready() -> void: 16 | connect("mouse_entered", self, "_mouse_entered") 17 | connect("mouse_exited", self, "_mouse_exited") 18 | 19 | upper_label.text = upper_text 20 | lower_label.text = lower_text 21 | 22 | func _input(event: InputEvent) -> void: 23 | if _is_mouse_entered: 24 | if event.is_action_pressed("click"): 25 | OS.clipboard = _fix_text(upper_label.text) 26 | 27 | ############################################################################### 28 | # Connections # 29 | ############################################################################### 30 | 31 | func _mouse_entered() -> void: 32 | _is_mouse_entered = true 33 | 34 | func _mouse_exited() -> void: 35 | _is_mouse_entered = false 36 | 37 | ############################################################################### 38 | # Private functions # 39 | ############################################################################### 40 | 41 | func _fix_text(text: String) -> String: 42 | return text.replace(" ", "\t") 43 | 44 | ############################################################################### 45 | # Public functions # 46 | ############################################################################### 47 | 48 | func get_value() -> String: 49 | return upper_label.text.strip_edges() 50 | -------------------------------------------------------------------------------- /tests/TestBase.gd: -------------------------------------------------------------------------------- 1 | class_name TestBase 2 | extends Reference 3 | 4 | ############################################################################### 5 | # Builtin functions # 6 | ############################################################################### 7 | 8 | ############################################################################### 9 | # Connections # 10 | ############################################################################### 11 | 12 | ############################################################################### 13 | # Private functions # 14 | ############################################################################### 15 | 16 | func _create_empty_result() -> GDLisp.Result: 17 | return GDLisp.Result.new(null, null) 18 | 19 | func _create_atom(token: String) -> GDLisp.Atom: 20 | if token.begins_with('"'): 21 | return GDLisp.Atom.new(GDLisp.Atom.Str, token.substr(1, token.length() - 2)) 22 | elif token.is_valid_float(): 23 | return GDLisp.Atom.new(GDLisp.Atom.Num, token.to_float()) 24 | else: 25 | return GDLisp.Atom.new(GDLisp.Atom.Sym, token) 26 | 27 | func _create_expression_from_atom(atom: GDLisp.Atom) -> GDLisp.Exp: 28 | return GDLisp.Exp.new(GDLisp.Exp.Atom, atom) 29 | 30 | ############################################################################### 31 | # Public functions # 32 | ############################################################################### 33 | 34 | func run_tests() -> void: 35 | var test_methods: Array = [] 36 | var methods: Array = get_method_list() 37 | 38 | for method in methods: 39 | var method_name: String = method["name"] 40 | if method_name.left(4).to_lower() == "test": 41 | test_methods.append(method_name) 42 | 43 | print("Running %s tests" % test_methods.size()) 44 | for method in test_methods: 45 | print("\n%s" % method) 46 | call(method) 47 | -------------------------------------------------------------------------------- /tests/TestRunner.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://tests/TestRunner.gd" type="Script" id=1] 4 | 5 | [node name="TestRunner" type="Control"] 6 | anchor_right = 1.0 7 | anchor_bottom = 1.0 8 | script = ExtResource( 1 ) 9 | __meta__ = { 10 | "_edit_use_anchors_": false 11 | } 12 | 13 | [node name="CanvasLayer" type="CanvasLayer" parent="."] 14 | 15 | [node name="VBoxContainer" type="VBoxContainer" parent="CanvasLayer"] 16 | anchor_right = 1.0 17 | anchor_bottom = 1.0 18 | __meta__ = { 19 | "_edit_use_anchors_": false 20 | } 21 | 22 | [node name="HBoxContainer" type="HBoxContainer" parent="CanvasLayer/VBoxContainer"] 23 | margin_right = 1024.0 24 | margin_bottom = 298.0 25 | size_flags_horizontal = 3 26 | size_flags_vertical = 3 27 | __meta__ = { 28 | "_edit_use_anchors_": false 29 | } 30 | 31 | [node name="Sanity" type="Button" parent="CanvasLayer/VBoxContainer/HBoxContainer"] 32 | margin_right = 201.0 33 | margin_bottom = 298.0 34 | focus_mode = 0 35 | size_flags_horizontal = 3 36 | text = "Sanity" 37 | 38 | [node name="Tokenizer" type="Button" parent="CanvasLayer/VBoxContainer/HBoxContainer"] 39 | margin_left = 205.0 40 | margin_right = 407.0 41 | margin_bottom = 298.0 42 | focus_mode = 0 43 | size_flags_horizontal = 3 44 | text = "Tokenizer" 45 | 46 | [node name="Parser" type="Button" parent="CanvasLayer/VBoxContainer/HBoxContainer"] 47 | margin_left = 411.0 48 | margin_right = 612.0 49 | margin_bottom = 298.0 50 | focus_mode = 0 51 | size_flags_horizontal = 3 52 | text = "Parser" 53 | 54 | [node name="Evaluator" type="Button" parent="CanvasLayer/VBoxContainer/HBoxContainer"] 55 | margin_left = 616.0 56 | margin_right = 818.0 57 | margin_bottom = 298.0 58 | focus_mode = 0 59 | size_flags_horizontal = 3 60 | text = "Evaluator" 61 | 62 | [node name="EndToEnd" type="Button" parent="CanvasLayer/VBoxContainer/HBoxContainer"] 63 | margin_left = 822.0 64 | margin_right = 1024.0 65 | margin_bottom = 298.0 66 | focus_mode = 0 67 | size_flags_horizontal = 3 68 | text = "End-to-end" 69 | 70 | [node name="RunAll" type="Button" parent="CanvasLayer/VBoxContainer"] 71 | margin_top = 302.0 72 | margin_right = 1024.0 73 | margin_bottom = 600.0 74 | focus_mode = 0 75 | size_flags_horizontal = 3 76 | size_flags_vertical = 3 77 | text = "Run all" 78 | -------------------------------------------------------------------------------- /project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=4 10 | 11 | _global_script_classes=[ { 12 | "base": "Reference", 13 | "class": "GDLisp", 14 | "language": "GDScript", 15 | "path": "res://utils/GDLisp.gd" 16 | }, { 17 | "base": "Reference", 18 | "class": "GDLispSyntaxDoc", 19 | "language": "GDScript", 20 | "path": "res://utils/GDLispSyntaxDoc.gd" 21 | }, { 22 | "base": "Reference", 23 | "class": "SaveDataUtil", 24 | "language": "GDScript", 25 | "path": "res://utils/SaveDataUtil.gd" 26 | }, { 27 | "base": "Reference", 28 | "class": "TestBase", 29 | "language": "GDScript", 30 | "path": "res://tests/TestBase.gd" 31 | } ] 32 | _global_script_class_icons={ 33 | "GDLisp": "", 34 | "GDLispSyntaxDoc": "", 35 | "SaveDataUtil": "", 36 | "TestBase": "" 37 | } 38 | 39 | [application] 40 | 41 | config/name="GDLisp" 42 | run/main_scene="res://screens/MainScreen.tscn" 43 | config/icon="res://assets/icon.png" 44 | 45 | [autoload] 46 | 47 | AppManager="*res://utils/AppManager.gd" 48 | 49 | [input] 50 | 51 | send_input={ 52 | "deadzone": 0.5, 53 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777221,"physical_scancode":0,"unicode":0,"echo":false,"script":null) 54 | ] 55 | } 56 | super={ 57 | "deadzone": 0.5, 58 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777238,"physical_scancode":0,"unicode":0,"echo":false,"script":null) 59 | ] 60 | } 61 | click={ 62 | "deadzone": 0.5, 63 | "events": [ Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":1,"pressed":false,"doubleclick":false,"script":null) 64 | , Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":2,"pressed":false,"doubleclick":false,"script":null) 65 | ] 66 | } 67 | 68 | [physics] 69 | 70 | common/enable_pause_aware_picking=true 71 | 72 | [rendering] 73 | 74 | quality/driver/driver_name="GLES2" 75 | vram_compression/import_etc=true 76 | vram_compression/import_etc2=false 77 | environment/default_environment="res://assets/default_env.tres" 78 | -------------------------------------------------------------------------------- /utils/SaveDataUtil.gd: -------------------------------------------------------------------------------- 1 | class_name SaveDataUtil 2 | extends Reference 3 | 4 | const SAVE_FILE_EXTENSION: String = "fpwb" 5 | 6 | const METADATA_FILE_NAME: String = "fpwb.fpwb" 7 | const LAST_SAVE_FILE_KEY: String = "last_save_file" 8 | const METADATA_TEMPLATE: Dictionary = { 9 | LAST_SAVE_FILE_KEY: "" 10 | } 11 | 12 | var save_directory_path: String 13 | 14 | ############################################################################### 15 | # Builtin functions # 16 | ############################################################################### 17 | 18 | func _init() -> void: 19 | if not OS.is_debug_build(): 20 | save_directory_path = OS.get_executable_path().get_base_dir() 21 | else: 22 | save_directory_path = "res://export/" 23 | 24 | ############################################################################### 25 | # Connections # 26 | ############################################################################### 27 | 28 | ############################################################################### 29 | # Private functions # 30 | ############################################################################### 31 | 32 | func _load_metadata() -> Dictionary: 33 | """ 34 | metadata is in format: 35 | { 36 | "last_save_file": String 37 | } 38 | """ 39 | var result: Dictionary = {} 40 | 41 | var metadata_exists: bool = false 42 | 43 | var dir: Directory = Directory.new() 44 | if dir.open(save_directory_path) == OK: 45 | dir.list_dir_begin(true, true) 46 | var file_name = dir.get_next() 47 | while file_name != "": 48 | if file_name.get_file() == METADATA_FILE_NAME: 49 | metadata_exists = true 50 | break 51 | file_name = dir.get_next() 52 | 53 | if metadata_exists: 54 | var metadata: Dictionary = load_data(METADATA_FILE_NAME) 55 | if not metadata.empty(): 56 | 57 | pass 58 | 59 | return result 60 | 61 | func _save_metadata(last_save_file: String) -> void: 62 | var save_data: Dictionary = METADATA_TEMPLATE.duplicate() 63 | save_data[LAST_SAVE_FILE_KEY] = last_save_file 64 | 65 | var file_path: String = "%s/%s.%s" % [self.save_directory_path, self.METADATA_FILE_NAME, self.SAVE_FILE_EXTENSION] 66 | var save_file: File = File.new() 67 | save_file.open(file_path, File.WRITE) 68 | 69 | save_file.store_string(to_json(save_data)) 70 | 71 | save_file.close() 72 | 73 | ############################################################################### 74 | # Public functions # 75 | ############################################################################### 76 | 77 | func does_metadata_exist() -> bool: 78 | var result: bool = false 79 | 80 | var dir: Directory = Directory.new() 81 | if dir.open(save_directory_path) == OK: 82 | dir.list_dir_begin(true, true) 83 | var file_name = dir.get_next() 84 | while file_name != "": 85 | if file_name.get_file() == METADATA_FILE_NAME: 86 | result = true 87 | break 88 | file_name = dir.get_next() 89 | 90 | return result 91 | 92 | func load_data(file_name: String = "") -> Dictionary: 93 | """ 94 | Loads a .fpwb file from the given directory 95 | 96 | returns an empty dictionary if no file is found 97 | """ 98 | var result: Dictionary = {} 99 | 100 | if file_name.empty(): 101 | var metadata: Dictionary = _load_metadata() 102 | 103 | if metadata.empty(): 104 | return result 105 | 106 | file_name = metadata[LAST_SAVE_FILE_KEY] 107 | 108 | var file_path: String = "%s/%s" % [self.save_directory_path, file_name] 109 | 110 | var save_file: File = File.new() 111 | save_file.open(file_path, File.READ) 112 | 113 | var data: JSONParseResult = JSON.parse(save_file.get_as_text()) 114 | if (data.error == OK and typeof(data.result) == TYPE_DICTIONARY): 115 | result = data.result 116 | 117 | save_file.close() 118 | 119 | return result 120 | 121 | func save_data(file_name: String, data: Dictionary) -> void: 122 | """ 123 | data is in format: 124 | { 125 | ... 126 | } 127 | """ 128 | var file_path: String = "%s/%s.%s" % [self.save_directory_path, file_name, self.SAVE_FILE_EXTENSION] 129 | var data_to_save: Dictionary = data["data"] 130 | 131 | var save_file: File = File.new() 132 | save_file.open(file_path, File.WRITE) 133 | 134 | save_file.store_string(to_json(data_to_save)) 135 | 136 | save_file.close() 137 | 138 | _save_metadata(file_name) 139 | -------------------------------------------------------------------------------- /utils/GDLispSyntaxDoc.gd: -------------------------------------------------------------------------------- 1 | class_name GDLispSyntaxDoc 2 | extends Reference 3 | 4 | ############################################################################### 5 | # Builtin functions # 6 | ############################################################################### 7 | 8 | ############################################################################### 9 | # Connections # 10 | ############################################################################### 11 | 12 | ############################################################################### 13 | # Private functions # 14 | ############################################################################### 15 | 16 | ############################################################################### 17 | # Public functions # 18 | ############################################################################### 19 | 20 | static func get_long() -> String: 21 | return """ *** GDLisp Syntax *** 22 | ### Define a variable `(def )` 23 | Creates a variable in the current scope and sets it to the specified value. Can shadow outer variables if used in an inner scope. 24 | 25 | The below example will set variable `x` to value `0` in the current scope. 26 | 27 | `(def x 0)` 28 | 29 | ### Update a variable `(= )` 30 | Finds a variable in the current or any outer scope (in that order) and sets it to the specified value. 31 | 32 | The below example will set variable `x` to value `10`. 33 | 34 | `(= x 10)` 35 | 36 | ### Create a list `[ ... ]` or `(list ... )` 37 | Creates and returns a list with the specified values. An empty list can be created if no values are specified. 38 | 39 | The below example creates a list with the values `1`, `2`, and `3` and stores it in variable `x`. 40 | 41 | `(def x [1 2 3])` 42 | 43 | ### Create a table `{ ... }` or `(table ...)` 44 | Creates and returns a table (aka a dictionary) with the specified values. An empty table can be created if no values are specified. 45 | 46 | The below example creates a table with the key/value pair `"hello": "world"` and stores it in variable `x`. 47 | 48 | `(def x { "hello" "world" })` 49 | 50 | ### Create a lambda expression `(lam [] ...)` 51 | Creates and returns a lambda expression that takes the specified parameters and runs the specified expressions. A lambda expression does not need any parameters but must have at least 1 expression. 52 | 53 | The below example creates a lambda expression and assigns it to the variable `double`. The lambda expression is then used to double the value `2`. The returned value will equal `4`. 54 | 55 | ``` 56 | (def double (lam [ x ] (+ x x))) 57 | 58 | (double 2) 59 | ``` 60 | 61 | ### Create a macro `(macro [] ...)` 62 | Not yet implemented. 63 | 64 | ### Call a method from a raw value `(raw ...)` 65 | Gets an object and calls the `callv` function on it. 66 | 67 | ### If statements `(if )` 68 | Evaluates the condition and selects an expression to execute. 69 | 70 | The below example checks to see if 1 is less than 2. If this is true, the `double` lambda expression will be executed, otherwise the built-in `print` function will be called. 71 | 72 | `(if (< 1 2) (double 1) (print "this is not possible"))` 73 | 74 | ### While statements `(while ...)` 75 | Executes the given expressions while the condition is true. There must be at least 1 expression to execute. 76 | 77 | The below example creates and increments a variable `x` from 0 to 5. 78 | 79 | ``` 80 | (def x 0) 81 | (while (<= x 5) (= x (+ x 1))) 82 | ``` 83 | 84 | ### Do block `(do ...)` 85 | Executes the given expressions. There must be at least 1 expression to execute. Do blocks create an implicit inner scope. 86 | 87 | The below example creates a variable `x` and sets it equal to `0`. Then the do block will shadow the variable `x` and set it equal to `10`. Outside of the do block, the built-in function `print` will print the value of `x`, which will be `0`. 88 | 89 | ``` 90 | (def x 0) 91 | (do (def x 10)) 92 | (print x) 93 | ``` 94 | 95 | ### Label `(label )` 96 | Not yet implemented. 97 | Marks the current expression as a `goto` location. 98 | 99 | ### Goto `(goto