diff --git a/KSP.sublime-syntax b/KSP.sublime-syntax index 7a9e1e7..c228597 100644 --- a/KSP.sublime-syntax +++ b/KSP.sublime-syntax @@ -73,7 +73,7 @@ contexts: init| listener| midi_in| - note| + note(_controller)?| (n)?rpn| release| persistence_changed| @@ -118,7 +118,7 @@ contexts: identifier| key_(alt|control|shift)| label| - (max|min)_value|midi_export_area_idx|mouse_(behaviour(_x|_y)?|mode)| + (max|min)(_value)?|midi_export_area_idx|mouse_(behaviour(_x|_y)?|mode)| nks_(num_values|str_values|style|type)|none|num_items| parallax_(x|y)| parent_panel|picture(_state)?|(pos_)?(x|y)|range_(max|min)|receive_drag_events| @@ -131,13 +131,14 @@ contexts: allow_group| custom| + (dis)?allow_key_type| id| midi_(byte_[1-2]|channel|command)|mod_value_(ex_)?id| note(_length)?| pan|(play_)?pos|par_[0-3]| source| track_nr|tune| - (rel_)?velocity|volume| + (rel_)?velocity|velocity_2|volume| zone_id ) \b' # empty line separates control parameter shorthands from event parameter shorthands! @@ -246,7 +247,7 @@ contexts: pop: true - include: main - - match: '(?!#)\b(struct|define|literals|on|(inst)?pers|read|list *|end list|call|step|function|taskfunc|macro|declare|const|polyphonic|end|local|global|family|import|as|property|override|declare|ui_(label|button|switch|slider|menu|value_edit|waveform|wavetable|knob|table|xy|text_edit|level_meter|file_selector|panel|mouse_area))\b(?!#)' + - match: '(?!#)\b(struct|define|literals|on|(inst)?pers|read|list *|end list|call|step|function|taskfunc|node|vo_field|macro|declare|const|polyphonic|end|local|global|family|import|as|property|override|ui_(label|button|switch|slider|menu|value_edit|waveform|wavetable|knob|table|xy|text_edit|level_meter|file_selector|panel|mouse_area))\b(?!#)' comment: Other keywords scope: keyword.other.source.ksp @@ -292,21 +293,21 @@ contexts: DURATION_(BAR|EIGHTH(_TRIPLET)?|QUARTER(_TRIPLET)?|SIXTEENTH(_TRIPLET)?)| EFFECT_TYPE_( ACBOX|AET_FILTER| - BASSINVADER|BASSPRO|BIGFUZZ|BITE|BUS_COMP| + BASSINVADER|BASSPRO|BEAT(MASHER|SLICER)|BIGFUZZ|BITE|BUS_COMP| CABINET|CAT|CHORAL|CHORUS|COMPRESSOR|CRYWAH| DELAY|DIRT|DISTORTION|DSTORTION| EP_PREAMPS| FB_COMP|FILTER|FLAIR|FLANGER|FREAK|FUZZ| - GAINER| + GAINER|GATER| HOTSOLO| INVERTER|IRC| JUMP| LIMITER|LOFI| NONE| PHASER|PHASIS|PLATEREVERB|PSYCHEDELAY| - RAUM|REPLIKA|REVERB(2)?|RINGMOD|ROTATOR| + RAUM|REPLIKA|REVERB(2)?|REVGRAIN|RINGMOD|ROTATOR| SEND_LEVELS|SHAPER|SKREAMER|SOLID_GEQ|STEREO(_TUNE)?|SUPERGT|SURROUND_PANNER| - TAPE_SAT|TRANS_MASTER|TRANSLIM|TWANG|TWINDELAY| + TAPE_SAT|TRANS_(MASTER|STRETCH)|TRANSLIM|TWANG|TWINDELAY| VAN51|VIBRATO_CHORUS| WOWFLUTTER )| @@ -317,6 +318,8 @@ contexts: BANDWIDTH| BASSINVADER_(BASS|BOOST|HI_BOOST|HI_MID|LO_CUT|LO_MID|MASTER|MID_CONTOUR|TREBLE|VOLUME)| BASSPRO_(BASS|BRIGHT|DRIVE|GAIN|GEQ(_10K|_180|_1K|_2K|_300|_40|_4K|_500|_90|_VOLUME)?|MASTER|MID|MIDFREQ|MONO|TREBLE|ULTRA(HI|LO))| + BEATMASHER_(GATE|LENGTH|MASH|MIX|REVERSE|ROTATE|WRAP)| + BEATSLICER_(BUZZ|GATE|MIX|PATTERN|SLICE|STYLE|TWOBARS)| BIGFUZZ_(BASS|MONO|SUSTAIN|TONE|TREBLE)| BITE_(BITS|CRUNCH|DC_QUANT|DITHER|EXPAND|FREQUENCY|HPF|JITTER|MIX|(POST|PRE)FILTER|SATURATE)| BITS|BREAK|BW[1-3]| @@ -343,12 +346,13 @@ contexts: FILTER_(BYPA|BYPB|BYPC|GAIN|LADDER_HQ|RESB|RESC|SHIFTB|SHIFTC|TYPEA|TYPEB|TYPEC)| FL_(COLOR|DEPTH|FEEDBACK|PHASE|SPEED(_UNIT)?)| FLAIR_(AMOUNT|CHORD|DAMPING|DETUNE|FEEDBACK|INVERT_PHASE|MIX|MODE|OFFSET|PITCH|RATE(_UNIT)?|SCANMODE|VOICES|WIDTH)| - FLEXENV_(LOOP_START|LOOP_END|NUM_STAGES|ONESHOT|STAGE_LEVEL|STAGE_SLOPE|STAGE_TIME)| + FLEXENV_(LOOP(_START|_END)?|NUM_STAGES|ONESHOT|STAGE_LEVEL|STAGE_SLOPE|STAGE_TIME)| FORMANT(_(SHARP|SHIFT|SIZE|TALK))?| FREAK_(ANTIFOLD|BP_(FILTER|FREQ)|CARRIER|CONTOUR|DEMOD|FEEDBACK|FREQUENCY|GATE|HARMONICS|MIX|MODE|RELEASE|STEREO|TUNING|TYPE|WIDE_RANGE|WIDTH)| FREQ[1-3]|FREQUENCY| FUZZ_(AMOUNT|BASS|MONO|TREBLE)| GAIN([1-3])?| + GATER_(GATE|MIX|MUTE_INPUT|NOISE|RATE(_SYNC)?|SHAPE|STUTTER)| GLIDE_COEF(_UNIT)?| GN_GAIN|GRAIN_LENGTH|GROUP_DYNAMICS| HOLD(_UNIT)?|(HP|LP)_CUTOFF|HQI_MODE| @@ -360,7 +364,7 @@ contexts: LFO_(DELAY(_UNIT)?|NORMALIZE|PHASE|RAND|RECT|SAW|SINE|TRI)| LIM_(IN_GAIN|RELEASE)| LR_SWAP| - MOD_TARGET_(MP_)?INTENSITY| + MOD_TARGET_(MP_)?INTENSITY|MIDI_CHANNEL| NOISE(COLOR|LEVEL)| OUTPUT_CHANNEL| PAN|PHASE_INVERT| @@ -371,8 +375,9 @@ contexts: PSYDL_(DETUNE|(DETUNE|REVERSE)_STEREO|(CROSS_)?FEEDBACK|LR_OFFSET|PITCH|REVERSE|TIME(_UNIT)?)| RATIO| RAUM_(DAMPING|DECAY|DIFFUSION|FEEDBACK|FREEZE|HIGHCUT|LOWSHELF|MOD|PREDELAY(_UNIT)?|RATE|REVERB|SIZE|SPARSE|TYPE)| - RDL_(AMOUNT|BBDTYPE|DENSE|DEPTH|FEEDBACK|FLUTTER|(HIGH|LOW)CUT|MODULATION|NOISE|PINGPONG|QUALITY|RATE|SATURATION|SIZE|STEREO|TAPEAGE|TIME(_UNIT)?|TYPE)| + RDL_(ACCENT|AMOUNT|BBDTYPE|DENSE|DEPTH|DUCKING_(AMOUNT|RELEASE|SENSE)|FEEDBACK|FEEL|FLUTTER|(HIGH|LOW)CUT|LR_OFFSET|MODULATION|NOISE|PAN|PINGPONG(_FLIP)?|QUALITY|RATE|SATURATION|SHUFFLE|SIZE|STEREO|TAPEAGE|TIME(_UNIT)?|TYPE|WIDTH)| RELEASE(_UNIT|_TRIGGER)?|RESONANCE| + RG_(FORWARD|GRAIN|INVERT_GRAINS|MIX|PITCH|REVERSE|SPEED)| RINGMOD_(EDGE|FAST_MODE|FM|FREQUENCY|LFO_(AMOUNT|RATE(_UNIT)?|WAVE)|RING)| RT_(ACCEL_(HI|LO)|BALANCE|DISTANCE|MIX|SPEED)| RV2_(DAMPING|DIFF|HIGHCUT|LOWSHELF|MOD|PREDELAY|SIZE|STEREO|TIME|TYPE)| @@ -403,21 +408,23 @@ contexts: TR_(ATTACK|INPUT|SMOOTH|SUSTAIN)| TRACKING|TRANSIENT_SIZE| TRANSLIM_(CEILING|RELEASE|THRESHOLD)| + TS_(AMOUNT|GRAIN|KEY|MIX|SIZE|STRETCH|TWOBARS)| TUNE| TW_(BASS|BRIGHT|MID|MONO|TREBLE|VOLUME)| V5_(BASS|BRIGHT|CRUNCH|(HI|POST)GAIN|LEADCHANNEL|MID|MONO|PREGAIN(LEAD|RHYTHM)|PRESENCE|RESONANCE|TREBLE)| VC_(BLEND|COLOR|DEPTH|MIX|RATE|WIDTH)| - VOLUME| + VOICE_GROUP|VOLUME| WOWFLUTTER_(AGE|FLUTTER|GATE|MIX|SATURATION|SCRAPE|SPEED|STEREO|WOW)| - WT_(FORM(_MODE)?|INHARMONIC(_MODE)?|PHASE(_RAND)?|POSITION|QUALITY) + WT_(FORM(2)?(_MODE)?|INHARMONIC(_MODE)?|MOD_(AMOUNT|TUNE(_UNIT)?|TYPE|WAVE)|PHASE(_RAND)?|POSITION|QUALITY) )| ENGINE_UPTIME| ENV_TYPE_(AHDSR|DBD|FLEX)| - EVENT_(ID|NOTE|STATUS_INACTIVE|STATUS_MIDI_QUEUE|STATUS_NOTE_QUEUE|VELOCITY)| + EVENT_(ID|NOTE|STATUS_INACTIVE|STATUS_MIDI_QUEUE|STATUS_NOTE_QUEUE|VELOCITY(_2)?)| EVENT_PAR_( [0-3]| ALLOW_GROUP| CUSTOM| + (DIS)?ALLOW_KEY_TYPE| ID| MIDI_(BYTE_[1-2]|CHANNEL|COMMAND)|MOD_VALUE_(EX_)?ID| NOTE(_LENGTH)?| @@ -425,7 +432,7 @@ contexts: REL_VELOCITY| SOURCE| TRACK_NR|TUNE| - VELOCITY|VOLUME| + VELOCITY(_2)?|VOLUME| ZONE_ID )| FILTER_TYPE_( @@ -445,7 +452,7 @@ contexts: VERSATILE| VOWEL[A-B] )| - GET_FOLDER_(FACTORY|INSTALL|LIBRARY|PATCH)_DIR| + GET_FOLDER_(FACTORY|INSTALL|LIBRARY|PATCH|RC)_DIR| HIDE_(PART_(BG|CURSOR|MOD_LIGHT|NOTHING|TITLE|VALUE)|WHOLE_CONTROL)| IDX_OFFSET_EXT_MOD| INST_(ICON|LIB_(COPYRIGHT|DESCRIPTION|PIC_ONE|PIC_TWO)|PERFVIEW|WALLPAPER)_ID| @@ -458,13 +465,15 @@ contexts: MARK_([1-9]|1[0-9]|2[0-8])| MIDI_(BYTE_[1-2]|(CHANNEL|COMMAND)|COMMAND_(CC|MONO_AT|NOTE_OFF|NOTE_ON|NRPN|PITCH_BEND|POLY_AT|PROGRAM_CHANGE|RPN))| MOD_TARGET_INVERT_SOURCE| + NC_(NOTE|NUM|VALUE)| NI_( - ASYNC_(EXIT_STATUS|ID)| + ASYNC_(EXIT_STATUS|ID|PAR_[1-2]|TYPE|VALUE)| BAR_START_POSITION| + BEATMASHER_LENGTH_(32ND|16TH|8TH(_DOTTED)?|QUARTER(_DOTTED)?|HALF(_DOTTED)?|BAR)| BUS_OFFSET| BITE_HPF_MODE_(5|100|200)| CALLBACK_(ID|TYPE)| - CB_TYPE_(ASYNC_COMPLETE|CONTROLLER|INIT|LISTENER|MIDI_IN|NOTE|NRPN|PERSISTENCE_CHANGED|PGS|POLY_AT|RELEASE|RPN|UI_CONTROL(S)?|UI_UPDATE)| + CB_TYPE_(ASYNC_COMPLETE|CONTROLLER|INIT|LISTENER|MIDI_IN|NOTE(_CONTROLLER)?|(N)?RPN|PERSISTENCE_CHANGED|PGS|POLY_AT|RELEASE|UI_CONTROL(S)?|UI_UPDATE)| CHORAL_MODE_(DIMENSION|ENSEMBLE|SYNTH|UNIVERSAL)| COMP_TYPE_(CLASSIC|ENHANCED|PRO)| CONTROL_PAR_IDX|CONTROL_TYPE_(BUTTON|FILE_SELECTOR|KNOB|LABEL|LEVEL_METER|MENU|MOUSE_AREA|NONE|PANEL|SLIDER|SWITCH|TABLE|TEXT_EDIT|VALUE_EDIT|WAVEFORM|WAVETABLE|XY)| @@ -472,6 +481,7 @@ contexts: DETECT_( DRUM_TYPE_(CLAP|CLOSED_HH|CYMBAL|INVALID|KICK|OPEN_HH|PERC_DRUM|PERC_OTHER|SHAKER|SNARE|TOM)| INSTRUMENT_TYPE_(BASS|BOWED_STRING|BRASS|FLUTE|GUITAR|INVALID|KEYBOARD|MALLET|ORGAN|PLUCKED_STRING|REED|SYNTH|VOCAL)| + KEY_(([A-G]|CSHARP|DSHARP|FSHARP|GSHARP|ASHARP)_(MIN|MAJ)OR|INVALID)| SAMPLE_TYPE_(DRUM|INSTRUMENT|INVALID) )| DIRT_(MODE_(I){1,3}|ROUTING_(ATOB|BTOA|PARALLEL))| @@ -482,15 +492,17 @@ contexts: FILE_TYPE_(ARRAY|AUDIO|MIDI)| FLAIR_(MODE_(SCAN|STANDARD|THRU_ZERO)|SCANMODE_(SAW_DOWN|SAW_UP|TRIANGLE))| FREAK_MODE_(OSCILLATOR|RADIO|SIDECHAIN)| - GROUP_PAR_(COLOR|(HIGH|LOW)_KEY|NUM_ZONES)| + GLOBAL_(KEY|SCALE)|GROUP_PAR_(COLOR|(HIGH|LOW)_KEY|NUM_ZONES)| HQI_MODE_(HIGH|PERFECT|STANDARD)| + INST_NAME|INTERNAL_MARK| (INSERT|MAIN|SEND)_BUS| KEY_TYPE_(CONTROL|DEFAULT|NONE)| KONTAKT_IS_(HEADLESS|STANDALONE)| - LEVEL_METER_(GROUP|INSERT|MAIN)| + LEAP_(DEFAULT_START_KEY|KEYBOARD_TONALITY)|LEVEL_METER_(GROUP|INSERT|MAIN)| LOG_(MESSAGE|WARNING|WATCHING)| MOUSE_(EVENT_(TYPE(_DRAG|_DROP|_LEFT_BUTTON_DOWN|_LEFT_BUTTON_UP)?)|OVER_CONTROL)| - NKS_(STYLE_(FILTERTYPE|KNOB|MENU|POWER|TEMPOSYNC|VALUE|WAVEFORM)|TYPE_(CONTINUOUS_(BI|UNI)|DISCRETE_(BI|UNI)|TOGGLE))| + NUM_SCALES| + NKS_(STYLE_(DEFAULT|FILTERTYPE|KNOB|MENU|POWER|TEMPOSYNC|VALUE|WAVEFORM)|TYPE_(CONTINUOUS_(BI|UNI)|DISCRETE_(BI|UNI)|TOGGLE))| NOT_FOUND| RAUM_TYPE_(AIRY|COSMIC|GROUNDED)| REPLIKA_TYPE_(ANALOGUE|DIFFUSION|MODERN|TAPE|VINTAGE)| @@ -499,16 +511,18 @@ contexts: S1200_FILTER_((HIGH|LOW)(_MID)?|NONE)| SHAPE_TYPE_(CLASSIC|DRUMS|ENHANCED)| SIGNAL_(TIMER_(BEAT|MS)|TRANSP_(START|STOP)|TYPE)| + SLOT_(IDX|MIDI_CHANNEL)| SONG_POSITION|SOURCE_MODE_(BEAT_MACHINE|DFD|MP60_MACHINE|SAMPLER|S1200_MACHINE|TIME_MACHINE_(1|2|PRO)|TONE_MACHINE|WAVETABLE)| SUPERGT_(CHANNEL_LINK_MODE_(DUAL_MONO|MS|STEREO)|CHAR_MODE_(BRIGHT|FAT|WARM)|HPF_MODE_(100|300|OFF)|SAT_MODE_(HOT|MILD|MODERATE))| SYNC_UNIT_(16TH(_TRIPLET)?|256TH|32ND(_TRIPLET)?|64TH(_TRIPLET)?|8TH(_TRIPLET)?|ABS|HALF(_TRIPLET)?|QUARTER(_TRIPLET)?|WHOLE(_TRIPLET)?|ZONE)| TIME_(HOUR|MINUTE|SECOND)| - TRANSPORT_RUNNING| + TRANSPORT_(ACTIVE|RUNNING)| UI_ID| VC_(COLOR_TYPE_[A-C]|DEPTH_[1-6])| VL_TMPRO_(HQ|STANDARD)| WF_VIS_MODE_[1-3]| WT_FORM_([2468]BLINDS|ASYM(2)?(M?P?)|BEND(2)?(M?P?)|EXP|FLIP|FOLD|LINEAR|LOG(EXP)?|MIRROR|PWM|QUANTIZE|SATURATE|SEESAW|SYNC[1-6]|WRAP)| + WT_MOD_(TUNE_UNIT_(SEMITONES|RATIO|HZ)|TYPE_(FM[1-3]|MIX|PM[1-3]|RM|OFF)|WAVE_(SINE|TRIANGLE|TX[2-8]))| WT_QUALITY_(BEST|HIGH|LOFI|MEDIUM)| WT_VIS_(2|3)D| ZONE_STATUS_(EMPTY|IGNORED|LOADED|PURGED) @@ -518,13 +532,14 @@ contexts: OUTPUT_TYPE_(AUX_OUT|BUS_OUT|DEFAULT|MASTER_OUT)| PLAYED_VOICES_(INST|TOTAL)| POLY_AT_NUM| - REF_GROUP_IDX|RPN_(ADDRESS|VALUE)| + REF_(GROUP|SLOT)_IDX|RPN_(ADDRESS|VALUE)| + SLOT_TYPE_(BANK|EMPTY|INSTRUMENT)| SIGNATURE_(DENOM|NUM)| START_CRITERIA_((AND(_NOT)?|OR)_NEXT|CYCLE_(RANDOM|ROUND_ROBIN)|NONE|ON_(CONTROLLER|KEY)|SLICE_TRIGGER)| UI_WAVEFORM_(TABLE_IS_BIPOLAR|USE_MIDI_DRAG|USE_SLICES|USE_TABLE)| UI_WF_PROP_(FLAGS|MIDI_DRAG_START_NOTE|PLAY_CURSOR|TABLE_IDX_HIGHLIGHT|TABLE_VAL)| VALUE_EDIT_MODE_NOTE_NAMES| - VCC_(MONO_AT|PITCH_BEND)| + VCC_(MONO_AT|PITCH_BEND)|VNC_PITCH_BEND| ZONE_PAR_(BPM|(FADE_)?(HIGH|LOW)_(KEY|VELO)|GROUP|PAN|ROOT_KEY|SAMPLE_(END|MOD_RANGE|RATE|START)|SELECTED|TUNE|VOLUME)| NI_DND_ITEMS_(ARRAY|AUDIO|MIDI) )\b| @@ -542,10 +557,15 @@ contexts: (~)? \b( - NI_DETECT_(LOUDNESS|PEAK|PITCH|RMS)_INVALID| + NI_DETECT_(LOUDNESS|PEAK|PITCH|RMS|TEMPO)_INVALID| NI_MATH_(E|PI) )\b| + (@)? + \b( + NI_ASYNC_STR_VALUE + )\b| + (!)? \b( NI_DND_ITEMS_(ARRAY|AUDIO|MIDI) @@ -572,13 +592,14 @@ contexts: abs|(a)?(cos|sin|tan)|add_(menu_item|text_line)|array_equal||attach_(level_meter|zone)| by_(marks|track)| - cc_delivery_request|ceil|change_(listener_par|note|pan|time_with_pitch|tune|velo|vol)|(cb|sq)rt| - dec|delete_event_mark|detect_(pitch|rms|peak|loudness|sample_type|instrument_type|drum_type)|disable_logging|(dis)?allow_group|dont_use_machine_mode| + cc_delivery_request|ceil|circle_of_fifths_distance|change_(listener_par|note|pan|time_with_pitch|tune|velo|vol)|(cb|sq)rt| + dec|delete_event_mark|detect_(drum_type|instrument_type|key|loudness|peak|pitch|rms|sample_type|tempo)|disable_logging|(dis)?allow_group|dont_use_machine_mode| event_status|exit|exp(2)?|expose_controls| fade_(in|out)|find_(group|mod|target|zone)|floor|fs_(get_filename|navigate)| get_( control_par(_arr|_real_arr|_str|_str_arr)?| - engine_par_(disp_)?m| + connected_slots| + engine_par_((disp_)?m|disp_ext)| event_(ids|mark|par(_arr)?)| font_id| (group|mod|target)_idx| @@ -590,6 +611,7 @@ contexts: nks_nav_(name|par)| num_zones| purge_state| + scale(_name)?| sample(_length)?|sel_zones_idx| ui_(id|wf_property)| voice_limit| @@ -598,7 +620,7 @@ contexts: group_name|guiidx_to_slotidx| hide_part| ignore_(controller|event|midi)|import_nckp|in_range|inc|int(_to_real)?|is_zone_empty|iterate_(post_)?macro| - literate_(post_)?macro|load_(array(_str)?|ir_sample_m|native_ui|midi_file|(next_)?patch|performance_view)|log(2|10)?|[l-m]sb| + literate_(post_)?macro|load_(array(_str)?|ir_sample_m|(native|komplete)_ui|midi_file|(next_)?patch|performance_view)|log(2|10)?|[l-m]sb| make_((instr_)?persistent|perfview)|message| mf_( copy_export_area| @@ -641,13 +663,13 @@ contexts: note_off|num_(elements|slices_zone)| output_channel_name| pgs_(create_str_key|(get|set)_str_key_val|str_key_exists)|play_note|pow|purge_group| - random|real(_to_int)?|redirect_(midi|output)|remove_keyrange|reset_(engine|ksp_timer|nks_nav)|round| - save_(array(_str)?|midi_file)|search| + random|real(_to_int)?|redirect_(midi|output)|remove_keyrange|reset_(engine|ksp_timer|nks_nav)|request_value_async|round| + save_(array(_str)?|midi_file)|search|subscribe_async| set_( bounds| (button|knob|label|level_meter|menu|slider|switch|table|text_edit|value_edit|waveform|wavetable2d|wavetable3d)_properties| control_(help|par(_arr|_real_arr|_str|_str_arr)?)| - controller| + (note_)?controller| engine_par_m| event_(mark|par(_arr)?)| group_dyn_par_name| @@ -662,6 +684,7 @@ contexts: nks_nav_(name|par)| (n)?rpn| num_user_zones| + poly_at| sample| script_title| skin_offset| @@ -669,7 +692,7 @@ contexts: table_steps_shown| text| ui_(color|height(_px)?|wf_property|width_px)| - voice_limit| + value_async(_str)?|voice_limit| zone_par )| sgn|sh_(left|right)|show_library_tab|signbit|slotidx_to_guiidx|sort|stop_wait| diff --git a/compiler/ksp_builtins_data.py b/compiler/ksp_builtins_data.py index 87f3750..b4bcd10 100644 --- a/compiler/ksp_builtins_data.py +++ b/compiler/ksp_builtins_data.py @@ -99,6 +99,8 @@ $EFFECT_TYPE_AET_FILTER $EFFECT_TYPE_BASSINVADER $EFFECT_TYPE_BASSPRO +$EFFECT_TYPE_BEATMASHER +$EFFECT_TYPE_BEATSLICER $EFFECT_TYPE_BIGFUZZ $EFFECT_TYPE_BITE $EFFECT_TYPE_BUS_COMP @@ -120,6 +122,7 @@ $EFFECT_TYPE_FREAK $EFFECT_TYPE_FUZZ $EFFECT_TYPE_GAINER +$EFFECT_TYPE_GATER $EFFECT_TYPE_HOTSOLO $EFFECT_TYPE_INVERTER $EFFECT_TYPE_IRC @@ -135,6 +138,7 @@ $EFFECT_TYPE_REPLIKA $EFFECT_TYPE_REVERB $EFFECT_TYPE_REVERB2 +$EFFECT_TYPE_REVGRAIN $EFFECT_TYPE_RINGMOD $EFFECT_TYPE_ROTATOR $EFFECT_TYPE_SEND_LEVELS @@ -147,6 +151,7 @@ $EFFECT_TYPE_SURROUND_PANNER $EFFECT_TYPE_TAPE_SAT $EFFECT_TYPE_TRANS_MASTER +$EFFECT_TYPE_TRANS_STRETCH $EFFECT_TYPE_TRANSLIM $EFFECT_TYPE_TWANG $EFFECT_TYPE_TWINDELAY @@ -201,6 +206,20 @@ $ENGINE_PAR_BASSPRO_TREBLE $ENGINE_PAR_BASSPRO_ULTRAHI $ENGINE_PAR_BASSPRO_ULTRALO +$ENGINE_PAR_BEATMASHER_GATE +$ENGINE_PAR_BEATMASHER_LENGTH +$ENGINE_PAR_BEATMASHER_MASH +$ENGINE_PAR_BEATMASHER_MIX +$ENGINE_PAR_BEATMASHER_REVERSE +$ENGINE_PAR_BEATMASHER_ROTATE +$ENGINE_PAR_BEATMASHER_WRAP +$ENGINE_PAR_BEATSLICER_BUZZ +$ENGINE_PAR_BEATSLICER_GATE +$ENGINE_PAR_BEATSLICER_MIX +$ENGINE_PAR_BEATSLICER_PATTERN +$ENGINE_PAR_BEATSLICER_SLICE +$ENGINE_PAR_BEATSLICER_STYLE +$ENGINE_PAR_BEATSLICER_TWOBARS $ENGINE_PAR_BIGFUZZ_BASS $ENGINE_PAR_BIGFUZZ_MONO $ENGINE_PAR_BIGFUZZ_SUSTAIN @@ -404,6 +423,14 @@ $ENGINE_PAR_GAIN1 $ENGINE_PAR_GAIN2 $ENGINE_PAR_GAIN3 +$ENGINE_PAR_GATER_GATE +$ENGINE_PAR_GATER_MIX +$ENGINE_PAR_GATER_MUTE_INPUT +$ENGINE_PAR_GATER_NOISE +$ENGINE_PAR_GATER_RATE +$ENGINE_PAR_GATER_RATE_SYNC +$ENGINE_PAR_GATER_SHAPE +$ENGINE_PAR_GATER_STUTTER $ENGINE_PAR_GLIDE_COEF $ENGINE_PAR_GLIDE_COEF_UNIT $ENGINE_PAR_GN_GAIN @@ -463,6 +490,7 @@ $ENGINE_PAR_LIM_RELEASE $ENGINE_PAR_LP_CUTOFF $ENGINE_PAR_LR_SWAP +$ENGINE_PAR_MIDI_CHANNEL $ENGINE_PAR_MOD_TARGET_INTENSITY $ENGINE_PAR_MOD_TARGET_MP_INTENSITY $ENGINE_PAR_NOISECOLOR @@ -520,30 +548,47 @@ $ENGINE_PAR_RAUM_SIZE $ENGINE_PAR_RAUM_SPARSE $ENGINE_PAR_RAUM_TYPE +$ENGINE_PAR_RDL_ACCENT $ENGINE_PAR_RDL_AMOUNT $ENGINE_PAR_RDL_BBDTYPE $ENGINE_PAR_RDL_DENSE $ENGINE_PAR_RDL_DEPTH +$ENGINE_PAR_RDL_DUCKING_AMOUNT +$ENGINE_PAR_RDL_DUCKING_RELEASE +$ENGINE_PAR_RDL_DUCKING_SENSE $ENGINE_PAR_RDL_FEEDBACK +$ENGINE_PAR_RDL_FEEL $ENGINE_PAR_RDL_FLUTTER $ENGINE_PAR_RDL_HIGHCUT $ENGINE_PAR_RDL_LOWCUT +$ENGINE_PAR_RDL_LR_OFFSET $ENGINE_PAR_RDL_MODULATION $ENGINE_PAR_RDL_NOISE +$ENGINE_PAR_RDL_PAN $ENGINE_PAR_RDL_PINGPONG +$ENGINE_PAR_RDL_PINGPONG_FLIP $ENGINE_PAR_RDL_QUALITY $ENGINE_PAR_RDL_RATE $ENGINE_PAR_RDL_SATURATION +$ENGINE_PAR_RDL_SHUFFLE $ENGINE_PAR_RDL_SIZE $ENGINE_PAR_RDL_STEREO $ENGINE_PAR_RDL_TAPEAGE $ENGINE_PAR_RDL_TIME $ENGINE_PAR_RDL_TIME_UNIT $ENGINE_PAR_RDL_TYPE +$ENGINE_PAR_RDL_WIDTH $ENGINE_PAR_RELEASE $ENGINE_PAR_RELEASE_TRIGGER $ENGINE_PAR_RELEASE_UNIT $ENGINE_PAR_RESONANCE +$ENGINE_PAR_RG_FORWARD +$ENGINE_PAR_RG_GRAIN +$ENGINE_PAR_RG_INVERT_GRAINS +$ENGINE_PAR_RG_MIX +$ENGINE_PAR_RG_PITCH +$ENGINE_PAR_RG_REVERSE +$ENGINE_PAR_RG_SPEED $ENGINE_PAR_RINGMOD_EDGE $ENGINE_PAR_RINGMOD_FAST_MODE $ENGINE_PAR_RINGMOD_FM @@ -701,6 +746,13 @@ $ENGINE_PAR_TRANSLIM_CEILING $ENGINE_PAR_TRANSLIM_RELEASE $ENGINE_PAR_TRANSLIM_THRESHOLD +$ENGINE_PAR_TS_AMOUNT +$ENGINE_PAR_TS_GRAIN +$ENGINE_PAR_TS_KEY +$ENGINE_PAR_TS_MIX +$ENGINE_PAR_TS_SIZE +$ENGINE_PAR_TS_STRETCH +$ENGINE_PAR_TS_TWOBARS $ENGINE_PAR_TUNE $ENGINE_PAR_TW_BASS $ENGINE_PAR_TW_BRIGHT @@ -727,6 +779,7 @@ $ENGINE_PAR_VC_MIX $ENGINE_PAR_VC_RATE $ENGINE_PAR_VC_WIDTH +$ENGINE_PAR_VOICE_GROUP $ENGINE_PAR_VOLUME $ENGINE_PAR_WOWFLUTTER_AGE $ENGINE_PAR_WOWFLUTTER_FLUTTER @@ -739,8 +792,15 @@ $ENGINE_PAR_WOWFLUTTER_WOW $ENGINE_PAR_WT_FORM $ENGINE_PAR_WT_FORM_MODE +$ENGINE_PAR_WT_FORM2 +$ENGINE_PAR_WT_FORM2_MODE $ENGINE_PAR_WT_INHARMONIC $ENGINE_PAR_WT_INHARMONIC_MODE +$ENGINE_PAR_WT_MOD_AMOUNT +$ENGINE_PAR_WT_MOD_TUNE +$ENGINE_PAR_WT_MOD_TUNE_UNIT +$ENGINE_PAR_WT_MOD_TYPE +$ENGINE_PAR_WT_MOD_WAVE $ENGINE_PAR_WT_PHASE $ENGINE_PAR_WT_PHASE_RAND $ENGINE_PAR_WT_POSITION @@ -753,7 +813,9 @@ $EVENT_PAR_2 $EVENT_PAR_3 $EVENT_PAR_ALLOW_GROUP +$EVENT_PAR_ALLOW_KEY_TYPE $EVENT_PAR_CUSTOM +$EVENT_PAR_DISALLOW_KEY_TYPE $EVENT_PAR_ID $EVENT_PAR_MIDI_BYTE_1 $EVENT_PAR_MIDI_BYTE_2 @@ -771,6 +833,7 @@ $EVENT_PAR_TRACK_NR $EVENT_PAR_TUNE $EVENT_PAR_VELOCITY +$EVENT_PAR_VELOCITY_2 $EVENT_PAR_VOLUME $EVENT_PAR_ZONE_ID $EVENT_STATUS_INACTIVE @@ -841,6 +904,7 @@ $GET_FOLDER_INSTALL_DIR $GET_FOLDER_LIBRARY_DIR $GET_FOLDER_PATCH_DIR +$GET_FOLDER_RC_DIR $HIDE_PART_BG $HIDE_PART_CURSOR $HIDE_PART_MOD_LIGHT @@ -941,9 +1005,21 @@ $MIDI_COMMAND_PROGRAM_CHANGE $MIDI_COMMAND_RPN $MOD_TARGET_INVERT_SOURCE +$NC_NOTE +$NC_NUM +$NC_VALUE +$NI_BEATMASHER_LENGTH_32ND +$NI_BEATMASHER_LENGTH_16TH +$NI_BEATMASHER_LENGTH_8TH +$NI_BEATMASHER_LENGTH_8TH_DOTTED +$NI_BEATMASHER_LENGTH_QUARTER +$NI_BEATMASHER_LENGTH_QUARTER_DOTTED +$NI_BEATMASHER_LENGTH_HALF +$NI_BEATMASHER_LENGTH_HALF_DOTTED +$NI_BEATMASHER_LENGTH_BAR +$NI_BITE_HPF_MODE_5 $NI_BITE_HPF_MODE_100 $NI_BITE_HPF_MODE_200 -$NI_BITE_HPF_MODE_5 $NI_BUS_OFFSET $NI_CB_TYPE_ASYNC_COMPLETE $NI_CB_TYPE_CONTROLLER @@ -951,6 +1027,7 @@ $NI_CB_TYPE_LISTENER $NI_CB_TYPE_MIDI_IN $NI_CB_TYPE_NOTE +$NI_CB_TYPE_NOTE_CONTROLLER $NI_CB_TYPE_NRPN $NI_CB_TYPE_PERSISTENCE_CHANGED $NI_CB_TYPE_PGS @@ -1011,6 +1088,31 @@ $NI_DETECT_INSTRUMENT_TYPE_REED $NI_DETECT_INSTRUMENT_TYPE_SYNTH $NI_DETECT_INSTRUMENT_TYPE_VOCAL +$NI_DETECT_KEY_C_MAJOR +$NI_DETECT_KEY_CSHARP_MAJOR +$NI_DETECT_KEY_D_MAJOR +$NI_DETECT_KEY_DSHARP_MAJOR +$NI_DETECT_KEY_E_MAJOR +$NI_DETECT_KEY_F_MAJOR +$NI_DETECT_KEY_FSHARP_MAJOR +$NI_DETECT_KEY_G_MAJOR +$NI_DETECT_KEY_GSHARP_MAJOR +$NI_DETECT_KEY_A_MAJOR +$NI_DETECT_KEY_ASHARP_MAJOR +$NI_DETECT_KEY_B_MAJOR +$NI_DETECT_KEY_C_MINOR +$NI_DETECT_KEY_CSHARP_MINOR +$NI_DETECT_KEY_D_MINOR +$NI_DETECT_KEY_DSHARP_MINOR +$NI_DETECT_KEY_E_MINOR +$NI_DETECT_KEY_F_MINOR +$NI_DETECT_KEY_FSHARP_MINOR +$NI_DETECT_KEY_G_MINOR +$NI_DETECT_KEY_GSHARP_MINOR +$NI_DETECT_KEY_A_MINOR +$NI_DETECT_KEY_ASHARP_MINOR +$NI_DETECT_KEY_B_MINOR +$NI_DETECT_KEY_INVALID $NI_DETECT_SAMPLE_TYPE_DRUM $NI_DETECT_SAMPLE_TYPE_INSTRUMENT $NI_DETECT_SAMPLE_TYPE_INVALID @@ -1062,6 +1164,8 @@ $NI_FREAK_MODE_OSCILLATOR $NI_FREAK_MODE_RADIO $NI_FREAK_MODE_SIDECHAIN +$NI_GLOBAL_KEY +$NI_GLOBAL_SCALE $NI_GROUP_PAR_COLOR $NI_GROUP_PAR_HIGH_KEY $NI_GROUP_PAR_LOW_KEY @@ -1070,9 +1174,13 @@ $NI_HQI_MODE_PERFECT $NI_HQI_MODE_STANDARD $NI_INSERT_BUS +$NI_INST_NAME +$NI_INTERNAL_MARK $NI_KEY_TYPE_CONTROL $NI_KEY_TYPE_DEFAULT $NI_KEY_TYPE_NONE +$NI_LEAP_KEYBOARD_TONALITY +$NI_LEAP_DEFAULT_START_KEY $NI_LEVEL_METER_GROUP $NI_LEVEL_METER_INSERT $NI_LEVEL_METER_MAIN @@ -1085,6 +1193,7 @@ $NI_MOUSE_EVENT_TYPE_LEFT_BUTTON_DOWN $NI_MOUSE_EVENT_TYPE_LEFT_BUTTON_UP $NI_MOUSE_OVER_CONTROL +$NI_NKS_STYLE_DEFAULT $NI_NKS_STYLE_FILTERTYPE $NI_NKS_STYLE_KNOB $NI_NKS_STYLE_MENU @@ -1098,6 +1207,7 @@ $NI_NKS_TYPE_DISCRETE_UNI $NI_NKS_TYPE_TOGGLE $NI_NOT_FOUND +$NI_NUM_SCALES $NI_RAUM_TYPE_AIRY $NI_RAUM_TYPE_COSMIC $NI_RAUM_TYPE_GROUNDED @@ -1124,6 +1234,8 @@ $NI_SIGNAL_TRANSP_START $NI_SIGNAL_TRANSP_STOP $NI_SIGNAL_TYPE +$NI_SLOT_IDX +$NI_SLOT_MIDI_CHANNEL $NI_SOURCE_MODE_BEAT_MACHINE $NI_SOURCE_MODE_DFD $NI_SOURCE_MODE_MP60_MACHINE @@ -1214,6 +1326,27 @@ $NI_WT_FORM_SYNC5 $NI_WT_FORM_SYNC6 $NI_WT_FORM_WRAP +$NI_WT_MOD_TUNE_UNIT_SEMITONES +$NI_WT_MOD_TUNE_UNIT_RATIO +$NI_WT_MOD_TUNE_UNIT_HZ +$NI_WT_MOD_TYPE_OFF +$NI_WT_MOD_TYPE_FM1 +$NI_WT_MOD_TYPE_FM2 +$NI_WT_MOD_TYPE_FM3 +$NI_WT_MOD_TYPE_PM1 +$NI_WT_MOD_TYPE_PM2 +$NI_WT_MOD_TYPE_PM3 +$NI_WT_MOD_TYPE_RM +$NI_WT_MOD_TYPE_MIX +$NI_WT_MOD_WAVE_SINE +$NI_WT_MOD_WAVE_TRIANGLE +$NI_WT_MOD_WAVE_TX2 +$NI_WT_MOD_WAVE_TX3 +$NI_WT_MOD_WAVE_TX4 +$NI_WT_MOD_WAVE_TX5 +$NI_WT_MOD_WAVE_TX6 +$NI_WT_MOD_WAVE_TX7 +$NI_WT_MOD_WAVE_TX8 $NI_WT_QUALITY_BEST $NI_WT_QUALITY_HIGH $NI_WT_QUALITY_LOFI @@ -1228,6 +1361,9 @@ $OUTPUT_TYPE_BUS_OUT $OUTPUT_TYPE_DEFAULT $OUTPUT_TYPE_MASTER_OUT +$SLOT_TYPE_BANK +$SLOT_TYPE_EMPTY +$SLOT_TYPE_INSTRUMENT $START_CRITERIA_AND_NEXT $START_CRITERIA_AND_NOT_NEXT $START_CRITERIA_CYCLE_RANDOM @@ -1249,6 +1385,7 @@ $VALUE_EDIT_MODE_NOTE_NAMES $VCC_MONO_AT $VCC_PITCH_BEND +$VNC_PITCH_BEND $ZONE_PAR_BPM $ZONE_PAR_FADE_HIGH_KEY $ZONE_PAR_FADE_HIGH_VELO @@ -1272,6 +1409,7 @@ ~NI_DETECT_PEAK_INVALID ~NI_DETECT_PITCH_INVALID ~NI_DETECT_RMS_INVALID +~NI_DETECT_TEMPO_INVALID ~NI_MATH_E ~NI_MATH_PI @@ -1291,6 +1429,7 @@ $EVENT_ID $EVENT_NOTE $EVENT_VELOCITY +$EVENT_VELOCITY_2 $KSP_TIMER $MIDI_BYTE_1 $MIDI_BYTE_2 @@ -1298,6 +1437,10 @@ $MIDI_COMMAND $NI_ASYNC_EXIT_STATUS $NI_ASYNC_ID +$NI_ASYNC_VALUE +$NI_ASYNC_PAR_1 +$NI_ASYNC_PAR_2 +$NI_ASYNC_TYPE $NI_BAR_START_POSITION $NI_CALLBACK_ID $NI_CALLBACK_TYPE @@ -1306,6 +1449,7 @@ $NI_KONTAKT_IS_STANDALONE $NI_MOUSE_EVENT_TYPE $NI_SONG_POSITION +$NI_TRANSPORT_ACTIVE $NI_TRANSPORT_RUNNING $NI_UI_ID $NOTE_HELD @@ -1316,6 +1460,7 @@ $PLAYED_VOICES_TOTAL $POLY_AT_NUM $REF_GROUP_IDX +$REF_SLOT_IDX $RPN_ADDRESS $RPN_VALUE $SIGNATURE_DENOM @@ -1330,6 +1475,7 @@ %NI_USER_ZONE_IDS %NOTE_DURATION %POLY_AT +@NI_ASYNC_STR_VALUE !NI_DND_ITEMS_ARRAY !NI_DND_ITEMS_AUDIO !NI_DND_ITEMS_MIDI @@ -1373,6 +1519,7 @@ by_track():integer cc_delivery_request() ceil():real +circle_of_fifths_distance(, ):integer change_listener_par(, ) change_note(, ) change_pan(, , ) @@ -1384,14 +1531,16 @@ continue cos():real dec():integer +detect_drum_type(, ):integer delete_event_mark(, ) +detect_instrument_type(, ):integer +detect_key(, ):integer +detect_tempo(, ):integer +detect_loudness(, ):integer +detect_peak(, ):integer detect_pitch(, ):integer detect_rms(, ):integer -detect_peak(, ):integer -detect_loudness(, ):integer detect_sample_type(, ):integer -detect_instrument_type(, ):integer -detect_drum_type(, ):integer disable_logging() disallow_group() dont_use_machine_mode() @@ -1409,6 +1558,7 @@ floor():real fs_get_filename(, ):string fs_navigate(, ) +get_connected_slots():integer get_control_par(, ):integer get_control_par_arr(, , ):integer get_control_par_real_arr(, , ):real @@ -1416,6 +1566,7 @@ get_control_par_str_arr(, , ):string get_engine_par(, , , ):integer get_engine_par_disp(, , , ):string +get_engine_par_disp_ext(, , , , ):string get_engine_par_disp_m(, , , ):string get_engine_par_m(, , , ):integer get_event_ids() @@ -1442,6 +1593,8 @@ get_nks_nav_par(, ):integer get_num_zones():integer get_purge_state():integer +get_scale(,) +get_scale_name():string get_sample(,):string get_sample_length():integer get_sel_zones_idx() @@ -1472,6 +1625,7 @@ load_array_str(, ):integer load_ir_sample(, , ):integer load_ir_sample_m(, , , ):integer +load_komplete_ui() load_native_ui() load_midi_file():integer load_patch(, ) @@ -1527,6 +1681,7 @@ ms_to_ticks():integer msb():integer note_off() +note_off(, ) num_elements():integer num_slices():integer num_slices_zone():integer @@ -1548,7 +1703,8 @@ real_to_int():integer redirect_midi(, , ) redirect_output(, , ) -remove_keyrange() +remove_keyrange() +request_value_async(, , ) reset_engine() reset_ksp_timer reset_nks_nav() @@ -1575,11 +1731,11 @@ set_event_par_arr(, , , ) set_group_dyn_par_name(, ) set_key_color(, ) -set_key_name(, ) -set_key_pressed(, ) +set_key_name(, ) +set_key_pressed(, ) set_key_pressed_support() set_key_type(, key-type-constant>) -set_keyrange(, , ) +set_keyrange(, , ) set_kill_priority(, ) set_knob_defval(, ) set_knob_label(, ) @@ -1597,8 +1753,10 @@ set_midi(, , , ) set_nks_nav_name(, , ) set_nks_nav_par(, , ) +set_note_controller(, , ) set_nrpn(
, ) set_num_user_zones() +set_poly_at(, ) set_rpn(
, ) set_sample(, ):integer set_script_title() @@ -1615,6 +1773,8 @@ set_ui_height_px() set_ui_wf_property(, , , ) set_ui_width_px() +set_value_async(, , , ):integer +set_value_async_str(, , , ):integer set_value_edit_properties(, , , , ) set_voice_limit(,) set_waveform_properties(, , , , , , , , ) @@ -1637,6 +1797,7 @@ sort(, , , ) sqrt():real stop_wait(, ) +subscribe_async(, , ) tan():real ticks_to_ms():integer unload_slot() @@ -1718,6 +1879,7 @@ on listener on midi_in on note +on note_controller on nrpn on persistence_changed on pgs_changed diff --git a/compiler/ksp_compiler.py b/compiler/ksp_compiler.py index 9f97eec..a824590 100644 --- a/compiler/ksp_compiler.py +++ b/compiler/ksp_compiler.py @@ -368,12 +368,12 @@ def process_f_string(line): f_connect = False hyphen_connect = False escaping = False - + all_args = [] arg_content = '' record_arg = False f_spots = [] - + for i, c in enumerate(line): if record_arg == True: if c == '>' and not escaping and not hyphen_connect: @@ -394,30 +394,30 @@ def process_f_string(line): elif c == "'" and not escaping: in_string = not in_string in_f_string = f_connect and in_string - + if in_f_string: f_spots.append(i - 1) elif in_f_string and not escaping: if c == '<': record_arg = True - + if c != 'f': f_connect = False if c != '-': hyphen_connect = False if c != '\\': escaping = False - + new_line = line deleted = 0 for s in f_spots: index = s - deleted new_line = new_line[:index] + new_line[index+1:] deleted += 1 - + for a in all_args: new_line = new_line.replace("<{}>".format(a), "\' & {} & \'".format(a.replace('\\>', '>').replace('\\<', '<'))) - + return new_line if namespaces is None: @@ -425,7 +425,7 @@ def process_f_string(line): lines = s.replace('\r\n', '\n').replace('\r', '\n').split('\n') lines = [process_f_string(l) for l in lines] - + # encode lines numbers as '[[[lineno]]]' at the beginning of each line lines = ['[[[%.5d]]]%s' % (lineno+1, x) for (lineno, x) in enumerate(lines)] @@ -2198,10 +2198,10 @@ def examine_pragmas(self, code, namespaces): dir_path = os.path.dirname(dir_check) - if not os.path.exists(dir_path): - raise Exception('The filepath specified in save_compiled_source does not exist!\n' + dir_path) - else: + if os.path.exists(dir_path) or (dir_path == '' and dir_check.endswith('.txt')): self.output_files.append(dir_check) + else: + raise Exception('The filepath specified in save_compiled_source does not exist!\n' + dir_path) # find info about which variable names not to compact pragma_re = re.compile(r'\{\s*\#pragma\s+preserve_names\s+(.*?)\s*\}') diff --git a/compiler/ksp_parser.py b/compiler/ksp_parser.py index 829ca43..86d4c10 100644 --- a/compiler/ksp_parser.py +++ b/compiler/ksp_parser.py @@ -80,7 +80,7 @@ def t_BITWISE_NOT(t): return t def t_BEGIN_CALLBACK(t): - r'on\s+(init|note|release|midi_in|controller|(n)?rpn|ui_update|(_)?pgs_changed|poly_at|listener|async_complete|persistence_changed|ui_controls|(ui_control\s*?\(.+?\))|)' + r'on\s+(init|note(_controller)?|release|midi_in|controller|(n)?rpn|ui_update|(_)?pgs_changed|poly_at|listener|async_complete|persistence_changed|ui_controls|(ui_control\s*?\(.+?\))|)' t.type = 'BEGIN_CALLBACK' variable = None parts = t.value.split() diff --git a/compiler/preprocessor_plugins.py b/compiler/preprocessor_plugins.py index 06465ab..cc579f5 100644 --- a/compiler/preprocessor_plugins.py +++ b/compiler/preprocessor_plugins.py @@ -52,1791 +52,1788 @@ #================================================================================================= def substituteDefines(lines, define_cache = None): - cache = handleDefineConstants(lines, define_cache) - handleDefineLiterals(lines) # Define literals are only avilable for backwards compatibility as regular defines now serve this purpose. + cache = handleDefineConstants(lines, define_cache) + handleDefineLiterals(lines) # Define literals are only avilable for backwards compatibility as regular defines now serve this purpose. - return cache + return cache def pre_macro_functions(lines): - ''' This function is called before the macros have been expanded. - Returns the resulting define objects as a cache to be re-used. - lines is a deque of Line objects - see ksp_compiler.py.''' - createBuiltinDefines(lines) + ''' This function is called before the macros have been expanded. + Returns the resulting define objects as a cache to be re-used. + lines is a deque of Line objects - see ksp_compiler.py.''' + createBuiltinDefines(lines) - return substituteDefines(lines) + return substituteDefines(lines) def macro_iter_functions(lines, placeholders=placeholders): - ''' Will process macro iteration and return true if any were found ''' - return (handleIterateMacro(lines, placeholders) or handleLiterateMacro(lines, placeholders)) + ''' Will process macro iteration and return true if any were found ''' + return (handleIterateMacro(lines, placeholders) or handleLiterateMacro(lines, placeholders)) def post_macro_iter_functions(lines, placeholders=placeholders): - ''' Will process macro iteration and return true if any were found ''' - return (handleIteratePostMacro(lines, placeholders)) or (handleLiteratePostMacro(lines, placeholders)) + ''' Will process macro iteration and return true if any were found ''' + return (handleIteratePostMacro(lines, placeholders)) or (handleLiteratePostMacro(lines, placeholders)) def post_macro_functions(lines): - ''' This function is called after the regular macros have been expanded. - lines is a deque of Line objects - see ksp_compiler.py. ''' - handleIncrementer(lines) - handleConstBlock(lines) - handleStructs(lines) - handleUIArrays(lines) - handleSameLineDeclaration(lines) - handleMultidimensionalArrays(lines) - handleListBlocks(lines) - handleOpenSizeArrays(lines) - handlePersistence(lines) - handleLists(lines) - handleUIFunctions(lines) - ''' continued in ksp_compiler.py, run_post_macro_functions() - because KSPCompiler has to own running of handleStringArrayInitialisation method, - so that string placeholders can be properly used there when compiling from command line - (this fixed the circular import dependancy that was there before, which prevented compiling - from command line properly)''' + ''' This function is called after the regular macros have been expanded. + lines is a deque of Line objects - see ksp_compiler.py. ''' + handleIncrementer(lines) + handleConstBlock(lines) + handleStructs(lines) + handleUIArrays(lines) + handleSameLineDeclaration(lines) + handleMultidimensionalArrays(lines) + handleListBlocks(lines) + handleOpenSizeArrays(lines) + handlePersistence(lines) + handleLists(lines) + handleUIFunctions(lines) + ''' continued in ksp_compiler.py, run_post_macro_functions() + because KSPCompiler has to own running of handleStringArrayInitialisation method, + so that string placeholders can be properly used there when compiling from command line + (this fixed the circular import dependancy that was there before, which prevented compiling + from command line properly)''' #================================================================================================= def simplifyAdditionString(string): - ''' Evaluates a string of add operations, any add pairs that cannot be evalutated are left. - e.g. "2 + 2 + 3 + 4 + x + y + 2" => "11 + x + y + 2 ''' - parts = string.split("+") - count = 0 - - while count < len(parts) - 1: - try: - simplified = int(parts[count]) + int(parts[count+1]) - parts[count] = str(simplified) - parts.remove(parts[count + 1]) - except: - count += 1 - pass - - return("+".join(parts)) + ''' Evaluates a string of add operations, any add pairs that cannot be evalutated are left. + e.g. "2 + 2 + 3 + 4 + x + y + 2" => "11 + x + y + 2 ''' + parts = string.split("+") + count = 0 + + while count < len(parts) - 1: + try: + simplified = int(parts[count]) + int(parts[count+1]) + parts[count] = str(simplified) + parts.remove(parts[count + 1]) + except: + count += 1 + pass + + return("+".join(parts)) def tryStringEval(expression, line, name): - ''' Evaluates a maths expression in the same way Kontakt would (only integers). ''' - try: - final = stringEvaluator.eval(str(expression).strip()) - except: - raise ParseException(line, - "Invalid syntax in %s value! This number must able to be evaluated to a single number at compile time. Please use only define constants, numbers or math operations here!\n" % name) + ''' Evaluates a maths expression in the same way Kontakt would (only integers). ''' + try: + final = stringEvaluator.eval(str(expression).strip()) + except: + raise ParseException(line, + "Invalid syntax in %s value! This number must able to be evaluated to a single number at compile time. Please use only define constants, numbers or math operations here!\n" % name) - return (final) + return (final) def replaceLines(original, new): - original.clear() - original.extend(new) + original.clear() + original.extend(new) def countFamily(lineText, famCount): - ''' Checks the line for family start or end and returns the current family depth ''' - if lineText.startswith("family ") or lineText.startswith("family "): - famCount += 1 - elif famCount != 0: - if re.search(familyEndRe, lineText): - famCount -= 1 + ''' Checks the line for family start or end and returns the current family depth ''' + if lineText.startswith("family ") or lineText.startswith("family "): + famCount += 1 + elif famCount != 0: + if re.search(familyEndRe, lineText): + famCount -= 1 - return(famCount) + return(famCount) def inspectFamilyState(lines, textLineno): - ''' If the given line is in at least 1 family, return the family prefixes. ''' - currentFamilyNames = [] + ''' If the given line is in at least 1 family, return the family prefixes. ''' + currentFamilyNames = [] - for i in range(len(lines)): - if i == textLineno: - if currentFamilyNames: - return (".".join(currentFamilyNames) + ".") - else: - return (None) - break + for i in range(len(lines)): + if i == textLineno: + if currentFamilyNames: + return (".".join(currentFamilyNames) + ".") + else: + return (None) + break - line = lines[i].command.strip() + line = lines[i].command.strip() - if "family" in line: - m = re.search(familyStartRe, line) + if "family" in line: + m = re.search(familyStartRe, line) - if m: - currentFamilyNames.append(m.group("famname")) - elif re.search(familyEndRe, line): - currentFamilyNames.pop() + if m: + currentFamilyNames.append(m.group("famname")) + elif re.search(familyEndRe, line): + currentFamilyNames.pop() #================================================================================================= class StructMember(object): - def __init__(self, name, command, prefix): - self.name = name - self.command = command - self.prefix = prefix # The prefix symbol of the member (@!%$) - self.numElements = None - - def makeMemberAnArray(self, numElements): - ''' Make the command of this member into an array. numElements is a string of any amount of numbers seperated by commas. - Structs exploit the fact the you can put the square brackets of an array after any 'subname' of a dot seperated name. ''' - cmd = self.command - - if "[" in self.command: - bracketLocation = cmd.find("[") - self.command = cmd[: bracketLocation + 1] + numElements + ", " + cmd[bracketLocation + 1 :] - else: - self.command = re.sub(r"\b%s\b" % self.name, "%s[%s]" % (self.name, numElements), cmd) - - if ":=" in self.command: - assignOperatorLocation = self.command.find(":=") + 2 - self.command = "%s(%s)" % (self.command[ : assignOperatorLocation], self.command[assignOperatorLocation : ]) - - if self.prefix == "@": - self.prefix = "!" - - def addNamePrefix(self, namePrefix): - ''' Add the prefix to the member with a dot operator. ''' - self.command = re.sub(r"\b%s\b" % self.name, "%s%s.%s" % (self.prefix, namePrefix, self.name), self.command) + def __init__(self, name, command, prefix): + self.name = name + self.command = command + self.prefix = prefix # The prefix symbol of the member (@!%$) + self.numElements = None + + def makeMemberAnArray(self, numElements): + ''' Make the command of this member into an array. numElements is a string of any amount of numbers seperated by commas. + Structs exploit the fact the you can put the square brackets of an array after any 'subname' of a dot seperated name. ''' + cmd = self.command + + if "[" in self.command: + bracketLocation = cmd.find("[") + self.command = cmd[: bracketLocation + 1] + numElements + ", " + cmd[bracketLocation + 1 :] + else: + self.command = re.sub(r"\b%s\b" % self.name, "%s[%s]" % (self.name, numElements), cmd) + + if ":=" in self.command: + assignOperatorLocation = self.command.find(":=") + 2 + self.command = "%s(%s)" % (self.command[ : assignOperatorLocation], self.command[assignOperatorLocation : ]) + + if self.prefix == "@": + self.prefix = "!" + + def addNamePrefix(self, namePrefix): + ''' Add the prefix to the member with a dot operator. ''' + self.command = re.sub(r"\b%s\b" % self.name, "%s%s.%s" % (self.prefix, namePrefix, self.name), self.command) class Struct(object): - def __init__(self, name): - self.name = name - self.members = [] + def __init__(self, name): + self.name = name + self.members = [] - def addMember(self, memberObj): - self.members.append(memberObj) + def addMember(self, memberObj): + self.members.append(memberObj) - def deleteMember(self, index): - del self.members[index] + def deleteMember(self, index): + del self.members[index] - def insertMember(self, location, memberObj): - self.members.insert(location, memberObj) + def insertMember(self, location, memberObj): + self.members.insert(location, memberObj) def handleStructs(lines): - prefix = r'&' - structs = [] + prefix = r'&' + structs = [] - def findStructs(): - ''' Find all the struct blocks and build struct objects of them. ''' - isCurrentlyInAStructBlock = False + def findStructs(): + ''' Find all the struct blocks and build struct objects of them. ''' + isCurrentlyInAStructBlock = False - for lineIdx in range(len(lines)): - line = lines[lineIdx].command.strip() + for lineIdx in range(len(lines)): + line = lines[lineIdx].command.strip() - # Find the start of a struct block - if line.startswith("struct"): - m = re.search(r"^struct\s+%s$" % variableNameRe, line) + # Find the start of a struct block + if line.startswith("struct"): + m = re.search(r"^struct\s+%s$" % variableNameRe, line) - if m: - structObj = Struct(m.group("name")) + if m: + structObj = Struct(m.group("name")) - if isCurrentlyInAStructBlock: - raise ParseException(lines[lineIdx], "Struct definitions cannot be nested!\n") + if isCurrentlyInAStructBlock: + raise ParseException(lines[lineIdx], "Struct definitions cannot be nested!\n") - isCurrentlyInAStructBlock = True - lines[lineIdx].command = "" - # Find the end of a struct block - elif line.startswith("end"): - if re.search(r"^end\s+struct$", line): - isCurrentlyInAStructBlock = False - structs.append(structObj) - lines[lineIdx].command = "" - # If in a struct, add each member as an object to the struct - elif isCurrentlyInAStructBlock: - if line: - if not line.startswith("declare ") and not line.startswith("declare "): - raise ParseException(lines[lineIdx], "Structs can only consist of variable declarations!\n") + isCurrentlyInAStructBlock = True + lines[lineIdx].command = "" + # Find the end of a struct block + elif line.startswith("end"): + if re.search(r"^end\s+struct$", line): + isCurrentlyInAStructBlock = False + structs.append(structObj) + lines[lineIdx].command = "" + # If in a struct, add each member as an object to the struct + elif isCurrentlyInAStructBlock: + if line: + if not line.startswith("declare ") and not line.startswith("declare "): + raise ParseException(lines[lineIdx], "Structs can only consist of variable declarations!\n") - m = re.search(nameInDeclareStmtRe, line) + m = re.search(nameInDeclareStmtRe, line) - if m: - variableName = m.group("whole") - structDeclMatch = re.search(r"\&\s*%s" % variableNameRe, line) + if m: + variableName = m.group("whole") + structDeclMatch = re.search(r"\&\s*%s" % variableNameRe, line) - if structDeclMatch: - variableName = "%s%s %s" % ("&", structDeclMatch.group("whole"), variableName) + if structDeclMatch: + variableName = "%s%s %s" % ("&", structDeclMatch.group("whole"), variableName) - prefixSymbol = "" + prefixSymbol = "" - if re.match(varPrefixRe, variableName): - prefixSymbol = variableName[:1] - variableName = variableName[1:] + if re.match(varPrefixRe, variableName): + prefixSymbol = variableName[:1] + variableName = variableName[1:] - structObj.addMember(StructMember(variableName, line.replace("%s%s" % (prefixSymbol, variableName), variableName), prefixSymbol)) + structObj.addMember(StructMember(variableName, line.replace("%s%s" % (prefixSymbol, variableName), variableName), prefixSymbol)) - lines[lineIdx].command = "" + lines[lineIdx].command = "" - findStructs() + findStructs() - if structs: - # Make the struct names a list so they are easily searchable - structNames = [structs[i].name for i in range(len(structs))] + if structs: + # Make the struct names a list so they are easily searchable + structNames = [structs[i].name for i in range(len(structs))] - def resolveStructsWithinStructs(): - ''' Where structs have been declared as members of another struct, flatten them. ''' - for i in range(len(structs)): - j = 0 - counter = 0 - stillRemainginStructs = False + def resolveStructsWithinStructs(): + ''' Where structs have been declared as members of another struct, flatten them. ''' + for i in range(len(structs)): + j = 0 + counter = 0 + stillRemainginStructs = False - # Struct member may themselves have struct members, so this is looped until it is fully resolved. - while j < len(structs[i].members) or stillRemainginStructs == True: - m = re.search(r"^([^%s]+\.)?%s\s*%s\s+%s" % (prefix, prefix, variableNameUnRe, variableNameUnRe), structs[i].members[j].name) + # Struct member may themselves have struct members, so this is looped until it is fully resolved. + while j < len(structs[i].members) or stillRemainginStructs == True: + m = re.search(r"^([^%s]+\.)?%s\s*%s\s+%s" % (prefix, prefix, variableNameUnRe, variableNameUnRe), structs[i].members[j].name) - if m: - structs[i].deleteMember(j) - structNum = structNames.index(m.group(2)) - structVariable = m.group(5).strip() + if m: + structs[i].deleteMember(j) + structNum = structNames.index(m.group(2)) + structVariable = m.group(5).strip() - if m.group(1): - structVariable = m.group(1) + structVariable + if m.group(1): + structVariable = m.group(1) + structVariable - if structNum == i: - raise ParseException(lines[0], "Declared struct cannot be the same as struct parent!\n") + if structNum == i: + raise ParseException(lines[0], "Declared struct cannot be the same as struct parent!\n") - insertLocation = j + insertLocation = j - for memberIdx in range(len(structs[structNum].members)): - structMember = structs[structNum].members[memberIdx] - varName = structVariable + "." + structMember.name - newCommand = re.sub(r"\b%s\b" % structMember.name, varName, structMember.command) - structs[i].insertMember(insertLocation, StructMember(varName, newCommand, structMember.prefix)) - insertLocation += 1 + for memberIdx in range(len(structs[structNum].members)): + structMember = structs[structNum].members[memberIdx] + varName = structVariable + "." + structMember.name + newCommand = re.sub(r"\b%s\b" % structMember.name, varName, structMember.command) + structs[i].insertMember(insertLocation, StructMember(varName, newCommand, structMember.prefix)) + insertLocation += 1 - # If there are still any struct member declarations, keep looping to resolve them. - for name in structs[i].members[j].name: - mm = re.search(r"^(?:[^%s]+\.)?%s\s*%s\s+%s" % (prefix, prefix, variableNameUnRe, variableNameUnRe), name) + # If there are still any struct member declarations, keep looping to resolve them. + for name in structs[i].members[j].name: + mm = re.search(r"^(?:[^%s]+\.)?%s\s*%s\s+%s" % (prefix, prefix, variableNameUnRe, variableNameUnRe), name) - if mm: - stillRemainginStructs = True - j += 1 + if mm: + stillRemainginStructs = True + j += 1 - if j >= len(structs[i].members) and stillRemainginStructs: - stillRemainginStructs = False - j = 0 - counter += 1 + if j >= len(structs[i].members) and stillRemainginStructs: + stillRemainginStructs = False + j = 0 + counter += 1 - if counter > 100000: - raise ParseException(lines[0], "Error: too many iterations while building structs!") - break + if counter > 100000: + raise ParseException(lines[0], "Error: too many iterations while building structs!") + break - resolveStructsWithinStructs() + resolveStructsWithinStructs() - def findAndHandleStructInstanceDeclarations(): - ''' Find all places where an instance of a struct has been declared and build the lines necesary. ''' - newLines = collections.deque() + def findAndHandleStructInstanceDeclarations(): + ''' Find all places where an instance of a struct has been declared and build the lines necesary. ''' + newLines = collections.deque() - for i in range(len(lines)): - line = lines[i].command.strip() - m = re.search(r"^declare\s+%s\s*%s\s+%s(?:\[(.*)\])?$" % (prefix, variableNameUnRe, variableNameUnRe), line) + for i in range(len(lines)): + line = lines[i].command.strip() + m = re.search(r"^declare\s+%s\s*%s\s+%s(?:\[(.*)\])?$" % (prefix, variableNameUnRe, variableNameUnRe), line) - if m: - structName = m.group(1) - declaredName = m.group(4) + if m: + structName = m.group(1) + declaredName = m.group(4) - try: - structIdx = structNames.index(structName) - except ValueError: - raise ParseException(lines[i], "Undeclared struct %s!\n" % structName) + try: + structIdx = structNames.index(structName) + except ValueError: + raise ParseException(lines[i], "Undeclared struct %s!\n" % structName) - newMembers = copy.deepcopy(structs[structIdx].members) + newMembers = copy.deepcopy(structs[structIdx].members) - # If necessary make the struct members into arrays. - arrayNumElements = m.group(7) + # If necessary make the struct members into arrays. + arrayNumElements = m.group(7) - if arrayNumElements: - for j in range(len(newMembers)): - newMembers[j].makeMemberAnArray(arrayNumElements) + if arrayNumElements: + for j in range(len(newMembers)): + newMembers[j].makeMemberAnArray(arrayNumElements) - if "," in arrayNumElements: - arrayNumElements = utils.split_args(arrayNumElements, lines[i]) + if "," in arrayNumElements: + arrayNumElements = utils.split_args(arrayNumElements, lines[i]) - for dimIdx in range(len(arrayNumElements)): - newLines.append(lines[i].copy("declare const %s.SIZE_D%d := %s" % (declaredName, dimIdx + 1, arrayNumElements[dimIdx]))) - else: - newLines.append(lines[i].copy("declare const %s.SIZE := %s" % (declaredName, arrayNumElements))) + for dimIdx in range(len(arrayNumElements)): + newLines.append(lines[i].copy("declare const %s.SIZE_D%d := %s" % (declaredName, dimIdx + 1, arrayNumElements[dimIdx]))) + else: + newLines.append(lines[i].copy("declare const %s.SIZE := %s" % (declaredName, arrayNumElements))) - # Add the declared names as a prefix and add the memebers to the newLines deque - for j in range(len(newMembers)): - newMembers[j].addNamePrefix(declaredName) - newLines.append(lines[i].copy(newMembers[j].command)) - else: - newLines.append(lines[i]) + # Add the declared names as a prefix and add the memebers to the newLines deque + for j in range(len(newMembers)): + newMembers[j].addNamePrefix(declaredName) + newLines.append(lines[i].copy(newMembers[j].command)) + else: + newLines.append(lines[i]) - replaceLines(lines, newLines) + replaceLines(lines, newLines) - findAndHandleStructInstanceDeclarations() + findAndHandleStructInstanceDeclarations() #================================================================================================= class Incrementer(object): - def __init__(self, name, start, step, line): - self.name = name - self.iterationVal = start - self.step = step - self.line = line + def __init__(self, name, start, step, line): + self.name = name + self.iterationVal = start + self.step = step + self.line = line - def increaseVal(self): - self.iterationVal += self.step + def increaseVal(self): + self.iterationVal += self.step def handleIncrementer(lines): - iterObjs = [] - found_end = True - - for i in range(len(lines)): - line = lines[i].command.strip() - - # Check for START_INC and add the object to the array. - if line.startswith("START_INC"): - mm = re.search(r"^%s\s*\(\s*%s\s*\,\s*(.+)s*\,\s*(.+)\s*\)" % ("START_INC", variableNameUnRe), line) - - if found_end: - found_end = False - - if mm: - lines[i].command = "" - iterObjs.append(Incrementer(mm.group(1), tryStringEval(mm.group(4), lines[i], "start"), tryStringEval(mm.group(5), lines[i], "step"), lines[i])) - else: - raise ParseException(lines[i], "Incorrect parameters for START_INC! Expected: START_INC(, , )\n") - # If any incremeter has ended, pop the last object off the array. - elif line == "END_INC": - found_end = True - lines[i].command = "" - - try: - iterObjs.pop() - except: - raise ParseException(lines[i], "Did not find a corresponding 'START_INC'!") - # If there are any iterators active, scan the line and replace occurances of the name with it's value. - elif iterObjs: - for iterationObj in iterObjs: - mm = re.search(r"\b%s\b" % iterationObj.name, line) - - if mm: - lines[i].command = re.sub(r"\b%s\b" % iterationObj.name, str(iterationObj.iterationVal), lines[i].command) - iterationObj.increaseVal() - - if iterObjs: - raise ParseException(iterObjs[0].line, "Did not find a corresponding 'END_INC'!") + iterObjs = [] + found_end = True + + for i in range(len(lines)): + line = lines[i].command.strip() + + # Check for START_INC and add the object to the array. + if line.startswith("START_INC"): + mm = re.search(r"^%s\s*\(\s*%s\s*\,\s*(.+)s*\,\s*(.+)\s*\)" % ("START_INC", variableNameUnRe), line) + + if found_end: + found_end = False + + if mm: + lines[i].command = "" + iterObjs.append(Incrementer(mm.group(1), tryStringEval(mm.group(4), lines[i], "start"), tryStringEval(mm.group(5), lines[i], "step"), lines[i])) + else: + raise ParseException(lines[i], "Incorrect parameters for START_INC! Expected: START_INC(, , )\n") + # If any incremeter has ended, pop the last object off the array. + elif line == "END_INC": + found_end = True + lines[i].command = "" + + try: + iterObjs.pop() + except: + raise ParseException(lines[i], "Did not find a corresponding 'START_INC'!") + # If there are any iterators active, scan the line and replace occurances of the name with it's value. + elif iterObjs: + for iterationObj in iterObjs: + mm = re.search(r"\b%s\b" % iterationObj.name, line) + + if mm: + lines[i].command = re.sub(r"\b%s\b" % iterationObj.name, str(iterationObj.iterationVal), lines[i].command) + iterationObj.increaseVal() + + if iterObjs: + raise ParseException(iterObjs[0].line, "Did not find a corresponding 'END_INC'!") #================================================================================================= class ArrayConcat(object): - def __init__(self, arrayToFill, declare, brackets, size, arraysToConcat, line): - self.line = line - self.arrayToFill = arrayToFill - self.declare = declare - self.size = size - self.brackets = brackets - self.arraysToConcat = arraysToConcat.split(",") - - def checkArraySize(self, origLineIdx, lines): - ''' If the concat function is used on a declared empty size array, the size of the array needs to be calculated. ''' - if self.declare: - if not self.brackets: - raise ParseException(self.line, "No array size given. Leave brackets [] empty to have the size auto generated.\n") - elif not self.size: - def findArrays(): - ''' Scan through the lines up to this point to find all the the arrays that have been chosen to be concatenated, - and from these add their number of elements to determine the total size needed. ''' - sizes = [] - arrayNameList = list(self.arraysToConcat) - - for i in range(origLineIdx): - lineText = lines[i].command.strip() - - if lineText.startswith("declare"): - for arr in arrayNameList: - try: # The regex doesn't like it when there are [] or () in the arr list. - mm = re.search(r"^declare\s+%s?%s\s*(\[.*\])" % (varPrefixRe, arr.strip()), lineText) - - if mm: - sizes.append(mm.group(1)) - arrayNameList.remove(arr) - break - except: - raise ParseException(lines[i], "Syntax error!\n") - if arrayNameList: # If everything was found, then the list will be empty. - raise ParseException(self.line, "Undeclared array(s) in %s function: %s!\n" % (concatSyntax, ', '.join(arrayNameList).strip())) - - return(simplifyAdditionString(re.sub(r"[\[\]]", "", '+'.join(sizes)))) - - self.size = findArrays() - - def getRawArrayDeclaration(self): - ''' Return the command that should replace line that triggered the concat. ''' - return("declare %s[%s]" % (self.arrayToFill, str(self.size))) - - def buildLines(self): - ''' Return all the lines needed to perfrom the concat. ''' - newLines = collections.deque() - numArgs = len(self.arraysToConcat) - - offsets = ["0"] - offsets.extend(["num_elements(%s)" % arrName for arrName in self.arraysToConcat]) - - addOffset = "" - - if numArgs != 1: - addOffset = " + concat_offset" - newLines.append(self.line.copy("concat_offset := 0")) - - offsetCommand = "concat_offset := concat_offset + #offset#" - templateText = [ - "for concat_it := 0 to num_elements(#arg#) - 1", - " #parent#[concat_it%s] := #arg#[concat_it]" % addOffset, - "end for"] - - for j in range(numArgs): - if j != 0 and numArgs != 1: - newLines.append(self.line.copy(offsetCommand.replace("#offset#", offsets[j]))) - - for text in templateText: - newLines.append(self.line.copy(text.replace("#arg#", self.arraysToConcat[j]).replace("#parent#", self.arrayToFill))) - - return(newLines) + def __init__(self, arrayToFill, declare, brackets, size, arraysToConcat, line): + self.line = line + self.arrayToFill = arrayToFill + self.declare = declare + self.size = size + self.brackets = brackets + self.arraysToConcat = arraysToConcat.split(",") + + def checkArraySize(self, origLineIdx, lines): + ''' If the concat function is used on a declared empty size array, the size of the array needs to be calculated. ''' + if self.declare: + if not self.brackets: + raise ParseException(self.line, "No array size given. Leave brackets [] empty to have the size auto generated.\n") + elif not self.size: + def findArrays(): + ''' Scan through the lines up to this point to find all the the arrays that have been chosen to be concatenated, + and from these add their number of elements to determine the total size needed. ''' + sizes = [] + arrayNameList = list(self.arraysToConcat) + + for i in range(origLineIdx): + lineText = lines[i].command.strip() + + if lineText.startswith("declare"): + for arr in arrayNameList: + try: # The regex doesn't like it when there are [] or () in the arr list. + mm = re.search(r"^declare\s+%s?%s\s*(\[.*\])" % (varPrefixRe, arr.strip()), lineText) + + if mm: + sizes.append(mm.group(1)) + arrayNameList.remove(arr) + break + except: + raise ParseException(lines[i], "Syntax error!\n") + if arrayNameList: # If everything was found, then the list will be empty. + raise ParseException(self.line, "Undeclared array(s) in %s function: %s!\n" % (concatSyntax, ', '.join(arrayNameList).strip())) + + return(simplifyAdditionString(re.sub(r"[\[\]]", "", '+'.join(sizes)))) + + self.size = findArrays() + + def getRawArrayDeclaration(self): + ''' Return the command that should replace line that triggered the concat. ''' + return("declare %s[%s]" % (self.arrayToFill, str(self.size))) + + def buildLines(self): + ''' Return all the lines needed to perfrom the concat. ''' + newLines = collections.deque() + numArgs = len(self.arraysToConcat) + + offsets = ["0"] + offsets.extend(["num_elements(%s)" % arrName for arrName in self.arraysToConcat]) + + addOffset = "" + + if numArgs != 1: + addOffset = " + concat_offset" + newLines.append(self.line.copy("concat_offset := 0")) + + offsetCommand = "concat_offset := concat_offset + #offset#" + templateText = [ + "for concat_it := 0 to num_elements(#arg#) - 1", + " #parent#[concat_it%s] := #arg#[concat_it]" % addOffset, + "end for"] + + for j in range(numArgs): + if j != 0 and numArgs != 1: + newLines.append(self.line.copy(offsetCommand.replace("#offset#", offsets[j]))) + + for text in templateText: + newLines.append(self.line.copy(text.replace("#arg#", self.arraysToConcat[j]).replace("#parent#", self.arrayToFill))) + + return(newLines) def handleArrayConcat(lines): - arrayConcatRe = r"(?P^\s*declare\s+)?%s\s*(?P\[(?P.*)\])?\s*:=\s*%s\s*\((?P[^\)]*)" % (variableNameRe, concatSyntax) - newLines = collections.deque() + arrayConcatRe = r"(?P^\s*declare\s+)?%s\s*(?P\[(?P.*)\])?\s*:=\s*%s\s*\((?P[^\)]*)" % (variableNameRe, concatSyntax) + newLines = collections.deque() - for lineIdx in range(len(lines)): - line = lines[lineIdx].command.strip() + for lineIdx in range(len(lines)): + line = lines[lineIdx].command.strip() - if "concat" in line: - m = re.search(arrayConcatRe, line) + if "concat" in line: + m = re.search(arrayConcatRe, line) - if m: - concatObj = ArrayConcat(m.group("whole"), m.group("declare"), m.group("brackets"), m.group("arraysize"), m.group("arraylist"), lines[lineIdx]) - concatObj.checkArraySize(lineIdx, lines) + if m: + concatObj = ArrayConcat(m.group("whole"), m.group("declare"), m.group("brackets"), m.group("arraysize"), m.group("arraylist"), lines[lineIdx]) + concatObj.checkArraySize(lineIdx, lines) - if m.group("declare"): - newLines.append(lines[lineIdx].copy(concatObj.getRawArrayDeclaration())) + if m.group("declare"): + newLines.append(lines[lineIdx].copy(concatObj.getRawArrayDeclaration())) - newLines.extend(concatObj.buildLines()) + newLines.extend(concatObj.buildLines()) - continue - # The variables needed are declared at the start of the init callback. - elif line.startswith("on"): - if re.search(initRe, line): - newLines.append(lines[lineIdx]) + continue + # The variables needed are declared at the start of the init callback. + elif line.startswith("on"): + if re.search(initRe, line): + newLines.append(lines[lineIdx]) - # Only add preprocessor variable if not previously declared - if not (any(l.command == "declare concat_i" for l in newLines) or any(l.command == "declare concat_offset" for l in newLines)): - newLines.append(lines[lineIdx].copy("declare concat_it")) - newLines.append(lines[lineIdx].copy("declare concat_offset")) + # Only add preprocessor variable if not previously declared + if not (any(l.command == "declare concat_i" for l in newLines) or any(l.command == "declare concat_offset" for l in newLines)): + newLines.append(lines[lineIdx].copy("declare concat_it")) + newLines.append(lines[lineIdx].copy("declare concat_offset")) - continue + continue - newLines.append(lines[lineIdx]) + newLines.append(lines[lineIdx]) - replaceLines(lines, newLines) + replaceLines(lines, newLines) #================================================================================================= class MultiDimensionalArray(object): - def __init__(self, name, prefix, dimensionsString, persistence, assignment, familyPrefix, line): - self.name = name - self.prefix = prefix or "" - self.assignment = assignment or "" - self.dimensions = utils.split_args(dimensionsString, line) - self.persistence = persistence or "" - self.rawArrayName = familyPrefix + "_" + self.name - - def getRawArrayDeclaration(self): - newName = self.prefix + "_" + self.name - totalArraySize = "*".join(["(" + dim + ")" for dim in self.dimensions]) - - return("declare %s %s [%s] %s" % (self.persistence, newName, totalArraySize, self.assignment)) - - def buildPropertyAndConstants(self, line): - propertyTemplate = [ - "property #propName#", - "function get(#dimList#) -> result", - "result := #rawArrayName#[#calculatedDimList#]", - "end function", - "function set(#dimList#, val)", - "#rawArrayName#[#calculatedDimList#] := val", - "end function ", - "end property"] - constTemplate = "declare const #name#.SIZE_D#dimNum# := #val#" - - newLines = collections.deque() - - # Build the declare const lines and add them to the newLines deque. - for dimNum, dimSize in enumerate(self.dimensions): - declareConstText = constTemplate \ - .replace("#name#", self.name) \ - .replace("#dimNum#", str(dimNum + 1)) \ - .replace("#val#", dimSize) - newLines.append(line.copy(declareConstText)) - - # Build the list of arguments, eg: "d1, d2, d3" - dimensionArgList = ["d" + str(dimNum + 1) for dimNum in range(len(self.dimensions))] - dimensionArgString = ",".join(dimensionArgList) - - # Create the maths for mapping multiple dimensions to a single dimension array, eg: "d1 * (20) + d2" - numDimensions = len(self.dimensions) - calculatedDimList = [] - - for dimNum in range(numDimensions - 1): - for i in range(numDimensions - 1, dimNum, -1): - calculatedDimList.append("(%s) * " % self.dimensions[i]) - - calculatedDimList.append(dimensionArgList[dimNum] + " + ") - - calculatedDimList.append(dimensionArgList[numDimensions - 1]) - calculatedDimensions = "".join(calculatedDimList) - - for propLine in propertyTemplate: - propertyText = propLine \ - .replace("#propName#", self.name) \ - .replace("#dimList#", dimensionArgString) \ - .replace("#rawArrayName#", self.rawArrayName) \ - .replace("#calculatedDimList#", calculatedDimensions) - newLines.append(line.copy(propertyText)) - - return(newLines) + def __init__(self, name, prefix, dimensionsString, persistence, assignment, familyPrefix, line): + self.name = name + self.prefix = prefix or "" + self.assignment = assignment or "" + self.dimensions = utils.split_args(dimensionsString, line) + self.persistence = persistence or "" + self.rawArrayName = familyPrefix + "_" + self.name + + def getRawArrayDeclaration(self): + newName = self.prefix + "_" + self.name + totalArraySize = "*".join(["(" + dim + ")" for dim in self.dimensions]) + + return("declare %s %s [%s] %s" % (self.persistence, newName, totalArraySize, self.assignment)) + + def buildPropertyAndConstants(self, line): + propertyTemplate = [ + "property #propName#", + "function get(#dimList#) -> result", + "result := #rawArrayName#[#calculatedDimList#]", + "end function", + "function set(#dimList#, val)", + "#rawArrayName#[#calculatedDimList#] := val", + "end function ", + "end property"] + constTemplate = "declare const #name#.SIZE_D#dimNum# := #val#" + + newLines = collections.deque() + + # Build the declare const lines and add them to the newLines deque. + for dimNum, dimSize in enumerate(self.dimensions): + declareConstText = constTemplate \ + .replace("#name#", self.name) \ + .replace("#dimNum#", str(dimNum + 1)) \ + .replace("#val#", dimSize) + newLines.append(line.copy(declareConstText)) + + # Build the list of arguments, eg: "d1, d2, d3" + dimensionArgList = ["d" + str(dimNum + 1) for dimNum in range(len(self.dimensions))] + dimensionArgString = ",".join(dimensionArgList) + + # Create the maths for mapping multiple dimensions to a single dimension array, eg: "d1 * (20) + d2" + numDimensions = len(self.dimensions) + calculatedDimList = [] + + for dimNum in range(numDimensions - 1): + for i in range(numDimensions - 1, dimNum, -1): + calculatedDimList.append("(%s) * " % self.dimensions[i]) + + calculatedDimList.append(dimensionArgList[dimNum] + " + ") + + calculatedDimList.append(dimensionArgList[numDimensions - 1]) + calculatedDimensions = "".join(calculatedDimList) + + for propLine in propertyTemplate: + propertyText = propLine \ + .replace("#propName#", self.name) \ + .replace("#dimList#", dimensionArgString) \ + .replace("#rawArrayName#", self.rawArrayName) \ + .replace("#calculatedDimList#", calculatedDimensions) + newLines.append(line.copy(propertyText)) + + return(newLines) # TODO: Check whether making this only init callback is ok. def handleMultidimensionalArrays(lines): - multipleDimensionsRe = r"\[(?P[^\]]+(?:\,[^\]]+)+)\]" # Match square brackets with 2 or more comma separated dimensions. - multidimensionalArrayRe = r"^declare\s+%s%s\s*%s(?P\s*:=.+)?$" % (persistenceRe, variableNameRe, multipleDimensionsRe) - - newLines = collections.deque() - famCount = 0 - initFlag = False - - for lineIdx in range(len(lines)): - line = lines[lineIdx].command.strip() - - if not initFlag: - if re.search(initRe, line): - initFlag = True - - newLines.append(lines[lineIdx]) - else: # Multidimensional arrays are only allowed in the init callback. - if re.search(endOnRe, line): - initFlag = False # In case there are other init CBs (Combine Duplciate Callbacks) - newLines.append(lines[lineIdx]) - else: - # If a multidim array is found, if necessary the family prefix is added and the lines needed for the property are added. - famCount = countFamily(line, famCount) - - if line.startswith("declare"): - m = re.search(multidimensionalArrayRe, line) - - if m: - famPrefix = "" - - if famCount != 0: - famPrefix = inspectFamilyState(lines, lineIdx) - - name = m.group("name") - multiDim = MultiDimensionalArray(name, \ - m.group("prefix"), \ - m.group("dimensions"), \ - m.group("persistence"), \ - m.group("assignment"), \ - famPrefix, \ - lines[lineIdx]) - newLines.append(lines[lineIdx].copy(multiDim.getRawArrayDeclaration())) - newLines.extend(multiDim.buildPropertyAndConstants(lines[lineIdx])) - else: - newLines.append(lines[lineIdx]) - else: - newLines.append(lines[lineIdx]) - - replaceLines(lines, newLines) + multipleDimensionsRe = r"\[(?P[^\]]+(?:\,[^\]]+)+)\]" # Match square brackets with 2 or more comma separated dimensions. + multidimensionalArrayRe = r"^declare\s+%s%s\s*%s(?P\s*:=.+)?$" % (persistenceRe, variableNameRe, multipleDimensionsRe) + + newLines = collections.deque() + famCount = 0 + initFlag = False + + for lineIdx in range(len(lines)): + line = lines[lineIdx].command.strip() + + if not initFlag: + if re.search(initRe, line): + initFlag = True + + newLines.append(lines[lineIdx]) + else: # Multidimensional arrays are only allowed in the init callback. + if re.search(endOnRe, line): + initFlag = False # In case there are other init CBs (Combine Duplciate Callbacks) + newLines.append(lines[lineIdx]) + else: + # If a multidim array is found, if necessary the family prefix is added and the lines needed for the property are added. + famCount = countFamily(line, famCount) + + if line.startswith("declare"): + m = re.search(multidimensionalArrayRe, line) + + if m: + famPrefix = "" + + if famCount != 0: + famPrefix = inspectFamilyState(lines, lineIdx) + + name = m.group("name") + multiDim = MultiDimensionalArray(name, \ + m.group("prefix"), \ + m.group("dimensions"), \ + m.group("persistence"), \ + m.group("assignment"), \ + famPrefix, \ + lines[lineIdx]) + newLines.append(lines[lineIdx].copy(multiDim.getRawArrayDeclaration())) + newLines.extend(multiDim.buildPropertyAndConstants(lines[lineIdx])) + else: + newLines.append(lines[lineIdx]) + else: + newLines.append(lines[lineIdx]) + + replaceLines(lines, newLines) #=========================================================================================== class UIPropertyTemplate: - def __init__(self, name, argString): - self.name = name - self.args = argString.replace(" ", "").split(",") + def __init__(self, name, argString): + self.name = name + self.args = argString.replace(" ", "").split(",") class UIPropertyFunction: - def __init__(self, functionType, args, line): - self.functionType = functionType - self.args = args[1:] + def __init__(self, functionType, args, line): + self.functionType = functionType + self.args = args[1:] - if len(self.args) > len(functionType.args): - raise ParseException(line, "Too many arguments! Maximum is %d, got %d.\n" % (len(functionType.args), len(self.args))) - elif len(self.args) == 0: - raise ParseException(line, "Function requires at least 2 arguments!\n") + if len(self.args) > len(functionType.args): + raise ParseException(line, "Too many arguments! Maximum is %d, got %d.\n" % (len(functionType.args), len(self.args))) + elif len(self.args) == 0: + raise ParseException(line, "Function requires at least 2 arguments!\n") - self.uiId = args[0] + self.uiId = args[0] - def buildUiPropertyLines(self, line): - ''' Return the set ui property commands, e.g. name -> par := val ''' - newLines = collections.deque() + def buildUiPropertyLines(self, line): + ''' Return the set ui property commands, e.g. name -> par := val ''' + newLines = collections.deque() - for argNum in range(len(self.args)): - newLines.append(line.copy("%s -> %s := %s" % (self.uiId, self.functionType.args[argNum], self.args[argNum]))) + for argNum in range(len(self.args)): + newLines.append(line.copy("%s -> %s := %s" % (self.uiId, self.functionType.args[argNum], self.args[argNum]))) - return(newLines) + return(newLines) def handleUIFunctions(lines): - # Templates for the functions. Note the ui-id as the first arg and the functions start - # with'set_' is assumed to be true later on. - uiControlPropertyFunctionTemplates = [ - "set_bounds(ui-id, x, y, width, height)", - "set_button_properties(ui-id, text, picture, text_alignment, font_type, textpos_y)", - "set_knob_properties(ui-id, text, default)", - "set_label_properties(ui-id, text, picture, text_alignment, font_type, textpos_y)", - "set_level_meter_properties(ui-id, bg_color, off_color, on_color, overload_color)", - "set_menu_properties(ui-id, picture, font_type, text_alignment, textpos_y)", - "set_slider_properties(ui-id, default, picture, mouse_behaviour)", - "set_switch_properties(ui-id, text, picture, text_alignment, font_type, textpos_y)", - "set_table_properties(ui-id, bar_color, zero_line_color)", - "set_text_edit_properties(ui-id, text, picture, text_alignment, font_type, textpos_y)", - "set_value_edit_properties(ui-id, text, font_type, textpos_y, show_arrows)", - "set_waveform_properties(ui-id, bar_color, zero_line_color, bg_color, bg_alpha, wave_color, wave_cursor_color, slicemarkers_color, wf_vis_mode)", - "set_wavetable2d_properties(ui-id, wt_zone, bg_color, bg_alpha, wave_color, wave_alpha, wave_end_color, wave_end_alpha)", - "set_wavetable3d_properties(ui-id, wt_zone, bg_color, bg_alpha, wavetable_color, wavetable_alpha, wavetable_end_color, wavetable_end_alpha, parallax_x, parallax_y)" ] - - # Use the template string above to build a list of UIProperyTemplate objects. - uiFuncs = [] - - for funcTemplate in uiControlPropertyFunctionTemplates: - m = re.search(r"^(?P[^\(]+)\(ui-id,(?P[^\)]+)", funcTemplate) - uiFuncs.append(UIPropertyTemplate(m.group("name"), m.group("args"))) - - newLines = collections.deque() - - for lineIdx in range(len(lines)): - line = lines[lineIdx].command.strip() - foundProp = False - - if line.startswith("set_"): - for func in uiFuncs: - if re.search(r"^%s\b" % func.name, line): - foundProp = True - paramString = line[line.find("(") + 1 : len(line) - 1].strip() - paramList = utils.split_args(paramString, lines[lineIdx]) - uiPropertyObj = UIPropertyFunction(func, paramList, lines[lineIdx]) - newLines.extend(uiPropertyObj.buildUiPropertyLines(lines[lineIdx])) - break - - if not foundProp: - newLines.append(lines[lineIdx]) - - replaceLines(lines, newLines) + # Templates for the functions. Note the ui-id as the first arg and the functions start + # with'set_' is assumed to be true later on. + uiControlPropertyFunctionTemplates = [ + "set_bounds(ui-id, x, y, width, height)", + "set_button_properties(ui-id, text, picture, text_alignment, font_type, textpos_y)", + "set_knob_properties(ui-id, text, default)", + "set_label_properties(ui-id, text, picture, text_alignment, font_type, textpos_y)", + "set_level_meter_properties(ui-id, bg_color, off_color, on_color, overload_color)", + "set_menu_properties(ui-id, picture, font_type, text_alignment, textpos_y)", + "set_slider_properties(ui-id, default, picture, mouse_behaviour)", + "set_switch_properties(ui-id, text, picture, text_alignment, font_type, textpos_y)", + "set_table_properties(ui-id, bar_color, zero_line_color)", + "set_text_edit_properties(ui-id, text, picture, text_alignment, font_type, textpos_y)", + "set_value_edit_properties(ui-id, text, font_type, textpos_y, show_arrows)", + "set_waveform_properties(ui-id, bar_color, zero_line_color, bg_color, bg_alpha, wave_color, wave_cursor_color, slicemarkers_color, wf_vis_mode)", + "set_wavetable2d_properties(ui-id, wt_zone, bg_color, bg_alpha, wave_color, wave_alpha, wave_end_color, wave_end_alpha)", + "set_wavetable3d_properties(ui-id, wt_zone, bg_color, bg_alpha, wavetable_color, wavetable_alpha, wavetable_end_color, wavetable_end_alpha, parallax_x, parallax_y)" ] + + # Use the template string above to build a list of UIProperyTemplate objects. + uiFuncs = [] + + for funcTemplate in uiControlPropertyFunctionTemplates: + m = re.search(r"^(?P[^\(]+)\(ui-id,(?P[^\)]+)", funcTemplate) + uiFuncs.append(UIPropertyTemplate(m.group("name"), m.group("args"))) + + newLines = collections.deque() + + for lineIdx in range(len(lines)): + line = lines[lineIdx].command.strip() + foundProp = False + + if line.startswith("set_"): + for func in uiFuncs: + if re.search(r"^%s\b" % func.name, line): + foundProp = True + paramString = line[line.find("(") + 1 : len(line) - 1].strip() + paramList = utils.split_args(paramString, lines[lineIdx]) + uiPropertyObj = UIPropertyFunction(func, paramList, lines[lineIdx]) + newLines.extend(uiPropertyObj.buildUiPropertyLines(lines[lineIdx])) + break + + if not foundProp: + newLines.append(lines[lineIdx]) + + replaceLines(lines, newLines) #================================================================================================= def handleSameLineDeclaration(lines): - ''' When a variable is declared and initialised on the same line, check to see if the value needs to be - moved over to the next line. ''' - newLines = collections.deque() - famCount = 0 + ''' When a variable is declared and initialised on the same line, check to see if the value needs to be + moved over to the next line. ''' + newLines = collections.deque() + famCount = 0 - for lineIdx in range(len(lines)): - line = lines[lineIdx].command.strip() - famCount = countFamily(line, famCount) + for lineIdx in range(len(lines)): + line = lines[lineIdx].command.strip() + famCount = countFamily(line, famCount) - if line.startswith("declare"): - m = re.search(r"^declare\s+(?:(polyphonic|global|local)\s+)*%s%s\s*:=" % (persistenceRe, variableNameRe), line) + if line.startswith("declare"): + m = re.search(r"^declare\s+(?:(polyphonic|global|local)\s+)*%s%s\s*:=" % (persistenceRe, variableNameRe), line) - if m and not re.search(r"\b%s\s*\(" % concatSyntax, line): - valueIsConstantInteger = False - value = line[line.find(":=") + 2 :] + if m and not re.search(r"\b%s\s*\(" % concatSyntax, line): + valueIsConstantInteger = False + value = line[line.find(":=") + 2 :] - if not re.search(stringOrPlaceholderRe, line): - try: - # Ideally this would check to see if the value is a Kontakt constant as those are valid inline as well... - eval(value) # Just used as a test to see if the the value is a constant. - valueIsConstantInteger = True - except: - pass + if not re.search(stringOrPlaceholderRe, line): + try: + # Ideally this would check to see if the value is a Kontakt constant as those are valid inline as well... + eval(value) # Just used as a test to see if the the value is a constant. + valueIsConstantInteger = True + except: + pass - if not valueIsConstantInteger: - preAssignmentText = line[: line.find(":=")] - variableName = m.group("name") + if not valueIsConstantInteger: + preAssignmentText = line[: line.find(":=")] + variableName = m.group("name") - if famCount != 0: - variableName = inspectFamilyState(lines, lineIdx) + variableName + if famCount != 0: + variableName = inspectFamilyState(lines, lineIdx) + variableName - newLines.append(lines[lineIdx].copy(preAssignmentText)) - newLines.append(lines[lineIdx].copy(variableName + " " + line[line.find(":=") :])) - continue + newLines.append(lines[lineIdx].copy(preAssignmentText)) + newLines.append(lines[lineIdx].copy(variableName + " " + line[line.find(":=") :])) + continue - newLines.append(lines[lineIdx]) + newLines.append(lines[lineIdx]) - replaceLines(lines, newLines) + replaceLines(lines, newLines) #================================================================================================= class ConstBlock(object): - def __init__(self, name): - self.name = name - self.memberValues = [] - self.memberNames = [] - self.previousVal = "-1" + def __init__(self, name): + self.name = name + self.memberValues = [] + self.memberNames = [] + self.previousVal = "-1" - def addMember(self, name, value): - ''' Add a constant number ''' - self.memberNames.append(name) - newVal = value + def addMember(self, name, value): + ''' Add a constant number ''' + self.memberNames.append(name) + newVal = value - if not value: - newVal = self.previousVal + "+1" + if not value: + newVal = self.previousVal + "+1" - newVal = simplifyAdditionString(newVal) - self.memberValues.append(newVal) - self.previousVal = newVal + newVal = simplifyAdditionString(newVal) + self.memberValues.append(newVal) + self.previousVal = newVal - def buildLines(self, line): - ''' Return the the commands for the whole const block. ''' - newLines = collections.deque() - newLines.append(line.copy("declare %s[%s] := (%s)" % (self.name, len(self.memberNames), ", ".join(self.memberValues)))) - newLines.append(line.copy("declare const %s.SIZE := %s" % (self.name, len(self.memberNames)))) + def buildLines(self, line): + ''' Return the the commands for the whole const block. ''' + newLines = collections.deque() + newLines.append(line.copy("declare %s[%s] := (%s)" % (self.name, len(self.memberNames), ", ".join(self.memberValues)))) + newLines.append(line.copy("declare const %s.SIZE := %s" % (self.name, len(self.memberNames)))) - for memNum in range(len(self.memberNames)): - newLines.append(line.copy("declare const %s.%s := %s" % (self.name, self.memberNames[memNum], self.memberValues[memNum]))) + for memNum in range(len(self.memberNames)): + newLines.append(line.copy("declare const %s.%s := %s" % (self.name, self.memberNames[memNum], self.memberValues[memNum]))) - return(newLines) + return(newLines) def handleConstBlock(lines): - constBlockStartRe = r"^const\s+%s$" % variableNameRe - constBlockEndRe = r"^end\s+const$" - constBlockMemberRe = r"^%s(?:$|\s*\:=\s*(?P.+))" % variableNameRe + constBlockStartRe = r"^const\s+%s$" % variableNameRe + constBlockEndRe = r"^end\s+const$" + constBlockMemberRe = r"^%s(?:$|\s*\:=\s*(?P.+))" % variableNameRe - newLines = collections.deque() - constBlockObj = None - inConstBlock = False + newLines = collections.deque() + constBlockObj = None + inConstBlock = False - for lineIdx in range(len(lines)): - line = lines[lineIdx].command.strip() + for lineIdx in range(len(lines)): + line = lines[lineIdx].command.strip() - if line.startswith("const"): - m = re.search(constBlockStartRe, line) + if line.startswith("const"): + m = re.search(constBlockStartRe, line) - if m: - constBlockObj = ConstBlock(m.group("name")) - inConstBlock = True - continue - elif line.startswith("end"): - if re.search(constBlockEndRe, line): - if constBlockObj.memberValues: - newLines.extend(constBlockObj.buildLines(lines[lineIdx])) + if m: + constBlockObj = ConstBlock(m.group("name")) + inConstBlock = True + continue + elif line.startswith("end"): + if re.search(constBlockEndRe, line): + if constBlockObj.memberValues: + newLines.extend(constBlockObj.buildLines(lines[lineIdx])) - inConstBlock = False + inConstBlock = False - continue - elif inConstBlock: - m = re.search(constBlockMemberRe, line) + continue + elif inConstBlock: + m = re.search(constBlockMemberRe, line) - if m: - constBlockObj.addMember(m.group("whole"), m.group("value")) - continue - elif not line.strip() == "": - raise ParseException(lines[lineIdx], "Syntax error: in a const block, list constant names and optionally assign them a constant value.") + if m: + constBlockObj.addMember(m.group("whole"), m.group("value")) + continue + elif not line.strip() == "": + raise ParseException(lines[lineIdx], "Syntax error: in a const block, list constant names and optionally assign them a constant value.") - newLines.append(lines[lineIdx]) + newLines.append(lines[lineIdx]) - replaceLines(lines, newLines) + replaceLines(lines, newLines) #================================================================================================= class ListBlock(object): - def __init__(self, name, size): - self.name = name - self.size = size or "" - self.isMultiDim = False + def __init__(self, name, size): + self.name = name + self.size = size or "" + self.isMultiDim = False - if size: - self.isMultiDim = "," in size + if size: + self.isMultiDim = "," in size - self.members = [] + self.members = [] - def addMember(self, command): - self.members.append(command) + def addMember(self, command): + self.members.append(command) - def buildLines(self, line): - ''' The list block just builds lines ready for the list function later on to interpret them. ''' - newLines = collections.deque() - newLines.append(line.copy("declare list %s[%s]" % (self.name, self.size))) + def buildLines(self, line): + ''' The list block just builds lines ready for the list function later on to interpret them. ''' + newLines = collections.deque() + newLines.append(line.copy("declare list %s[%s]" % (self.name, self.size))) - for memNum in range(len(self.members)): - memberName = self.members[memNum] + for memNum in range(len(self.members)): + memberName = self.members[memNum] - # If the member is a comma separated list, then we first need to assign the list to an array in kontakt. - if self.isMultiDim: - stringList = utils.split_args(memberName, line) + # If the member is a comma separated list, then we first need to assign the list to an array in kontakt. + if self.isMultiDim: + stringList = utils.split_args(memberName, line) - if len(stringList) != 1: - memberName = self.name + str(memNum) - newLines.append(line.copy("declare %s[] := (%s)" % (memberName, self.members[memNum]))) + if len(stringList) != 1: + memberName = self.name + str(memNum) + newLines.append(line.copy("declare %s[] := (%s)" % (memberName, self.members[memNum]))) - newLines.append(line.copy("list_add(%s, %s)" % (self.name, memberName))) + newLines.append(line.copy("list_add(%s, %s)" % (self.name, memberName))) - return(newLines) + return(newLines) def handleListBlocks(lines): - listBlockStartRe = r"^list\s*%s\s*(?:\[(?P%s)?\])?$" % (variableNameRe, variableOrInt) - listBlockEndRe = r"^end\s+list$" - newLines = collections.deque() - listBlockObj = None - isListBlock = False - - for lineIdx in range(len(lines)): - line = lines[lineIdx].command.strip() - m = re.search(listBlockStartRe, line) - - if m: - isListBlock = True - listBlockObj = ListBlock(m.group("whole"), m.group("size")) - elif isListBlock and not line == "": - if re.search(listBlockEndRe, line): - isListBlock = False - - if listBlockObj.members: - newLines.extend(listBlockObj.buildLines(lines[lineIdx])) - else: - listBlockObj.addMember(line) - else: - newLines.append(lines[lineIdx]) - - replaceLines(lines, newLines) + listBlockStartRe = r"^list\s*%s\s*(?:\[(?P%s)?\])?$" % (variableNameRe, variableOrInt) + listBlockEndRe = r"^end\s+list$" + newLines = collections.deque() + listBlockObj = None + isListBlock = False + + for lineIdx in range(len(lines)): + line = lines[lineIdx].command.strip() + m = re.search(listBlockStartRe, line) + + if m: + isListBlock = True + listBlockObj = ListBlock(m.group("whole"), m.group("size")) + elif isListBlock and not line == "": + if re.search(listBlockEndRe, line): + isListBlock = False + + if listBlockObj.members: + newLines.extend(listBlockObj.buildLines(lines[lineIdx])) + else: + listBlockObj.addMember(line) + else: + newLines.append(lines[lineIdx]) + + replaceLines(lines, newLines) #================================================================================================= class List(object): - def __init__(self, name, prefix, persistence, isMatrix, familyPrefix): - self.name = name - - if isMatrix: - self.name = "_%s" % self.name - - self.noUnderscoreName = name - self.prefix = prefix or "" - self.persistence = persistence or "" - self.isMatrix = isMatrix - self.familyPrefix = familyPrefix or "" - self.inc = "0" - self.sizeList = [] # If this is a matrix, the sizes of each element are stored. - - def getListDeclaration(self, line): - ''' This function returns the lines for a list declaration. Because the size of the list caluated based on how - many list_add() functions have been used, this function must be called after all list_add() are resolved. ''' - newLines = collections.deque() - - if not self.isMatrix: - newLines.append(line.copy("declare %s %s%s[%s]" % (self.persistence, self.prefix, self.name, self.inc))) - newLines.append(line.copy("declare const %s.SIZE := %s" % (self.noUnderscoreName, self.inc))) - else: - listMatrixTemplate = [ - "declare #list#.sizes[#size#] := (#sizeList#)", - "declare #list#.pos[#size#] := (#posList#)", - "property #list#", - "function get(d1, d2) -> result", - "result := _#list#[#list#.pos[d1] + d2]", - "end function", - "function set(d1, d2, val)", - "_#list#[#list#.pos[d1] + d2] := val", - "end function", - "end property"] - - newLines.append(line.copy("declare %s %s%s[%s]" % (self.persistence, self.prefix, self.name, self.inc))) - newLines.append(line.copy("declare const %s.SIZE := %s" % (self.noUnderscoreName, len(self.sizeList)))) - - sizeCounter = "0" - posList = ["0"] - - for i in range(len(self.sizeList) - 1): - sizeCounter = simplifyAdditionString("%s+%s" % (sizeCounter, self.sizeList[i])) - posList.append(sizeCounter) - - for text in listMatrixTemplate: - replacedText = text.replace("#list#", self.noUnderscoreName) \ - .replace("#sizeList#", ",".join(self.sizeList)) \ - .replace("#posList#", ",".join(posList)) \ - .replace("#size#", str(len(self.sizeList))) - newLines.append(line.copy(replacedText)) - - return(newLines) - - def increaseInc(self, value): - self.inc = simplifyAdditionString("%s+%s" % (self.inc, str(value))) - self.sizeList.append(str(value)) - - def getListAddLine(self, value, line): - ''' Return the line for single list add command. ''' - string = "%s[%s] := %s" % (self.familyPrefix + self.name, self.inc, value) - self.increaseInc(1) - - return(line.copy(string)) - - def getArrayListAddLines(self, value, line, arrayName, arraySize): - ''' This is called when an array is being added to a list with list_add. The lines necessary are returned. ''' - newLines = collections.deque() - addArrayToListTemplate = [ - "for list_it := 0 to #size# - 1", - "#list#[list_it + #offset#] := #arr#[list_it]", - "end for"] - - for templateLine in addArrayToListTemplate: - text = templateLine.replace("#size#", arraySize) \ - .replace("#list#", self.familyPrefix + self.name) \ - .replace("#offset#", self.inc) \ - .replace("#arr#", arrayName) - newLines.append(line.copy(text)) - - self.increaseInc(arraySize) - - return(newLines) + def __init__(self, name, prefix, persistence, isMatrix, familyPrefix): + self.name = name + + if isMatrix: + self.name = "_%s" % self.name + + self.noUnderscoreName = name + self.prefix = prefix or "" + self.persistence = persistence or "" + self.isMatrix = isMatrix + self.familyPrefix = familyPrefix or "" + self.inc = "0" + self.sizeList = [] # If this is a matrix, the sizes of each element are stored. + + def getListDeclaration(self, line): + ''' This function returns the lines for a list declaration. Because the size of the list caluated based on how + many list_add() functions have been used, this function must be called after all list_add() are resolved. ''' + newLines = collections.deque() + + if not self.isMatrix: + newLines.append(line.copy("declare %s %s%s[%s]" % (self.persistence, self.prefix, self.name, self.inc))) + newLines.append(line.copy("declare const %s.SIZE := %s" % (self.noUnderscoreName, self.inc))) + else: + listMatrixTemplate = [ + "declare #list#.sizes[#size#] := (#sizeList#)", + "declare #list#.pos[#size#] := (#posList#)", + "property #list#", + "function get(d1, d2) -> result", + "result := _#list#[#list#.pos[d1] + d2]", + "end function", + "function set(d1, d2, val)", + "_#list#[#list#.pos[d1] + d2] := val", + "end function", + "end property"] + + newLines.append(line.copy("declare %s %s%s[%s]" % (self.persistence, self.prefix, self.name, self.inc))) + newLines.append(line.copy("declare const %s.SIZE := %s" % (self.noUnderscoreName, len(self.sizeList)))) + + sizeCounter = "0" + posList = ["0"] + + for i in range(len(self.sizeList) - 1): + sizeCounter = simplifyAdditionString("%s+%s" % (sizeCounter, self.sizeList[i])) + posList.append(sizeCounter) + + for text in listMatrixTemplate: + replacedText = text.replace("#list#", self.noUnderscoreName) \ + .replace("#sizeList#", ",".join(self.sizeList)) \ + .replace("#posList#", ",".join(posList)) \ + .replace("#size#", str(len(self.sizeList))) + newLines.append(line.copy(replacedText)) + + return(newLines) + + def increaseInc(self, value): + self.inc = simplifyAdditionString("%s+%s" % (self.inc, str(value))) + self.sizeList.append(str(value)) + + def getListAddLine(self, value, line): + ''' Return the line for single list add command. ''' + string = "%s[%s] := %s" % (self.familyPrefix + self.name, self.inc, value) + self.increaseInc(1) + + return(line.copy(string)) + + def getArrayListAddLines(self, value, line, arrayName, arraySize): + ''' This is called when an array is being added to a list with list_add. The lines necessary are returned. ''' + newLines = collections.deque() + addArrayToListTemplate = [ + "for list_it := 0 to #size# - 1", + "#list#[list_it + #offset#] := #arr#[list_it]", + "end for"] + + for templateLine in addArrayToListTemplate: + text = templateLine.replace("#size#", arraySize) \ + .replace("#list#", self.familyPrefix + self.name) \ + .replace("#offset#", self.inc) \ + .replace("#arr#", arrayName) + newLines.append(line.copy(text)) + + self.increaseInc(arraySize) + + return(newLines) def handleLists(lines): - def findAllArrays(lines): - ''' Scan the all the lines and store arrays and their sizes. ''' - arrayNames = [] - arraySizes = [] - initFlag = False - - for i in range(len(lines)): - line = lines[i].command.strip() - - if initFlag == False: - if line.startswith("on"): - if re.search(initRe, line): - initFlag = True - else: - if line.startswith("end"): - if re.search(endOnRe, line): - break - - if line.startswith("declare"): - m = re.search(r"^declare\s+%s%s\s*(?:\[(%s)\])" % (persistenceRe, variableNameUnRe, variableOrInt), line) - - if m: - arrayNames.append(re.sub(varPrefixRe, "", m.group(2))) - arraySizes.append(m.group(5)) - - return (arrayNames, arraySizes) - - # The names and sizes are needed because the multidimensional lists need to use the sizes to calculate the total. - arrayNames, arraySizes = findAllArrays(lines) - - newLines = collections.deque() - lists = {} # The list names and list objects are stored in a dict for quick searching. - listAddRe = r"^list_add\s*\(\s*%s\s*,(?P.+)\)$" % variableNameRe - listDeclareRe = r"^\s*declare\s+%slist\s*%s\s*(?:\[(?P[^\]]+)?\])?" % (persistenceRe, variableNameRe) - listDeclareTag = "LIST=>" # A tag is left on the list declaration lines as these need to be resolved at the end. - isInInit = False - preInit = True - addInitVar = False - loopBlockCounter = 0 - famCount = 0 - - for lineIdx in range(len(lines)): - line = lines[lineIdx].command.strip() - - if isInInit == False: - if preInit: - if line.startswith("on"): - if re.search(initRe, line): - preInit = False - isInInit = True - addInitVar = True - - if line.startswith("list_add"): - if re.search(listAddRe, line): - raise ParseException(lines[lineIdx], "list_add() can only be used in the init callback!\n") - - newLines.append(lines[lineIdx]) - - if addInitVar: - if not any(l.command == "declare list_it" for l in newLines): # Only add preprocessor variable if not previously declared - newLines.append(lines[lineIdx].copy("declare list_it")) - - addInitVar = False - - continue - else: - # Check for the end of the init callback - if line.startswith("end"): - if re.search(endOnRe, line): - isInInit = False - preInit = True # In case there are multiple on init CBs (Combine Duplicate Callbacks) - newLines.append(lines[lineIdx]) - - continue - - def findLoop(lineText, loopCount): - ''' Check for any for, while or if statements. This is laid out like this for speed reasons. ''' - startVal = loopCount - - if lineText.startswith("for"): - if re.search(forRe, lineText): - loopCount += 1 - elif lineText.startswith("while"): - if re.search(whileRe, lineText): - loopCount += 1 - elif lineText.startswith("if"): - if re.search(ifRe, lineText): - loopCount += 1 - elif loopCount != 0: - if lineText.startswith("end"): - if re.search(endForRe, lineText): - loopCount -= 1 - elif re.search(endIfRe, lineText): - loopCount -= 1 - elif re.search(endWhileRe, lineText): - loopCount -= 1 - - return(loopCount, startVal != loopCount) - - shouldExit = False - loopBlockCounter, shouldExit = findLoop(line, loopBlockCounter) - - if shouldExit: - newLines.append(lines[lineIdx]) - continue - - famCount = countFamily(line, famCount) - - # Check for a list declaration - if line.startswith("declare"): - m = re.search(listDeclareRe, line) - - if m: - name = m.group("name") - famPre = "" - - if famCount != 0: - famPre = inspectFamilyState(lines, lineIdx) - - isMatrix = False - - if m.group("size"): - isMatrix = "," in m.group("size") - - listObj = List(name, m.group("prefix"), m.group("persistence"), isMatrix, famPre) - name = "%s%s" % (famPre, name) - lists[name] = listObj - newLines.append(lines[lineIdx].copy("%s%s" % (listDeclareTag, name))) # Mark this line as we will need to go back and fill in the declaration later. - - continue - - # Check for a list_add - elif line.startswith("list_add"): - m = re.search(listAddRe, line) - - if m: - name = m.group("name") - value = m.group("value").strip() - - try: - listObj = lists[name] - except KeyError: - raise ParseException(lines[lineIdx], "Undeclared list: %s!\n" % name) - - if listObj.isMatrix: - try: - arrayIdx = arrayNames.index(re.sub(varPrefixRe, "", value)) - newLines.extend(listObj.getArrayListAddLines(value, lines[lineIdx], arrayNames[arrayIdx], arraySizes[arrayIdx])) - except ValueError: - newLines.append(listObj.getListAddLine(value, lines[lineIdx])) - pass - else: - newLines.append(listObj.getListAddLine(value, lines[lineIdx])) + def findAllArrays(lines): + ''' Scan the all the lines and store arrays and their sizes. ''' + arrayNames = [] + arraySizes = [] + initFlag = False + + for i in range(len(lines)): + line = lines[i].command.strip() + + if initFlag == False: + if line.startswith("on"): + if re.search(initRe, line): + initFlag = True + else: + if line.startswith("end"): + if re.search(endOnRe, line): + break + + if line.startswith("declare"): + m = re.search(r"^declare\s+%s%s\s*(?:\[(%s)\])" % (persistenceRe, variableNameUnRe, variableOrInt), line) + + if m: + arrayNames.append(re.sub(varPrefixRe, "", m.group(2))) + arraySizes.append(m.group(5)) + + return (arrayNames, arraySizes) + + # The names and sizes are needed because the multidimensional lists need to use the sizes to calculate the total. + arrayNames, arraySizes = findAllArrays(lines) + + newLines = collections.deque() + lists = {} # The list names and list objects are stored in a dict for quick searching. + listAddRe = r"^list_add\s*\(\s*%s\s*,(?P.+)\)$" % variableNameRe + listDeclareRe = r"^\s*declare\s+%slist\s*%s\s*(?:\[(?P[^\]]+)?\])?" % (persistenceRe, variableNameRe) + listDeclareTag = "LIST=>" # A tag is left on the list declaration lines as these need to be resolved at the end. + isInInit = False + preInit = True + addInitVar = False + loopBlockCounter = 0 + famCount = 0 + + for lineIdx in range(len(lines)): + line = lines[lineIdx].command.strip() + + if isInInit == False: + if preInit: + if line.startswith("on"): + if re.search(initRe, line): + preInit = False + isInInit = True + addInitVar = True + + if line.startswith("list_add"): + if re.search(listAddRe, line): + raise ParseException(lines[lineIdx], "list_add() can only be used in the init callback!\n") + + newLines.append(lines[lineIdx]) + + if addInitVar: + if not any(l.command == "declare list_it" for l in newLines): # Only add preprocessor variable if not previously declared + newLines.append(lines[lineIdx].copy("declare list_it")) + + addInitVar = False + + continue + else: + # Check for the end of the init callback + if line.startswith("end"): + if re.search(endOnRe, line): + isInInit = False + preInit = True # In case there are multiple on init CBs (Combine Duplicate Callbacks) + newLines.append(lines[lineIdx]) + + continue + + def findLoop(lineText, loopCount): + ''' Check for any for, while or if statements. This is laid out like this for speed reasons. ''' + startVal = loopCount + + if lineText.startswith("for"): + if re.search(forRe, lineText): + loopCount += 1 + elif lineText.startswith("while"): + if re.search(whileRe, lineText): + loopCount += 1 + elif lineText.startswith("if"): + if re.search(ifRe, lineText): + loopCount += 1 + elif loopCount != 0: + if lineText.startswith("end"): + if re.search(endForRe, lineText): + loopCount -= 1 + elif re.search(endIfRe, lineText): + loopCount -= 1 + elif re.search(endWhileRe, lineText): + loopCount -= 1 + + return(loopCount, startVal != loopCount) + + shouldExit = False + loopBlockCounter, shouldExit = findLoop(line, loopBlockCounter) + + if shouldExit: + newLines.append(lines[lineIdx]) + continue + + famCount = countFamily(line, famCount) + + # Check for a list declaration + if line.startswith("declare"): + m = re.search(listDeclareRe, line) + + if m: + name = m.group("name") + famPre = "" + + if famCount != 0: + famPre = inspectFamilyState(lines, lineIdx) + + isMatrix = False + + if m.group("size"): + isMatrix = "," in m.group("size") + + listObj = List(name, m.group("prefix"), m.group("persistence"), isMatrix, famPre) + name = "%s%s" % (famPre, name) + lists[name] = listObj + newLines.append(lines[lineIdx].copy("%s%s" % (listDeclareTag, name))) # Mark this line as we will need to go back and fill in the declaration later. + + continue + + # Check for a list_add + elif line.startswith("list_add"): + m = re.search(listAddRe, line) + + if m: + name = m.group("name") + value = m.group("value").strip() + + try: + listObj = lists[name] + except KeyError: + raise ParseException(lines[lineIdx], "Undeclared list: %s!\n" % name) + + if listObj.isMatrix: + try: + arrayIdx = arrayNames.index(re.sub(varPrefixRe, "", value)) + newLines.extend(listObj.getArrayListAddLines(value, lines[lineIdx], arrayNames[arrayIdx], arraySizes[arrayIdx])) + except ValueError: + newLines.append(listObj.getListAddLine(value, lines[lineIdx])) + pass + else: + newLines.append(listObj.getListAddLine(value, lines[lineIdx])) - continue + continue - newLines.append(lines[lineIdx]) + newLines.append(lines[lineIdx]) - # Replace the list declartion tags with the actual values. - newerLines = collections.deque() - - for line in newLines: - if line.command.startswith(listDeclareTag): - listObj = lists[line.command[len(listDeclareTag) :]] - - if listObj.inc != "0": - newerLines.extend(lists[line.command[len(listDeclareTag) :]].getListDeclaration(line)) - else: - newerLines.append(line) + # Replace the list declartion tags with the actual values. + newerLines = collections.deque() + + for line in newLines: + if line.command.startswith(listDeclareTag): + listObj = lists[line.command[len(listDeclareTag) :]] + + if listObj.inc != "0": + newerLines.extend(lists[line.command[len(listDeclareTag) :]].getListDeclaration(line)) + else: + newerLines.append(line) - replaceLines(lines, newerLines) + replaceLines(lines, newerLines) #================================================================================================= def handleOpenSizeArrays(lines): - ''' When an array size is left with an open number of elements, use the list of initialisers to provide the array size. - Const variables are also generated for the array size. ''' - openArrayRe = r"^\s*declare\s+%s%s\s*\[\s*\]\s*:=\s*\(" % (persistenceRe, variableNameRe) - newLines = collections.deque() - - for lineIdx in range(len(lines)): - line = lines[lineIdx].command.strip() - m = re.search(openArrayRe, line) - - if m: - stringList = utils.split_args(line[line.find("(") + 1 : len(line) - 1], line) - numElements = len(stringList) - name = m.group("name") - newLines.append(lines[lineIdx].copy(line[: line.find("[") + 1] + str(numElements) + line[line.find("[") + 1 :])) - newLines.append(lines[lineIdx].copy("declare const %s.SIZE := %s" % (name, str(numElements)))) - else: - newLines.append(lines[lineIdx]) - - replaceLines(lines, newLines) + ''' When an array size is left with an open number of elements, use the list of initialisers to provide the array size. + Const variables are also generated for the array size. ''' + openArrayRe = r"^\s*declare\s+%s%s\s*\[\s*\]\s*:=\s*\(" % (persistenceRe, variableNameRe) + newLines = collections.deque() + + for lineIdx in range(len(lines)): + line = lines[lineIdx].command.strip() + m = re.search(openArrayRe, line) + + if m: + stringList = utils.split_args(line[line.find("(") + 1 : len(line) - 1], line) + numElements = len(stringList) + name = m.group("name") + newLines.append(lines[lineIdx].copy(line[: line.find("[") + 1] + str(numElements) + line[line.find("[") + 1 :])) + newLines.append(lines[lineIdx].copy("declare const %s.SIZE := %s" % (name, str(numElements)))) + else: + newLines.append(lines[lineIdx]) + + replaceLines(lines, newLines) #================================================================================================= def handleSanitizeExitCommand(lines): - newLines = collections.deque() + newLines = collections.deque() - for i in range(len(lines)): - line = lines[i].command.strip() + for i in range(len(lines)): + line = lines[i].command.strip() - if line.startswith("on"): - if re.search(initRe, line): - newLines.append(lines[i]) + if line.startswith("on"): + if re.search(initRe, line): + newLines.append(lines[i]) - if not any(l.command == "declare sksp_dummy" for l in newLines): # Only add preprocessor variable if not previously declared - newLines.append(lines[i].copy("declare sksp_dummy")) + if not any(l.command == "declare sksp_dummy" for l in newLines): # Only add preprocessor variable if not previously declared + newLines.append(lines[i].copy("declare sksp_dummy")) - continue + continue - if line == "exit": - newLines.append(lines[i].copy("sksp_dummy := sksp_dummy")) + if line == "exit": + newLines.append(lines[i].copy("sksp_dummy := sksp_dummy")) - newLines.append(lines[i]) + newLines.append(lines[i]) - replaceLines(lines, newLines) + replaceLines(lines, newLines) #================================================================================================= def handleStringArrayInitialisation(lines, placeholders): - ''' Convert the single-line list of strings to one string per line for Kontakt to understand. ''' - stringArrayRe = r"^declare\s+%s\s*\[(?P[^\]]+)\]\s*:=\s*\((?P.+)\)$" % variableNameRe - stringListRe = r"\s*%s(\s*,\s*%s)*\s*" % (stringOrPlaceholderRe, stringOrPlaceholderRe) - newLines = collections.deque() - famCount = 0 + ''' Convert the single-line list of strings to one string per line for Kontakt to understand. ''' + stringArrayRe = r"^declare\s+%s\s*\[(?P[^\]]+)\]\s*:=\s*\((?P.+)\)$" % variableNameRe + stringListRe = r"\s*%s(\s*,\s*%s)*\s*" % (stringOrPlaceholderRe, stringOrPlaceholderRe) + newLines = collections.deque() + famCount = 0 - for i in range(len(lines)): - line = lines[i].command.strip() - famCount = countFamily(line, famCount) + for i in range(len(lines)): + line = lines[i].command.strip() + famCount = countFamily(line, famCount) - if line.startswith("on"): - if re.search(initRe, line): - newLines.append(lines[i]) + if line.startswith("on"): + if re.search(initRe, line): + newLines.append(lines[i]) - if not any(l.command == "declare string_it" for l in newLines): # Only add preprocessor variable if not previously declared - newLines.append(lines[i].copy("declare string_it")) + if not any(l.command == "declare string_it" for l in newLines): # Only add preprocessor variable if not previously declared + newLines.append(lines[i].copy("declare string_it")) - continue + continue - if line.startswith("declare"): - m = re.search(stringArrayRe, line) + if line.startswith("declare"): + m = re.search(stringArrayRe, line) - if m: - if m.group("prefix") == "!": - if not re.search(stringListRe, m.group("initlist")): - raise ParseException(lines[i], "Expected integers, got strings!\n") + if m: + if m.group("prefix") == "!": + if not re.search(stringListRe, m.group("initlist")): + raise ParseException(lines[i], "Expected integers, got strings!\n") - stringList = utils.split_args(m.group("initlist"), lines[i]) - name = m.group("name") + stringList = utils.split_args(m.group("initlist"), lines[i]) + name = m.group("name") - if famCount != 0: - name = inspectFamilyState(lines, i) + name + if famCount != 0: + name = inspectFamilyState(lines, i) + name - newLines.append(lines[i].copy(line[: line.find(":")])) + newLines.append(lines[i].copy(line[: line.find(":")])) - if len(stringList) != 1: - for ii in range(len(stringList)): - strVal = "" + if len(stringList) != 1: + for ii in range(len(stringList)): + strVal = "" - # first check if we're dealing with a placeholder - check = re.match(r"(.*)(\{\d+\})(.*)", stringList[ii]) + # first check if we're dealing with a placeholder + check = re.match(r"(.*)(\{\d+\})(.*)", stringList[ii]) - # recreate the string from placeholders array - # including any possible prefix/postfix non-placeholders - # (i.e. string defines concatenated with regular strings) - if check: - strVal = str(check.group(1)) + placeholders[int(check.group(2)[1:-1])] + str(check.group(3)) + # recreate the string from placeholders array + # including any possible prefix/postfix non-placeholders + # (i.e. string defines concatenated with regular strings) + if check: + strVal = str(check.group(1)) + placeholders[int(check.group(2)[1:-1])] + str(check.group(3)) - # if it's an empty string don't add it to new lines - if strVal != '\"\"': - newLines.append(lines[i].copy("%s[%s] := %s" % (name, str(ii), stringList[ii]))) - else: - newLines.append(lines[i].copy("for string_it := 0 to %s - 1" % m.group("arraysize"))) - newLines.append(lines[i].copy("%s[string_it] := %s" % (name, "".join(stringList)))) - newLines.append(lines[i].copy("end for")) + # if it's an empty string don't add it to new lines + if strVal != '\"\"': + newLines.append(lines[i].copy("%s[%s] := %s" % (name, str(ii), stringList[ii]))) + else: + newLines.append(lines[i].copy("for string_it := 0 to %s - 1" % m.group("arraysize"))) + newLines.append(lines[i].copy("%s[string_it] := %s" % (name, "".join(stringList)))) + newLines.append(lines[i].copy("end for")) - continue + continue - newLines.append(lines[i]) + newLines.append(lines[i]) - replaceLines(lines, newLines) + replaceLines(lines, newLines) #================================================================================================= def handlePersistence(lines): - ''' Simply adds make_persistent() or read_perisitent_var() lines when the pers or read keywords are found. ''' - newLines = collections.deque() - famCount = 0 + ''' Simply adds make_persistent() or read_perisitent_var() lines when the pers or read keywords are found. ''' + newLines = collections.deque() + famCount = 0 - for i in range(len(lines)): - line = lines[i].command.strip() - famCount = countFamily(line, famCount) + for i in range(len(lines)): + line = lines[i].command.strip() + famCount = countFamily(line, famCount) - if line.startswith("declare"): - # The name of the variable is assumed to either be the first word before a [ or ( or before the end of the line - m = re.search(r"\b(?Ppers|instpers|read)\b" , line) + if line.startswith("declare"): + # The name of the variable is assumed to either be the first word before a [ or ( or before the end of the line + m = re.search(r"\b(?Ppers|instpers|read)\b" , line) - if m: - persWord = m.group("persistence") - m = re.search(nameInDeclareStmtRe, line) + if m: + persWord = m.group("persistence") + m = re.search(nameInDeclareStmtRe, line) - if m: - variableName = m.group("name") + if m: + variableName = m.group("name") - if famCount != 0: # Counting the family state is much faster than inspecting on every line. - famPre = inspectFamilyState(lines, i) + if famCount != 0: # Counting the family state is much faster than inspecting on every line. + famPre = inspectFamilyState(lines, i) - if famPre: - variableName = famPre + variableName.strip() + if famPre: + variableName = famPre + variableName.strip() - variableName = m.group("prefix") + variableName - newLines.append(lines[i].copy(re.sub(r"\b%s\b" % persWord, "", line))) + variableName = m.group("prefix") + variableName + newLines.append(lines[i].copy(re.sub(r"\b%s\b" % persWord, "", line))) - if persWord == "pers": - newLines.append(lines[i].copy("make_persistent(%s)" % variableName)) + if persWord == "pers": + newLines.append(lines[i].copy("make_persistent(%s)" % variableName)) - if persWord == "instpers": - newLines.append(lines[i].copy("make_instr_persistent(%s)" % variableName)) + if persWord == "instpers": + newLines.append(lines[i].copy("make_instr_persistent(%s)" % variableName)) - if persWord == "read": - newLines.append(lines[i].copy("make_persistent(%s)" % variableName)) - newLines.append(lines[i].copy("read_persistent_var(%s)" % variableName)) + if persWord == "read": + newLines.append(lines[i].copy("make_persistent(%s)" % variableName)) + newLines.append(lines[i].copy("read_persistent_var(%s)" % variableName)) - continue + continue - newLines.append(lines[i]) + newLines.append(lines[i]) - replaceLines(lines, newLines) + replaceLines(lines, newLines) #================================================================================================= class IterateMacro(object): - def __init__(self, macroName, minVal, maxVal, step, direction, line, placeholders): - self.line = line - self.macroName = macroName - self.isSingleLine = '#n#' in self.macroName - self.minVal = int(tryStringEval(minVal, line, 'min')) - self.maxVal = int(tryStringEval(maxVal, line, 'max')) - self.direction = direction - self.step = 1 - self.placeholders = placeholders - - if step: - self.step = int(tryStringEval(step, line, 'step')) - - def buildLines(self): - newLines = collections.deque() - offset = 1 - - if self.direction == 'downto': - self.step = -self.step - offset = -1 - - if not ((self.minVal > self.maxVal and self.direction == 'to') or (self.minVal < self.maxVal and self.direction == 'downto')): - if not self.isSingleLine: - for i in range(self.minVal, self.maxVal + offset, self.step): - newLines.append(self.line.copy('%s(%s)' % (self.macroName, str(i)))) - else: - for i in range(self.minVal, self.maxVal + offset, self.step): - l = Line(self.macroName) - l.replace_placeholders(placeholders=self.placeholders) - newLines.append(self.line.copy(l.command.replace('#n#', str(i)))) - - return(newLines) + def __init__(self, macroName, minVal, maxVal, step, direction, line, placeholders): + self.line = line + self.macroName = macroName + self.isSingleLine = '#n#' in self.macroName + self.minVal = int(tryStringEval(minVal, line, 'min')) + self.maxVal = int(tryStringEval(maxVal, line, 'max')) + self.direction = direction + self.step = 1 + self.placeholders = placeholders + + if step: + self.step = int(tryStringEval(step, line, 'step')) + + def buildLines(self): + newLines = collections.deque() + offset = 1 + + if self.direction == 'downto': + self.step = -self.step + offset = -1 + + if not ((self.minVal > self.maxVal and self.direction == 'to') or (self.minVal < self.maxVal and self.direction == 'downto')): + if not self.isSingleLine: + for i in range(self.minVal, self.maxVal + offset, self.step): + newLines.append(self.line.copy('%s(%s)' % (self.macroName, str(i)))) + else: + for i in range(self.minVal, self.maxVal + offset, self.step): + l = Line(self.macroName) + l.replace_placeholders(placeholders=self.placeholders) + newLines.append(self.line.copy(l.command.replace('#n#', str(i)))) + + return(newLines) def handleIterateMacro(lines, placeholders): - scan = False - newLines = collections.deque() + scan = False + newLines = collections.deque() - for l in lines: - command = l.command.strip() + for l in lines: + command = l.command.strip() - if command.startswith('iterate_macro'): - scan = True - m = re.search(r'^iterate_macro\s*\((?P.+)\)\s*:=\s*(?P.+)\b(?Pto|downto)(?P(?:.(?!\bstep\b))+)(?:\s+step\s+(?P.+))?$', - command) + if command.startswith('iterate_macro'): + scan = True + m = re.search(r'^iterate_macro\s*\((?P.+)\)\s*:=\s*(?P.+)\b(?Pto|downto)(?P(?:.(?!\bstep\b))+)(?:\s+step\s+(?P.+))?$', + command) - if m: - iterateObj = IterateMacro(m.group('macro'), m.group('min'), m.group('max'), m.group('step'), m.group('direction'), l, placeholders) - newLines.extend(iterateObj.buildLines()) - else: - raise ParseException(l, 'Syntax error in iterate_macro: incomplete or missing parameters!\n') - else: - newLines.append(l) + if m: + iterateObj = IterateMacro(m.group('macro'), m.group('min'), m.group('max'), m.group('step'), m.group('direction'), l, placeholders) + newLines.extend(iterateObj.buildLines()) + else: + raise ParseException(l, 'Syntax error in iterate_macro: incomplete or missing parameters!\n') + else: + newLines.append(l) - replaceLines(lines, newLines) + replaceLines(lines, newLines) - return scan + return scan #================================================================================================= def handleIteratePostMacro(lines, placeholders): - scan = False - newLines = collections.deque() + scan = False + newLines = collections.deque() - for l in lines: - command = l.command.strip() + for l in lines: + command = l.command.strip() - if command.startswith('iterate_post_macro'): - scan = True - m = re.search(r'^iterate_post_macro\s*\((?P.+)\)\s*:=\s*(?P.+)\b(?Pto|downto)(?P(?:.(?!\bstep\b))+)(?:\s+step\s+(?P.+))?$', - command) + if command.startswith('iterate_post_macro'): + scan = True + m = re.search(r'^iterate_post_macro\s*\((?P.+)\)\s*:=\s*(?P.+)\b(?Pto|downto)(?P(?:.(?!\bstep\b))+)(?:\s+step\s+(?P.+))?$', + command) - if m: - iterateObj = IterateMacro(m.group('macro'), m.group('min'), m.group('max'), m.group('step'), m.group('direction'), l, placeholders) - newLines.extend(iterateObj.buildLines()) - else: - newLines.append(l) - else: - newLines.append(l) + if m: + iterateObj = IterateMacro(m.group('macro'), m.group('min'), m.group('max'), m.group('step'), m.group('direction'), l, placeholders) + newLines.extend(iterateObj.buildLines()) + else: + newLines.append(l) + else: + newLines.append(l) - replaceLines(lines, newLines) + replaceLines(lines, newLines) - return scan + return scan #================================================================================================= def handleDefineLiterals(lines): - ''' Finds all define literals, and just replaces their occurances with the list of literals. ''' + ''' Finds all define literals, and just replaces their occurances with the list of literals. ''' - defineTitles = [] - defineValues = [] - defineLinePos = [] + defineTitles = [] + defineValues = [] + defineLinePos = [] - for index in range(len(lines)): - line = lines[index].command.strip() + for index in range(len(lines)): + line = lines[index].command.strip() - if line.startswith("define"): - if re.search(r"^define\s+literals\s+", line): - if re.search(r"^define\s+literals\s+" + variableNameUnRe + r"\s*:=", line): - textWithoutDefine = re.sub(r"^define\s+literals\s*", "", line) - colonBracketPos = textWithoutDefine.find(":=") + if line.startswith("define"): + if re.search(r"^define\s+literals\s+", line): + if re.search(r"^define\s+literals\s+" + variableNameUnRe + r"\s*:=", line): + textWithoutDefine = re.sub(r"^define\s+literals\s*", "", line) + colonBracketPos = textWithoutDefine.find(":=") - # before the assign operator is the title - title = textWithoutDefine[ : colonBracketPos].strip() - defineTitles.append(title) + # before the assign operator is the title + title = textWithoutDefine[ : colonBracketPos].strip() + defineTitles.append(title) - # after the assign operator is the value - value = textWithoutDefine[colonBracketPos + 2 : ].strip() - m = re.search(r"^\((([a-zA-Z_][a-zA-Z0-9_.]*)?(\s*,\s*[a-zA-Z_][a-zA-Z0-9_.]*)*)\)$", value) + # after the assign operator is the value + value = textWithoutDefine[colonBracketPos + 2 : ].strip() + m = re.search(r"^\((([a-zA-Z_][a-zA-Z0-9_.]*)?(\s*,\s*[a-zA-Z_][a-zA-Z0-9_.]*)*)\)$", value) - if not m: - raise ParseException(lines[index], "Syntax error in define literals: Comma-separated identifier list expected in parentheses.\n") + if not m: + raise ParseException(lines[index], "Syntax error in define literals: Comma-separated identifier list expected in parentheses.\n") - value = m.group(1) - value = ",".join([val.strip() for val in value.split(",")]) # remove whitespace + value = m.group(1) + value = ",".join([val.strip() for val in value.split(",")]) # remove whitespace - defineValues.append(value) - defineLinePos.append(index) + defineValues.append(value) + defineLinePos.append(index) - # remove the line - lines[index].command = re.sub(r'[^\r\n]', '', line) - else: - raise ParseException(lines[index], "Syntax error in define literals!\n") + # remove the line + lines[index].command = re.sub(r'[^\r\n]', '', line) + else: + raise ParseException(lines[index], "Syntax error in define literals!\n") - # if at least one define const exsists - if defineTitles: - # scan the code can replace any occurances of the variable with it's value - for lineObj in lines: - line = lineObj.command + # if at least one define const exsists + if defineTitles: + # scan the code can replace any occurances of the variable with it's value + for lineObj in lines: + line = lineObj.command - for index, item in enumerate(defineTitles): - if re.search(r"\b" + item + r"\b", line): - lineObj.command = lineObj.command.replace(item, str(defineValues[index])) + for index, item in enumerate(defineTitles): + if re.search(r"\b" + item + r"\b", line): + lineObj.command = lineObj.command.replace(item, str(defineValues[index])) #================================================================================================= def handleLiterateMacro(lines, placeholders): - scan = False - newLines = collections.deque() + scan = False + newLines = collections.deque() - for lineIdx in range(len(lines)): - line = lines[lineIdx].command.strip() + for lineIdx in range(len(lines)): + line = lines[lineIdx].command.strip() - if line.startswith("literate_macro"): - scan = True - m = re.search(r"^literate_macro\s*\((?P.+)\)\s+on\s+(?P.+)$", line) + if line.startswith("literate_macro"): + scan = True + m = re.search(r"^literate_macro\s*\((?P.+)\)\s+on\s+(?P.+)$", line) - if m: - name = m.group("macro") - targets = utils.split_args(m.group("target"), lines[lineIdx]) + if m: + name = m.group("macro") + targets = utils.split_args(m.group("target"), lines[lineIdx]) - if not "#l#" in name: - for text in targets: - newLines.append(lines[lineIdx].copy("%s(%s)" % (name, text))) - else: - for index, text in enumerate(targets): - l = Line(name) - l.replace_placeholders(placeholders=placeholders) - newLines.append(lines[lineIdx].copy(l.command.replace("#l#", text).replace("#n#", str(index)))) - continue - else: - raise ParseException(lines[lineIdx], "Syntax error in literate_macro: incomplete or missing parameters!\n") + if not "#l#" in name: + for text in targets: + newLines.append(lines[lineIdx].copy("%s(%s)" % (name, text))) + else: + for index, text in enumerate(targets): + l = Line(name) + l.replace_placeholders(placeholders=placeholders) + newLines.append(lines[lineIdx].copy(l.command.replace("#l#", text).replace("#n#", str(index)))) + continue + else: + raise ParseException(lines[lineIdx], "Syntax error in literate_macro: incomplete or missing parameters!\n") - newLines.append(lines[lineIdx]) + newLines.append(lines[lineIdx]) - replaceLines(lines, newLines) + replaceLines(lines, newLines) - return scan + return scan #================================================================================================= def handleLiteratePostMacro(lines, placeholders): - scan = False - newLines = collections.deque() + scan = False + newLines = collections.deque() - for lineIdx in range(len(lines)): - line = lines[lineIdx].command.strip() + for lineIdx in range(len(lines)): + line = lines[lineIdx].command.strip() - if line.startswith("literate_post_macro"): - scan = True - m = re.search(r"^literate_post_macro\s*\((?P.+)\)\s+on\s+(?P.+)$", line) + if line.startswith("literate_post_macro"): + scan = True + m = re.search(r"^literate_post_macro\s*\((?P.+)\)\s+on\s+(?P.+)$", line) - if m: - name = m.group("macro") - targets = utils.split_args(m.group("target"), lines[lineIdx]) + if m: + name = m.group("macro") + targets = utils.split_args(m.group("target"), lines[lineIdx]) - if not "#l#" in name: - for text in targets: - newLines.append(lines[lineIdx].copy("%s(%s)" % (name, text))) - else: - for index, text in enumerate(targets): - l = Line(name) - l.replace_placeholders(placeholders=placeholders) - newLines.append(lines[lineIdx].copy(l.command.replace("#l#", text).replace("#n#", str(index)))) - continue + if not "#l#" in name: + for text in targets: + newLines.append(lines[lineIdx].copy("%s(%s)" % (name, text))) + else: + for index, text in enumerate(targets): + l = Line(name) + l.replace_placeholders(placeholders=placeholders) + newLines.append(lines[lineIdx].copy(l.command.replace("#l#", text).replace("#n#", str(index)))) + continue - newLines.append(lines[lineIdx]) + newLines.append(lines[lineIdx]) - replaceLines(lines, newLines) + replaceLines(lines, newLines) - return scan + return scan #================================================================================================= class DefineConstant(object): - def __init__(self, name, value, argString, line): - self.name = name - self.value = value + def __init__(self, name, value, argString, line): + self.name = name + self.value = value - # In previous versions of the compiler the value had to wrapped in # symbols. - if self.value.startswith("#") and self.value.endswith("#") and not "#" in self.value[1 : len(self.value) - 1]: - self.value = self.value[1 : len(self.value) - 1] + # In previous versions of the compiler the value had to wrapped in # symbols. + if self.value.startswith("#") and self.value.endswith("#") and not "#" in self.value[1 : len(self.value) - 1]: + self.value = self.value[1 : len(self.value) - 1] - self.args = [] + self.args = [] - if argString: - self.args = utils.split_args(argString, line) + if argString: + self.args = utils.split_args(argString, line) - self.line = line + self.line = line - if re.search(r"\b%s\b" % self.name, self.value): - raise ParseException(self.line, "Define constant cannot call itself!") + if re.search(r"\b%s\b" % self.name, self.value): + raise ParseException(self.line, "Define constant cannot call itself!") - def getName(self): - return(self.name) + def getName(self): + return(self.name) - def getValue(self): - return(self.value) + def getValue(self): + return(self.value) - def setValue(self, val): - self.value = val + def setValue(self, val): + self.value = val - def evaluateValue(self): - '''Attempt to evaluate value as maths expression''' - # Try to evaluate the value of the define constant as a maths expression. - # But don't do it if the define value is a string (starting with ")! - newVal = self.value + def evaluateValue(self): + '''Attempt to evaluate value as maths expression''' + # Try to evaluate the value of the define constant as a maths expression. + # But don't do it if the define value is a string (starting with ")! + newVal = self.value - nonMath = ["\"", "\'"] - if not any(s in newVal for s in nonMath): - try: - val = re.sub(r"\bmod\b", "%", self.value) - newVal = str(stringEvaluator.eval(val)) - except: - pass + nonMath = ["\"", "\'"] + if not any(s in newVal for s in nonMath): + try: + val = re.sub(r"\bmod\b", "%", self.value) + newVal = str(stringEvaluator.eval(val)) + except: + pass - self.setValue(newVal) + self.setValue(newVal) - def substituteValue(self, command, listOfOtherDefines, line=None): - ''' Replace all occurances of the define constant in the given command with its value. ''' - newCommand = command + def substituteValue(self, command, listOfOtherDefines, line=None): + ''' Replace all occurances of the define constant in the given command with its value. ''' + newCommand = command - if self.name in command: - if not self.args: - newCommand = re.sub(r"\b%s\b" % self.name, self.value, command) - else: - lineObj = line or self.line + if self.name in command: + if not self.args: + newCommand = re.sub(r"\b%s\b" % self.name, self.value, command) + else: + lineObj = line or self.line - matchIt = re.finditer(r"\b%s\b" % self.name, command) + matchIt = re.finditer(r"\b%s\b" % self.name, command) - for match in matchIt: - # Parse the match - matchPos = match.start() - parenthCount = 0 - preBracketFlag = True # Flag to show when the first bracket is found. - foundString = [] + for match in matchIt: + # Parse the match + matchPos = match.start() + parenthCount = 0 + preBracketFlag = True # Flag to show when the first bracket is found. + foundString = [] - for char in command[matchPos:]: - if char == "(": - parenthCount += 1 - preBracketFlag = False - elif char == ")": - parenthCount -= 1 + for char in command[matchPos:]: + if char == "(": + parenthCount += 1 + preBracketFlag = False + elif char == ")": + parenthCount -= 1 - foundString.append(char) + foundString.append(char) - if parenthCount == 0 and preBracketFlag == False: - break + if parenthCount == 0 and preBracketFlag == False: + break - foundString = "".join(foundString) + foundString = "".join(foundString) - # Check whether the args are valid - openBracketPos = foundString.find("(") + # Check whether the args are valid + openBracketPos = foundString.find("(") - if openBracketPos == -1: - raise ParseException(lineObj, "No arguments found for define macro: %s!" % foundString) + if openBracketPos == -1: + raise ParseException(lineObj, "No arguments found for define macro: %s!" % foundString) - argsString = foundString[openBracketPos + 1 : len(foundString) - 1] - foundArgs = utils.split_args(argsString, lineObj) + argsString = foundString[openBracketPos + 1 : len(foundString) - 1] + foundArgs = utils.split_args(argsString, lineObj) - if len(foundArgs) != len(self.args): - # The number of args could be incorrect because there are other defines in the arg list, therefore first evaluate - # all other defines in the args. If still incorrect, raise an exception. - for defineObj in listOfOtherDefines: - argsString = defineObj.substituteValue(argsString, listOfOtherDefines) + if len(foundArgs) != len(self.args): + # The number of args could be incorrect because there are other defines in the arg list, therefore first evaluate + # all other defines in the args. If still incorrect, raise an exception. + for defineObj in listOfOtherDefines: + argsString = defineObj.substituteValue(argsString, listOfOtherDefines) - foundArgs = utils.split_args(argsString, lineObj) + foundArgs = utils.split_args(argsString, lineObj) - if len(foundArgs) != len(self.args): - raise ParseException(lineObj, "Incorrect number of arguments in define macro: %s! Expected %d, got %d.\n" % (foundString, len(self.args), len(foundArgs))) + if len(foundArgs) != len(self.args): + raise ParseException(lineObj, "Incorrect number of arguments in define macro: %s! Expected %d, got %d.\n" % (foundString, len(self.args), len(foundArgs))) - # Build the new value using the given args - newVal = self.value + # Build the new value using the given args + newVal = self.value - for argIdx, arg in enumerate(self.args): - if arg.startswith("#") and arg.endswith("#"): - newVal = re.sub(arg, foundArgs[argIdx], newVal) - else: - newVal = re.sub(r"\b%s\b" % arg, foundArgs[argIdx], newVal) + for argIdx, arg in enumerate(self.args): + if arg.startswith("#") and arg.endswith("#"): + newVal = re.sub(arg, foundArgs[argIdx], newVal) + else: + newVal = re.sub(r"\b%s\b" % arg, foundArgs[argIdx], newVal) - newCommand = newCommand.replace(foundString, newVal) + newCommand = newCommand.replace(foundString, newVal) - return(newCommand) + return(newCommand) def handleDefineConstants(lines, define_cache = None): - defineRe = r"^define\s+%s\s*(?:\((?P.+)\))?\s*:=(?P.+)$" % variableNameRe - defineAppendRe = r"^define\s+%s\s*(?:\((?P.+)\))?\s*\+=(?P.+)$" % variableNameRe - definePrependRe = r"^define\s+%s\s*(?:\((?P.+)\))?\s*=\+(?P.+)$" % variableNameRe - - if define_cache is not None: - defineConstants = define_cache - - for l in lines: - for dc in defineConstants: - l.command = dc.substituteValue(l.command, defineConstants, l) - else: - defineConstants = collections.deque() - - newLines = collections.deque() - defineNames = set() - - # Scan through all the lines to find define declarations. - for l in lines: - command = l.command.strip() - - if not command.startswith("define"): - newLines.append(l) - continue - - define_type = 'none' - m = re.search(defineRe, command) - - if m: - define_type = 'new' - - if define_type == 'none': - m = re.search(defineAppendRe, command) - - if m: - define_type = 'append' - - if define_type == 'none': - m = re.search(definePrependRe, command) - - if m: - define_type = 'prepend' - - if define_type == 'none': - newLines.append(l) - continue - - # count how many literals we have - num_literals = m.group("val").count(",") + 1 - - if num_literals > 1: - # add define for amount of entries in a literal define (.SIZE suffix) - defineSizeObj = DefineConstant(m.group("whole") + '.SIZE', str(num_literals), None, l) - defineConstants.append(defineSizeObj) - - # Create define and evaluate if legitimate - defineObj = None - existing = list(filter(lambda d: d.name == m.group("name"), defineConstants))\ - - if define_type == 'append' and len(existing) > 0: - # If appending to existing, remove existing and concatente - defineObj = DefineConstant(m.group("whole"), existing[0].value + ', ' + m.group("val").strip(), m.group("args"), l) - defineConstants.remove(existing[0]) - elif define_type == 'prepend' and len(existing) > 0: - # If appending to existing, remove existing and concatente - defineObj = DefineConstant(m.group("whole"), m.group("val").strip() + ', ' + existing[0].value, m.group("args"), l) - defineConstants.remove(existing[0]) - elif define_type == 'new' and len(existing) > 0: - # If new and exists already, raise Exception - if existing[0].value != m.group("val").strip(): - raise ParseException(l, "Define constant was already declared!") - else: - # All other cases, create a new define - defineObj = DefineConstant(m.group("whole"), m.group("val").strip(), m.group("args"), l) - - if defineObj: - defineConstants.append(defineObj) - - if defineConstants: - # Replace all occurences where other defines are used in define values - do it a few times to catch some deeper nested defines. - if define_cache is None: - for n in range(0, 3): - for dc_i in defineConstants: - for dc_j in defineConstants: - dc_i.setValue(dc_j.substituteValue(dc_i.getValue(), defineConstants)) - - dc_i.evaluateValue() - - for l in newLines: - for dc in defineConstants: - l.command = dc.substituteValue(l.command, defineConstants, l) - - replaceLines(lines, newLines) - - return defineConstants + defineRe = r"^define\s+%s\s*(?:\((?P.+)\))?\s*:=(?P.+)$" % variableNameRe + defineAppendRe = r"^define\s+%s\s*(?:\((?P.+)\))?\s*\+=(?P.+)$" % variableNameRe + definePrependRe = r"^define\s+%s\s*(?:\((?P.+)\))?\s*=\+(?P.+)$" % variableNameRe + + if define_cache is not None: + defineConstants = define_cache + + for l in lines: + for dc in defineConstants: + l.command = dc.substituteValue(l.command, defineConstants, l) + else: + defineConstants = collections.deque() + + newLines = collections.deque() + defineNames = set() + + # Scan through all the lines to find define declarations. + for l in lines: + command = l.command.strip() + + if not command.startswith("define"): + newLines.append(l) + continue + + define_type = 'none' + m = re.search(defineRe, command) + if m: + define_type = 'new' + + if define_type == 'none': + m = re.search(defineAppendRe, command) + if m: + define_type = 'append' + + if define_type == 'none': + m = re.search(definePrependRe, command) + if m: + define_type = 'prepend' + + if define_type == 'none': + newLines.append(l) + continue + + # count how many literals we have + num_literals = m.group("val").count(",") + 1 + + if num_literals > 1: + # add define for amount of entries in a literal define (.SIZE suffix) + defineSizeObj = DefineConstant(m.group("whole") + '.SIZE', str(num_literals), None, l) + defineConstants.append(defineSizeObj) + + # Create define and evaluate if legitimate + defineObj = None + existing = list(filter(lambda d: d.name == m.group("name"), defineConstants))\ + + if define_type == 'append' and len(existing) > 0: + # If appending to existing, remove existing and concatente + defineObj = DefineConstant(m.group("whole"), existing[0].value + ', ' + m.group("val").strip(), m.group("args"), l) + defineConstants.remove(existing[0]) + elif define_type == 'prepend' and len(existing) > 0: + # If appending to existing, remove existing and concatente + defineObj = DefineConstant(m.group("whole"), m.group("val").strip() + ', ' + existing[0].value, m.group("args"), l) + defineConstants.remove(existing[0]) + elif define_type == 'new' and len(existing) > 0: + # If new and exists already, raise Exception + if existing[0].value != m.group("val").strip(): + raise ParseException(l, "Define constant was already declared!") + else: + # All other cases, create a new define + defineObj = DefineConstant(m.group("whole"), m.group("val").strip(), m.group("args"), l) + + if defineObj: + defineConstants.append(defineObj) + + if defineConstants: + # Replace all occurences where other defines are used in define values - do it a few times to catch some deeper nested defines. + if define_cache is None: + for n in range(0, 3): + for dc_i in defineConstants: + for dc_j in defineConstants: + dc_i.setValue(dc_j.substituteValue(dc_i.getValue(), defineConstants)) + + dc_i.evaluateValue() + + for l in newLines: + for dc in defineConstants: + l.command = dc.substituteValue(l.command, defineConstants, l) + + replaceLines(lines, newLines) + + return defineConstants def createBuiltinDefines(lines): - # Create date-time variables + # Create date-time variables - timecodes = ['%S', '%M', '%H', '%I', '%p', '%d', '%m', '%Y', '%y', '%B', '%b', '%x', '%X'] - timenames = ['__SEC__','__MIN__','__HOUR__','__HOUR12__','__AMPM__','__DAY__','__MONTH__','__YEAR__','__YEAR2__','__LOCALE_MONTH__','__LOCALE_MONTH_ABBR__','__LOCALE_DATE__','__LOCALE_TIME__'] - defines = ['define {0} := \"{1}\"'.format(timenames[i], strftime(timecodes[i], localtime())) for i in range(len(timecodes))] + timecodes = ['%S', '%M', '%H', '%I', '%p', '%d', '%m', '%Y', '%y', '%B', '%b', '%x', '%X'] + timenames = ['__SEC__','__MIN__','__HOUR__','__HOUR12__','__AMPM__','__DAY__','__MONTH__','__YEAR__','__YEAR2__','__LOCALE_MONTH__','__LOCALE_MONTH_ABBR__','__LOCALE_DATE__','__LOCALE_TIME__'] + defines = ['define {0} := \"{1}\"'.format(timenames[i], strftime(timecodes[i], localtime())) for i in range(len(timecodes))] - newLines = collections.deque() + newLines = collections.deque() - # append our defines on top of the script in a temporary deque - for string in defines: - newLines.append(lines[0].copy(string)) + # append our defines on top of the script in a temporary deque + for string in defines: + newLines.append(lines[0].copy(string)) - # merge with the original unmodified script - for line in lines: - newLines.append(line) + # merge with the original unmodified script + for line in lines: + newLines.append(line) - # replace original deque with modified one - replaceLines(lines, newLines) + # replace original deque with modified one + replaceLines(lines, newLines) #================================================================================================= class UIArray(object): - def __init__(self, name, uiType, size, persistence, familyPrefix, uiParams, tableSize, prefixSymbol, line): - self.name = name - self.familyPrefix = familyPrefix or "" - self.uiType = uiType - self.prefixSymbol = prefixSymbol + def __init__(self, name, uiType, size, persistence, familyPrefix, uiParams, tableSize, prefixSymbol, line): + self.name = name + self.familyPrefix = familyPrefix or "" + self.uiType = uiType + self.prefixSymbol = prefixSymbol - if self.uiType == "ui_text_edit": - self.prefixSymbol = "@" + if self.uiType == "ui_text_edit": + self.prefixSymbol = "@" - self.uiParams = uiParams or "" - self.numElements = size - self.dimensionsString = size - self.underscore = "" + self.uiParams = uiParams or "" + self.numElements = size + self.dimensionsString = size + self.underscore = "" - if "," in size: - self.underscore = "_" - self.numElements = "*".join(["(%s)" % dim for dim in size.split(",")]) + if "," in size: + self.underscore = "_" + self.numElements = "*".join(["(%s)" % dim for dim in size.split(",")]) - self.numElements = tryStringEval(self.numElements, line, "UI array size") - self.persistence = persistence or "" - self.tableSize = tableSize + self.numElements = tryStringEval(self.numElements, line, "UI array size") + self.persistence = persistence or "" + self.tableSize = tableSize - def getRawArrayDeclaration(self): - ''' Get the command string for declaring the raw ID array. ''' - return("declare %s[%s]" % (self.name, self.dimensionsString)) + def getRawArrayDeclaration(self): + ''' Get the command string for declaring the raw ID array. ''' + return("declare %s[%s]" % (self.name, self.dimensionsString)) - def buildLines(self, line): - ''' Return the deque of lines for the ui declaration (just a load of declare ui and get_ui_id()). ''' - newLines = collections.deque() + def buildLines(self, line): + ''' Return the deque of lines for the ui declaration (just a load of declare ui and get_ui_id()). ''' + newLines = collections.deque() - for i in range(self.numElements): - uiName = self.underscore + self.name + for i in range(self.numElements): + uiName = self.underscore + self.name - if self.uiType == "ui_table" or self.uiType == "ui_xy": - text = "declare %s %s %s %s" % (self.persistence, self.uiType, self.prefixSymbol + uiName + str(i), self.tableSize + self.uiParams) - else: - text = "declare %s %s %s %s" % (self.persistence, self.uiType, self.prefixSymbol + uiName + str(i), self.uiParams) + if self.uiType == "ui_table" or self.uiType == "ui_xy": + text = "declare %s %s %s %s" % (self.persistence, self.uiType, self.prefixSymbol + uiName + str(i), self.tableSize + self.uiParams) + else: + text = "declare %s %s %s %s" % (self.persistence, self.uiType, self.prefixSymbol + uiName + str(i), self.uiParams) - newLines.append(line.copy(text)) + newLines.append(line.copy(text)) - newLines.append(line.copy("for preproc_i := 0 to %s" % (self.numElements - 1))) - newLines.append(line.copy("%s[preproc_i] := get_ui_id(%s) + preproc_i" % (self.familyPrefix + uiName, self.familyPrefix + uiName + '0'))) - newLines.append(line.copy("end for")) + newLines.append(line.copy("for preproc_i := 0 to %s" % (self.numElements - 1))) + newLines.append(line.copy("%s[preproc_i] := get_ui_id(%s) + preproc_i" % (self.familyPrefix + uiName, self.familyPrefix + uiName + '0'))) + newLines.append(line.copy("end for")) - return(newLines) + return(newLines) def handleUIArrays(lines): - uiTypeRe = r"\b(?Pui_\w*)\b" - uiArrayRe = r"^declare\s+%s%s\s+%s\s*\[(?P[^\]]+)\]\s*(?P\[[^\]]+\]\s*)?(?P\(.*)?" % (persistenceRe, uiTypeRe, variableNameRe) - uiArrayTypeFirstRe = r"^declare\s+%s\s+%s\s*%s\s*\[(?P[^\]]+)\]\s*(?P\[[^\]]+\]\s*)?(?P\(.*)?" % (uiTypeRe, persistenceRe, variableNameRe) - newLines = collections.deque() - famCount = 0 - - for lineNum in range(len(lines)): - line = lines[lineNum].command.strip() - famCount = countFamily(line, famCount) - - if line.startswith("on"): - if re.search(initRe, line): - newLines.append(lines[lineNum]) - - if not any(l.command == "declare preproc_i" for l in newLines): # Only add preprocessor variable if not previously declared - newLines.append(lines[lineNum].copy("declare preproc_i")) - - continue - elif line.startswith("decl"): - m = re.search(uiArrayRe, line) - n = re.search(uiArrayTypeFirstRe, line) - r = m if m else n - - if r: - uiType = r.group("uitype") - famPre = None - - if famCount != 0: - famPre = inspectFamilyState(lines, lineNum) - - if ((uiType == "ui_table" or uiType == "ui_xy") and r.group("tablesize")) or (uiType != "ui_table" and uiType != "ui_xy"): - arrayObj = UIArray(r.group("name"), - uiType, - r.group("arraysize"), - r.group("persistence"), - famPre, - r.group("uiparams"), - r.group("tablesize"), - r.group("prefix"), - lines[lineNum]) - newLines.append(lines[lineNum].copy(arrayObj.getRawArrayDeclaration())) - newLines.extend(arrayObj.buildLines(lines[lineNum])) - - continue - - newLines.append(lines[lineNum]) - - replaceLines(lines, newLines) + uiTypeRe = r"\b(?Pui_\w*)\b" + uiArrayRe = r"^declare\s+%s%s\s+%s\s*\[(?P[^\]]+)\]\s*(?P\[[^\]]+\]\s*)?(?P\(.*)?" % (persistenceRe, uiTypeRe, variableNameRe) + uiArrayTypeFirstRe = r"^declare\s+%s\s+%s\s*%s\s*\[(?P[^\]]+)\]\s*(?P\[[^\]]+\]\s*)?(?P\(.*)?" % (uiTypeRe, persistenceRe, variableNameRe) + newLines = collections.deque() + famCount = 0 + + for lineNum in range(len(lines)): + line = lines[lineNum].command.strip() + famCount = countFamily(line, famCount) + + if line.startswith("on"): + if re.search(initRe, line): + newLines.append(lines[lineNum]) + + if not any(l.command == "declare preproc_i" for l in newLines): # Only add preprocessor variable if not previously declared + newLines.append(lines[lineNum].copy("declare preproc_i")) + + continue + elif line.startswith("decl"): + m = re.search(uiArrayRe, line) + n = re.search(uiArrayTypeFirstRe, line) + r = m if m else n + + if r: + uiType = r.group("uitype") + famPre = None + + if famCount != 0: + famPre = inspectFamilyState(lines, lineNum) + + if ((uiType == "ui_table" or uiType == "ui_xy") and r.group("tablesize")) or (uiType != "ui_table" and uiType != "ui_xy"): + arrayObj = UIArray(r.group("name"), + uiType, + r.group("arraysize"), + r.group("persistence"), + famPre, + r.group("uiparams"), + r.group("tablesize"), + r.group("prefix"), + lines[lineNum]) + newLines.append(lines[lineNum].copy(arrayObj.getRawArrayDeclaration())) + newLines.extend(arrayObj.buildLines(lines[lineNum])) + + continue + + newLines.append(lines[lineNum]) + + replaceLines(lines, newLines) diff --git a/snippets/on note_controller.sublime-snippet b/snippets/on note_controller.sublime-snippet new file mode 100644 index 0000000..ea25923 --- /dev/null +++ b/snippets/on note_controller.sublime-snippet @@ -0,0 +1,10 @@ + + + note controller callback + nccb + source.ksp +